From 8501843d7d74c70855af229fbf88cfe9d1b8432d Mon Sep 17 00:00:00 2001 From: wivern Date: Tue, 5 Dec 2023 11:46:34 +0300 Subject: [PATCH] Cohort Definition Calculation Designer --- .../CriteriaTypes/Measurement.js | 4 ++- .../cohortbuilder/InputTypes/Calculation.js | 26 +++++++++++++++++ js/components/cohortbuilder/components.js | 3 ++ .../cohortbuilder/components/Measurement.js | 14 ++++++++-- .../components/MeasurementOperand.js | 28 +++++++++++++++++++ .../components/MeasurementOperand.less | 3 ++ .../MeasurementOperandTemplate.html | 15 ++++++++++ .../components/MeasurementTemplate.html | 6 ++++ js/components/cohortbuilder/const.js | 10 +++++-- js/components/cohortbuilder/options.js | 13 ++++++++- .../components/MeasurementOperand.js | 23 +++++++++++++++ .../MeasurementOperandTemplate.html | 9 ++++++ .../components/MeasurementTemplate.html | 3 ++ js/components/cohortdefinitionviewer/main.js | 4 ++- 14 files changed, 154 insertions(+), 7 deletions(-) create mode 100644 js/components/cohortbuilder/InputTypes/Calculation.js create mode 100644 js/components/cohortbuilder/components/MeasurementOperand.js create mode 100644 js/components/cohortbuilder/components/MeasurementOperand.less create mode 100644 js/components/cohortbuilder/components/MeasurementOperandTemplate.html create mode 100644 js/components/cohortdefinitionviewer/components/MeasurementOperand.js create mode 100644 js/components/cohortdefinitionviewer/components/MeasurementOperandTemplate.html diff --git a/js/components/cohortbuilder/CriteriaTypes/Measurement.js b/js/components/cohortbuilder/CriteriaTypes/Measurement.js index 27077cfb8..d5bafbf2c 100644 --- a/js/components/cohortbuilder/CriteriaTypes/Measurement.js +++ b/js/components/cohortbuilder/CriteriaTypes/Measurement.js @@ -1,4 +1,4 @@ -define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputTypes/Concept', '../InputTypes/Text'], function (ko, Criteria, Range, Concept, Text) { +define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputTypes/Concept', '../InputTypes/Text', '../InputTypes/Calculation'], function (ko, Criteria, Range, Concept, Text, Calculation) { function Measurement(data, conceptSets) { var self = this; @@ -65,6 +65,8 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType return new Concept(d); }))); + self.MeasurementOperand = ko.observable(data.MeasurementOperand != null ? new Calculation(data.MeasurementOperand, conceptSets) : null); + } Measurement.prototype = new Criteria(); diff --git a/js/components/cohortbuilder/InputTypes/Calculation.js b/js/components/cohortbuilder/InputTypes/Calculation.js new file mode 100644 index 000000000..11eabfc53 --- /dev/null +++ b/js/components/cohortbuilder/InputTypes/Calculation.js @@ -0,0 +1,26 @@ +define([ + "knockout", + "../CriteriaTypes/Measurement", + "../CriteriaTypes", + "../InputTypes/Range", +], function ( + ko, + Measurement, + CriteriaTypes, + Range, +){ + function Calculation(data, conceptSets) { + var self = this; + data = data || {}; + + // var measurement = new Measurement((data.Measurement && data.Measurement.Measurement) ? data.Measurement.Measurement : {}, conceptSets); + // self.Measurement = ko.observable({Measurement: measurement}); + self.Measurement = ko.observable({ Measurement: new CriteriaTypes.Measurement((data.Measurement && data.Measurement.Measurement) ? data.Measurement.Measurement : {}, conceptSets) }); + self.Operator = ko.observable(data.Operator || "-"); + self.Limit = ko.observable(data.Limit || "First"); + self.SameVisit = ko.observable(!!data.SameVisit); + self.ValueAsNumber = ko.observable(new Range(data.ValueAsNumber)); + } + + return Calculation; +}); \ No newline at end of file diff --git a/js/components/cohortbuilder/components.js b/js/components/cohortbuilder/components.js index f4591c826..bf174bb22 100644 --- a/js/components/cohortbuilder/components.js +++ b/js/components/cohortbuilder/components.js @@ -98,6 +98,9 @@ define(function (require, exports) { var locationRegion = require('./components/LocationRegion'); ko.components.register('location-region-criteria', locationRegion); + var measurementOperand = require("./components/MeasurementOperand"); + ko.components.register("measurement-operand-criteria", measurementOperand); + require('./components/WindowedCriteria'); }); diff --git a/js/components/cohortbuilder/components/Measurement.js b/js/components/cohortbuilder/components/Measurement.js index d98991d00..435377ac9 100644 --- a/js/components/cohortbuilder/components/Measurement.js +++ b/js/components/cohortbuilder/components/Measurement.js @@ -4,15 +4,16 @@ define([ "../utils", "../InputTypes/Range", "../InputTypes/DateAdjustment", + "../InputTypes/Calculation", "../CriteriaGroup", "text!./MeasurementTemplate.html", "../const" -], function (ko, options, utils, Range, DateAdjustment, CriteriaGroup, template, constants) { +], function (ko, options, utils, Range, DateAdjustment, Calculation, CriteriaGroup, template, constants) { function MeasurementViewModel(params) { var self = this; self.expression = ko.utils.unwrapObservable(params.expression); - self.Criteria = params.criteria.Measurement; + self.Criteria = ko.unwrap(params.criteria).Measurement; self.options = options; self.addActions = [ @@ -160,6 +161,15 @@ define([ self.Criteria.MeasurementSourceConcept(ko.observable()); }, }, + { + ...constants.measurementAttributes.addMeasurementOperand, + selected: false, + action: () => { + if (self.Criteria.MeasurementOperand() == null) { + self.Criteria.MeasurementOperand(new Calculation({}, self.expression.ConceptSets)); + } + } + }, { ...constants.measurementAttributes.addNested, selected: false, diff --git a/js/components/cohortbuilder/components/MeasurementOperand.js b/js/components/cohortbuilder/components/MeasurementOperand.js new file mode 100644 index 000000000..f3b262b13 --- /dev/null +++ b/js/components/cohortbuilder/components/MeasurementOperand.js @@ -0,0 +1,28 @@ +define([ + "knockout", + "../options", + "text!./MeasurementOperandTemplate.html", + "less!./MeasurementOperand.less", +], function ( + ko, + options, + template, +) { + + function MeasurementOperandViewModel(params) { + var self = this; + self.expression = ko.unwrap(params.expression); + var criteria = ko.unwrap(params.criteria); + self.Measurement = criteria.Measurement; + self.Operator = criteria.Operator; + self.Limit = criteria.Limit; + self.SameVisit = criteria.SameVisit; + self.ValueAsNumber = criteria.ValueAsNumber; + self.options = options; + } + + return { + viewModel: MeasurementOperandViewModel, + template: template, + }; +}); \ No newline at end of file diff --git a/js/components/cohortbuilder/components/MeasurementOperand.less b/js/components/cohortbuilder/components/MeasurementOperand.less new file mode 100644 index 000000000..e17b77383 --- /dev/null +++ b/js/components/cohortbuilder/components/MeasurementOperand.less @@ -0,0 +1,3 @@ +.measurement-operand { + margin-bottom: 0.5rem; +} \ No newline at end of file diff --git a/js/components/cohortbuilder/components/MeasurementOperandTemplate.html b/js/components/cohortbuilder/components/MeasurementOperandTemplate.html new file mode 100644 index 000000000..2702eb294 --- /dev/null +++ b/js/components/cohortbuilder/components/MeasurementOperandTemplate.html @@ -0,0 +1,15 @@ +
+
+ + + of +
+ +
+ +
+
+ + +
+
\ No newline at end of file diff --git a/js/components/cohortbuilder/components/MeasurementTemplate.html b/js/components/cohortbuilder/components/MeasurementTemplate.html index 59f426932..169ad4370 100644 --- a/js/components/cohortbuilder/components/MeasurementTemplate.html +++ b/js/components/cohortbuilder/components/MeasurementTemplate.html @@ -194,6 +194,12 @@ + + + + + + diff --git a/js/components/cohortbuilder/const.js b/js/components/cohortbuilder/const.js index 755a2ddd2..7b97b2b36 100644 --- a/js/components/cohortbuilder/const.js +++ b/js/components/cohortbuilder/const.js @@ -1062,7 +1062,12 @@ define(["knockout"], function (ko) { descriptionGroup: 'const.eventsList.addLocationRegion.desc_group', defaultDescriptionGroup: 'Find patients within geographical area.', }, - + addMeasurementOperand: { + titleMeasurement: 'const.eventsList.addMeasurementOperand.title_measurement', + defaultTitleMeasurement: 'Add Calculation', + descriptionMeasurement: 'const.eventsList.addMeasurementOperand.desc_measurement', + defaultDescriptionMeasurement: 'Add calculated value based on a Measurement', + }, }; function setCriteria(criteria) { @@ -1235,7 +1240,8 @@ define(["knockout"], function (ko) { 'addRangeHighRatio', 'addProviderSpecialty', 'addSourceConcept', - 'addNested' + 'addMeasurementOperand', + 'addNested', ]; const drugexposureAttributesList = [ 'addDateAdjustment', diff --git a/js/components/cohortbuilder/options.js b/js/components/cohortbuilder/options.js index 9cece2728..f4b9eed21 100644 --- a/js/components/cohortbuilder/options.js +++ b/js/components/cohortbuilder/options.js @@ -103,7 +103,18 @@ define(["knockout"], function (ko) { name: ko.i18n('options.eventEnds', 'event ends'), value: true }] - + + options.operatorOptions = [ + '+', '-', '*', '/' + ]; + + options.eventsLimit = [{ + name: ko.i18n('options.earliestEvents', 'earliest event'), + id: "First" + }, { + name: ko.i18n('options.latestEvents', 'latest event'), + id: "Last" + }]; return options; }); \ No newline at end of file diff --git a/js/components/cohortdefinitionviewer/components/MeasurementOperand.js b/js/components/cohortdefinitionviewer/components/MeasurementOperand.js new file mode 100644 index 000000000..40dde8192 --- /dev/null +++ b/js/components/cohortdefinitionviewer/components/MeasurementOperand.js @@ -0,0 +1,23 @@ +define([ + "knockout", + "text!./MeasurementOperandTemplate.html", +], function ( + ko, + template, +){ + function MeasurementOperandViewModel(params) { + var self = this; + + self.expression = ko.unwrap(params.expression); + var criteria = ko.unwrap(params.criteria); + self.Criteria = criteria; + const measurement = criteria.Measurement; + self.CodesetId = measurement.Measurement && measurement.Measurement.CodesetId; + self.ValueAsNumber = criteria.ValueAsNumber; + } + + return { + viewModel: MeasurementOperandViewModel, + template: template, + }; +}); \ No newline at end of file diff --git a/js/components/cohortdefinitionviewer/components/MeasurementOperandTemplate.html b/js/components/cohortdefinitionviewer/components/MeasurementOperandTemplate.html new file mode 100644 index 000000000..93c5f939b --- /dev/null +++ b/js/components/cohortdefinitionviewer/components/MeasurementOperandTemplate.html @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/js/components/cohortdefinitionviewer/components/MeasurementTemplate.html b/js/components/cohortdefinitionviewer/components/MeasurementTemplate.html index 85670c600..a4eabfeb0 100644 --- a/js/components/cohortdefinitionviewer/components/MeasurementTemplate.html +++ b/js/components/cohortdefinitionviewer/components/MeasurementTemplate.html @@ -87,6 +87,9 @@ + + + diff --git a/js/components/cohortdefinitionviewer/main.js b/js/components/cohortdefinitionviewer/main.js index 9dfa719cd..b2b4b37aa 100644 --- a/js/components/cohortdefinitionviewer/main.js +++ b/js/components/cohortdefinitionviewer/main.js @@ -86,5 +86,7 @@ define(function (require, exports) { var locationRegion = require('./components/LocationRegion'); ko.components.register('location-region-viewer', locationRegion); - + + var measurementOperand = require("./components/MeasurementOperand"); + ko.components.register("measurement-operand-viewer", measurementOperand); });