diff --git a/todo/constant_murley_score/index.ts b/src/scores/constant_murley_score/index.ts similarity index 100% rename from todo/constant_murley_score/index.ts rename to src/scores/constant_murley_score/index.ts diff --git a/todo/constant_murley_score/orthotoolkit_version/README.md b/src/scores/constant_murley_score/orthotoolkit_version/README.md similarity index 100% rename from todo/constant_murley_score/orthotoolkit_version/README.md rename to src/scores/constant_murley_score/orthotoolkit_version/README.md diff --git a/todo/constant_murley_score/orthotoolkit_version/__testdata__/cms_test_responses.ts b/src/scores/constant_murley_score/orthotoolkit_version/__testdata__/cms_test_responses.ts similarity index 100% rename from todo/constant_murley_score/orthotoolkit_version/__testdata__/cms_test_responses.ts rename to src/scores/constant_murley_score/orthotoolkit_version/__testdata__/cms_test_responses.ts diff --git a/src/scores/constant_murley_score/orthotoolkit_version/constant_murley_score.test.ts b/src/scores/constant_murley_score/orthotoolkit_version/constant_murley_score.test.ts new file mode 100644 index 0000000..0d172a1 --- /dev/null +++ b/src/scores/constant_murley_score/orthotoolkit_version/constant_murley_score.test.ts @@ -0,0 +1,257 @@ +import { ZodError } from 'zod' +import { + best_response, + median_response, + random_response, + worst_response, +} from './__testdata__/cms_test_responses' +import { constant_murley_score } from './constant_murley_score' +import { Score } from '../../../classes' +import { ScoreLibrary } from '../../library' + +const BEST_SCORES = { + PAIN_SUBSCALE: 15, + ADL_SUBSCALE: 20, + MOBILITY_SUBSCALE: 40, + STRENGTH_SUBSCALE: 25, + TOTAL: 100, +} + +const MEDIAN_SCORES = { + PAIN_SUBSCALE: 7, + ADL_SUBSCALE: 10, + MOBILITY_SUBSCALE: 20, + STRENGTH_SUBSCALE: 13, + TOTAL: 50, +} + +const WORST_SCORES = { + PAIN_SUBSCALE: 0, + ADL_SUBSCALE: 0, + MOBILITY_SUBSCALE: 0, + STRENGTH_SUBSCALE: 0, + TOTAL: 0, +} + +const cms_calculation = new Score(constant_murley_score) + +describe('constant_murley_score_orthotoolkit', function () { + it('constant_murley_score_orthotoolkit calculation function should be available as a calculation', function () { + expect(ScoreLibrary).toHaveProperty('constant_murley_score_orthotoolkit') + }) + + describe('specific_steps_cms_calc', function () { + describe('the score includes the correct input fields', function () { + it('should have all the expected input ids configured', function () { + const EXPECTED_INPUT_IDS = [ + 'Q01_PAIN_SCORE', + 'Q02_SLEEP_SCORE', + 'Q03_WORK_ADL_SCORE', + 'Q04_SPORTS_HOBBY_SCORE', + 'Q05_ADL_FUNCTIONING_SCORE', + 'Q06_FLEXION_ROM', + 'Q07_ABDUCTION_ROM', + 'Q08_ENDOROTATION_ROM', + 'Q09_EXOROTATION_ROM', + 'Q10_ATTEMPT_1', + 'Q11_ATTEMPT_2', + 'Q12_ATTEMPT_3', + ] + + const configured_input_ids = Object.keys(cms_calculation.inputSchema) + expect(EXPECTED_INPUT_IDS).toEqual(configured_input_ids) + }) + }) + + describe('each calculated score includes the correct output result and correct score title', function () { + const outcome = cms_calculation.calculate({ payload: worst_response }) + + it('should return 5 calculations results', function () { + expect(Object.keys(outcome).length).toEqual(5) + }) + + it('should have all the correct calculation ids', function () { + const EXPECTED_CALCULATION_IDS = [ + 'PAIN', + 'ADL', + 'MOBILITY', + 'STRENGTH', + 'TS', + ] + + const extracted_calculation_ids_from_outcome = Object.keys(outcome) + + expect(EXPECTED_CALCULATION_IDS).toEqual( + extracted_calculation_ids_from_outcome, + ) + }) + }) + + describe('a score is only calculated when all mandatory fields are entered', function () { + describe('when an empty response is passed', function () { + const outcome = cms_calculation.calculate({ + payload: {}, + opts: { + returnMissingOnZodError: true, + }, + }) + + it('should return null for the "Pain" subscale', function () { + expect(outcome.PAIN).toEqual(null) + }) + + it('should return null for the "ADL" subscale', function () { + expect(outcome.ADL).toEqual(null) + }) + + it('should return null for the "Mobility" subscale', function () { + expect(outcome.MOBILITY).toEqual(null) + }) + + it('should return null for the "Strength" subscale', function () { + expect(outcome.STRENGTH).toEqual(null) + }) + + it('should return null for the total score', function () { + expect(outcome.TS).toEqual(null) + }) + }) + }) + + describe('each calculated score includes the correct formula and outputs the correct result', function () { + describe('when worst response is passed', function () { + const outcome = cms_calculation.calculate({ payload: worst_response }) + + it('should return the worst score for the "Pain" subscale', function () { + expect(outcome.PAIN).toEqual(WORST_SCORES.PAIN_SUBSCALE) + }) + + it('should return the worst score for the "ADL" subscale', function () { + expect(outcome.ADL).toEqual(WORST_SCORES.ADL_SUBSCALE) + }) + + it('should return the worst score for the "Mobility" subscale', function () { + expect(outcome.MOBILITY).toEqual(WORST_SCORES.MOBILITY_SUBSCALE) + }) + + it('should return the worst score for the "Strength" subscale', function () { + expect(outcome.STRENGTH).toEqual(WORST_SCORES.STRENGTH_SUBSCALE) + }) + + it('should return the worst total score', function () { + expect(outcome.TS).toEqual(WORST_SCORES.TOTAL) + }) + }) + + describe('when a median response is passed', function () { + const outcome = cms_calculation.calculate({ + payload: median_response, + }) + + it('should return the median score for the "Pain" subscale', function () { + expect(outcome.PAIN).toEqual(MEDIAN_SCORES.PAIN_SUBSCALE) + }) + + it('should return the median score for the "ADL" subscale', function () { + expect(outcome.ADL).toEqual(MEDIAN_SCORES.ADL_SUBSCALE) + }) + + it('should return the median score for the "Mobility" subscale', function () { + expect(outcome.MOBILITY).toEqual(MEDIAN_SCORES.MOBILITY_SUBSCALE) + }) + + it('should return the median score for the "Strength" subscale', function () { + expect(outcome.STRENGTH).toEqual(MEDIAN_SCORES.STRENGTH_SUBSCALE) + }) + + it('should return the median total score', function () { + expect(outcome.TS).toEqual(MEDIAN_SCORES.TOTAL) + }) + }) + + describe('when best response is passed', function () { + const outcome = cms_calculation.calculate({ payload: best_response }) + + it('should return the best score for the "Pain" subscale', function () { + expect(outcome.PAIN).toEqual(BEST_SCORES.PAIN_SUBSCALE) + }) + + it('should return the best score for the "ADL" subscale', function () { + expect(outcome.ADL).toEqual(BEST_SCORES.ADL_SUBSCALE) + }) + + it('should return the best score for the "Mobility" subscale', function () { + expect(outcome.MOBILITY).toEqual(BEST_SCORES.MOBILITY_SUBSCALE) + }) + + it('should return the best score for the "Strength" subscale', function () { + expect(outcome.STRENGTH).toEqual(BEST_SCORES.STRENGTH_SUBSCALE) + }) + + it('should return the best total score', function () { + expect(outcome.TS).toEqual(BEST_SCORES.TOTAL) + }) + }) + + describe('when a random response is passed', function () { + const outcome = cms_calculation.calculate({ payload: random_response }) + + it('should return the expected score for the "Pain" subscale', function () { + expect(outcome.PAIN).toEqual(11) + }) + + it('should return the expected score for the "ADL" subscale', function () { + expect(outcome.ADL).toEqual(8) + }) + + it('should return the expected score for the "Mobility" subscale', function () { + expect(outcome.MOBILITY).toEqual(12) + }) + + it('should return the expected score for the "Strength" subscale', function () { + expect(outcome.STRENGTH).toEqual(25) + }) + + it('should return the expected total score', function () { + expect(outcome.TS).toEqual(56) + }) + }) + }) + + describe('values entered by the user are checked to verify they are inside specified ranges', function () { + describe('when an answer is not a number', function () { + it('should throw an error', function () { + expect(() => + cms_calculation.calculate({ + payload: { + Q01_PAIN_SCORE: "I'm not a number", + }, + }), + ).toThrow(ZodError) + }) + }) + describe('when an answer is not allowed (e.g. is below the expected range)', function () { + it('should throw an error', function () { + expect(() => + cms_calculation.calculate({ + payload: { + Q01_PAIN_SCORE: -1, + }, + }), + ).toThrow(ZodError) + }) + }) + describe('when an answer is not allowed (e.g. is above the expected range)', function () { + it('should return throw an error', function () { + expect(() => + cms_calculation.calculate({ + payload: { + Q01_PAIN_SCORE: 16, + }, + }), + ).toThrow(ZodError) + }) + }) + }) + }) +}) diff --git a/src/scores/constant_murley_score/orthotoolkit_version/constant_murley_score.ts b/src/scores/constant_murley_score/orthotoolkit_version/constant_murley_score.ts new file mode 100644 index 0000000..4d54a5b --- /dev/null +++ b/src/scores/constant_murley_score/orthotoolkit_version/constant_murley_score.ts @@ -0,0 +1,65 @@ +import { ScoreType } from '../../../types' +import { CMS_INPUTS, CMS_OUTPUT } from './definition' +import { sum, max, min } from 'lodash' + +export const constant_murley_score: ScoreType< + typeof CMS_INPUTS, + typeof CMS_OUTPUT +> = { + name: 'Constant Murley Score (CMS) - OrthoToolKit version', + readmeLocation: __dirname, + inputSchema: CMS_INPUTS, + outputSchema: CMS_OUTPUT, + calculate: ({ data }) => { + /** + * Mobility + */ + const POINTS_PER_EXOROTATION_CRITERIUM = 2 + const exorotation_input = data.Q09_EXOROTATION_ROM + const exorotation_score = + exorotation_input.length * POINTS_PER_EXOROTATION_CRITERIUM + + const other_mobility_inputs = [ + data.Q06_FLEXION_ROM, + data.Q07_ABDUCTION_ROM, + data.Q08_ENDOROTATION_ROM, + ] + + const mobility_score = sum(other_mobility_inputs) + exorotation_score + + /** Strength */ + const MAX_SCALE_SCORE = 25 + const CONVERT_KG_TO_LBS = 2.2 + + const strength_inputs = [ + data.Q10_ATTEMPT_1, + data.Q11_ATTEMPT_2, + data.Q12_ATTEMPT_3, + ] + + const max_strength_attempt = max(strength_inputs) + const strength_score = min([ + Math.round((max_strength_attempt as number) * CONVERT_KG_TO_LBS), + MAX_SCALE_SCORE, + ]) + + /** ADL */ + const adl_score = sum([ + data.Q03_WORK_ADL_SCORE, + data.Q04_SPORTS_HOBBY_SCORE, + data.Q02_SLEEP_SCORE, + data.Q05_ADL_FUNCTIONING_SCORE, + ]) + + /** Pain */ + const pain_score = data.Q01_PAIN_SCORE + + return { + PAIN: pain_score, + ADL: adl_score, + MOBILITY: mobility_score, + STRENGTH: strength_score ?? null, + TS: pain_score + adl_score + mobility_score + (strength_score ?? 0), + } + }, +} diff --git a/src/scores/constant_murley_score/orthotoolkit_version/definition/constant_murley_inputs.ts b/src/scores/constant_murley_score/orthotoolkit_version/definition/constant_murley_inputs.ts new file mode 100644 index 0000000..a439e2e --- /dev/null +++ b/src/scores/constant_murley_score/orthotoolkit_version/definition/constant_murley_inputs.ts @@ -0,0 +1,370 @@ +import { z } from 'zod' +import { type ScoreInputSchemaType } from '../../../../types' + +export const CMS_INPUTS = { + Q01_PAIN_SCORE: { + label: { + en: 'Score the highest pain level that you have experienced in your shoulder during ordinary activities within the last 24 hours.', + nl: 'Wat is de hoogste pijnscore die u de laatste 24u heeft ervaren in uw schouder tijdens het uitvoeren van normale activiteiten?', + }, + type: z.number().min(0).max(15), + uiOptions: { + component: 'slider', + range: { + min: { + label: { en: 'Intolerable pain', nl: 'Ondraaglijke pijn' }, + }, + max: { + label: { en: 'No pain', nl: 'Geen pijn' }, + }, + }, + }, + }, + Q02_SLEEP_SCORE: { + label: { + en: 'Is your sleep disturbed by your shoulder?', + nl: 'Is je slaap verstoord door uw schouder?', + }, + type: z.union([z.literal(2), z.literal(1), z.literal(0)]), + uiOptions: { + options: [ + { + value: 2, + label: { en: 'Undisturbed sleep', nl: 'Ik slaap onverstoord' }, + }, + { + value: 1, + label: { + en: 'Occasional disturbance', + nl: 'Mijn slaap is occasioneel verstoord', + }, + }, + { + value: 0, + label: { + en: 'Every night', + nl: 'Mijn slaap is elke nacht verstoord', + }, + }, + ], + }, + }, + Q03_WORK_ADL_SCORE: { + label: { + en: 'How much of your normal daily work does your shoulder allow you to perform?', + nl: 'Hoeveel van uw normale dagelijkse werk laat uw schouder toe om te doen?', + }, + type: z.number().min(0).max(4), + uiOptions: { + component: 'slider', + range: { + min: { + label: { en: 'None', nl: 'Geen' }, + }, + max: { + label: { en: 'All', nl: 'Allemaal' }, + }, + }, + }, + }, + Q04_SPORTS_HOBBY_SCORE: { + label: { + en: 'How much of your normal recreational activity does your shoulder allow you to perform?', + nl: 'Hoeveel van uw normale recreationale activiteiten laat uw schouder toe om te doen?', + }, + type: z.number().min(0).max(4), + uiOptions: { + component: 'slider', + range: { + min: { + label: { en: 'None', nl: 'Geen' }, + }, + max: { + label: { en: 'All', nl: 'Allemaal' }, + }, + }, + }, + }, + Q05_ADL_FUNCTIONING_SCORE: { + label: { + en: 'To which level can you use your hand comfortably?', + nl: 'Tot welke hoogte kan u uw hand comfortabel gebruiken?', + }, + type: z.union([ + z.literal(0), + z.literal(2), + z.literal(4), + z.literal(6), + z.literal(8), + z.literal(10), + ]), + uiOptions: { + options: [ + { + value: 0, + label: { en: 'Below the waist', nl: 'Hand onder middel' }, + }, + { + value: 2, + label: { en: 'Up to the waist', nl: 'Hand tot middel' }, + }, + { + value: 4, + label: { + en: 'Up to the xiphoid/sternum', + nl: 'Hand tot borsthoogte', + }, + }, + { + value: 6, + label: { en: 'Up to the neck', nl: 'Hand schouderhoogte / nek' }, + }, + { + value: 8, + label: { + en: 'Up to the top of the head', + nl: 'Hand op hoofdhoogte', + }, + }, + { + value: 10, + label: { en: 'Above the head', nl: 'Hand boven hoofd hoogte' }, + }, + ], + }, + }, + Q06_FLEXION_ROM: { + label: { + en: 'Forward flexion', + nl: 'Elevatie', + }, + type: z.union([ + z.literal(0), + z.literal(2), + z.literal(4), + z.literal(6), + z.literal(8), + z.literal(10), + ]), + uiOptions: { + options: [ + { + value: 0, + label: { en: '0-30 degrees', nl: '0-30 graden' }, + }, + { + value: 2, + label: { en: '31-60 degrees', nl: '31-60 graden' }, + }, + { + value: 4, + label: { + en: '61-90 degrees', + nl: '61-90 graden', + }, + }, + { + value: 6, + label: { en: '91-120 degrees', nl: '91-120 graden' }, + }, + { + value: 8, + label: { + en: '121-150 degrees', + nl: '121-150 graden', + }, + }, + { + value: 10, + label: { en: '151+ degrees', nl: '151+ graden' }, + }, + ], + }, + }, + Q07_ABDUCTION_ROM: { + label: { + en: 'Lateral elevation (abduction)', + nl: 'Abductie', + }, + type: z.union([ + z.literal(0), + z.literal(2), + z.literal(4), + z.literal(6), + z.literal(8), + z.literal(10), + ]), + uiOptions: { + options: [ + { + value: 0, + label: { en: '0-30 degrees', nl: '0-30 graden' }, + }, + { + value: 2, + label: { en: '31-60 degrees', nl: '31-60 graden' }, + }, + { + value: 4, + label: { + en: '61-90 degrees', + nl: '61-90 graden', + }, + }, + { + value: 6, + label: { en: '91-120 degrees', nl: '91-120 graden' }, + }, + { + value: 8, + label: { + en: '121-150 degrees', + nl: '121-150 graden', + }, + }, + { + value: 10, + label: { en: '151+ degrees', nl: '151+ graden' }, + }, + ], + }, + }, + Q08_ENDOROTATION_ROM: { + label: { + en: 'Internal rotation', + nl: 'Interne rotatie', + }, + info: { + en: 'Patient points to anatomical landmarks with thumb', + }, + type: z.union([ + z.literal(0), + z.literal(2), + z.literal(4), + z.literal(6), + z.literal(8), + z.literal(10), + ]), + uiOptions: { + options: [ + { + value: 0, + label: { + nl: 'Hand laterale zijde trochanter', + en: 'Lateral aspect of the thigh', + }, + }, + { + value: 2, + label: { + nl: 'Duim achter bil / hand naar bil', + en: 'Behind the buttock', + }, + }, + { + value: 4, + label: { + nl: 'Duim op andere SIG / hand naar lumbosacraal', + en: 'Sacroiliac joint', + }, + }, + { value: 6, label: { nl: 'Duim/hand naar lumbaal', en: 'Waist' } }, + { + value: 8, + label: { + nl: 'Duim op Th12 / hand naar TLO', + en: '12th thoracic vertebra', + }, + }, + { + value: 10, + label: { + nl: 'Duim tussen scapulae / Hand los van de rug', + en: 'Interscapular level', + }, + }, + ], + }, + }, + Q09_EXOROTATION_ROM: { + label: { + en: 'External rotation (check all that apply)', + nl: 'Externe rotatie (check all that apply)', + }, + type: z.array( + z.union([ + z.literal(1), + z.literal(2), + z.literal(3), + z.literal(4), + z.literal(5), + ]), + ), + uiOptions: { + options: [ + { + value: 1, + label: { + en: 'Hands behind head, elbows forward', + nl: 'Hand achter op het hoofd met elleboog naar voren', + }, + }, + { + value: 2, + label: { + en: 'Hands behind head, elbows back', + nl: 'Hand achter op het hoofd met elleboog naar buiten', + }, + }, + { + value: 3, + label: { + en: 'Hands to the top of the head, elbows forward', + nl: 'Hand op het hoofd met elleboog naar voren', + }, + }, + { + value: 4, + label: { + en: 'Hands to the top of the head, elbows back', + nl: 'Hand op het hoofd met elleboog naar buiten', + }, + }, + { + value: 5, + label: { + en: 'Full elevation', + nl: 'Volledige elevatie vanaf bovenkant hoofd', + }, + }, + ], + }, + }, + Q10_ATTEMPT_1: { + label: { en: 'Stength - Attempt 1', nl: 'Kracht - Poging 1' }, + type: z.number(), + info: { + en: 'Result should be entered in kg', + nl: 'Geef resultaat in kg in', + }, + unit: { en: 'kg' }, + }, + Q11_ATTEMPT_2: { + label: { en: 'Stength - Attempt 2', nl: 'Kracht - Poging 2' }, + type: z.number().optional(), + info: { + en: 'Result should be entered in kg', + nl: 'Geef resultaat in kg in', + }, + unit: { en: 'kg' }, + }, + Q12_ATTEMPT_3: { + label: { en: 'Stength - Attempt 3', nl: 'Kracht - Poging 3' }, + type: z.number().optional(), + info: { + en: 'Result should be entered in kg', + nl: 'Geef resultaat in kg in', + }, + unit: { en: 'kg' }, + }, +} satisfies ScoreInputSchemaType diff --git a/src/scores/constant_murley_score/orthotoolkit_version/definition/constant_murley_output.ts b/src/scores/constant_murley_score/orthotoolkit_version/definition/constant_murley_output.ts new file mode 100644 index 0000000..4a7efa5 --- /dev/null +++ b/src/scores/constant_murley_score/orthotoolkit_version/definition/constant_murley_output.ts @@ -0,0 +1,34 @@ +import { z } from 'zod' +import { type ScoreOutputSchemaType } from '../../../../types' + +export const CMS_OUTPUT = { + PAIN: { + label: { en: 'Pain', nl: 'Pijn' }, + type: z.number(), + }, + ADL: { + label: { + en: 'Activities Daily living', + nl: 'Activiteiten dagelijks leven', + }, + type: z.number(), + }, + MOBILITY: { + label: { + en: 'Mobility', + nl: 'Mobiliteit', + }, + type: z.number(), + }, + STRENGTH: { + label: { + en: 'Strength', + nl: 'Kracht', + }, + type: z.number(), + }, + TS: { + label: { en: 'Total score' }, + type: z.number(), + }, +} satisfies ScoreOutputSchemaType diff --git a/src/scores/constant_murley_score/orthotoolkit_version/definition/constant_murley_scales.ts b/src/scores/constant_murley_score/orthotoolkit_version/definition/constant_murley_scales.ts new file mode 100644 index 0000000..32deefa --- /dev/null +++ b/src/scores/constant_murley_score/orthotoolkit_version/definition/constant_murley_scales.ts @@ -0,0 +1,18 @@ +export type ScaleType = 'PAIN' | 'ADL' | 'MOBILITY' | 'STRENGTH' + +export const CMS_SUBSCALES: Record = { + PAIN: ['Q01_PAIN_SCORE'], + ADL: [ + 'Q03_WORK_ADL_SCORE', + 'Q04_SPORTS_HOBBY_SCORE', + 'Q02_SLEEP_SCORE', + 'Q05_ADL_FUNCTIONING_SCORE', + ], + MOBILITY: [ + 'Q06_FLEXION_ROM', + 'Q07_ABDUCTION_ROM', + 'Q08_ENDOROTATION_ROM', + 'Q09_EXOROTATION_ROM', + ], + STRENGTH: ['Q10_ATTEMPT_1', 'Q11_ATTEMPT_2', 'Q12_ATTEMPT_3'], +} diff --git a/src/scores/constant_murley_score/orthotoolkit_version/definition/index.ts b/src/scores/constant_murley_score/orthotoolkit_version/definition/index.ts new file mode 100644 index 0000000..ed45aac --- /dev/null +++ b/src/scores/constant_murley_score/orthotoolkit_version/definition/index.ts @@ -0,0 +1,3 @@ +export { CMS_INPUTS } from './constant_murley_inputs' +export { CMS_OUTPUT } from './constant_murley_output' +export { CMS_SUBSCALES, ScaleType } from './constant_murley_scales' diff --git a/src/scores/library.ts b/src/scores/library.ts index 381aa99..aba2926 100644 --- a/src/scores/library.ts +++ b/src/scores/library.ts @@ -17,7 +17,7 @@ import { CHA2DS2_VASc_Score } from './CHA2DS2_VASc_Score/CHA2DS2_VASc_Score' // import { femmes_enceintes_triage } from './chc/femmes_enceintes/triage/triage' // import { comi_back, comi_neck } from './comi' // import { compass_31 } from './compass_31/compass_31' -// import { constant_murley_score_orthotoolkit } from './constant_murley_score' +import { constant_murley_score_orthotoolkit } from './constant_murley_score' import { core_om } from './core_om/core_om' import { cpdi } from './cpdi/cpdi' import { csi } from './csi/csi' @@ -161,7 +161,7 @@ export const ScoreLibrary = createScoreLibrary({ // comi_back, // comi_neck, // compass_31, - // constant_murley_score_orthotoolkit, + constant_murley_score_orthotoolkit, core_om, cpdi, csi, diff --git a/todo/constant_murley_score/orthotoolkit_version/constant_murley_score.test.ts b/todo/constant_murley_score/orthotoolkit_version/constant_murley_score.test.ts deleted file mode 100644 index 77f91f6..0000000 --- a/todo/constant_murley_score/orthotoolkit_version/constant_murley_score.test.ts +++ /dev/null @@ -1,358 +0,0 @@ -import { expect } from 'chai' -import R from 'ramda' - -import { ZodError } from '../../../errors' -import { execute_test_calculation } from '../../../lib/execute_test_calculation' -import { get_result_ids_from_calculation_output } from '../../../lib/get_result_ids_from_calculation_output' -import { view_result } from '../../../lib/view_result' -import { view_status } from '../../../lib/view_status' -import { CALCULATED_STATUS, MISSING_STATUS } from '../../../PARAMETERS' -import { CALCULATIONS } from '../../calculation_library' -import { - get_input_ids_for_specific_subscale, - get_input_ids_in_subscale, -} from '../../shared_functions' -import { - best_response, - median_response, - random_response, - worst_response, -} from './__testdata__/cms_test_responses' -import { constant_murley_score } from './constant_murley_score' -import { CMS_SUBSCALES } from './definition/constant_murley_scales' - -const BEST_SCORES = { - PAIN_SUBSCALE: 15, - ADL_SUBSCALE: 20, - MOBILITY_SUBSCALE: 40, - STRENGTH_SUBSCALE: 25, - TOTAL: 100, -} - -const MEDIAN_SCORES = { - PAIN_SUBSCALE: 7, - ADL_SUBSCALE: 10, - MOBILITY_SUBSCALE: 20, - STRENGTH_SUBSCALE: 13, - TOTAL: 50, -} - -const WORST_SCORES = { - PAIN_SUBSCALE: 0, - ADL_SUBSCALE: 0, - MOBILITY_SUBSCALE: 0, - STRENGTH_SUBSCALE: 0, - TOTAL: 0, -} - -const cms_calculation = execute_test_calculation(constant_murley_score) - -describe('constant_murley_score_orthotoolkit', function () { - it('constant_murley_score_orthotoolkit calculation function should be available as a calculation', function () { - expect(CALCULATIONS).toHaveProperty('constant_murley_score_orthotoolkit') - }) - - describe('specific_steps_cms_calc', function () { - describe('the score includes the correct input fields', function () { - it('should have all the expected input ids configured', function () { - const EXPECTED_INPUT_IDS = [ - 'Q01_PAIN_SCORE', - 'Q02_SLEEP_SCORE', - 'Q03_WORK_ADL_SCORE', - 'Q04_SPORTS_HOBBY_SCORE', - 'Q05_ADL_FUNCTIONING_SCORE', - 'Q06_FLEXION_ROM', - 'Q07_ABDUCTION_ROM', - 'Q08_ENDOROTATION_ROM', - 'Q09_EXOROTATION_ROM', - 'Q10_ATTEMPT_1', - 'Q11_ATTEMPT_2', - 'Q12_ATTEMPT_3', - ].sort() - - const configured_input_ids = R.compose( - (input_ids: string[]) => input_ids.sort(), - R.flatten, - R.map(get_input_ids_in_subscale), - )(CMS_SUBSCALES) - - expect(EXPECTED_INPUT_IDS).toEqual(configured_input_ids) - }) - - it('should have the expected input ids configured for the "Pain" subscale', function () { - const EXPECTED_INPUT_IDS = ['Q01_PAIN_SCORE'] - - expect(EXPECTED_INPUT_IDS).toEqual( - get_input_ids_for_specific_subscale('PAIN')(CMS_SUBSCALES), - ) - }) - - it('should have the expected input ids configured for the "ADL" subscale', function () { - const EXPECTED_INPUT_IDS = [ - 'Q02_SLEEP_SCORE', - 'Q03_WORK_ADL_SCORE', - 'Q04_SPORTS_HOBBY_SCORE', - 'Q05_ADL_FUNCTIONING_SCORE', - ] - - expect(EXPECTED_INPUT_IDS).toEqual( - get_input_ids_for_specific_subscale('ADL')(CMS_SUBSCALES).sort(), - ) - }) - - it('should have the expected input ids configured for the "Mobility" subscale', function () { - const EXPECTED_INPUT_IDS = [ - 'Q06_FLEXION_ROM', - 'Q07_ABDUCTION_ROM', - 'Q08_ENDOROTATION_ROM', - 'Q09_EXOROTATION_ROM', - ] - - expect(EXPECTED_INPUT_IDS).toEqual( - get_input_ids_for_specific_subscale('MOBILITY')(CMS_SUBSCALES), - ) - }) - - it('should have the expected input ids configured for the "Strength" subscale', function () { - const EXPECTED_INPUT_IDS = [ - 'Q10_ATTEMPT_1', - 'Q11_ATTEMPT_2', - 'Q12_ATTEMPT_3', - ] - - expect(EXPECTED_INPUT_IDS).toEqual( - get_input_ids_for_specific_subscale('STRENGTH')(CMS_SUBSCALES), - ) - }) - }) - - describe('each calculated score includes the correct output result and correct score title', function () { - const outcome = cms_calculation(worst_response) - - it('should return 5 calculations results', function () { - expect(outcome).toHaveLength(5) - }) - - it('should have all the correct calculation ids', function () { - const EXPECTED_CALCULATION_IDS = [ - 'PAIN', - 'ADL', - 'MOBILITY', - 'STRENGTH', - 'TS', - ] - - const extracted_calculation_ids_from_outcome = - get_result_ids_from_calculation_output(outcome) - - expect(EXPECTED_CALCULATION_IDS).toEqual( - extracted_calculation_ids_from_outcome, - ) - }) - }) - - describe('a score is only calculated when all mandatory fields are entered', function () { - describe('when an empty response is passed', function () { - const outcome = cms_calculation({}) - - it('should return undefined result and missing status for "Pain" subscale', function () { - const score = view_result('PAIN')(outcome) - const status = view_status('PAIN')(outcome) - - expect(score).toEqual(undefined) - expect(status).toEqual(MISSING_STATUS) - }) - - it('should return undefined result and missing status for "ADL" subscale', function () { - const score = view_result('ADL')(outcome) - const status = view_status('ADL')(outcome) - - expect(score).toEqual(undefined) - expect(status).toEqual(MISSING_STATUS) - }) - - it('should return a score of 1 and calculated status for "Mobility" subscale', function () { - const score = view_result('MOBILITY')(outcome) - const status = view_status('MOBILITY')(outcome) - - expect(score).toEqual(0) - expect(status).toEqual(CALCULATED_STATUS) - }) - - it('should return undefiend result and missing status for "Strength" subscale', function () { - const score = view_result('STRENGTH')(outcome) - const status = view_status('STRENGTH')(outcome) - - expect(score).toEqual(undefined) - expect(status).toEqual(MISSING_STATUS) - }) - - it('should return a total score of 1 and calculated status for the total score', function () { - const score = view_result('TS')(outcome) - const status = view_status('TS')(outcome) - - expect(score).toEqual(0) - expect(status).toEqual(CALCULATED_STATUS) - }) - }) - }) - - describe('each calculated score includes the correct formula and outputs the correct result', function () { - describe('when worst response is passed', function () { - const outcome = cms_calculation(worst_response) - - it('should return the worst score for the "Pain" subscale', function () { - const score = view_result('PAIN')(outcome) - expect(score).toEqual(WORST_SCORES.PAIN_SUBSCALE) - }) - - it('should return the worst score for the "ADL" subscale', function () { - const score = view_result('ADL')(outcome) - expect(score).toEqual(WORST_SCORES.ADL_SUBSCALE) - }) - - it('should return the worst score for the "Mobility" subscale', function () { - const score = view_result('MOBILITY')(outcome) - expect(score).toEqual(WORST_SCORES.MOBILITY_SUBSCALE) - }) - - it('should return the worst score for the "Strength" subscale', function () { - const score = view_result('STRENGTH')(outcome) - expect(score).toEqual(WORST_SCORES.STRENGTH_SUBSCALE) - }) - - it('should return the worst total score', function () { - const score = view_result('TS')(outcome) - expect(score).toEqual(WORST_SCORES.TOTAL) - }) - }) - - describe('when a median response is passed', function () { - const outcome = cms_calculation(median_response) - - it('should return the median score for the "Pain" subscale', function () { - const score = view_result('PAIN')(outcome) - expect(score).toEqual(MEDIAN_SCORES.PAIN_SUBSCALE) - }) - - it('should return the median score for the "ADL" subscale', function () { - const score = view_result('ADL')(outcome) - expect(score).toEqual(MEDIAN_SCORES.ADL_SUBSCALE) - }) - - it('should return the median score for the "Mobility" subscale', function () { - const score = view_result('MOBILITY')(outcome) - expect(score).toEqual(MEDIAN_SCORES.MOBILITY_SUBSCALE) - }) - - it('should return the median score for the "Strength" subscale', function () { - const score = view_result('STRENGTH')(outcome) - expect(score).toEqual(MEDIAN_SCORES.STRENGTH_SUBSCALE) - }) - - it('should return the median total score', function () { - const score = view_result('TS')(outcome) - expect(score).toEqual(MEDIAN_SCORES.TOTAL) - }) - }) - - describe('when best response is passed', function () { - const outcome = cms_calculation(best_response) - - it('should return the best score for the "Pain" subscale', function () { - const score = view_result('PAIN')(outcome) - expect(score).toEqual(BEST_SCORES.PAIN_SUBSCALE) - }) - - it('should return the best score for the "ADL" subscale', function () { - const score = view_result('ADL')(outcome) - expect(score).toEqual(BEST_SCORES.ADL_SUBSCALE) - }) - - it('should return the best score for the "Mobility" subscale', function () { - const score = view_result('MOBILITY')(outcome) - expect(score).toEqual(BEST_SCORES.MOBILITY_SUBSCALE) - }) - - it('should return the best score for the "Strength" subscale', function () { - const score = view_result('STRENGTH')(outcome) - expect(score).toEqual(BEST_SCORES.STRENGTH_SUBSCALE) - }) - - it('should return the best total score', function () { - const score = view_result('TS')(outcome) - expect(score).toEqual(BEST_SCORES.TOTAL) - }) - }) - - describe('when a random response is passed', function () { - const outcome = cms_calculation(random_response) - - it('should return the expected score for the "Pain" subscale', function () { - const score = view_result('PAIN')(outcome) - const EXPECTED_SCORE = 11 - - expect(score).toEqual(EXPECTED_SCORE) - }) - - it('should return the expected score for the "ADL" subscale', function () { - const score = view_result('ADL')(outcome) - const EXPECTED_SCORE = 8 - - expect(score).toEqual(EXPECTED_SCORE) - }) - - it('should return the expected score for the "Mobility" subscale', function () { - const score = view_result('MOBILITY')(outcome) - const EXPECTED_SCORE = 12 - - expect(score).toEqual(EXPECTED_SCORE) - }) - - it('should return the expected score for the "Strength" subscale', function () { - const score = view_result('STRENGTH')(outcome) - const EXPECTED_SCORE = 25 - - expect(score).toEqual(EXPECTED_SCORE) - }) - - it('should return the expected total score', function () { - const score = view_result('TS')(outcome) - const EXPECTED_SCORE = 56 - - expect(score).toEqual(EXPECTED_SCORE) - }) - }) - }) - - describe('values entered by the user are checked to verify they are inside specified ranges', function () { - describe('when an answer is not a number', function () { - it('should throw an error', function () { - expect(() => - cms_calculation({ - Q01_PAIN_SCORE: "I'm not a number", - }), - ).toThrow(ZodError) - }) - }) - describe('when an answer is not allowed (e.g. is below the expected range)', function () { - it('should throw an error', function () { - expect(() => - cms_calculation({ - Q01_PAIN_SCORE: -1, - }), - ).toThrow(ZodError) - }) - }) - describe('when an answer is not allowed (e.g. is above the expected range)', function () { - it('should return throw an error', function () { - expect(() => - cms_calculation({ - Q01_PAIN_SCORE: 16, - }), - ).toThrow(ZodError) - }) - }) - }) - }) -}) diff --git a/todo/constant_murley_score/orthotoolkit_version/constant_murley_score.ts b/todo/constant_murley_score/orthotoolkit_version/constant_murley_score.ts deleted file mode 100644 index 3befc3a..0000000 --- a/todo/constant_murley_score/orthotoolkit_version/constant_murley_score.ts +++ /dev/null @@ -1,121 +0,0 @@ -import R from 'ramda' - -import type { - CalculationType, - IncomingCalculationInputType, - SubscaleType, - WIPCalculationResultType, -} from '../../../src/types/calculations.types' -import { rawInputValueLens } from '../../../lib/calculation_variants/api/input/lenses' -import { - inputsInSubscaleLens, - scoreLens, - subscaleIdLens, -} from '../../../lib/calculation_variants/api/subscale/lenses' -import { add_response_values_to_subscale_inputs } from '../../../lib/calculation_variants/calculation_with_subscales' -import { create_calculation } from '../../../lib/create_calculation' -import { MISSING_MESSAGE } from '../../../PARAMETERS' -import { is_numeric } from '../../../src/calculation_suite/calculations/shared_functions' -import { CMS_OUTPUT, CMS_SUBSCALES } from './definition' -import { - calculate_mobility_subscale_score, - calculate_stength_subscale_score, -} from './helpers' - -const calculate_score_for_each_subscale = ( - subscale: SubscaleType, -): SubscaleType => { - const subscale_id = R.view(subscaleIdLens, subscale) - const inputs_in_subscale = R.view(inputsInSubscaleLens, subscale) - - /** - * Calculate score for strength subscale - */ - if (subscale_id === 'STRENGTH') - return calculate_stength_subscale_score(subscale) - - /** - * Calculate score for strength subscale - */ - if (subscale_id === 'MOBILITY') - return calculate_mobility_subscale_score(subscale) - - /** All other subscales */ - //@ts-expect-error to do - const valid_answers = R.compose( - R.defaultTo([]), - R.filter(is_numeric), - R.map(raw_value => Number(raw_value)), - R.map(input => R.view(rawInputValueLens, input)), - )(inputs_in_subscale) - - //@ts-expect-error to do - if (valid_answers.length !== inputs_in_subscale.length) { - return R.set(scoreLens, MISSING_MESSAGE, subscale) - } - - //@ts-expect-error to do - return R.set(scoreLens, R.sum(valid_answers), subscale) -} - -export const calculate_subscale_scores = ( - calculation_input: IncomingCalculationInputType, -): SubscaleType[] => - R.compose( - R.map(calculate_score_for_each_subscale), - add_response_values_to_subscale_inputs(calculation_input), - )(CMS_SUBSCALES) - -const add_total_score = ( - subscales_with_scores: Array, -): WIPCalculationResultType => { - //@ts-expect-error to do - const valid_subscale_scores = R.compose( - R.defaultTo([]), - R.filter(is_numeric), - R.map(score => Number(score)), - R.flatten, - R.map(subscale => R.view(scoreLens, subscale)), - )(subscales_with_scores) - - //@ts-expect-error to do - if (valid_subscale_scores.length === 0) { - return [ - ...subscales_with_scores.map(({ id, score }) => ({ - id, - score, - })), - { - id: 'TS', - score: MISSING_MESSAGE, - }, - ] - } - - return [ - ...subscales_with_scores.map(({ id, score }) => ({ - id, - score, - })), - { - id: 'TS', - //@ts-expect-error to do - score: R.sum(valid_subscale_scores), - }, - ] -} - -export const specific_steps_cms_calc = [ - add_total_score, - calculate_subscale_scores, -] - -export const constant_murley_score: CalculationType = create_calculation({ - calculation_name: 'Constant Murley Score (CMS) - OrthoToolKit version', - readme_location: __dirname, - calculation_steps: specific_steps_cms_calc, - calculation_definition: { - input_definition: CMS_SUBSCALES, - output_definition: CMS_OUTPUT, - }, -}) diff --git a/todo/constant_murley_score/orthotoolkit_version/definition/constant_murley_output.ts b/todo/constant_murley_score/orthotoolkit_version/definition/constant_murley_output.ts deleted file mode 100644 index becfd64..0000000 --- a/todo/constant_murley_score/orthotoolkit_version/definition/constant_murley_output.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { CalculationOutputDefinition } from '../../../../src/types/calculations.types' - -export const CMS_OUTPUT: CalculationOutputDefinition[] = [ - { - subresult_id: 'PAIN', - label: { en: 'Pain', nl: 'Pijn' }, - type: 'number', - }, - { - subresult_id: 'ADL', - label: { - en: 'Activities Daily living', - nl: 'Activiteiten dagelijks leven', - }, - type: 'number', - }, - { - subresult_id: 'MOBILITY', - label: { - en: 'Mobility', - nl: 'Mobiliteit', - }, - type: 'number', - }, - { - subresult_id: 'STRENGTH', - label: { - en: 'Strength', - nl: 'Kracht', - }, - type: 'number', - }, - { - subresult_id: 'TS', - label: { en: 'Total score' }, - type: 'number', - }, -] diff --git a/todo/constant_murley_score/orthotoolkit_version/definition/constant_murley_scales.ts b/todo/constant_murley_score/orthotoolkit_version/definition/constant_murley_scales.ts deleted file mode 100644 index 5b31b3d..0000000 --- a/todo/constant_murley_score/orthotoolkit_version/definition/constant_murley_scales.ts +++ /dev/null @@ -1,377 +0,0 @@ -import { type DefaultSubscaleType } from '../../../../src/types/calculations.types' - -export const CMS_SUBSCALES: Array = [ - { - id: 'PAIN', - input_ids_in_subscale: [ - { - input_id: 'Q01_PAIN_SCORE', - label: { - en: 'Score the highest pain level that you have experienced in your shoulder during ordinary activities within the last 24 hours.', - nl: 'Wat is de hoogste pijnscore die u de laatste 24u heeft ervaren in uw schouder tijdens het uitvoeren van normale activiteiten?', - }, - type: { - type: 'number', - component: 'slider', - range: { - min: { - value: 0, - label: { en: 'Intolerable pain', nl: 'Ondraaglijke pijn' }, - }, - max: { - value: 15, - label: { en: 'No pain', nl: 'Geen pijn' }, - }, - }, - }, - }, - ], - }, - { - id: 'ADL', - input_ids_in_subscale: [ - { - input_id: 'Q03_WORK_ADL_SCORE', - label: { - en: 'How much of your normal daily work does your shoulder allow you to perform?', - nl: 'Hoeveel van uw normale dagelijkse werk laat uw schouder toe om te doen?', - }, - type: { - type: 'number', - component: 'slider', - range: { - min: { - value: 0, - label: { en: 'None', nl: 'Geen' }, - }, - max: { - value: 4, - label: { en: 'All', nl: 'Allemaal' }, - }, - }, - }, - }, - { - input_id: 'Q04_SPORTS_HOBBY_SCORE', - label: { - en: 'How much of your normal recreational activity does your shoulder allow you to perform?', - nl: 'Hoeveel van uw normale recreationale activiteiten laat uw schouder toe om te doen?', - }, - type: { - type: 'number', - component: 'slider', - range: { - min: { - value: 0, - label: { en: 'None', nl: 'Geen' }, - }, - max: { - value: 4, - label: { en: 'All', nl: 'Allemaal' }, - }, - }, - }, - }, - { - input_id: 'Q02_SLEEP_SCORE', - label: { - en: 'Is your sleep disturbed by your shoulder?', - nl: 'Is je slaap verstoord door uw schouder?', - }, - type: { - type: 'number', - allowed_answers: [ - { - value: 2, - label: { en: 'Undisturbed sleep', nl: 'Ik slaap onverstoord' }, - }, - { - value: 1, - label: { - en: 'Occasional disturbance', - nl: 'Mijn slaap is occasioneel verstoord', - }, - }, - { - value: 0, - label: { - en: 'Every night', - nl: 'Mijn slaap is elke nacht verstoord', - }, - }, - ], - }, - }, - { - input_id: 'Q05_ADL_FUNCTIONING_SCORE', - label: { - en: 'To which level can you use your hand comfortably?', - nl: 'Tot welke hoogte kan u uw hand comfortabel gebruiken?', - }, - type: { - type: 'number', - allowed_answers: [ - { - value: 0, - label: { en: 'Below the waist', nl: 'Hand onder middel' }, - }, - { - value: 2, - label: { en: 'Up to the waist', nl: 'Hand tot middel' }, - }, - { - value: 4, - label: { - en: 'Up to the xiphoid/sternum', - nl: 'Hand tot borsthoogte', - }, - }, - { - value: 6, - label: { en: 'Up to the neck', nl: 'Hand schouderhoogte / nek' }, - }, - { - value: 8, - label: { - en: 'Up to the top of the head', - nl: 'Hand op hoofdhoogte', - }, - }, - { - value: 10, - label: { en: 'Above the head', nl: 'Hand boven hoofd hoogte' }, - }, - ], - }, - }, - ], - }, - { - id: 'MOBILITY', - input_ids_in_subscale: [ - { - input_id: 'Q06_FLEXION_ROM', - label: { - en: 'Forward flexion', - nl: 'Elevatie', - }, - type: { - type: 'number', - allowed_answers: [ - { - value: 0, - label: { en: '0-30 degrees', nl: '0-30 graden' }, - }, - { - value: 2, - label: { en: '31-60 degrees', nl: '31-60 graden' }, - }, - { - value: 4, - label: { - en: '61-90 degrees', - nl: '61-90 graden', - }, - }, - { - value: 6, - label: { en: '91-120 degrees', nl: '91-120 graden' }, - }, - { - value: 8, - label: { - en: '121-150 degrees', - nl: '121-150 graden', - }, - }, - { - value: 10, - label: { en: '151+ degrees', nl: '151+ graden' }, - }, - ], - }, - }, - { - input_id: 'Q07_ABDUCTION_ROM', - label: { - en: 'Lateral elevation (abduction)', - nl: 'Abductie', - }, - type: { - type: 'number', - allowed_answers: [ - { - value: 0, - label: { en: '0-30 degrees', nl: '0-30 graden' }, - }, - { - value: 2, - label: { en: '31-60 degrees', nl: '31-60 graden' }, - }, - { - value: 4, - label: { - en: '61-90 degrees', - nl: '61-90 graden', - }, - }, - { - value: 6, - label: { en: '91-120 degrees', nl: '91-120 graden' }, - }, - { - value: 8, - label: { - en: '121-150 degrees', - nl: '121-150 graden', - }, - }, - { - value: 10, - label: { en: '151+ degrees', nl: '151+ graden' }, - }, - ], - }, - }, - { - input_id: 'Q08_ENDOROTATION_ROM', - label: { - en: 'Internal rotation', - nl: 'Interne rotatie', - }, - info: { - en: 'Patient points to anatomical landmarks with thumb', - }, - type: { - type: 'number', - allowed_answers: [ - { - value: 0, - label: { - nl: 'Hand laterale zijde trochanter', - en: 'Lateral aspect of the thigh', - }, - }, - { - value: 2, - label: { - nl: 'Duim achter bil / hand naar bil', - en: 'Behind the buttock', - }, - }, - { - value: 4, - label: { - nl: 'Duim op andere SIG / hand naar lumbosacraal', - en: 'Sacroiliac joint', - }, - }, - { value: 6, label: { nl: 'Duim/hand naar lumbaal', en: 'Waist' } }, - { - value: 8, - label: { - nl: 'Duim op Th12 / hand naar TLO', - en: '12th thoracic vertebra', - }, - }, - { - value: 10, - label: { - nl: 'Duim tussen scapulae / Hand los van de rug', - en: 'Interscapular level', - }, - }, - ], - }, - }, - { - input_id: 'Q09_EXOROTATION_ROM', - label: { - en: 'External rotation (check all that apply)', - nl: 'Externe rotatie (check all that apply)', - }, - type: { - type: 'numbers_array', - allowed_answers: [ - { - value: 1, - label: { - en: 'Hands behind head, elbows forward', - nl: 'Hand achter op het hoofd met elleboog naar voren', - }, - }, - { - value: 2, - label: { - en: 'Hands behind head, elbows back', - nl: 'Hand achter op het hoofd met elleboog naar buiten', - }, - }, - { - value: 3, - label: { - en: 'Hands to the top of the head, elbows forward', - nl: 'Hand op het hoofd met elleboog naar voren', - }, - }, - { - value: 4, - label: { - en: 'Hands to the top of the head, elbows back', - nl: 'Hand op het hoofd met elleboog naar buiten', - }, - }, - { - value: 5, - label: { - en: 'Full elevation', - nl: 'Volledige elevatie vanaf bovenkant hoofd', - }, - }, - ], - }, - }, - ], - }, - { - id: 'STRENGTH', - input_ids_in_subscale: [ - { - input_id: 'Q10_ATTEMPT_1', - label: { en: 'Stength - Attempt 1', nl: 'Kracht - Poging 1' }, - type: { - type: 'number', - }, - info: { - en: 'Result should be entered in kg', - nl: 'Geef resultaat in kg in', - }, - format: 'kg', - }, - { - input_id: 'Q11_ATTEMPT_2', - label: { en: 'Stength - Attempt 2', nl: 'Kracht - Poging 2' }, - type: { - type: 'number', - }, - info: { - en: 'Result should be entered in kg', - nl: 'Geef resultaat in kg in', - }, - format: 'kg', - }, - { - input_id: 'Q12_ATTEMPT_3', - label: { en: 'Stength - Attempt 3', nl: 'Kracht - Poging 3' }, - type: { - type: 'number', - }, - info: { - en: 'Result should be entered in kg', - nl: 'Geef resultaat in kg in', - }, - format: 'kg', - }, - ], - }, -] diff --git a/todo/constant_murley_score/orthotoolkit_version/definition/index.ts b/todo/constant_murley_score/orthotoolkit_version/definition/index.ts deleted file mode 100644 index fc29d6d..0000000 --- a/todo/constant_murley_score/orthotoolkit_version/definition/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { CMS_OUTPUT } from './constant_murley_output' -export { CMS_SUBSCALES } from './constant_murley_scales' diff --git a/todo/constant_murley_score/orthotoolkit_version/helpers/calculate_mobility_subscale_score.ts b/todo/constant_murley_score/orthotoolkit_version/helpers/calculate_mobility_subscale_score.ts deleted file mode 100644 index 9aa3bba..0000000 --- a/todo/constant_murley_score/orthotoolkit_version/helpers/calculate_mobility_subscale_score.ts +++ /dev/null @@ -1,51 +0,0 @@ -import R from 'ramda' - -import type { SubscaleType } from '../../../../src/types/calculations.types' -import { - inputIdLens, - rawInputValueLens, -} from '../../../../lib/calculation_variants/api/input/lenses' -import { - inputsInSubscaleLens, - scoreLens, -} from '../../../../lib/calculation_variants/api/subscale/lenses' -import { MISSING_MESSAGE } from '../../../../PARAMETERS' -import { is_numeric } from '../../../../src/calculation_suite/calculations/shared_functions' - -export const calculate_mobility_subscale_score = ( - subscale: SubscaleType, -): SubscaleType => { - const inputs_in_subscale = R.view(inputsInSubscaleLens, subscale) - const EXOROTATION_INPUT_ID = 'Q09_EXOROTATION_ROM' - - //@ts-expect-error to do - const inputs_without_exorotation = R.compose( - R.defaultTo([]), - R.filter(is_numeric), - R.map(raw_value => Number(raw_value)), - R.map(input => R.view(rawInputValueLens, input)), - R.filter(input => R.view(inputIdLens, input) !== EXOROTATION_INPUT_ID), - )(inputs_in_subscale) - - const exorotation_input = R.compose( - R.flatten, - R.map(input => { - const value = R.view(rawInputValueLens, input) - - if (R.isNil(value) || value === MISSING_MESSAGE) return [] - - return value - }), - R.filter(input => R.view(inputIdLens, input) === EXOROTATION_INPUT_ID), - )(inputs_in_subscale) - - //@ts-expect-error to do - const inputs_without_exorotation_score = R.sum(inputs_without_exorotation) - - const POINTS_PER_CRITERIUM = 2 - const exorotation_score = exorotation_input.length * POINTS_PER_CRITERIUM - - const score = inputs_without_exorotation_score + exorotation_score - - return R.set(scoreLens, score, subscale) -} diff --git a/todo/constant_murley_score/orthotoolkit_version/helpers/calculate_stength_subscale_score.ts b/todo/constant_murley_score/orthotoolkit_version/helpers/calculate_stength_subscale_score.ts deleted file mode 100644 index c105625..0000000 --- a/todo/constant_murley_score/orthotoolkit_version/helpers/calculate_stength_subscale_score.ts +++ /dev/null @@ -1,37 +0,0 @@ -import R from 'ramda' - -import type { SubscaleType } from '../../../../src/types/calculations.types' -import { rawInputValueLens } from '../../../../lib/calculation_variants/api/input/lenses' -import { - inputsInSubscaleLens, - scoreLens, -} from '../../../../lib/calculation_variants/api/subscale/lenses' -import { MISSING_MESSAGE } from '../../../../PARAMETERS' -import { is_numeric } from '../../../../src/calculation_suite/calculations/shared_functions' - -export const calculate_stength_subscale_score = ( - subscale: SubscaleType, -): SubscaleType => { - const inputs_in_subscale = R.view(inputsInSubscaleLens, subscale) - const MAX_SCALE_SCORE = 25 - const CONVERT_KG_TO_LBS = 2.2 - - //@ts-expect-error to do - const stength_attempts = R.compose( - R.defaultTo([]), - R.filter(is_numeric), - R.map(raw_value => Number(raw_value)), - R.map(input => R.view(rawInputValueLens, input)), - )(inputs_in_subscale) - - //@ts-expect-error to do - if (stength_attempts.length === 0) { - return R.set(scoreLens, MISSING_MESSAGE, subscale) - } - - //@ts-expect-error to do - const max_strength_attempt = Math.max(...stength_attempts) - const score = Math.round(max_strength_attempt * CONVERT_KG_TO_LBS) - - return R.set(scoreLens, R.min(score, MAX_SCALE_SCORE), subscale) -} diff --git a/todo/constant_murley_score/orthotoolkit_version/helpers/index.ts b/todo/constant_murley_score/orthotoolkit_version/helpers/index.ts deleted file mode 100644 index 80cf3b6..0000000 --- a/todo/constant_murley_score/orthotoolkit_version/helpers/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { calculate_stength_subscale_score } from './calculate_stength_subscale_score' -export { calculate_mobility_subscale_score } from './calculate_mobility_subscale_score'