Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/tall-buckets-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@boostv/process-optimizer-frontend-core': minor
'@boostv/process-optimizer-frontend-ui': minor
---

Add migration 18 that renames score variables for multiobjective (quality, cost)
4 changes: 3 additions & 1 deletion packages/core/src/common/types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { z } from 'zod'
// Change the current version when doing structural
// changes to any types belonging to ExperimentType

export const currentVersion = '17'
export const currentVersion = '18'

export const scoreName = 'Quality (0-5)'
export const defaultScoreName = 'Score (0-5)'
export const scoreNames = [scoreName, 'Cost (0-5)']

const infoSchema = z.object({
name: z.string(),
Expand Down
102 changes: 102 additions & 0 deletions packages/core/src/common/util/migration/data-formats/18.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
{
"id": "1234",
"changedSinceLastEvaluation": true,
"lastEvaluationHash": "never-calculated",
"info": {
"dataFormatVersion": "18",
"swVersion": "v1.2.0-16",
"name": "Cake",
"description": "Yummy",
"version": 0,
"extras": {}
},
"categoricalVariables": [
{
"name": "Icing",
"description": "Sugary",
"options": ["White", "Brown"],
"enabled": true
}
],
"valueVariables": [
{
"name": "name1",
"description": "desc1",
"min": 10,
"max": 100,
"type": "discrete",
"enabled": true
},
{
"name": "name2",
"description": "desc2",
"min": 10.2,
"max": 100.3,
"type": "continuous",
"enabled": true
}
],
"scoreVariables": [
{
"name": "Quality (0-5)",
"description": "Quality (0-5)",
"enabled": true
}
],
"constraints": [
{
"type": "sum",
"dimensions": [],
"value": 0
}
],
"optimizerConfig": {
"baseEstimator": "GP",
"acqFunc": "EI",
"initialPoints": 3,
"kappa": 1.96,
"xi": 0.01
},
"results": {
"id": "",
"next": [[]],
"plots": [],
"pickled": "",
"expectedMinimum": [],
"extras": {}
},
"dataPoints": [
{
"meta": {
"enabled": true,
"valid": true,
"id": 1
},
"data": [
{
"type": "categorical",
"name": "Icing",
"value": "Brown"
},
{
"type": "numeric",
"name": "name1",
"value": 10
},
{
"type": "numeric",
"name": "name2",
"value": 10.2
},
{
"type": "score",
"name": "Quality (0-5)",
"value": 0.5
}
]
}
],
"extras": {
"experimentSuggestionCount": 1
}
}
128 changes: 124 additions & 4 deletions packages/core/src/common/util/migration/migration.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest'
import { JSONSchemaFaker } from 'json-schema-faker'
import { migrate, _migrate, MIGRATIONS } from './migration'
import version18 from './data-formats/18.json'
import version17 from './data-formats/17.json'
import version16 from './data-formats/16.json'
import version3 from './data-formats/3.json'
Expand All @@ -18,9 +19,10 @@ import {
ScoreVariableType,
experimentSchema,
scoreName,
scoreNames,
} from '@core/common/types'
import { storeLatestSchema, loadTestData } from './test-utils'
import { migrateToV17 } from './migrations/migrateToV17'
import { migrateToV17, migrateToV18 } from './migrations'

describe('Migration of data format', () => {
storeLatestSchema()
Expand Down Expand Up @@ -170,7 +172,7 @@ describe('Migration of data format', () => {
{
name: 'score2',
description: 'score',
enabled: true,
enabled: false,
},
] satisfies ScoreVariableType[]

Expand Down Expand Up @@ -264,14 +266,132 @@ describe('Migration of data format', () => {
)
})

// rename scores to [scoreName, scoreName 2...] from array of names
describe('migrateToV18', () => {
const scoreVarsMultiObjective = [
{
name: 'Quality (0-5)',
description: 'Quality (0-5)',
enabled: true,
},
{
name: 'Quality (0-5) 2',
description: 'Quality (0-5) 2',
enabled: true,
},
] satisfies ScoreVariableType[]

const scoreVarsMultiObjectiveDisabled = [
{
name: 'Quality (0-5)',
description: 'Quality (0-5)',
enabled: true,
},
{
name: 'Quality (0-5) 2',
description: 'Quality (0-5) 2',
enabled: false,
},
] satisfies ScoreVariableType[]

it.each([
['multiobjective, all enabled', scoreVarsMultiObjective],
['multiobjective, one disabled', scoreVarsMultiObjectiveDisabled],
])('scoreVariables should be renamed, %s', (_, scoreVariables) => {
const experiment17 = {
...version17,
scoreVariables,
} as unknown as ExperimentType
expect(migrateToV18(experiment17).scoreVariables).toEqual([
{
name: scoreNames[0],
description: scoreNames[0],
enabled: scoreVariables[0]?.enabled,
},
{
name: scoreNames[1],
description: scoreNames[1],
enabled: scoreVariables[1]?.enabled,
},
])
})

const dataPointsSingle = [...version17.dataPoints]
const dataPointsMulti = [...version17.dataPoints].map(dp => ({
...dp,
data: [...dp.data].concat([
{
type: 'score',
name: 'Quality (0-5) 2',
value: 2,
},
]),
}))

it.each([
['one score exists', dataPointsSingle, false],
['two scores exist', dataPointsMulti, true],
])(
'data points should be renamed, %s',
(_, dataPoints, isMultiObjective) => {
const experiment17 = {
...version17,
dataPoints,
} as unknown as ExperimentType
const actual = [
{
meta: {
enabled: true,
valid: true,
id: 1,
},
data: [
{
type: 'categorical',
name: 'Icing',
value: 'Brown',
},
{
type: 'numeric',
name: 'name1',
value: 10,
},
{
type: 'numeric',
name: 'name2',
value: 10.2,
},
{
type: 'score',
name: scoreNames[0],
value: 0.5,
},
].concat(
isMultiObjective
? [
{
type: 'score',
name: scoreNames[1],
value: 2,
},
]
: []
),
},
]
expect(migrateToV18(experiment17).dataPoints).toEqual(actual)
}
)
})

describe('experiment properties', () => {
//TODO: More/better tests - maybe this can be mabe obsolete by schema testing
it('newest data format json should match default empty experiment', () => {
expect(Object.keys(emptyExperiment).length).toBe(
Object.keys(version17).length
Object.keys(version18).length
)
Object.keys(emptyExperiment).forEach(p =>
expect(version17).toHaveProperty(p)
expect(version18).toHaveProperty(p)
)
})
})
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/common/util/migration/migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import {
migrateToV14,
migrateToV15,
migrateToV16,
migrateToV17,
migrateToV18,
} from './migrations'
import { migrateToV17 } from './migrations/migrateToV17'

export const migrate = (json: any): ExperimentType => {
const migrated = _migrate(
Expand Down Expand Up @@ -100,4 +101,5 @@ export const MIGRATIONS: Migration[] = [
{ version: '15', converter: migrateToV15 },
{ version: '16', converter: migrateToV16 },
{ version: '17', converter: migrateToV17 },
{ version: '18', converter: migrateToV18 },
]
2 changes: 2 additions & 0 deletions packages/core/src/common/util/migration/migrations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ export { migrateToV13 } from './migrateToV13'
export { migrateToV14 } from './migrateToV14'
export { migrateToV15 } from './migrateToV15'
export { migrateToV16 } from './migrateToV16'
export { migrateToV17 } from './migrateToV17'
export { migrateToV18 } from './migrateToV18'
61 changes: 35 additions & 26 deletions packages/core/src/common/util/migration/migrations/migrateToV17.ts
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only updates types to avoid "dataFormatVersion = 18" error

Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,41 @@ import { ExperimentType, scoreName } from '@core/common/types'
import { produce } from 'immer'

export const migrateToV17 = (json: ExperimentType): ExperimentType => {
// renames all scores scores to ["scoreName", "scoreName 2"...]
return produce(json, draft => {
draft.info.dataFormatVersion = '17'
draft.scoreVariables = json.scoreVariables.map((s, i) => ({
name: getScoreName(scoreName, i),
description: scoreName,
enabled: s.enabled,
}))
draft.dataPoints = json.dataPoints.map(dp => {
let scoreIndex = 0
return {
...dp,
data: dp.data.map(d => {
let newName = d.name
if (d.type === 'score') {
newName = getScoreName(scoreName, scoreIndex)
scoreIndex++
}
return {
...d,
name: newName,
}
}),
}
})
})
// renames all scores to ["scoreName", "scoreName 2"...]
return produce(
json,
(draft: {
info: { dataFormatVersion: string }
scoreVariables: { name: string; description: string; enabled: boolean }[]
dataPoints: {
data: { name: string; type: string }[]
}[]
}) => {
draft.info.dataFormatVersion = '17'
draft.scoreVariables = json.scoreVariables.map((s, i) => ({
name: getScoreName(scoreName, i),
description: scoreName,
enabled: s.enabled,
}))
draft.dataPoints = json.dataPoints.map(dp => {
let scoreIndex = 0
return {
...dp,
data: dp.data.map(d => {
let newName = d.name
if (d.type === 'score') {
newName = getScoreName(scoreName, scoreIndex)
scoreIndex++
}
return {
...d,
name: newName,
}
}),
}
})
}
)
}

const getScoreName = (name: string, index: number) =>
Expand Down
Loading
Loading