@openmrs/esm-form-engine-lib 2.1.0-pre.1362
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +12 -0
- package/.eslintignore +2 -0
- package/.eslintrc +58 -0
- package/.husky/pre-commit +6 -0
- package/.husky/pre-push +6 -0
- package/.prettierignore +4 -0
- package/LICENSE.txt +401 -0
- package/README.md +136 -0
- package/__mocks__/concepts.mock.json +140 -0
- package/__mocks__/forms/afe-forms/component_art.json +38 -0
- package/__mocks__/forms/afe-forms/component_preclinic-review.json +38 -0
- package/__mocks__/forms/afe-forms/demo_hts-form.json +62 -0
- package/__mocks__/forms/afe-forms/form-component.json +38 -0
- package/__mocks__/forms/afe-forms/mini-form.json +31 -0
- package/__mocks__/forms/afe-forms/nested-form1.json +38 -0
- package/__mocks__/forms/afe-forms/nested-form2.json +38 -0
- package/__mocks__/forms/afe-forms/test-orders.json +72 -0
- package/__mocks__/forms/afe-forms/test-schema-transformer-form.json +88 -0
- package/__mocks__/forms/rfe-forms/age-validation-form.json +58 -0
- package/__mocks__/forms/rfe-forms/bmi-test-form.json +69 -0
- package/__mocks__/forms/rfe-forms/bsa-test-form.json +69 -0
- package/__mocks__/forms/rfe-forms/component_art.json +1705 -0
- package/__mocks__/forms/rfe-forms/component_preclinic-review.json +480 -0
- package/__mocks__/forms/rfe-forms/conditional-answered-form.json +97 -0
- package/__mocks__/forms/rfe-forms/conditional-required-form.json +281 -0
- package/__mocks__/forms/rfe-forms/demo_hts-form.json +346 -0
- package/__mocks__/forms/rfe-forms/edd-test-form.json +88 -0
- package/__mocks__/forms/rfe-forms/external_data_source_form.json +43 -0
- package/__mocks__/forms/rfe-forms/filter-answer-options-test-form.json +87 -0
- package/__mocks__/forms/rfe-forms/form-component.json +43 -0
- package/__mocks__/forms/rfe-forms/forms-loader.test.schema.ts +209 -0
- package/__mocks__/forms/rfe-forms/historical-expressions-form.json +170 -0
- package/__mocks__/forms/rfe-forms/labour_and_delivery_test_form.json +374 -0
- package/__mocks__/forms/rfe-forms/mini-form.json +29 -0
- package/__mocks__/forms/rfe-forms/mockHistoricalvisitsEncounter.json +89 -0
- package/__mocks__/forms/rfe-forms/months-on-art-form.json +90 -0
- package/__mocks__/forms/rfe-forms/multi-select-form.json +86 -0
- package/__mocks__/forms/rfe-forms/nested-form1.json +43 -0
- package/__mocks__/forms/rfe-forms/nested-form2.json +43 -0
- package/__mocks__/forms/rfe-forms/next-visit-test-form.json +78 -0
- package/__mocks__/forms/rfe-forms/obs-group-test_form.json +137 -0
- package/__mocks__/forms/rfe-forms/obs-list-data.ts +37 -0
- package/__mocks__/forms/rfe-forms/post-submission-test-form.json +116 -0
- package/__mocks__/forms/rfe-forms/reference-by-mapping-form.json +54 -0
- package/__mocks__/forms/rfe-forms/required-form.json +50 -0
- package/__mocks__/forms/rfe-forms/sample_fields.json +36 -0
- package/__mocks__/forms/rfe-forms/test-enrolment-form.json +241 -0
- package/__mocks__/forms/rfe-forms/treatment-end-date-test-form.json +121 -0
- package/__mocks__/forms/rfe-forms/viral-load-status-form.json +75 -0
- package/__mocks__/forms/rfe-forms/zscore-bmi-for-age-form.json +79 -0
- package/__mocks__/forms/rfe-forms/zscore-height-for-age-form.json +79 -0
- package/__mocks__/forms/rfe-forms/zscore-weight-height-form.json +77 -0
- package/__mocks__/packages/hiv/forms/hts_poc/1.0.json +8 -0
- package/__mocks__/packages/hiv/forms/hts_poc/1.1.json +91 -0
- package/__mocks__/packages/test-forms-registry.ts +12 -0
- package/__mocks__/patient.mock.ts +173 -0
- package/__mocks__/react-i18next.js +49 -0
- package/__mocks__/react-markdown.tsx +5 -0
- package/__mocks__/session.mock.ts +117 -0
- package/__mocks__/single-spa-react.js +11 -0
- package/__mocks__/use-initial-values/encounter.mock.json +963 -0
- package/__mocks__/use-initial-values/patient.mock.json +73 -0
- package/__mocks__/visit.mock.ts +19 -0
- package/dist/openmrs-esm-form-engine-lib.js +1 -0
- package/jest.config.js +30 -0
- package/package.json +104 -0
- package/prettier.config.js +8 -0
- package/readme/form-engine.jpeg +0 -0
- package/src/adapters/control-adapter.ts +29 -0
- package/src/adapters/encounter-datetime-adapter.ts +38 -0
- package/src/adapters/encounter-location-adapter.ts +39 -0
- package/src/adapters/encounter-provider-adapter.ts +48 -0
- package/src/adapters/encounter-role-adapter.ts +54 -0
- package/src/adapters/inline-date-adapter.ts +58 -0
- package/src/adapters/obs-adapter.ts +280 -0
- package/src/adapters/obs-comment-adapter.ts +60 -0
- package/src/adapters/orders-adapter.ts +75 -0
- package/src/adapters/patient-identifier-adapter.ts +40 -0
- package/src/adapters/program-state-adapter.ts +52 -0
- package/src/api/index.ts +178 -0
- package/src/components/error/error-modal.component.tsx +37 -0
- package/src/components/error/error.scss +4 -0
- package/src/components/extension/extension-parcel.component.tsx +32 -0
- package/src/components/field-label/field-label.component.tsx +32 -0
- package/src/components/field-label/field-label.scss +11 -0
- package/src/components/group/obs-group.component.tsx +29 -0
- package/src/components/group/obs-group.scss +12 -0
- package/src/components/inputs/content-switcher/content-switcher.component.tsx +71 -0
- package/src/components/inputs/content-switcher/content-switcher.scss +55 -0
- package/src/components/inputs/date/date.component.tsx +149 -0
- package/src/components/inputs/date/date.scss +36 -0
- package/src/components/inputs/file/camera/camera.component.tsx +34 -0
- package/src/components/inputs/file/camera/camera.scss +3 -0
- package/src/components/inputs/file/file.component.tsx +159 -0
- package/src/components/inputs/file/file.scss +101 -0
- package/src/components/inputs/fixed-value/fixed-value.component.tsx +19 -0
- package/src/components/inputs/markdown/markdown-wrapper.component.tsx +14 -0
- package/src/components/inputs/markdown/markdown.component.tsx +8 -0
- package/src/components/inputs/multi-select/multi-select.component.tsx +151 -0
- package/src/components/inputs/multi-select/multi-select.scss +25 -0
- package/src/components/inputs/multi-select/multi-select.test.tsx +90 -0
- package/src/components/inputs/number/number.component.tsx +69 -0
- package/src/components/inputs/number/number.scss +15 -0
- package/src/components/inputs/radio/radio.component.tsx +79 -0
- package/src/components/inputs/radio/radio.scss +36 -0
- package/src/components/inputs/select/dropdown.component.tsx +73 -0
- package/src/components/inputs/select/dropdown.scss +11 -0
- package/src/components/inputs/select/dropdown.test.tsx +120 -0
- package/src/components/inputs/text/text.component.tsx +65 -0
- package/src/components/inputs/text/text.scss +15 -0
- package/src/components/inputs/text/text.test.tsx +104 -0
- package/src/components/inputs/text-area/text-area.component.tsx +63 -0
- package/src/components/inputs/text-area/text-area.scss +11 -0
- package/src/components/inputs/toggle/toggle.component.tsx +66 -0
- package/src/components/inputs/toggle/toggle.scss +12 -0
- package/src/components/inputs/tooltip/tooltip.component.tsx +23 -0
- package/src/components/inputs/tooltip/tooltip.scss +8 -0
- package/src/components/inputs/ui-select-extended/ui-select-extended.component.tsx +187 -0
- package/src/components/inputs/ui-select-extended/ui-select-extended.scss +15 -0
- package/src/components/inputs/ui-select-extended/ui-select-extended.test.tsx +211 -0
- package/src/components/inputs/unspecified/unspecified.component.tsx +74 -0
- package/src/components/inputs/unspecified/unspecified.scss +7 -0
- package/src/components/inputs/unspecified/unspecified.test.tsx +95 -0
- package/src/components/inputs/workspace-launcher/workspace-launcher.component.tsx +35 -0
- package/src/components/inputs/workspace-launcher/workspace-launcher.scss +15 -0
- package/src/components/label/label.component.tsx +20 -0
- package/src/components/label/label.scss +11 -0
- package/src/components/loaders/loader.component.tsx +16 -0
- package/src/components/loaders/loader.scss +20 -0
- package/src/components/patient-banner/patient-banner.component.tsx +20 -0
- package/src/components/patient-banner/patient-banner.scss +12 -0
- package/src/components/previous-value-review/previous-value-review.component.tsx +49 -0
- package/src/components/previous-value-review/previous-value-review.scss +36 -0
- package/src/components/processor-factory/form-processor-factory.component.tsx +127 -0
- package/src/components/renderer/custom-hooks-renderer.component.tsx +30 -0
- package/src/components/renderer/field/fieldLogic.ts +214 -0
- package/src/components/renderer/field/form-field-renderer.component.tsx +281 -0
- package/src/components/renderer/field/form-field-renderer.scss +5 -0
- package/src/components/renderer/form/form-renderer.component.tsx +89 -0
- package/src/components/renderer/form/state.ts +54 -0
- package/src/components/renderer/page/page.renderer.component.tsx +50 -0
- package/src/components/renderer/page/page.renderer.scss +36 -0
- package/src/components/renderer/section/section-renderer.component.tsx +21 -0
- package/src/components/renderer/section/section-renderer.scss +19 -0
- package/src/components/repeat/helpers.test.ts +29 -0
- package/src/components/repeat/helpers.ts +68 -0
- package/src/components/repeat/repeat-controls.component.tsx +38 -0
- package/src/components/repeat/repeat-controls.scss +7 -0
- package/src/components/repeat/repeat.component.tsx +201 -0
- package/src/components/repeat/repeat.scss +30 -0
- package/src/components/repeat/repeat.test.ts +29 -0
- package/src/components/sidebar/sidebar.component.tsx +134 -0
- package/src/components/sidebar/sidebar.scss +121 -0
- package/src/components/value/value.component.tsx +27 -0
- package/src/components/value/value.scss +17 -0
- package/src/components/value/view/field-value-view.component.tsx +33 -0
- package/src/components/value/view/field-value-view.scss +31 -0
- package/src/constants.ts +12 -0
- package/src/datasources/concept-data-source.ts +42 -0
- package/src/datasources/data-source.ts +23 -0
- package/src/datasources/encounter-role-datasource.ts +15 -0
- package/src/datasources/historical-data-source.ts +11 -0
- package/src/datasources/location-data-source.ts +27 -0
- package/src/datasources/provider-datasource.ts +15 -0
- package/src/datasources/select-concept-answers-datasource.ts +15 -0
- package/src/declarations.d.ts +4 -0
- package/src/external-function-context.tsx +8 -0
- package/src/form-context.tsx +42 -0
- package/src/form-engine.component.tsx +178 -0
- package/src/form-engine.scss +140 -0
- package/src/form-engine.test.tsx +817 -0
- package/src/globals.ts +1 -0
- package/src/hooks/useClobData.tsx +21 -0
- package/src/hooks/useConcepts.tsx +55 -0
- package/src/hooks/useDatasourceDependentValue.ts +16 -0
- package/src/hooks/useEncounter.tsx +32 -0
- package/src/hooks/useEncounterRole.tsx +15 -0
- package/src/hooks/useEvaluateFormFieldExpressions.ts +138 -0
- package/src/hooks/useFieldValidationResults.ts +18 -0
- package/src/hooks/useFormCollapse.tsx +36 -0
- package/src/hooks/useFormFieldValidators.ts +22 -0
- package/src/hooks/useFormFieldValueAdapters.ts +24 -0
- package/src/hooks/useFormFields.ts +37 -0
- package/src/hooks/useFormFieldsMeta.ts +48 -0
- package/src/hooks/useFormJson.test.tsx +173 -0
- package/src/hooks/useFormJson.tsx +237 -0
- package/src/hooks/useFormStateHelpers.ts +50 -0
- package/src/hooks/useFormsConfig.tsx +27 -0
- package/src/hooks/useInitialValues.ts +38 -0
- package/src/hooks/usePatientData.tsx +32 -0
- package/src/hooks/usePatientPrograms.ts +32 -0
- package/src/hooks/usePostSubmissionActions.test.tsx +42 -0
- package/src/hooks/usePostSubmissionActions.ts +31 -0
- package/src/hooks/useProcessorDependencies.ts +30 -0
- package/src/hooks/useRestMaxResultsCount.ts +5 -0
- package/src/hooks/useSystemSetting.ts +36 -0
- package/src/hooks/useWorkspaceLayout.ts +29 -0
- package/src/index.ts +12 -0
- package/src/lifecycle.ts +33 -0
- package/src/post-submission-actions/program-enrollment-action.ts +138 -0
- package/src/processors/encounter/encounter-form-processor.ts +337 -0
- package/src/processors/encounter/encounter-processor-helper.ts +320 -0
- package/src/processors/form-processor.ts +41 -0
- package/src/provider/form-factory-helper.ts +100 -0
- package/src/provider/form-factory-provider.tsx +169 -0
- package/src/provider/form-provider.tsx +37 -0
- package/src/registry/inbuilt-components/InbuiltPostSubmissionActions.ts +9 -0
- package/src/registry/inbuilt-components/control-templates.ts +57 -0
- package/src/registry/inbuilt-components/inbuiltControls.ts +99 -0
- package/src/registry/inbuilt-components/inbuiltDataSources.ts +41 -0
- package/src/registry/inbuilt-components/inbuiltFieldValueAdapters.ts +64 -0
- package/src/registry/inbuilt-components/inbuiltTransformers.ts +10 -0
- package/src/registry/inbuilt-components/inbuiltValidators.ts +33 -0
- package/src/registry/inbuilt-components/template-component-map.ts +28 -0
- package/src/registry/registry.test.ts +20 -0
- package/src/registry/registry.ts +261 -0
- package/src/routes.json +1 -0
- package/src/setupI18n.ts +16 -0
- package/src/setupTests.ts +5 -0
- package/src/transformers/default-schema-transformer.test.ts +155 -0
- package/src/transformers/default-schema-transformer.ts +239 -0
- package/src/types/domain.ts +129 -0
- package/src/types/index.ts +130 -0
- package/src/types/schema.ts +238 -0
- package/src/typings.d.ts +9 -0
- package/src/utils/boolean-utils.ts +25 -0
- package/src/utils/common-expression-helpers.ts +503 -0
- package/src/utils/common-utils.test.ts +136 -0
- package/src/utils/common-utils.ts +55 -0
- package/src/utils/error-utils.ts +37 -0
- package/src/utils/expression-parser.test.ts +308 -0
- package/src/utils/expression-parser.ts +158 -0
- package/src/utils/expression-runner.test.ts +387 -0
- package/src/utils/expression-runner.ts +219 -0
- package/src/utils/form-helper.test.ts +482 -0
- package/src/utils/form-helper.ts +210 -0
- package/src/utils/form-page-utils.ts +13 -0
- package/src/utils/forms-loader.test.ts +323 -0
- package/src/utils/forms-loader.ts +306 -0
- package/src/utils/post-submission-action-helper.ts +71 -0
- package/src/utils/test-utils.ts +54 -0
- package/src/utils/zscore-service.ts +59 -0
- package/src/validators/conditional-answered-validator.test.ts +61 -0
- package/src/validators/conditional-answered-validator.ts +17 -0
- package/src/validators/date-validator.test.ts +46 -0
- package/src/validators/date-validator.ts +19 -0
- package/src/validators/default-value-validator.test.ts +90 -0
- package/src/validators/default-value-validator.ts +36 -0
- package/src/validators/form-validator.test.ts +188 -0
- package/src/validators/form-validator.ts +95 -0
- package/src/validators/js-expression-validator.test.ts +118 -0
- package/src/validators/js-expression-validator.ts +44 -0
- package/src/validators/schema.ts +34 -0
- package/src/zscore/bfa_boys_5_above.json +2522 -0
- package/src/zscore/bfa_girls_5_above.json +2522 -0
- package/src/zscore/hfa_boys_5_above.json +2186 -0
- package/src/zscore/hfa_boys_below5.json +22286 -0
- package/src/zscore/hfa_girls_5_above.json +2186 -0
- package/src/zscore/hfa_girls_below5.json +22286 -0
- package/src/zscore/wfl_boys_below5.json +7814 -0
- package/src/zscore/wfl_girls_below5.json +7814 -0
- package/src/zscore-tests/bmi-age.test.tsx +88 -0
- package/src/zscore-tests/height-age.test.tsx +96 -0
- package/src/zscore-tests/weight-height.test.tsx +87 -0
- package/tools/i18next-parser.config.js +93 -0
- package/translations/en.json +47 -0
- package/translations/es.json +38 -0
- package/translations/fr.json +38 -0
- package/translations/km.json +38 -0
- package/tsconfig.json +19 -0
- package/turbo.json +15 -0
- package/webpack.config.js +1 -0
@@ -0,0 +1,323 @@
|
|
1
|
+
import { type FormJsonFile, getForm, getFormByVersion, getLatestFormVersion, applyFormIntent } from './forms-loader';
|
2
|
+
import formsRegistry from '__mocks__/packages/test-forms-registry';
|
3
|
+
import {
|
4
|
+
htsHivtestResultingSchemaV2,
|
5
|
+
htsRetrospectiveResultingSchemaV2,
|
6
|
+
htsWildcardResultingSchemaV2,
|
7
|
+
testSchemaV2,
|
8
|
+
} from '__mocks__/forms/rfe-forms/forms-loader.test.schema';
|
9
|
+
|
10
|
+
const htsTestForms: FormJsonFile[] = [
|
11
|
+
{
|
12
|
+
version: '1.0',
|
13
|
+
semanticVersion: '1.0.0',
|
14
|
+
json: {
|
15
|
+
name: 'Test HTS POC',
|
16
|
+
pages: [],
|
17
|
+
processor: 'EncounterFormProcessor',
|
18
|
+
uuid: 'da24c540-cc83-43bc-978f-c1ef180a497f',
|
19
|
+
referencedForms: [],
|
20
|
+
encounterType: '79c1f50f-f77d-42e2-ad2a-d29304dde2fe',
|
21
|
+
},
|
22
|
+
},
|
23
|
+
{
|
24
|
+
version: '1.1',
|
25
|
+
semanticVersion: '1.1.0',
|
26
|
+
json: {
|
27
|
+
name: 'Test HTS POC',
|
28
|
+
pages: [],
|
29
|
+
processor: null,
|
30
|
+
uuid: 'da24c540-cc83-43bc-978f-c1ef180a497f',
|
31
|
+
referencedForms: [],
|
32
|
+
encounterType: '79c1f50f-f77d-42e2-ad2a-d29304dde2fe',
|
33
|
+
},
|
34
|
+
},
|
35
|
+
{
|
36
|
+
version: '2.0',
|
37
|
+
semanticVersion: '2.0.0',
|
38
|
+
json: {
|
39
|
+
name: 'Test HTS POC',
|
40
|
+
pages: [],
|
41
|
+
processor: null,
|
42
|
+
uuid: 'da24c540-cc83-43bc-978f-c1ef180a497f',
|
43
|
+
referencedForms: ['f23de883-ea70-45e5-bff7-eff334b28c4b'],
|
44
|
+
encounterType: '79c1f50f-f77d-42e2-ad2a-d29304dde2fe',
|
45
|
+
},
|
46
|
+
},
|
47
|
+
];
|
48
|
+
|
49
|
+
describe('Forms loader - getForm', () => {
|
50
|
+
it('should get latest form if no version was specified', () => {
|
51
|
+
// replay
|
52
|
+
const latestHTSForm = getForm('hiv', 'hts_poc', null, false, formsRegistry);
|
53
|
+
// verify
|
54
|
+
expect(latestHTSForm).toEqual({
|
55
|
+
name: 'Test HTS POC',
|
56
|
+
pages: [
|
57
|
+
{
|
58
|
+
label: 'Screening',
|
59
|
+
sections: [
|
60
|
+
{
|
61
|
+
label: 'Testing history',
|
62
|
+
isExpanded: 'true',
|
63
|
+
questions: [
|
64
|
+
{
|
65
|
+
label: 'When was the HIV test conducted?',
|
66
|
+
type: 'obs',
|
67
|
+
questionOptions: {
|
68
|
+
rendering: 'date',
|
69
|
+
concept: '164400AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
70
|
+
weeksList: '',
|
71
|
+
},
|
72
|
+
required: 'true',
|
73
|
+
unspecified: 'true',
|
74
|
+
hide: {
|
75
|
+
hideWhenExpression: 'false',
|
76
|
+
},
|
77
|
+
validators: [
|
78
|
+
{
|
79
|
+
type: 'date',
|
80
|
+
allowFutureDates: 'false',
|
81
|
+
},
|
82
|
+
{
|
83
|
+
type: 'js_expression',
|
84
|
+
failsWhenExpression: "myValue < '1/1/1980' || myValue > today()",
|
85
|
+
},
|
86
|
+
],
|
87
|
+
behaviours: [
|
88
|
+
{
|
89
|
+
intent: 'HTS_RETROSPECTIVE',
|
90
|
+
required: 'true',
|
91
|
+
unspecified: 'true',
|
92
|
+
hide: {
|
93
|
+
hideWhenExpression: 'false',
|
94
|
+
},
|
95
|
+
validators: [
|
96
|
+
{
|
97
|
+
type: 'date',
|
98
|
+
allowFutureDates: 'false',
|
99
|
+
},
|
100
|
+
{
|
101
|
+
type: 'js_expression',
|
102
|
+
failsWhenExpression: "myValue < '1/1/1980' || myValue > today()",
|
103
|
+
},
|
104
|
+
],
|
105
|
+
},
|
106
|
+
{
|
107
|
+
intent: 'HTS_HIVTEST',
|
108
|
+
required: 'true',
|
109
|
+
},
|
110
|
+
{
|
111
|
+
intent: '',
|
112
|
+
required: 'false',
|
113
|
+
hide: {
|
114
|
+
hideWhenExpression: "hivTestConducted !== 'cf82933b-3f3f-45e7-a5ab-5d31aaee3da3'",
|
115
|
+
},
|
116
|
+
validators: [
|
117
|
+
{
|
118
|
+
type: 'date',
|
119
|
+
allowFutureDates: 'false',
|
120
|
+
},
|
121
|
+
{
|
122
|
+
type: 'js_expression',
|
123
|
+
failsWhenExpression: "myValue < '1/1/1980' || myValue > today()",
|
124
|
+
},
|
125
|
+
],
|
126
|
+
},
|
127
|
+
],
|
128
|
+
id: 'dateTestPerformed',
|
129
|
+
},
|
130
|
+
],
|
131
|
+
},
|
132
|
+
],
|
133
|
+
},
|
134
|
+
],
|
135
|
+
availableIntents: ['HTS_RETROSPECTIVE', 'HTS_HIVTEST', '*'],
|
136
|
+
processor: 'EncounterFormProcessor',
|
137
|
+
uuid: 'da24c540-cc83-43bc-978f-c1ef180a497f',
|
138
|
+
referencedForms: [],
|
139
|
+
encounterType: '79c1f50f-f77d-42e2-ad2a-d29304dde2fe',
|
140
|
+
});
|
141
|
+
});
|
142
|
+
|
143
|
+
it('should get form with specified version', () => {
|
144
|
+
// replay
|
145
|
+
const htsFormV1_0 = getForm('hiv', 'hts_poc', '1.0', false, formsRegistry);
|
146
|
+
// verify
|
147
|
+
expect(htsFormV1_0).toEqual({
|
148
|
+
name: 'Test HTS POC',
|
149
|
+
pages: [],
|
150
|
+
processor: 'EncounterFormProcessor',
|
151
|
+
uuid: 'da24c540-cc83-43bc-978f-c1ef180a497f',
|
152
|
+
referencedForms: [],
|
153
|
+
encounterType: '79c1f50f-f77d-42e2-ad2a-d29304dde2fe',
|
154
|
+
});
|
155
|
+
});
|
156
|
+
|
157
|
+
it('should throw an error if specified version was not found while in strict mode', () => {
|
158
|
+
// replay
|
159
|
+
try {
|
160
|
+
getForm('hiv', 'hts_poc', '9.1', true, formsRegistry);
|
161
|
+
// fail test if this point is hit
|
162
|
+
fail('An error was expected to be called');
|
163
|
+
} catch (error) {
|
164
|
+
// verify
|
165
|
+
expect(error.message).toBe("Couldn't find form with version: 9.1");
|
166
|
+
}
|
167
|
+
});
|
168
|
+
|
169
|
+
it('should get lastet if required version was not found while in none strict mode', () => {
|
170
|
+
// replay
|
171
|
+
const latestForm = getForm('hiv', 'hts_poc', '9.1', false, formsRegistry);
|
172
|
+
// verify
|
173
|
+
expect(latestForm).toEqual({
|
174
|
+
name: 'Test HTS POC',
|
175
|
+
pages: [
|
176
|
+
{
|
177
|
+
label: 'Screening',
|
178
|
+
sections: [
|
179
|
+
{
|
180
|
+
label: 'Testing history',
|
181
|
+
isExpanded: 'true',
|
182
|
+
questions: [
|
183
|
+
{
|
184
|
+
label: 'When was the HIV test conducted?',
|
185
|
+
type: 'obs',
|
186
|
+
questionOptions: {
|
187
|
+
rendering: 'date',
|
188
|
+
concept: '164400AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
189
|
+
weeksList: '',
|
190
|
+
},
|
191
|
+
required: 'true',
|
192
|
+
unspecified: 'true',
|
193
|
+
hide: {
|
194
|
+
hideWhenExpression: 'false',
|
195
|
+
},
|
196
|
+
validators: [
|
197
|
+
{
|
198
|
+
type: 'date',
|
199
|
+
allowFutureDates: 'false',
|
200
|
+
},
|
201
|
+
{
|
202
|
+
type: 'js_expression',
|
203
|
+
failsWhenExpression: "myValue < '1/1/1980' || myValue > today()",
|
204
|
+
},
|
205
|
+
],
|
206
|
+
behaviours: [
|
207
|
+
{
|
208
|
+
intent: 'HTS_RETROSPECTIVE',
|
209
|
+
required: 'true',
|
210
|
+
unspecified: 'true',
|
211
|
+
hide: {
|
212
|
+
hideWhenExpression: 'false',
|
213
|
+
},
|
214
|
+
validators: [
|
215
|
+
{
|
216
|
+
type: 'date',
|
217
|
+
allowFutureDates: 'false',
|
218
|
+
},
|
219
|
+
{
|
220
|
+
type: 'js_expression',
|
221
|
+
failsWhenExpression: "myValue < '1/1/1980' || myValue > today()",
|
222
|
+
},
|
223
|
+
],
|
224
|
+
},
|
225
|
+
{
|
226
|
+
intent: 'HTS_HIVTEST',
|
227
|
+
required: 'true',
|
228
|
+
},
|
229
|
+
{
|
230
|
+
intent: '',
|
231
|
+
required: 'false',
|
232
|
+
hide: {
|
233
|
+
hideWhenExpression: "hivTestConducted !== 'cf82933b-3f3f-45e7-a5ab-5d31aaee3da3'",
|
234
|
+
},
|
235
|
+
validators: [
|
236
|
+
{
|
237
|
+
type: 'date',
|
238
|
+
allowFutureDates: 'false',
|
239
|
+
},
|
240
|
+
{
|
241
|
+
type: 'js_expression',
|
242
|
+
failsWhenExpression: "myValue < '1/1/1980' || myValue > today()",
|
243
|
+
},
|
244
|
+
],
|
245
|
+
},
|
246
|
+
],
|
247
|
+
id: 'dateTestPerformed',
|
248
|
+
},
|
249
|
+
],
|
250
|
+
},
|
251
|
+
],
|
252
|
+
},
|
253
|
+
],
|
254
|
+
availableIntents: ['HTS_RETROSPECTIVE', 'HTS_HIVTEST', '*'],
|
255
|
+
processor: 'EncounterFormProcessor',
|
256
|
+
uuid: 'da24c540-cc83-43bc-978f-c1ef180a497f',
|
257
|
+
referencedForms: [],
|
258
|
+
encounterType: '79c1f50f-f77d-42e2-ad2a-d29304dde2fe',
|
259
|
+
});
|
260
|
+
});
|
261
|
+
});
|
262
|
+
|
263
|
+
describe('Forms loader - getLatestFormVersion', () => {
|
264
|
+
it('should get latest form', () => {
|
265
|
+
// replay
|
266
|
+
const latest = getLatestFormVersion(htsTestForms);
|
267
|
+
|
268
|
+
// verify
|
269
|
+
expect(latest).toEqual({
|
270
|
+
version: '2.0',
|
271
|
+
semanticVersion: '2.0.0',
|
272
|
+
json: {
|
273
|
+
name: 'Test HTS POC',
|
274
|
+
pages: [],
|
275
|
+
processor: null,
|
276
|
+
uuid: 'da24c540-cc83-43bc-978f-c1ef180a497f',
|
277
|
+
referencedForms: ['f23de883-ea70-45e5-bff7-eff334b28c4b'],
|
278
|
+
encounterType: '79c1f50f-f77d-42e2-ad2a-d29304dde2fe',
|
279
|
+
},
|
280
|
+
});
|
281
|
+
});
|
282
|
+
});
|
283
|
+
|
284
|
+
describe('Forms loader - getFormByVersion', () => {
|
285
|
+
it('should get required form version', () => {
|
286
|
+
// replay
|
287
|
+
const htsFormV1_1 = getFormByVersion(htsTestForms, '1.1');
|
288
|
+
|
289
|
+
// verify
|
290
|
+
expect(htsFormV1_1).toEqual({
|
291
|
+
version: '1.1',
|
292
|
+
semanticVersion: '1.1.0',
|
293
|
+
json: {
|
294
|
+
name: 'Test HTS POC',
|
295
|
+
pages: [],
|
296
|
+
processor: null,
|
297
|
+
uuid: 'da24c540-cc83-43bc-978f-c1ef180a497f',
|
298
|
+
referencedForms: [],
|
299
|
+
encounterType: '79c1f50f-f77d-42e2-ad2a-d29304dde2fe',
|
300
|
+
},
|
301
|
+
});
|
302
|
+
});
|
303
|
+
});
|
304
|
+
|
305
|
+
xdescribe('Forms loader - applyFormIntent', () => {
|
306
|
+
it('should return correct fields for HTS_RETROSPECTIVE intent', () => {
|
307
|
+
let resultingSchema = applyFormIntent('HTS_RETROSPECTIVE', testSchemaV2);
|
308
|
+
|
309
|
+
expect(resultingSchema).toEqual(htsRetrospectiveResultingSchemaV2);
|
310
|
+
});
|
311
|
+
|
312
|
+
it('should return correct fields for HTS_HIVTEST intent', () => {
|
313
|
+
let resultingSchema = applyFormIntent('HTS_HIVTEST', testSchemaV2);
|
314
|
+
|
315
|
+
expect(resultingSchema).toEqual(htsHivtestResultingSchemaV2);
|
316
|
+
});
|
317
|
+
|
318
|
+
it('should return correct fields for * intent', () => {
|
319
|
+
let resultingSchema = applyFormIntent('*', testSchemaV2);
|
320
|
+
|
321
|
+
expect(resultingSchema).toEqual(htsWildcardResultingSchemaV2);
|
322
|
+
});
|
323
|
+
});
|
@@ -0,0 +1,306 @@
|
|
1
|
+
import * as semver from 'semver';
|
2
|
+
import { type FormField } from '../types';
|
3
|
+
|
4
|
+
let baseRegistry = {};
|
5
|
+
export interface FormJsonFile {
|
6
|
+
version: string;
|
7
|
+
semanticVersion?: string;
|
8
|
+
json: any;
|
9
|
+
}
|
10
|
+
|
11
|
+
/**
|
12
|
+
* This is a form behaviour property applied on `page` or `section` or `question`
|
13
|
+
*/
|
14
|
+
interface BehaviourProperty {
|
15
|
+
name: string;
|
16
|
+
type: 'field' | 'section' | 'page' | 'all';
|
17
|
+
value: string;
|
18
|
+
}
|
19
|
+
|
20
|
+
/**
|
21
|
+
* Convinience function for loading form(s) associated to a given package or form version.
|
22
|
+
*
|
23
|
+
* @param packageName The package associated with the form
|
24
|
+
* @param formNamespace The form namespace
|
25
|
+
* @param version The form version
|
26
|
+
* @param isStrict If `true`, throws error if specified form version wasn't found
|
27
|
+
* @param formsRegistry Form registry. (This was added for testing purposes)
|
28
|
+
* @returns The form json
|
29
|
+
*/
|
30
|
+
export function getForm(
|
31
|
+
packageName: string,
|
32
|
+
formNamespace: string,
|
33
|
+
version?: string,
|
34
|
+
isStrict?: boolean,
|
35
|
+
formsRegistry?: any,
|
36
|
+
) {
|
37
|
+
const forms = lookupForms(packageName, formNamespace, formsRegistry);
|
38
|
+
let form = null;
|
39
|
+
if (version) {
|
40
|
+
form = getFormByVersion(forms, version, isStrict);
|
41
|
+
}
|
42
|
+
if (!form) {
|
43
|
+
form = getLatestFormVersion(forms);
|
44
|
+
}
|
45
|
+
return loadSubforms(form.json);
|
46
|
+
}
|
47
|
+
|
48
|
+
export function loadSubforms(parentForm) {
|
49
|
+
parentForm.pages = parentForm.pages || [];
|
50
|
+
parentForm.pages.forEach((page) => {
|
51
|
+
if (page.isSubform && page.subform?.name && page.subform.package) {
|
52
|
+
try {
|
53
|
+
const subform = getForm(page.subform.package, page.subform.name);
|
54
|
+
if (!subform) {
|
55
|
+
console.error(`Form with name "${page.subform.package}/${page.subform.name}" was not found in registry.`);
|
56
|
+
}
|
57
|
+
page.subform.form = subform;
|
58
|
+
} catch (error) {
|
59
|
+
console.error(error);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
});
|
63
|
+
return parentForm;
|
64
|
+
}
|
65
|
+
|
66
|
+
export function getLatestFormVersion(forms: FormJsonFile[]) {
|
67
|
+
if (forms.length == 1) {
|
68
|
+
return forms[0];
|
69
|
+
}
|
70
|
+
const candidates = forms.map((f) => f.semanticVersion);
|
71
|
+
const latest = candidates.sort(formsVersionComparator)[candidates.length - 1];
|
72
|
+
return forms.find((f) => f.semanticVersion == latest);
|
73
|
+
}
|
74
|
+
|
75
|
+
export function getFormByVersion(forms: FormJsonFile[], requiredVersion: string, isStrict?: boolean) {
|
76
|
+
for (let form of forms) {
|
77
|
+
if (semver.satisfies(form.semanticVersion, requiredVersion)) {
|
78
|
+
return form;
|
79
|
+
}
|
80
|
+
}
|
81
|
+
if (isStrict) {
|
82
|
+
throw new Error(`Couldn't find form with version: ${requiredVersion}`);
|
83
|
+
} else {
|
84
|
+
return null;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
export function lookupForms(packageName, formNamespace, formsRegistry) {
|
89
|
+
const pkg = formsRegistry ? formsRegistry[packageName] : baseRegistry[packageName];
|
90
|
+
if (!pkg) {
|
91
|
+
throw Error(`Package with name ${packageName} was not found in registry`);
|
92
|
+
}
|
93
|
+
if (!pkg[formNamespace]) {
|
94
|
+
throw new Error(`Form namespace '${formNamespace}' was not found in forms registry`);
|
95
|
+
}
|
96
|
+
return Object.keys(pkg[formNamespace]).map((formVersion) => {
|
97
|
+
return {
|
98
|
+
version: formVersion,
|
99
|
+
semanticVersion: semver.coerce(formVersion).version,
|
100
|
+
json: pkg[formNamespace][formVersion],
|
101
|
+
};
|
102
|
+
});
|
103
|
+
}
|
104
|
+
|
105
|
+
/**
|
106
|
+
* Function parses JSON form input and filters validation behaviours according to a given intent
|
107
|
+
*
|
108
|
+
* @param {string} intent The specified intent
|
109
|
+
* @param {object} originalJson The original JSON form schema object
|
110
|
+
* @param parentOverrides An array of behaviour overrides from parent form to be applied to a subform
|
111
|
+
* @returns {object} The form json
|
112
|
+
*/
|
113
|
+
export function applyFormIntent(intent, originalJson, parentOverrides?: Array<BehaviourProperty>) {
|
114
|
+
if (!intent) {
|
115
|
+
return originalJson;
|
116
|
+
}
|
117
|
+
const jsonBuffer = JSON.parse(JSON.stringify(originalJson));
|
118
|
+
// Set the default page based on the current intent
|
119
|
+
jsonBuffer.defaultPage = jsonBuffer.availableIntents?.find(
|
120
|
+
(candidate) => candidate.intent === (intent?.intent || intent),
|
121
|
+
)?.defaultPage;
|
122
|
+
|
123
|
+
// filter form-level markdown behaviour
|
124
|
+
if (jsonBuffer.markdown) {
|
125
|
+
updateMarkdownRequiredBehaviour(jsonBuffer.markdown, intent);
|
126
|
+
}
|
127
|
+
|
128
|
+
// Before starting traversal, ensure nodes exist, at least as empty-arrays
|
129
|
+
jsonBuffer.pages = jsonBuffer.pages || [];
|
130
|
+
|
131
|
+
// Traverse the property tree with items of interest for validation
|
132
|
+
jsonBuffer.pages.forEach((page) => {
|
133
|
+
if (page.isSubform && page.subform?.form) {
|
134
|
+
const behaviourOverrides = [];
|
135
|
+
const targetBehaviour = page.subform.behaviours?.find(
|
136
|
+
(behaviour) => behaviour.intent == intent?.intent || intent,
|
137
|
+
);
|
138
|
+
if (targetBehaviour?.readonly !== undefined || targetBehaviour?.readonly != null) {
|
139
|
+
behaviourOverrides.push({ name: 'readonly', type: 'field', value: targetBehaviour?.readonly });
|
140
|
+
}
|
141
|
+
|
142
|
+
page.subform.form = applyFormIntent(
|
143
|
+
targetBehaviour?.subform_intent || '*',
|
144
|
+
page.subform?.form,
|
145
|
+
behaviourOverrides,
|
146
|
+
);
|
147
|
+
}
|
148
|
+
// TODO: Apply parentOverrides to pages if applicable
|
149
|
+
const pageBehaviour = page.behaviours?.find((behaviour) => behaviour.intent === (intent?.intent || intent));
|
150
|
+
if (pageBehaviour) {
|
151
|
+
page.hide = pageBehaviour?.hide;
|
152
|
+
page.readonly = pageBehaviour?.readonly;
|
153
|
+
} else {
|
154
|
+
const fallBackBehaviour = page.behaviours?.find((behaviour) => behaviour.intent === '*');
|
155
|
+
page.hide = fallBackBehaviour?.hide;
|
156
|
+
page.readonly = fallBackBehaviour?.readonly;
|
157
|
+
}
|
158
|
+
|
159
|
+
// filter page-level markdown behaviour
|
160
|
+
if (page.markdown) {
|
161
|
+
updateMarkdownRequiredBehaviour(page.markdown, intent);
|
162
|
+
}
|
163
|
+
// Before starting traversal, ensure nodes exist, at least as empty-arrays
|
164
|
+
page.sections = page.sections || [];
|
165
|
+
|
166
|
+
page.sections.forEach((section) => {
|
167
|
+
// TODO: Apply parentOverrides to sections if applicable
|
168
|
+
const secBehaviour = section.behaviours?.find((behaviour) => behaviour.intent === intent?.intent || intent);
|
169
|
+
if (secBehaviour) {
|
170
|
+
section.hide = secBehaviour?.hide;
|
171
|
+
} else {
|
172
|
+
const fallBackBehaviour = section.behaviours?.find((behaviour) => behaviour.intent === '*');
|
173
|
+
section.hide = fallBackBehaviour?.hide ?? section.hide;
|
174
|
+
}
|
175
|
+
|
176
|
+
// filter section-level markdown behaviour
|
177
|
+
if (section.markdown) {
|
178
|
+
updateMarkdownRequiredBehaviour(section.markdown, intent);
|
179
|
+
}
|
180
|
+
|
181
|
+
// Before starting traversal, ensure nodes exist, at least as empty-arrays
|
182
|
+
section.questions = section.questions || [];
|
183
|
+
|
184
|
+
section.questions.forEach((question: FormField) => {
|
185
|
+
if (question['behaviours']) {
|
186
|
+
updateQuestionRequiredBehaviour(question, intent?.intent || intent);
|
187
|
+
parentOverrides
|
188
|
+
?.filter((override) => override.type == 'all' || override.type == 'field')
|
189
|
+
?.forEach((override) => {
|
190
|
+
question[override.name] = override.value;
|
191
|
+
});
|
192
|
+
}
|
193
|
+
|
194
|
+
if (question.questions && question.questions.length) {
|
195
|
+
question.questions.forEach((childQuestion) => {
|
196
|
+
updateQuestionRequiredBehaviour(childQuestion, intent?.intent || intent);
|
197
|
+
|
198
|
+
parentOverrides
|
199
|
+
?.filter((override) => override.type == 'all' || override.type == 'field')
|
200
|
+
?.forEach((override) => {
|
201
|
+
childQuestion[override.name] = override.value;
|
202
|
+
});
|
203
|
+
});
|
204
|
+
}
|
205
|
+
});
|
206
|
+
});
|
207
|
+
});
|
208
|
+
return jsonBuffer;
|
209
|
+
}
|
210
|
+
|
211
|
+
// Helpers
|
212
|
+
|
213
|
+
function updateQuestionRequiredBehaviour(question, intent: string) {
|
214
|
+
const requiredIntentBehaviour = question.behaviours?.find((behaviour) => behaviour.intent === intent);
|
215
|
+
|
216
|
+
const defaultIntentBehaviour = question.behaviours?.find((bevahiour) => bevahiour.intent === '*');
|
217
|
+
// If both required and default intents exist, combine them and update to question
|
218
|
+
if (requiredIntentBehaviour || defaultIntentBehaviour) {
|
219
|
+
// Remove the intent name props from each object
|
220
|
+
delete requiredIntentBehaviour?.intent;
|
221
|
+
delete defaultIntentBehaviour?.intent;
|
222
|
+
|
223
|
+
// Combine required and default intents following the rules:
|
224
|
+
// 1. The default intent is applied to all other intents
|
225
|
+
// 2. Intent-specific behaviour overrides default behaviour
|
226
|
+
const combinedBehaviours = Object.assign(defaultIntentBehaviour || {}, requiredIntentBehaviour || {});
|
227
|
+
const defaultValue = combinedBehaviours.defaultValue;
|
228
|
+
if (defaultValue != undefined) {
|
229
|
+
// add the default value under the question options
|
230
|
+
question.questionOptions.defaultValue = defaultValue;
|
231
|
+
// delete it so that it's not added at the root level of the question
|
232
|
+
delete combinedBehaviours.defaultValue;
|
233
|
+
}
|
234
|
+
// Add the combinedBehaviours data to initial question
|
235
|
+
question = Object.assign(question, combinedBehaviours);
|
236
|
+
// Remove behaviours list
|
237
|
+
delete question.behaviours;
|
238
|
+
}
|
239
|
+
}
|
240
|
+
|
241
|
+
function updateMarkdownRequiredBehaviour(markdown, intent) {
|
242
|
+
const requiredIntentBehaviour = markdown.behaviours?.find((behaviour) => behaviour.intent === intent);
|
243
|
+
const defaultIntentBehaviour = markdown.behaviours?.find((behaviour) => behaviour.intent === '*');
|
244
|
+
|
245
|
+
if (requiredIntentBehaviour && defaultIntentBehaviour) {
|
246
|
+
delete requiredIntentBehaviour.intent;
|
247
|
+
delete defaultIntentBehaviour.intent;
|
248
|
+
const combinedBehaviours = Object.assign(defaultIntentBehaviour, requiredIntentBehaviour);
|
249
|
+
|
250
|
+
markdown = Object.assign(markdown, combinedBehaviours);
|
251
|
+
delete markdown.behaviours;
|
252
|
+
} else if (!requiredIntentBehaviour && defaultIntentBehaviour) {
|
253
|
+
delete defaultIntentBehaviour.intent;
|
254
|
+
|
255
|
+
markdown = Object.assign(markdown, defaultIntentBehaviour);
|
256
|
+
delete markdown.behaviours;
|
257
|
+
}
|
258
|
+
}
|
259
|
+
|
260
|
+
export function updateExcludeIntentBehaviour(excludedIntents: Array<string>, originalJson) {
|
261
|
+
originalJson.availableIntents = originalJson.availableIntents.filter(
|
262
|
+
(intent) => !excludedIntents.includes(intent?.intent || intent),
|
263
|
+
);
|
264
|
+
return originalJson;
|
265
|
+
}
|
266
|
+
|
267
|
+
export function addToBaseFormsRegistry(customRegistry: Record<string, any>) {
|
268
|
+
baseRegistry = { ...baseRegistry, ...customRegistry };
|
269
|
+
}
|
270
|
+
|
271
|
+
function isPositiveInteger(x) {
|
272
|
+
return /^\d+$/.test(x);
|
273
|
+
}
|
274
|
+
|
275
|
+
function formsVersionComparator(v1, v2) {
|
276
|
+
let v1parts = v1.split('.');
|
277
|
+
let v2parts = v2.split('.');
|
278
|
+
// First, validate both numbers are true version numbers
|
279
|
+
function validateParts(parts) {
|
280
|
+
for (let i = 0; i < parts.length; ++i) {
|
281
|
+
if (!isPositiveInteger(parts[i])) {
|
282
|
+
return false;
|
283
|
+
}
|
284
|
+
}
|
285
|
+
return true;
|
286
|
+
}
|
287
|
+
if (!validateParts(v1parts) || !validateParts(v2parts)) {
|
288
|
+
return NaN;
|
289
|
+
}
|
290
|
+
for (let i = 0; i < v1parts.length; ++i) {
|
291
|
+
if (v2parts.length === i) {
|
292
|
+
return 1;
|
293
|
+
}
|
294
|
+
if (v1parts[i] === v2parts[i]) {
|
295
|
+
continue;
|
296
|
+
}
|
297
|
+
if (v1parts[i] > v2parts[i]) {
|
298
|
+
return 1;
|
299
|
+
}
|
300
|
+
return -1;
|
301
|
+
}
|
302
|
+
if (v1parts.length != v2parts.length) {
|
303
|
+
return -1;
|
304
|
+
}
|
305
|
+
return 0;
|
306
|
+
}
|
@@ -0,0 +1,71 @@
|
|
1
|
+
export function evaluatePostSubmissionExpression(expression: string, encounters: any[]): boolean {
|
2
|
+
const encounter = encounters[0];
|
3
|
+
const regx = /(?:\w+|'(?:\\'|[^'\n])*')/g;
|
4
|
+
let match;
|
5
|
+
const fieldIds = new Set<string>();
|
6
|
+
try {
|
7
|
+
while ((match = regx.exec(expression))) {
|
8
|
+
const value = match[0].replace(/\\'/g, "'"); // Replace escaped single quotes
|
9
|
+
|
10
|
+
const isBoolean = /^(true|false)$/i.test(value);
|
11
|
+
const isNumber = /^-?\d+$/.test(value);
|
12
|
+
const isFloat = /^-?\d+\.\d+$/.test(value);
|
13
|
+
|
14
|
+
if (
|
15
|
+
!(value.startsWith("'") && value.endsWith("'")) &&
|
16
|
+
typeof value === 'string' &&
|
17
|
+
!isBoolean &&
|
18
|
+
!isNumber &&
|
19
|
+
!isFloat
|
20
|
+
) {
|
21
|
+
fieldIds.add(value);
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
let fieldToValueMap = {};
|
26
|
+
let replacedExpression;
|
27
|
+
if (fieldIds.size) {
|
28
|
+
fieldToValueMap = getFieldValues(fieldIds, encounter);
|
29
|
+
}
|
30
|
+
|
31
|
+
if (Object.keys(fieldToValueMap).length) {
|
32
|
+
replacedExpression = expression.replace(/(\w+)/g, (match) => {
|
33
|
+
return Object.prototype.hasOwnProperty.call(fieldToValueMap, match) ? fieldToValueMap[match] : match;
|
34
|
+
});
|
35
|
+
} else {
|
36
|
+
replacedExpression = expression;
|
37
|
+
}
|
38
|
+
|
39
|
+
return eval(replacedExpression);
|
40
|
+
} catch (error) {
|
41
|
+
throw new Error('Error evaluating expression');
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
function getFieldValues(fieldIds: Set<string>, encounter: any): Record<string, any> {
|
46
|
+
const result: Record<string, any> = {};
|
47
|
+
fieldIds.forEach((fieldId) => {
|
48
|
+
let value = encounter.obs?.find((item) => item.formFieldPath.includes(fieldId))?.value;
|
49
|
+
if (typeof value === 'object') {
|
50
|
+
value = value.uuid;
|
51
|
+
}
|
52
|
+
if (value) {
|
53
|
+
value = formatValue(value);
|
54
|
+
}
|
55
|
+
result[fieldId] = value;
|
56
|
+
});
|
57
|
+
|
58
|
+
return result;
|
59
|
+
}
|
60
|
+
|
61
|
+
//This function wraps string values in single quotes which Javascript will evaluate
|
62
|
+
function formatValue(value: any): any {
|
63
|
+
if (typeof value === 'string') {
|
64
|
+
if (value.length >= 2 && value[0] === "'" && value[value.length - 1] === "'") {
|
65
|
+
return value;
|
66
|
+
} else {
|
67
|
+
return `'${value}'`;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
return value;
|
71
|
+
}
|