diff --git a/.changeset/eight-hounds-draw.md b/.changeset/eight-hounds-draw.md new file mode 100644 index 00000000..1a42bb3f --- /dev/null +++ b/.changeset/eight-hounds-draw.md @@ -0,0 +1,6 @@ +--- +'@boostv/process-optimizer-frontend-core': minor +'@boostv/process-optimizer-frontend-ui': minor +--- + +Rename score to quality diff --git a/packages/core/src/common/types/common.ts b/packages/core/src/common/types/common.ts index a101d847..11094cba 100644 --- a/packages/core/src/common/types/common.ts +++ b/packages/core/src/common/types/common.ts @@ -3,7 +3,9 @@ import { z } from 'zod' // Change the current version when doing structural // changes to any types belonging to ExperimentType -export const currentVersion = '16' +export const currentVersion = '17' + +export const scoreName = 'Quality (0-5)' const infoSchema = z.object({ name: z.string(), diff --git a/packages/core/src/common/util/converters/converters.test.ts b/packages/core/src/common/util/converters/converters.test.ts index 6c9fde13..cf6711f6 100644 --- a/packages/core/src/common/util/converters/converters.test.ts +++ b/packages/core/src/common/util/converters/converters.test.ts @@ -5,6 +5,7 @@ import { ExperimentType, ScoreVariableType, ValueVariableType, + scoreName, } from '@core/common/types' import { initialState } from '@core/context' import { @@ -24,14 +25,14 @@ describe('converters', () => { { type: 'numeric', name: 'Peber', value: 982 }, { type: 'numeric', name: 'Hvedemel', value: 632 }, { type: 'categorical', name: 'Kunde', value: 'Mus' }, - { type: 'score', name: 'score', value: 0.1 }, + { type: 'score', name: scoreName, value: 0.1 }, ] satisfies DataPointType[], [ { type: 'numeric', name: 'Sukker', value: 15 }, { type: 'numeric', name: 'Peber', value: 123 }, { type: 'numeric', name: 'Hvedemel', value: 324 }, { type: 'categorical', name: 'Kunde', value: 'Ræv' }, - { type: 'score', name: 'score', value: 0.2 }, + { type: 'score', name: scoreName, value: 0.2 }, ] satisfies DataPointType[], ].map((data, idx) => ({ meta: { enabled: true, id: idx + 1, valid: true }, @@ -43,16 +44,16 @@ describe('converters', () => { { type: 'numeric', name: 'Peber', value: 982 }, { type: 'numeric', name: 'Hvedemel', value: 632 }, { type: 'categorical', name: 'Kunde', value: 'Mus' }, - { type: 'score', name: 'score', value: 0.1 }, - { type: 'score', name: 'score2', value: 0.3 }, + { type: 'score', name: scoreName, value: 0.1 }, + { type: 'score', name: scoreName + ' 2', value: 0.3 }, ] satisfies DataPointType[], [ { type: 'numeric', name: 'Sukker', value: 15 }, { type: 'numeric', name: 'Peber', value: 123 }, { type: 'numeric', name: 'Hvedemel', value: 324 }, { type: 'categorical', name: 'Kunde', value: 'Ræv' }, - { type: 'score', name: 'score', value: 0.2 }, - { type: 'score', name: 'score2', value: 0.4 }, + { type: 'score', name: scoreName, value: 0.2 }, + { type: 'score', name: scoreName + ' 2', value: 0.4 }, ] satisfies DataPointType[], ].map((data, idx) => ({ meta: { enabled: true, id: idx + 1, valid: true }, @@ -246,8 +247,8 @@ describe('converters', () => { sampleExperiment.categoricalVariables, sampleExperiment.valueVariables, [ - { name: 'score', description: '', enabled: true }, - { name: 'score2', description: '', enabled: true }, + { name: scoreName, description: '', enabled: true }, + { name: scoreName + ' 2', description: '', enabled: true }, ], sampleMultiObjectiveDataPoints ) @@ -263,8 +264,8 @@ describe('converters', () => { sampleExperiment.categoricalVariables, sampleExperiment.valueVariables, [ - { name: 'score', description: '', enabled: true }, - { name: 'score2', description: '', enabled: false }, + { name: scoreName, description: '', enabled: true }, + { name: scoreName + ' 2', description: '', enabled: false }, ], sampleMultiObjectiveDataPoints ) diff --git a/packages/core/src/common/util/migration/data-formats/17.json b/packages/core/src/common/util/migration/data-formats/17.json new file mode 100644 index 00000000..24d3bcde --- /dev/null +++ b/packages/core/src/common/util/migration/data-formats/17.json @@ -0,0 +1,102 @@ +{ + "id": "1234", + "changedSinceLastEvaluation": true, + "lastEvaluationHash": "never-calculated", + "info": { + "dataFormatVersion": "17", + "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 + } +} diff --git a/packages/core/src/common/util/migration/migration.test.ts b/packages/core/src/common/util/migration/migration.test.ts index 1d0c9a34..501069bb 100644 --- a/packages/core/src/common/util/migration/migration.test.ts +++ b/packages/core/src/common/util/migration/migration.test.ts @@ -1,5 +1,6 @@ import { JSONSchemaFaker } from 'json-schema-faker' import { migrate, _migrate, MIGRATIONS } from './migration' +import version17 from './data-formats/17.json' import version16 from './data-formats/16.json' import version3 from './data-formats/3.json' import version2 from './data-formats/2.json' @@ -11,8 +12,14 @@ import brokenV9Experiment from '@core/sample-data/version-9-with-strings-in-opti import large from '@core/sample-data/large.json' import { emptyExperiment } from '@core/context/experiment' import { formatNext } from './migrations/migrateToV9' -import { experimentSchema } from '@core/common/types' +import { + ExperimentType, + ScoreVariableType, + experimentSchema, + scoreName, +} from '@core/common/types' import { storeLatestSchema, loadTestData } from './test-utils' +import { migrateToV17 } from './migrations/migrateToV17' describe('Migration of data format', () => { storeLatestSchema() @@ -139,14 +146,131 @@ describe('Migration of data format', () => { }) }) + describe('migrateToV17', () => { + const scoreVarsMultiObjective = [ + { + name: 'score', + description: 'score', + enabled: true, + }, + { + name: 'score2', + description: 'score', + enabled: true, + }, + ] satisfies ScoreVariableType[] + + const scoreVarsMultiObjectiveDisabled = [ + { + name: 'score', + description: 'score', + enabled: true, + }, + { + name: 'score2', + description: 'score', + enabled: true, + }, + ] satisfies ScoreVariableType[] + + it.each([ + ['multiobjective, all enabled', scoreVarsMultiObjective], + ['multiobjective, one disabled', scoreVarsMultiObjectiveDisabled], + ])('scoreVariables should be renamed, %s', (_, scoreVariables) => { + const experiment16 = { + ...version16, + scoreVariables, + } as unknown as ExperimentType + expect(migrateToV17(experiment16).scoreVariables).toEqual([ + { + name: scoreName, + description: scoreName, + enabled: scoreVariables[0]?.enabled, + }, + { + name: scoreName + ' 2', + description: scoreName, + enabled: scoreVariables[1]?.enabled, + }, + ]) + }) + + const dataPointsSingle = [...version16.dataPoints] + const dataPointsMulti = [...version16.dataPoints].map(dp => ({ + ...dp, + data: [...dp.data].concat([ + { + type: 'score', + name: 'score2', + value: 2, + }, + ]), + })) + + it.each([ + ['one score exists', dataPointsSingle, false], + ['two scores exist', dataPointsMulti, true], + ])( + 'data points should be renamed, %s', + (_, dataPoints, isMultiObjective) => { + const experiment16 = { + ...version16, + 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: scoreName, + value: 0.5, + }, + ].concat( + isMultiObjective + ? [ + { + type: 'score', + name: scoreName + ' 2', + value: 2, + }, + ] + : [] + ), + }, + ] + expect(migrateToV17(experiment16).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(version16).length + Object.keys(version17).length ) Object.keys(emptyExperiment).forEach(p => - expect(version16).toHaveProperty(p) + expect(version17).toHaveProperty(p) ) }) }) diff --git a/packages/core/src/common/util/migration/migration.ts b/packages/core/src/common/util/migration/migration.ts index 9e11c401..7dda914d 100644 --- a/packages/core/src/common/util/migration/migration.ts +++ b/packages/core/src/common/util/migration/migration.ts @@ -18,6 +18,7 @@ import { migrateToV15, migrateToV16, } from './migrations' +import { migrateToV17 } from './migrations/migrateToV17' export const migrate = (json: any): ExperimentType => { const migrated = _migrate( @@ -98,4 +99,5 @@ export const MIGRATIONS: Migration[] = [ { version: '14', converter: migrateToV14 }, { version: '15', converter: migrateToV15 }, { version: '16', converter: migrateToV16 }, + { version: '17', converter: migrateToV17 }, ] diff --git a/packages/core/src/common/util/migration/migrations/migrateToV16.ts b/packages/core/src/common/util/migration/migrations/migrateToV16.ts index e62c61fa..9f2958a9 100644 --- a/packages/core/src/common/util/migration/migrations/migrateToV16.ts +++ b/packages/core/src/common/util/migration/migrations/migrateToV16.ts @@ -1,10 +1,15 @@ import { ExperimentType } from '@core/common/types' import { produce } from 'immer' - -export const migrateToV16 = (json: ExperimentType): ExperimentType => { - return produce(json, draft => { - draft.info.dataFormatVersion = '16' - draft.info.version = 0 - draft.info.extras = {} - }) +/* eslint-disable @typescript-eslint/no-explicit-any */ +export const migrateToV16 = (json: any): ExperimentType => { + return produce( + json, + (draft: { + info: { dataFormatVersion: string; version: number; extras: unknown } + }) => { + draft.info.dataFormatVersion = '16' + draft.info.version = 0 + draft.info.extras = {} + } + ) as unknown as ExperimentType } diff --git a/packages/core/src/common/util/migration/migrations/migrateToV17.ts b/packages/core/src/common/util/migration/migrations/migrateToV17.ts new file mode 100644 index 00000000..6f86b312 --- /dev/null +++ b/packages/core/src/common/util/migration/migrations/migrateToV17.ts @@ -0,0 +1,34 @@ +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, + } + }), + } + }) + }) +} + +const getScoreName = (name: string, index: number) => + name + (index > 0 ? ` ${index + 1}` : '') diff --git a/packages/core/src/common/util/migration/schemas/17.json b/packages/core/src/common/util/migration/schemas/17.json new file mode 100644 index 00000000..fd1e0132 --- /dev/null +++ b/packages/core/src/common/util/migration/schemas/17.json @@ -0,0 +1,397 @@ +{ + "$ref": "#/definitions/experiment-v17", + "definitions": { + "experiment-v17": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "lastEvaluationHash": { + "type": "string" + }, + "changedSinceLastEvaluation": { + "type": "boolean" + }, + "info": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "swVersion": { + "type": "string" + }, + "dataFormatVersion": { + "type": "string", + "const": "17" + }, + "version": { + "type": "number" + }, + "extras": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "name", + "description", + "swVersion", + "dataFormatVersion", + "version", + "extras" + ], + "additionalProperties": false + }, + "extras": { + "type": "object", + "additionalProperties": {} + }, + "categoricalVariables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "options": { + "type": "array", + "items": { + "type": "string" + } + }, + "enabled": { + "type": "boolean" + } + }, + "required": [ + "name", + "description", + "options", + "enabled" + ], + "additionalProperties": false + } + }, + "valueVariables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "discrete", + "continuous" + ] + }, + "name": { + "type": "string" + }, + "description": { + "type": "string", + "default": "" + }, + "min": { + "type": "number" + }, + "max": { + "type": "number" + }, + "enabled": { + "type": "boolean" + } + }, + "required": [ + "type", + "name", + "min", + "max", + "enabled" + ], + "additionalProperties": false + } + }, + "scoreVariables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "enabled": { + "type": "boolean" + } + }, + "required": [ + "name", + "description", + "enabled" + ], + "additionalProperties": false + } + }, + "constraints": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "sum" + }, + "value": { + "type": "number" + }, + "dimensions": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "type", + "value", + "dimensions" + ], + "additionalProperties": false + } + }, + "optimizerConfig": { + "type": "object", + "properties": { + "baseEstimator": { + "type": "string" + }, + "acqFunc": { + "type": "string" + }, + "initialPoints": { + "type": "number" + }, + "kappa": { + "type": "number" + }, + "xi": { + "type": "number" + } + }, + "required": [ + "baseEstimator", + "acqFunc", + "initialPoints", + "kappa", + "xi" + ], + "additionalProperties": false + }, + "results": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "plots": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "plot": { + "type": "string" + } + }, + "required": [ + "id", + "plot" + ], + "additionalProperties": false + } + }, + "next": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": [ + "number", + "string" + ] + } + } + }, + "pickled": { + "type": "string" + }, + "expectedMinimum": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "array", + "items": { + "type": [ + "number", + "string" + ] + } + }, + { + "type": "number" + } + ] + } + }, + "extras": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "plots", + "next", + "pickled", + "expectedMinimum", + "extras" + ], + "additionalProperties": false + }, + "dataPoints": { + "type": "array", + "items": { + "type": "object", + "properties": { + "meta": { + "type": "object", + "properties": { + "id": { + "type": "number", + "default": 0 + }, + "enabled": { + "type": "boolean", + "default": true + }, + "valid": { + "type": "boolean", + "default": true + }, + "description": { + "type": "string" + } + }, + "additionalProperties": false + }, + "data": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "numeric" + }, + "name": { + "type": "string" + }, + "value": { + "type": "number" + } + }, + "required": [ + "type", + "name", + "value" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "categorical" + }, + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "type", + "name", + "value" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "score" + }, + "name": { + "type": "string" + }, + "value": { + "type": "number" + } + }, + "required": [ + "type", + "name", + "value" + ], + "additionalProperties": false + } + ] + } + } + }, + "required": [ + "meta", + "data" + ], + "additionalProperties": false + } + } + }, + "required": [ + "id", + "changedSinceLastEvaluation", + "info", + "extras", + "categoricalVariables", + "valueVariables", + "scoreVariables", + "constraints", + "optimizerConfig", + "results", + "dataPoints" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/core/src/context/experiment/experiment-reducers.ts b/packages/core/src/context/experiment/experiment-reducers.ts index f7999ad0..f9a9a287 100644 --- a/packages/core/src/context/experiment/experiment-reducers.ts +++ b/packages/core/src/context/experiment/experiment-reducers.ts @@ -7,6 +7,7 @@ import { ScoreVariableType, ValueVariableType, experimentSchema, + scoreName, } from '@core/common/types' import { produce } from 'immer' import md5 from 'md5' @@ -379,8 +380,8 @@ export const experimentReducer = produce( if (state.scoreVariables.length < 2) { state.scoreVariables.push({ - name: 'score2', - description: 'score 2', + name: scoreName + ' 2', + description: scoreName + ' 2', enabled: true, }) const scoreNames = state.scoreVariables.map(it => it.name) diff --git a/packages/core/src/context/experiment/store.ts b/packages/core/src/context/experiment/store.ts index f7b17508..bee6b2f3 100644 --- a/packages/core/src/context/experiment/store.ts +++ b/packages/core/src/context/experiment/store.ts @@ -1,5 +1,5 @@ import { versionInfo } from '@core/common' -import { currentVersion } from '@core/common/types' +import { currentVersion, scoreName } from '@core/common/types' import { ExperimentType } from '@core/common/types' export const emptyExperiment: ExperimentType = { @@ -18,8 +18,8 @@ export const emptyExperiment: ExperimentType = { valueVariables: [], scoreVariables: [ { - name: 'score', - description: 'score', + name: scoreName, + description: scoreName, enabled: true, }, ], diff --git a/packages/ui/src/features/result-data/single-data-point.tsx b/packages/ui/src/features/result-data/single-data-point.tsx index f82313ab..95887006 100644 --- a/packages/ui/src/features/result-data/single-data-point.tsx +++ b/packages/ui/src/features/result-data/single-data-point.tsx @@ -14,6 +14,7 @@ import { import useStyles from './single-data-point.style' import { PNGPlot } from '@boostv/process-optimizer-frontend-plots' import { useState } from 'react' +import { scoreName } from '@boostv/process-optimizer-frontend-core' interface SingleDataPointProps { title: string @@ -46,7 +47,7 @@ export const SingleDataPoint = ({ {headers - .concat(['Score (95 % credibility interval)']) + .concat([scoreName + ' (95 % credibility interval)']) .map((h, idx) => ( {h}