diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..5bafefd75 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = crlf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false +quote_type = single \ No newline at end of file diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 80179bbfa..89fb41a4a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v2 # Caches NPM dependencies, as long as the package-lock.json is not modified - - uses: actions/cache@v2 + - uses: actions/cache@v4 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} @@ -50,7 +50,7 @@ jobs: - uses: actions/checkout@v2 - name: Cache Docker layers - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f0fc24b9e..8b0ba7bd3 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -23,7 +23,7 @@ jobs: node-version: 12 - name: NPM cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} diff --git a/Dockerfile b/Dockerfile index 7a606e82a..04f5d9692 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,7 +26,7 @@ RUN find . -type f "(" \ | xargs -0 -n 1 gzip -kf # Production Nginx image -FROM docker.io/nginxinc/nginx-unprivileged:1.23.3-alpine@sha256:c748ba587e7436aaa8729b64d4e0412410a486f0c592f0eec100fb3804ff9afd +FROM docker.io/nginxinc/nginx-unprivileged:1.27.2-alpine LABEL org.opencontainers.image.title="OHDSI-Atlas" LABEL org.opencontainers.image.authors="Joris Borgdorff , Lee Evans - www.ltscomputingllc.com, Shaun Turner" diff --git a/js/components/atlas-state.js b/js/components/atlas-state.js index 5f40c6437..88042e3bf 100644 --- a/js/components/atlas-state.js +++ b/js/components/atlas-state.js @@ -13,23 +13,28 @@ define(['knockout', 'lscache', 'services/job/jobDetail', 'assets/ohdsi.util', 'c state.vocabularyUrl = ko.observable(sessionStorage.vocabularyUrl); state.evidenceUrl = ko.observable(sessionStorage.evidenceUrl); state.resultsUrl = ko.observable(sessionStorage.resultsUrl); + state.currentVocabularyVersion = ko.observable(sessionStorage.currentVocabularyVersion); state.vocabularyUrl.subscribe(value => updateKey('vocabularyUrl', value)); state.evidenceUrl.subscribe(value => updateKey('evidenceUrl', value)); state.resultsUrl.subscribe(value => updateKey('resultsUrl', value)); + state.currentVocabularyVersion.subscribe(value => updateKey('currentVocabularyVersion', value)); // This default values are stored during initialization // and used to reset after session finished state.defaultVocabularyUrl = ko.observable(); state.defaultEvidenceUrl = ko.observable(); state.defaultResultsUrl = ko.observable(); + state.defaultVocabularyVersion = ko.observable(); state.defaultVocabularyUrl.subscribe((value) => state.vocabularyUrl(value)); state.defaultEvidenceUrl.subscribe((value) => state.evidenceUrl(value)); state.defaultResultsUrl.subscribe((value) => state.resultsUrl(value)); + state.defaultVocabularyVersion.subscribe((value) => state.currentVocabularyVersion(value)); state.resetCurrentDataSourceScope = function() { state.vocabularyUrl(state.defaultVocabularyUrl()); state.evidenceUrl(state.defaultEvidenceUrl()); state.resultsUrl(state.defaultResultsUrl()); + state.currentVocabularyVersion(state.defaultVocabularyVersion()); } state.sourceKeyOfVocabUrl = ko.computed(() => { diff --git a/js/components/authorship.html b/js/components/authorship.html index 6894ee015..d881bdcb4 100644 --- a/js/components/authorship.html +++ b/js/components/authorship.html @@ -1,14 +1,12 @@
- - - - , - - - - - - + , +
- \ No newline at end of file + diff --git a/js/components/cohortbuilder/CriteriaGroup.js b/js/components/cohortbuilder/CriteriaGroup.js index 78280d603..f5787f87c 100644 --- a/js/components/cohortbuilder/CriteriaGroup.js +++ b/js/components/cohortbuilder/CriteriaGroup.js @@ -25,7 +25,7 @@ define(function (require, exports, module) { if (data.DemographicCriteriaList && data.DemographicCriteriaList.length > 0) { data.DemographicCriteriaList.forEach(function (d) { - self.DemographicCriteriaList.push(new DemographicCriteria(d)); + self.DemographicCriteriaList.push(new DemographicCriteria(d, conceptSets)); }); } diff --git a/js/components/cohortbuilder/CriteriaTypes/ConditionEra.js b/js/components/cohortbuilder/CriteriaTypes/ConditionEra.js index 4bca662d0..c628944a3 100644 --- a/js/components/cohortbuilder/CriteriaTypes/ConditionEra.js +++ b/js/components/cohortbuilder/CriteriaTypes/ConditionEra.js @@ -1,4 +1,4 @@ -define(['knockout', './Criteria', '../InputTypes/Range', 'conceptpicker/InputTypes/Concept'], function (ko, Criteria, Range, Concept) { +define(['knockout', './Criteria', '../InputTypes/Range', 'conceptpicker/InputTypes/Concept', '../InputTypes/ConceptSetSelection'], function (ko, Criteria, Range, Concept, ConceptSetSelection) { function ConditionEra(data, conceptSets) { var self = this; @@ -35,6 +35,8 @@ define(['knockout', './Criteria', '../InputTypes/Range', 'conceptpicker/InputTyp self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); + } ConditionEra.prototype = new Criteria(); diff --git a/js/components/cohortbuilder/CriteriaTypes/ConditionOccurrence.js b/js/components/cohortbuilder/CriteriaTypes/ConditionOccurrence.js index c62c8904c..5f32fdc9f 100644 --- a/js/components/cohortbuilder/CriteriaTypes/ConditionOccurrence.js +++ b/js/components/cohortbuilder/CriteriaTypes/ConditionOccurrence.js @@ -1,4 +1,5 @@ -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/ConceptSetSelection' +], function (ko, Criteria, Range, Concept, Text,ConceptSetSelection) { function ConditionOccurrence(data, conceptSets) { var self = this; @@ -29,11 +30,14 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType return new Concept(d); }))); self.ConditionTypeExclude = ko.observable(data.ConditionTypeExclude || null); + self.ConditionTypeCS = ko.observable(data.ConditionTypeCS && new ConceptSetSelection(data.ConditionTypeCS, conceptSets)); + self.StopReason = ko.observable(data.StopReason && new Text(data.StopReason)); self.ConditionSourceConcept = ko.observable(data.ConditionSourceConcept != null ? ko.observable(data.ConditionSourceConcept) : null); self.ConditionStatus = ko.observable(data.ConditionStatus && ko.observableArray(data.ConditionStatus.map(function (d) { return new Concept(d); }))); + self.ConditionStatusCS = ko.observable(data.ConditionStatusCS && new ConceptSetSelection(data.ConditionStatusCS, conceptSets)); // Derived Fields self.First = ko.observable(data.First || null); @@ -43,14 +47,17 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); self.ProviderSpecialty = ko.observable(data.ProviderSpecialty && ko.observableArray(data.ProviderSpecialty.map(function (d) { return new Concept(d); }))); + self.ProviderSpecialtyCS = ko.observable(data.ProviderSpecialtyCS && new ConceptSetSelection(data.ProviderSpecialtyCS, conceptSets)); self.VisitType = ko.observable(data.VisitType && ko.observableArray(data.VisitType.map(function (d) { return new Concept(d); }))); + self.VisitTypeCS = ko.observable(data.VisitTypeCS && new ConceptSetSelection(data.VisitTypeCS, conceptSets)); } diff --git a/js/components/cohortbuilder/CriteriaTypes/Death.js b/js/components/cohortbuilder/CriteriaTypes/Death.js index 071742356..d42b1873a 100644 --- a/js/components/cohortbuilder/CriteriaTypes/Death.js +++ b/js/components/cohortbuilder/CriteriaTypes/Death.js @@ -1,4 +1,5 @@ -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/ConceptSetSelection'], + function (ko, Criteria, Range, Concept, ConceptSetSelection) { function Death(data, conceptSets) { var self = this; @@ -28,6 +29,8 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType return new Concept(d); }))); self.DeathTypeExclude = ko.observable(data.DeathTypeExclude || null); + self.DeathTypeCS = ko.observable(data.DeathTypeCS && new ConceptSetSelection(data.DeathTypeCS, conceptSets)); + self.DeathSourceConcept = ko.observable(data.DeathSourceConcept != null ? ko.observable(data.DeathSourceConcept) : null); // Derived Fields self.Age = ko.observable(data.Age && new Range(data.Age)); @@ -36,11 +39,8 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); - /* Do we still need prior enroll days inside the individual criteria? - self.PriorEnrollDays = ko.observable((typeof data.PriorEnrollDays == "number") ? data.PriorEnrollDays : null); - self.AfterEnrollDays = ko.observable((typeof data.AfterEnrollDays == "number") ? data.AfterEnrollDays : null); - */ } Death.prototype = new Criteria(); diff --git a/js/components/cohortbuilder/CriteriaTypes/DemographicCriteria.js b/js/components/cohortbuilder/CriteriaTypes/DemographicCriteria.js index 8772c53a2..c7e87ddbd 100644 --- a/js/components/cohortbuilder/CriteriaTypes/DemographicCriteria.js +++ b/js/components/cohortbuilder/CriteriaTypes/DemographicCriteria.js @@ -1,4 +1,4 @@ -define(['knockout', '../InputTypes/Range', 'conceptpicker/InputTypes/Concept'], function (ko, Range, Concept) { +define(['knockout', '../InputTypes/Range', 'conceptpicker/InputTypes/Concept', '../InputTypes/ConceptSetSelection'], function (ko, Range, Concept, ConceptSetSelection) { function DemographicCriteria(data, conceptSets) { var self = this; @@ -9,14 +9,17 @@ define(['knockout', '../InputTypes/Range', 'conceptpicker/InputTypes/Concept'], self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); self.Race = ko.observable(data.Race && ko.observableArray(data.Race.map(function (d) { return new Concept(d); }))); + self.RaceCS = ko.observable(data.RaceCS && new ConceptSetSelection(data.RaceCS, conceptSets)); self.Ethnicity = ko.observable(data.Ethnicity && ko.observableArray(data.Ethnicity.map(function (d) { return new Concept(d); }))); + self.EthnicityCS = ko.observable(data.EthnicityCS && new ConceptSetSelection(data.EthnicityCS, conceptSets)); self.OccurrenceStartDate = ko.observable(data.OccurrenceStartDate && new Range(data.OccurrenceStartDate)); diff --git a/js/components/cohortbuilder/CriteriaTypes/DeviceExposure.js b/js/components/cohortbuilder/CriteriaTypes/DeviceExposure.js index cc3cafd51..824d2e4c3 100644 --- a/js/components/cohortbuilder/CriteriaTypes/DeviceExposure.js +++ b/js/components/cohortbuilder/CriteriaTypes/DeviceExposure.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/ConceptSetSelection'], function (ko, Criteria, Range, Concept, Text, ConceptSetSelection) { function DeviceOccurence(data, conceptSets) { var self = this; @@ -29,6 +29,8 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType return new Concept(d); }))); self.DeviceTypeExclude = ko.observable(data.DeviceTypeExclude || null); + self.DeviceTypeCS = ko.observable(data.DeviceTypeCS && new ConceptSetSelection(data.DeviceTypeCS, conceptSets)); + self.UniqueDeviceId = ko.observable(data.UniqueDeviceId && new Text(data.StopReason)); self.Quantity = ko.observable(data.Quantity && new Range(data.Quantity)); self.DeviceSourceConcept = ko.observable(data.DeviceSourceConcept != null ? ko.observable(data.DeviceSourceConcept) : null); @@ -41,18 +43,17 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); - /* Do we still need prior enroll days inside the individual criteria? - self.PriorEnrollDays = ko.observable((typeof data.PriorEnrollDays == "number") ? data.PriorEnrollDays : null); - self.AfterEnrollDays = ko.observable((typeof data.AfterEnrollDays == "number") ? data.AfterEnrollDays : null); - */ - self.ProviderSpecialty = ko.observable(data.ProviderSpecialty && ko.observableArray(data.ProviderSpecialty.map(function (d) { return new Concept(d); }))); + self.ProviderSpecialtyCS = ko.observable(data.ProviderSpecialtyCS && new ConceptSetSelection(data.ProviderSpecialtyCS, conceptSets)); + self.VisitType = ko.observable(data.VisitType && ko.observableArray(data.VisitType.map(function (d) { return new Concept(d); }))); + self.VisitTypeCS = ko.observable(data.VisitTypeCS && new ConceptSetSelection(data.VisitTypeCS, conceptSets)); } diff --git a/js/components/cohortbuilder/CriteriaTypes/DoseEra.js b/js/components/cohortbuilder/CriteriaTypes/DoseEra.js index 1e7678d20..2dbf50d29 100644 --- a/js/components/cohortbuilder/CriteriaTypes/DoseEra.js +++ b/js/components/cohortbuilder/CriteriaTypes/DoseEra.js @@ -1,4 +1,4 @@ -define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputTypes/Concept'], function (ko, Criteria, Range, Concept) { +define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputTypes/Concept', '../InputTypes/ConceptSetSelection'], function (ko, Criteria, Range, Concept, ConceptSetSelection) { function DoseEra(data, conceptSets) { var self = this; @@ -26,6 +26,8 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.Unit = ko.observable(data.Unit && ko.observableArray(data.Unit.map(function (d) { return new Concept(d); }))); + self.UnitCS = ko.observable(data.UnitCS && new ConceptSetSelection(data.UnitCS, conceptSets)); + self.DoseValue = ko.observable(data.DoseValue && new Range(data.DoseValue)); self.EraLength = ko.observable(data.EraLength && new Range(data.EraLength)); @@ -38,6 +40,8 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); + } DoseEra.prototype = new Criteria(); diff --git a/js/components/cohortbuilder/CriteriaTypes/DrugEra.js b/js/components/cohortbuilder/CriteriaTypes/DrugEra.js index 3eefeabca..031e421b1 100644 --- a/js/components/cohortbuilder/CriteriaTypes/DrugEra.js +++ b/js/components/cohortbuilder/CriteriaTypes/DrugEra.js @@ -1,4 +1,4 @@ -define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputTypes/Concept'], function (ko, Criteria, Range, Concept) { +define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputTypes/Concept', '../InputTypes/ConceptSetSelection'], function (ko, Criteria, Range, Concept, ConceptSetSelection) { function DrugEra(data, conceptSets) { var self = this; @@ -36,6 +36,7 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); } DrugEra.prototype = new Criteria(); diff --git a/js/components/cohortbuilder/CriteriaTypes/DrugExposure.js b/js/components/cohortbuilder/CriteriaTypes/DrugExposure.js index ae26fd020..2d5ad8448 100644 --- a/js/components/cohortbuilder/CriteriaTypes/DrugExposure.js +++ b/js/components/cohortbuilder/CriteriaTypes/DrugExposure.js @@ -1,4 +1,5 @@ -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/ConceptSetSelection' +], function (ko, Criteria, Range, Concept, Text, ConceptSetSelection) { function DrugExposure(data, conceptSets) { var self = this; @@ -29,6 +30,7 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType return new Concept(d); }))); self.DrugTypeExclude = ko.observable(data.DrugTypeExclude || null); + self.DrugTypeCS = ko.observable(data.DrugTypeCS && new ConceptSetSelection(data.DrugTypeCS, conceptSets)); self.StopReason = ko.observable(data.StopReason && new Text(data.StopReason)); self.Refills = ko.observable(data.Refills && new Range(data.Refills)); self.Quantity = ko.observable(data.Quantity && new Range(data.Quantity)); @@ -36,10 +38,12 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.RouteConcept = ko.observable(data.RouteConcept && ko.observableArray(data.RouteConcept.map(function (d) { return new Concept(d); }))); + self.RouteConceptCS = ko.observable(data.RouteConceptCS && new ConceptSetSelection(data.RouteConceptCS, conceptSets)); self.EffectiveDrugDose = ko.observable(data.EffectiveDrugDose && new Range(data.EffectiveDrugDose)); self.DoseUnit = ko.observable(data.DoseUnit && ko.observableArray(data.DoseUnit.map(function (d) { return new Concept(d); }))); + self.DoseUnitCS = ko.observable(data.DoseUnitCS && new ConceptSetSelection(data.DoseUnitCS, conceptSets)); self.LotNumber = ko.observable(data.LotNumber && new Text(data.LotNumber)); self.DrugSourceConcept = ko.observable(data.DrugSourceConcept != null ? ko.observable(data.DrugSourceConcept) : null); @@ -51,18 +55,17 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); - /* Do we still need prior enroll days inside the individual criteria? - self.PriorEnrollDays = ko.observable((typeof data.PriorEnrollDays == "number") ? data.PriorEnrollDays : null); - self.AfterEnrollDays = ko.observable((typeof data.AfterEnrollDays == "number") ? data.AfterEnrollDays : null); - */ - self.ProviderSpecialty = ko.observable(data.ProviderSpecialty && ko.observableArray(data.ProviderSpecialty.map(function (d) { return new Concept(d); }))); + self.ProviderSpecialtyCS = ko.observable(data.ProviderSpecialtyCS && new ConceptSetSelection(data.ProviderSpecialtyCS, conceptSets)); + self.VisitType = ko.observable(data.VisitType && ko.observableArray(data.VisitType.map(function (d) { return new Concept(d); }))); + self.VisitTypeCS = ko.observable(data.VisitTypeCS && new ConceptSetSelection(data.VisitTypeCS, conceptSets)); } diff --git a/js/components/cohortbuilder/CriteriaTypes/Measurement.js b/js/components/cohortbuilder/CriteriaTypes/Measurement.js index 27077cfb8..fb3504174 100644 --- a/js/components/cohortbuilder/CriteriaTypes/Measurement.js +++ b/js/components/cohortbuilder/CriteriaTypes/Measurement.js @@ -1,4 +1,5 @@ -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/ConceptSetSelection' +], function (ko, Criteria, Range, Concept, ConceptSetSelection) { function Measurement(data, conceptSets) { var self = this; @@ -28,16 +29,20 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType return new Concept(d); }))); self.MeasurementTypeExclude = ko.observable(data.MeasurementTypeExclude || null); + self.MeasurementTypeCS = ko.observable(data.MeasurementTypeCS && new ConceptSetSelection(data.MeasurementTypeCS, conceptSets)); self.Operator = ko.observable(data.Operator && ko.observableArray(data.Operator.map(function (d) { return new Concept(d); }))); + self.OperatorCS = ko.observable(data.OperatorCS && new ConceptSetSelection(data.OperatorCS, conceptSets)); self.ValueAsNumber = ko.observable(data.ValueAsNumber && new Range(data.ValueAsNumber)); self.ValueAsConcept = ko.observable(data.ValueAsConcept && ko.observableArray(data.ValueAsConcept.map(function (d) { return new Concept(d); }))); + self.ValueAsConceptCS = ko.observable(data.ValueAsConceptCS && new ConceptSetSelection(data.ValueAsConceptCS, conceptSets)); self.Unit = ko.observable(data.Unit && ko.observableArray(data.Unit.map(function (d) { return new Concept(d); }))); + self.UnitCS = ko.observable(data.UnitCS && new ConceptSetSelection(data.UnitCS, conceptSets)); self.RangeLow = ko.observable(data.RangeLow && new Range(data.RangeLow)); self.RangeHigh = ko.observable(data.RangeHigh && new Range(data.RangeHigh)); self.MeasurementSourceConcept = ko.observable(data.MeasurementSourceConcept != null ? ko.observable(data.MeasurementSourceConcept) : null); @@ -52,18 +57,17 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); - /* Do we still need prior enroll days inside the individual criteria? - self.PriorEnrollDays = ko.observable((typeof data.PriorEnrollDays == "number") ? data.PriorEnrollDays : null); - self.AfterEnrollDays = ko.observable((typeof data.AfterEnrollDays == "number") ? data.AfterEnrollDays : null); - */ - self.ProviderSpecialty = ko.observable(data.ProviderSpecialty && ko.observableArray(data.ProviderSpecialty.map(function (d) { return new Concept(d); }))); + self.ProviderSpecialtyCS = ko.observable(data.ProviderSpecialtyCS && new ConceptSetSelection(data.ProviderSpecialtyCS, conceptSets)); + self.VisitType = ko.observable(data.VisitType && ko.observableArray(data.VisitType.map(function (d) { return new Concept(d); }))); + self.VisitTypeCS = ko.observable(data.VisitTypeCS && new ConceptSetSelection(data.VisitTypeCS, conceptSets)); } diff --git a/js/components/cohortbuilder/CriteriaTypes/Observation.js b/js/components/cohortbuilder/CriteriaTypes/Observation.js index e84a91480..94e956b6e 100644 --- a/js/components/cohortbuilder/CriteriaTypes/Observation.js +++ b/js/components/cohortbuilder/CriteriaTypes/Observation.js @@ -1,4 +1,5 @@ -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/ConceptSetSelection' +], function (ko, Criteria, Range, Concept, Text, ConceptSetSelection) { function Observation(data, conceptSets) { var self = this; @@ -28,17 +29,21 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType return new Concept(d); }))); self.ObservationTypeExclude = ko.observable(data.ObservationTypeExclude || null); + self.ObservationTypeCS = ko.observable(data.ObservationTypeCS && new ConceptSetSelection(data.ObservationTypeCS, conceptSets)); self.ValueAsNumber = ko.observable(data.ValueAsNumber && new Range(data.ValueAsNumber)); self.ValueAsString = ko.observable(data.ValueAsString && new Text(data.ValueAsString)); self.ValueAsConcept = ko.observable(data.ValueAsConcept && ko.observableArray(data.ValueAsConcept.map(function (d) { return new Concept(d); }))); + self.ValueAsConceptCS = ko.observable(data.ValueAsConceptCS && new ConceptSetSelection(data.ValueAsConceptCS, conceptSets)); self.Qualifier = ko.observable(data.Qualifier && ko.observableArray(data.Qualifier.map(function (d) { return new Concept(d); }))); + self.QualifierCS = ko.observable(data.QualifierCS && new ConceptSetSelection(data.QualifierCS, conceptSets)); self.Unit = ko.observable(data.Unit && ko.observableArray(data.Unit.map(function (d) { return new Concept(d); }))); + self.UnitCS = ko.observable(data.UnitCS && new ConceptSetSelection(data.UnitCS, conceptSets)); self.ObservationSourceConcept = ko.observable(data.ObservationSourceConcept != null ? ko.observable(data.ObservationSourceConcept) : null); // Derived Fields @@ -49,18 +54,17 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); - - /* Do we still need prior enroll days inside the individual criteria? - self.PriorEnrollDays = ko.observable((typeof data.PriorEnrollDays == "number") ? data.PriorEnrollDays : null); - self.AfterEnrollDays = ko.observable((typeof data.AfterEnrollDays == "number") ? data.AfterEnrollDays : null); - */ + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); self.ProviderSpecialty = ko.observable(data.ProviderSpecialty && ko.observableArray(data.ProviderSpecialty.map(function (d) { return new Concept(d); }))); + self.ProviderSpecialtyCS = ko.observable(data.ProviderSpecialtyCS && new ConceptSetSelection(data.ProviderSpecialtyCS, conceptSets)); + self.VisitType = ko.observable(data.VisitType && ko.observableArray(data.VisitType.map(function (d) { return new Concept(d); }))); + self.VisitTypeCS = ko.observable(data.VisitTypeCS && new ConceptSetSelection(data.VisitTypeCS, conceptSets)); } diff --git a/js/components/cohortbuilder/CriteriaTypes/ObservationPeriod.js b/js/components/cohortbuilder/CriteriaTypes/ObservationPeriod.js index 428dfd36d..8e65f55b9 100644 --- a/js/components/cohortbuilder/CriteriaTypes/ObservationPeriod.js +++ b/js/components/cohortbuilder/CriteriaTypes/ObservationPeriod.js @@ -1,4 +1,5 @@ -define(['knockout', './Criteria', '../InputTypes/Range', '../InputTypes/Period', 'conceptpicker/InputTypes/Concept'], function (ko, Criteria, Range, Period, Concept) { +define(['knockout', './Criteria', '../InputTypes/Range', '../InputTypes/Period', 'conceptpicker/InputTypes/Concept', '../InputTypes/ConceptSetSelection' +], function (ko, Criteria, Range, Period, Concept, ConceptSetSelection) { function ObservationPeriod(data, conceptSets) { var self = this; data = data || {}; @@ -11,6 +12,8 @@ define(['knockout', './Criteria', '../InputTypes/Range', '../InputTypes/Period', self.PeriodType = ko.observable(data.PeriodType && ko.observableArray(data.PeriodType.map(function (d) { return new Concept(d); }))); + self.PeriodTypeCS = ko.observable(data.PeriodTypeCS && new ConceptSetSelection(data.PeriodTypeCS, conceptSets)); + self.UserDefinedPeriod = ko.observable(data.UserDefinedPeriod && new Period(data.UserDefinedPeriod)); // Derived Fields diff --git a/js/components/cohortbuilder/CriteriaTypes/PayerPlanPeriod.js b/js/components/cohortbuilder/CriteriaTypes/PayerPlanPeriod.js index ac81f4b01..a32503af9 100644 --- a/js/components/cohortbuilder/CriteriaTypes/PayerPlanPeriod.js +++ b/js/components/cohortbuilder/CriteriaTypes/PayerPlanPeriod.js @@ -1,4 +1,5 @@ -define(['knockout', './Criteria', '../InputTypes/Range', '../InputTypes/Period', 'conceptpicker/InputTypes/Concept'], function (ko, Criteria, Range, Period, Concept) { +define(['knockout', './Criteria', '../InputTypes/Range', '../InputTypes/Period', 'conceptpicker/InputTypes/Concept', '../InputTypes/ConceptSetSelection' +], function (ko, Criteria, Range, Period, Concept, ConceptSetSelection) { function PayerPlanPeriod(data, conceptSets) { var self = this; data = data || {}; @@ -39,6 +40,7 @@ define(['knockout', './Criteria', '../InputTypes/Range', '../InputTypes/Period', self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); self.PayerConcept = ko.observable(data.PayerConcept != null ? ko.observable(data.PayerConcept) : null); self.PlanConcept = ko.observable(data.PlanConcept != null ? ko.observable(data.PlanConcept) : null); self.SponsorConcept = ko.observable(data.SponsorConcept != null ? ko.observable(data.SponsorConcept) : null); diff --git a/js/components/cohortbuilder/CriteriaTypes/ProcedureOccurrence.js b/js/components/cohortbuilder/CriteriaTypes/ProcedureOccurrence.js index c3853a4c8..b9d42e94b 100644 --- a/js/components/cohortbuilder/CriteriaTypes/ProcedureOccurrence.js +++ b/js/components/cohortbuilder/CriteriaTypes/ProcedureOccurrence.js @@ -1,4 +1,5 @@ -define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputTypes/Concept'], function (ko, Criteria, Range, Concept) { +define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputTypes/Concept', '../InputTypes/ConceptSetSelection' +], function (ko, Criteria, Range, Concept, ConceptSetSelection) { function ProcedureOccurrence(data, conceptSets) { var self = this; @@ -28,10 +29,12 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType return new Concept(d); }))); self.ProcedureTypeExclude = ko.observable(data.ProcedureTypeExclude || null); - + self.ProcedureTypeCS = ko.observable(data.ProcedureTypeCS && new ConceptSetSelection(data.ProcedureTypeCS, conceptSets)); + self.Modifier = ko.observable(data.Modifier && ko.observableArray(data.Modifier.map(function (d) { return new Concept(d); }))); + self.ModifierCS = ko.observable(data.ModifierCS && new ConceptSetSelection(data.ModifierCS, conceptSets)); self.Quantity = ko.observable(data.Quantity && new Range(data.Quantity)); @@ -45,18 +48,17 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); - /* Do we still need prior enroll days inside the individual criteria? - self.PriorEnrollDays = ko.observable((typeof data.PriorEnrollDays == "number") ? data.PriorEnrollDays : null); - self.AfterEnrollDays = ko.observable((typeof data.AfterEnrollDays == "number") ? data.AfterEnrollDays : null); - */ - self.ProviderSpecialty = ko.observable(data.ProviderSpecialty && ko.observableArray(data.ProviderSpecialty.map(function (d) { return new Concept(d); }))); + self.ProviderSpecialtyCS = ko.observable(data.ProviderSpecialtyCS && new ConceptSetSelection(data.ProviderSpecialtyCS, conceptSets)); + self.VisitType = ko.observable(data.VisitType && ko.observableArray(data.VisitType.map(function (d) { return new Concept(d); }))); + self.VisitTypeCS = ko.observable(data.VisitTypeCS && new ConceptSetSelection(data.VisitTypeCS, conceptSets)); } diff --git a/js/components/cohortbuilder/CriteriaTypes/Specimen.js b/js/components/cohortbuilder/CriteriaTypes/Specimen.js index 5fe50987c..ec54f35db 100644 --- a/js/components/cohortbuilder/CriteriaTypes/Specimen.js +++ b/js/components/cohortbuilder/CriteriaTypes/Specimen.js @@ -1,4 +1,5 @@ -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/ConceptSetSelection' +], function (ko, Criteria, Range, Concept, Text, ConceptSetSelection) { function Specimen(data, conceptSets) { var self = this; @@ -26,17 +27,21 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.SpecimenType = ko.observable(data.SpecimenType && ko.observableArray(data.SpecimenType.map(function (d) { return new Concept(d); }))); + self.SpecimenTypeCS = ko.observable(data.SpecimenTypeCS && new ConceptSetSelection(data.SpecimenTypeCS, conceptSets)); self.SpecimenTypeExclude = ko.observable(data.SpecimenTypeExclude || null); self.Quantity = ko.observable(data.Quantity && new Range(data.Quantity)); self.Unit = ko.observable(data.Unit && ko.observableArray(data.Unit.map(function (d) { return new Concept(d); }))); + self.UnitCS = ko.observable(data.UnitCS && new ConceptSetSelection(data.UnitCS, conceptSets)); self.AnatomicSite = ko.observable(data.AnatomicSite && ko.observableArray(data.AnatomicSite.map(function (d) { return new Concept(d); }))); + self.AnatomicSiteCS = ko.observable(data.AnatomicSiteCS && new ConceptSetSelection(data.AnatomicSiteCS, conceptSets)); self.DiseaseStatus = ko.observable(data.DiseaseStatus && ko.observableArray(data.DiseaseStatus.map(function (d) { return new Concept(d); }))); + self.DiseaseStatusCS = ko.observable(data.DiseaseStatusCS && new ConceptSetSelection(data.DiseaseStatusCS, conceptSets)); self.SourceId = ko.observable(data.SourceId && new Text(data.SourceId)); // Derived Fields @@ -47,6 +52,7 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); } Specimen.prototype = new Criteria(); diff --git a/js/components/cohortbuilder/CriteriaTypes/VisitDetail.js b/js/components/cohortbuilder/CriteriaTypes/VisitDetail.js index 1e5e95677..509d55dc1 100644 --- a/js/components/cohortbuilder/CriteriaTypes/VisitDetail.js +++ b/js/components/cohortbuilder/CriteriaTypes/VisitDetail.js @@ -15,19 +15,6 @@ define(['knockout', './Criteria', '../InputTypes/Range', '../InputTypes/ConceptS if (ko.utils.unwrapObservable(self.VisitDetailSourceConcept()) == change.value.id) { self.VisitSourceConcept()(null); } - if (ko.utils.unwrapObservable(self.VisitDetailTypeCS() && self.VisitDetailTypeCS().CodesetId) == change.value.id) { - self.VisitDetailTypeCS(new ConceptSetSelection()); - } - if (ko.utils.unwrapObservable(self.GenderCS() && self.GenderCS().CodesetId) == change.value.id) { - self.GenderCS(new ConceptSetSelection()); - } - if (ko.utils.unwrapObservable(self.ProviderSpecialtyCS() && self.ProviderSpecialtyCS().CodesetId) == change.value.id) { - self.ProviderSpecialtyCS(new ConceptSetSelection()); - } - if (ko.utils.unwrapObservable(self.PlaceOfServiceCS() && self.PlaceOfServiceCS().CodesetId) == change.value.id) { - self.PlaceOfServiceCS(new ConceptSetSelection()); - } - } }); }, null, "arrayChange"); @@ -39,8 +26,7 @@ define(['knockout', './Criteria', '../InputTypes/Range', '../InputTypes/ConceptS self.VisitDetailStartDate = ko.observable(data.VisitDetailStartDate && new Range(data.VisitDetailStartDate)); self.VisitDetailEndDate = ko.observable(data.VisitDetailEndDate && new Range(data.VisitDetailEndDate)); - self.VisitDetailTypeCS = ko.observable(data.VisitDetailTypeCS && new ConceptSetSelection(data.VisitDetailTypeCS)); - self.VisitDetailTypeExclude = ko.observable(data.VisitDetailTypeExclude || null); + self.VisitDetailTypeCS = ko.observable(data.VisitDetailTypeCS && new ConceptSetSelection(data.VisitDetailTypeCS, conceptSets)); self.VisitDetailSourceConcept = ko.observable(data.VisitDetailSourceConcept != null ? ko.observable(data.VisitDetailSourceConcept) : null); self.VisitDetailLength = ko.observable(data.VisitDetailLength && new Range(data.VisitDetailLength)); @@ -49,15 +35,9 @@ define(['knockout', './Criteria', '../InputTypes/Range', '../InputTypes/ConceptS self.Age = ko.observable(data.Age && new Range(data.Age)); // Linked Fields - self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS)); - - /* Do we still need prior enroll days inside the individual criteria? - self.PriorEnrollDays = ko.observable((typeof data.PriorEnrollDays == "number") ? data.PriorEnrollDays : null); - self.AfterEnrollDays = ko.observable((typeof data.AfterEnrollDays == "number") ? data.AfterEnrollDays : null); - */ - self.ProviderSpecialtyCS = ko.observable(data.ProviderSpecialtyCS && new ConceptSetSelection(data.ProviderSpecialtyCS)); - - self.PlaceOfServiceCS = ko.observable(data.PlaceOfServiceCS && new ConceptSetSelection(data.PlaceOfServiceCS)); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); + self.ProviderSpecialtyCS = ko.observable(data.ProviderSpecialtyCS && new ConceptSetSelection(data.ProviderSpecialtyCS, conceptSets)); + self.PlaceOfServiceCS = ko.observable(data.PlaceOfServiceCS && new ConceptSetSelection(data.PlaceOfServiceCS, conceptSets)); self.PlaceOfServiceLocation = ko.observable(data.PlaceOfServiceLocation != null ? ko.observable(data.PlaceOfServiceLocation) : null); self.PlaceOfServiceDistance = ko.observable(data.PlaceOfServiceDistance && new Range(data.PlaceOfServiceDistance)); diff --git a/js/components/cohortbuilder/CriteriaTypes/VisitOccurrence.js b/js/components/cohortbuilder/CriteriaTypes/VisitOccurrence.js index 041c9c16a..a5eff1c7d 100644 --- a/js/components/cohortbuilder/CriteriaTypes/VisitOccurrence.js +++ b/js/components/cohortbuilder/CriteriaTypes/VisitOccurrence.js @@ -1,4 +1,5 @@ -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/ConceptSetSelection', +], function (ko, Criteria, Range, Concept, ConceptSetSelection) { function VisitOccurence(data, conceptSets) { var self = this; @@ -29,6 +30,7 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType return new Concept(d); }))); self.VisitTypeExclude = ko.observable(data.VisitTypeExclude || null); + self.VisitTypeCS = ko.observable(data.VisitTypeCS && new ConceptSetSelection(data.VisitTypeCS, conceptSets)); self.VisitSourceConcept = ko.observable(data.VisitSourceConcept != null ? ko.observable(data.VisitSourceConcept) : null); self.VisitLength = ko.observable(data.VisitLength && new Range(data.VisitLength)); @@ -40,22 +42,19 @@ define(['knockout', './Criteria', '../InputTypes/Range','conceptpicker/InputType self.Gender = ko.observable(data.Gender && ko.observableArray(data.Gender.map(function (d) { return new Concept(d); }))); + self.GenderCS = ko.observable(data.GenderCS && new ConceptSetSelection(data.GenderCS, conceptSets)); - /* Do we still need prior enroll days inside the individual criteria? - self.PriorEnrollDays = ko.observable((typeof data.PriorEnrollDays == "number") ? data.PriorEnrollDays : null); - self.AfterEnrollDays = ko.observable((typeof data.AfterEnrollDays == "number") ? data.AfterEnrollDays : null); - */ - self.ProviderSpecialty = ko.observable(data.ProviderSpecialty && ko.observableArray(data.ProviderSpecialty.map(function (d) { return new Concept(d); }))); + self.ProviderSpecialtyCS = ko.observable(data.ProviderSpecialtyCS && new ConceptSetSelection(data.ProviderSpecialtyCS, conceptSets)); self.PlaceOfService = ko.observable(data.PlaceOfService && ko.observableArray(data.PlaceOfService.map(function (d) { return new Concept(d); }))); + self.PlaceOfServiceCS = ko.observable(data.PlaceOfServiceCS && new ConceptSetSelection(data.PlaceOfServiceCS, conceptSets)); self.PlaceOfServiceLocation = ko.observable(data.PlaceOfServiceLocation != null ? ko.observable(data.PlaceOfServiceLocation) : null); - self.PlaceOfServiceDistance = ko.observable(data.PlaceOfServiceDistance && new Range(data.PlaceOfServiceDistance)); } VisitOccurence.prototype = new Criteria(); diff --git a/js/components/cohortbuilder/InputTypes/ConceptSetSelection.js b/js/components/cohortbuilder/InputTypes/ConceptSetSelection.js index c6efcb3f6..b4a7af3aa 100644 --- a/js/components/cohortbuilder/InputTypes/ConceptSetSelection.js +++ b/js/components/cohortbuilder/InputTypes/ConceptSetSelection.js @@ -1,11 +1,21 @@ define(['knockout'], function (ko) { - function ConceptSetSelection(data) { + function ConceptSetSelection(data, conceptSets) { var self = this; data = data || {}; + self.IsExclusion = ko.observable(data.IsExclusion); + self.CodesetId = ko.observable(data.CodesetId); + + // set up subscription to update CodesetId if the item is removed from conceptSets + conceptSets.subscribe(function (changes) { + changes.forEach(function(change) { + if (change.status === 'deleted') { + if (ko.utils.unwrapObservable(self.CodesetId) == change.value.id) + self.CodesetId(null); + } + }); + }, null, "arrayChange"); - self.IsExclusion = ko.observable(data.IsExclusion ? true : data.IsExclusion || null); - self.CodesetId = ko.observable(data.CodesetId ? data.CodesetId : null); } return ConceptSetSelection; diff --git a/js/components/cohortbuilder/components/ConceptSetSelector.js b/js/components/cohortbuilder/components/ConceptSetSelector.js index 9f5c13421..c7791171e 100644 --- a/js/components/cohortbuilder/components/ConceptSetSelector.js +++ b/js/components/cohortbuilder/components/ConceptSetSelector.js @@ -13,7 +13,7 @@ define([ function conceptsetSelector(params) { var self = this; self.conceptSetId = params.conceptSetId; - self.defaultText = params.defaultText; + self.defaultName = params.defaultName; self.conceptSets = params.conceptSets; self.filterText = ko.observable(""); self.previewVisible = ko.observable(false); @@ -25,7 +25,7 @@ define([ self.sortedConceptSets = self.conceptSets.extend({ sorted: conceptSetSorter, }); - self.filteredConceptSets = ko.computed(function () { + self.filteredConceptSets = ko.pureComputed(function () { var selectedConceptSet = self.conceptSets().filter(function (item) { return item.id == self.conceptSetId(); }); @@ -41,13 +41,13 @@ define([ ), ]; }); - self.conceptSetName = ko.computed(function () { - var selectedConceptSet = self.conceptSets().filter(function (item) { + self.conceptSetName = ko.pureComputed(function () { + var selectedConceptSet = self.conceptSets().find(function (item) { return item.id == self.conceptSetId(); }); return ( - (selectedConceptSet.length > 0 && selectedConceptSet[0].name()) || - ko.unwrap(self.defaultText) + ((selectedConceptSet && selectedConceptSet.name()) || + ko.unwrap(self.defaultName)) ); }); diff --git a/js/components/cohortbuilder/components/ConditionEra.js b/js/components/cohortbuilder/components/ConditionEra.js index 5bc184440..2946e4752 100644 --- a/js/components/cohortbuilder/components/ConditionEra.js +++ b/js/components/cohortbuilder/components/ConditionEra.js @@ -4,10 +4,11 @@ define([ "../utils", "../InputTypes/Range", "../InputTypes/DateAdjustment", + "../InputTypes/ConceptSetSelection", "../CriteriaGroup", "text!./ConditionEraTemplate.html", "../const", -], function (ko, options, utils, Range, DateAdjustment, CriteriaGroup, template, constants) { +], function (ko, options, utils, Range, DateAdjustment, ConceptSetSelection, CriteriaGroup, template, constants) { function ConditionEraViewModel(params) { var self = this; self.expression = ko.utils.unwrapObservable(params.expression); @@ -57,6 +58,14 @@ define([ self.Criteria.Gender(ko.observableArray()); }, }, + { + ...constants.eraAttributes.addGenderCS, + selected: false, + action: function () { + if (self.Criteria.GenderCS() == null) + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.eraAttributes.addStartDate, selected: false, diff --git a/js/components/cohortbuilder/components/ConditionEraTemplate.html b/js/components/cohortbuilder/components/ConditionEraTemplate.html index 3a86f338f..d9819ebbc 100644 --- a/js/components/cohortbuilder/components/ConditionEraTemplate.html +++ b/js/components/cohortbuilder/components/ConditionEraTemplate.html @@ -9,7 +9,7 @@ + params="conceptSetId: CodesetId, conceptSets: $component.expression.ConceptSets, defaultName: ko.i18n('components.conditionEra.anyConditionButton', 'Any Condition')"> @@ -104,6 +104,18 @@ + + + +
+ + + + +
+ + diff --git a/js/components/cohortbuilder/components/ConditionOccurrence.js b/js/components/cohortbuilder/components/ConditionOccurrence.js index 46b53fe7d..161adb007 100644 --- a/js/components/cohortbuilder/components/ConditionOccurrence.js +++ b/js/components/cohortbuilder/components/ConditionOccurrence.js @@ -5,6 +5,7 @@ define([ "../InputTypes/Range", "../InputTypes/Text", "../InputTypes/DateAdjustment", + "../InputTypes/ConceptSetSelection", "../CriteriaGroup", "text!./ConditionOccurrenceTemplate.html", "../const", @@ -16,6 +17,7 @@ define([ Range, Text, DateAdjustment, + ConceptSetSelection, CriteriaGroup, template, constants @@ -60,6 +62,14 @@ define([ self.Criteria.Gender(ko.observableArray()); }, }, + { + ...constants.occurrenceAttributes.addGenderCS, + selected: false, + action: function () { + if (self.Criteria.GenderCS() == null) + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.occurrenceAttributes.addConditionStatus, selected: false, @@ -68,6 +78,14 @@ define([ self.Criteria.ConditionStatus(ko.observableArray()); } }, + { + ...constants.occurrenceAttributes.addConditionStatusCS, + selected: false, + action: function () { + if (self.Criteria.ConditionStatusCS() == null) + self.Criteria.ConditionStatusCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + } + }, { ...constants.occurrenceAttributes.addStartDate, selected: false, @@ -107,6 +125,14 @@ define([ self.Criteria.ConditionType(ko.observableArray()); }, }, + { + ...constants.occurrenceAttributes.addTypeCS, + selected: false, + action: function () { + if (self.Criteria.ConditionTypeCS() == null) + self.Criteria.ConditionTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.occurrenceAttributes.addVisit, selected: false, @@ -115,6 +141,14 @@ define([ self.Criteria.VisitType(ko.observableArray()); }, }, + { + ...constants.occurrenceAttributes.addVisitCS, + selected: false, + action: function () { + if (self.Criteria.VisitTypeCS() == null) + self.Criteria.VisitTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.occurrenceAttributes.addStopReason, selected: false, @@ -143,6 +177,14 @@ define([ self.Criteria.ProviderSpecialty(ko.observableArray()); }, }, + { + ...constants.occurrenceAttributes.addProviderSpecialtyCS, + selected: false, + action: function () { + if (self.Criteria.ProviderSpecialtyCS() == null) + self.Criteria.ProviderSpecialtyCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.occurrenceAttributes.addNested, selected: false, diff --git a/js/components/cohortbuilder/components/ConditionOccurrenceTemplate.html b/js/components/cohortbuilder/components/ConditionOccurrenceTemplate.html index 412cabad9..aa49a0343 100644 --- a/js/components/cohortbuilder/components/ConditionOccurrenceTemplate.html +++ b/js/components/cohortbuilder/components/ConditionOccurrenceTemplate.html @@ -9,7 +9,7 @@ + params="conceptSetId: CodesetId, conceptSets: $component.expression.ConceptSets, defaultName:ko.i18n('components.conditionOccurrence.anyCondition', 'Any Condition')"> @@ -76,6 +76,19 @@ + + + + +
+ + + + +
+ + @@ -88,6 +101,18 @@ + + + +
+ + + + +
+ + @@ -103,7 +128,7 @@ + params="conceptSetId: ConditionSourceConcept(), conceptSets: $component.expression.ConceptSets, defaultName: ko.i18n('components.conditionOccurrence.anyCondition', 'Any Condition')"> @@ -129,24 +154,18 @@ - @@ -159,6 +178,18 @@ + + + +
+ + + + +
+ + @@ -169,6 +200,18 @@ + + + +
+ + + + +
+ + diff --git a/js/components/cohortbuilder/components/CriteriaGroupTemplate.html b/js/components/cohortbuilder/components/CriteriaGroupTemplate.html index c3205f73d..ecb8d9f5e 100644 --- a/js/components/cohortbuilder/components/CriteriaGroupTemplate.html +++ b/js/components/cohortbuilder/components/CriteriaGroupTemplate.html @@ -51,8 +51,6 @@ - @@ -81,7 +79,7 @@ data-bind="text: ko.i18n('components.criteriaGroup.withFollowingCriteria', 'with the following event criteria:')">
- +
diff --git a/js/components/cohortbuilder/components/CustomEraStrategyTemplate.html b/js/components/cohortbuilder/components/CustomEraStrategyTemplate.html index 8a1930bd0..192e57516 100644 --- a/js/components/cohortbuilder/components/CustomEraStrategyTemplate.html +++ b/js/components/cohortbuilder/components/CustomEraStrategyTemplate.html @@ -9,7 +9,7 @@ + params="conceptSetId: strategy().DrugCodesetId, conceptSets: conceptSets, defaultName: ko.i18n('components.customEraStrategy.selectDrugConceptSet', 'Select Drug Concept Set')">
  • diff --git a/js/components/cohortbuilder/components/Death.js b/js/components/cohortbuilder/components/Death.js index 0ab798585..9ed29eaf1 100644 --- a/js/components/cohortbuilder/components/Death.js +++ b/js/components/cohortbuilder/components/Death.js @@ -4,6 +4,7 @@ define([ "../utils", "../InputTypes/Range", "../InputTypes/DateAdjustment", + "../InputTypes/ConceptSetSelection", "../CriteriaGroup", "text!./DeathTemplate.html", "../const", @@ -13,6 +14,7 @@ define([ utils, Range, DateAdjustment, + ConceptSetSelection, CriteriaGroup, template, constants @@ -36,6 +38,14 @@ define([ self.Criteria.Gender(ko.observableArray()); }, }, + { + ...constants.occurrenceAttributes.addGenderCS, + selected: false, + action: function () { + if (self.Criteria.GenderCS() == null) + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.deathAttributes.addDate, selected: false, @@ -63,6 +73,14 @@ define([ self.Criteria.DeathType(ko.observableArray()); }, }, + { + ...constants.deathAttributes.addTypeCS, + selected: false, + action: function () { + if (self.Criteria.DeathTypeCS() == null) + self.Criteria.DeathTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.deathAttributes.addSourceConcept, selected: false, diff --git a/js/components/cohortbuilder/components/DeathTemplate.html b/js/components/cohortbuilder/components/DeathTemplate.html index 4bbedf593..36ec5dc12 100644 --- a/js/components/cohortbuilder/components/DeathTemplate.html +++ b/js/components/cohortbuilder/components/DeathTemplate.html @@ -10,7 +10,7 @@ + defaultName: ko.i18n('components.conditionDeath.anyDeath', 'Any Death')"> @@ -62,6 +62,18 @@ + + + +
    + + + + +
    + + @@ -71,7 +83,7 @@ + defaultName: ko.i18n('components.conditionDeath.anyDeath', 'Any Death')"> @@ -95,6 +107,18 @@ + + + +
    + + + + +
    + + @@ -103,20 +127,6 @@ - \ No newline at end of file diff --git a/js/components/cohortbuilder/components/DemographicCriteria.js b/js/components/cohortbuilder/components/DemographicCriteria.js index 10343255e..7148a4f73 100644 --- a/js/components/cohortbuilder/components/DemographicCriteria.js +++ b/js/components/cohortbuilder/components/DemographicCriteria.js @@ -3,12 +3,14 @@ define([ "../options", "../InputTypes/Range", "../utils", + '../InputTypes/ConceptSetSelection', "text!./DemographicCriteriaTemplate.html", "../const", "./ConceptSetSelector", -], function (ko, options, Range, utils, template, constants) { +], function (ko, options, Range, utils, ConceptSetSelection, template, constants, ) { function DemographicCriteriaViewModel(params) { var self = this; + self.expression = ko.utils.unwrapObservable(params.expression); self.Criteria = ko.utils.unwrapObservable(params.criteria); self.options = options; self.formatOption = utils.formatDropDownOption; @@ -28,6 +30,14 @@ define([ self.Criteria.Gender(ko.observableArray()); }, }, + { + ...constants.demographicAttributes.addGenderCS, + selected: false, + action: function () { + if (self.Criteria.GenderCS() == null) + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.demographicAttributes.addStartDate, selected: false, @@ -60,6 +70,14 @@ define([ self.Criteria.Race(ko.observableArray()); }, }, + { + ...constants.demographicAttributes.addRaceCS, + selected: false, + action: function () { + if (self.Criteria.RaceCS() == null) + self.Criteria.RaceCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.demographicAttributes.addEthnicity, selected: false, @@ -68,6 +86,14 @@ define([ self.Criteria.Ethnicity(ko.observableArray()); }, }, + { + ...constants.demographicAttributes.addEthnicityCS, + selected: false, + action: function () { + if (self.Criteria.EthnicityCS() == null) + self.Criteria.EthnicityCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, ]; self.removeCriterion = function (propertyName) { diff --git a/js/components/cohortbuilder/components/DemographicCriteriaTemplate.html b/js/components/cohortbuilder/components/DemographicCriteriaTemplate.html index 140cf19bb..e28741d76 100644 --- a/js/components/cohortbuilder/components/DemographicCriteriaTemplate.html +++ b/js/components/cohortbuilder/components/DemographicCriteriaTemplate.html @@ -34,6 +34,18 @@ + + + +
    + + + + +
    + + @@ -43,6 +55,18 @@ + + + +
    + + + + +
    + + @@ -52,6 +76,18 @@ + + + +
    + + + + +
    + + diff --git a/js/components/cohortbuilder/components/DeviceExposure.js b/js/components/cohortbuilder/components/DeviceExposure.js index 35a9938ae..e650af157 100644 --- a/js/components/cohortbuilder/components/DeviceExposure.js +++ b/js/components/cohortbuilder/components/DeviceExposure.js @@ -5,6 +5,7 @@ define([ "../InputTypes/Range", "../InputTypes/Text", "../InputTypes/DateAdjustment", + "../InputTypes/ConceptSetSelection", "../CriteriaGroup", "text!./DeviceExposureTemplate.html", "../const", @@ -15,6 +16,7 @@ define([ Range, Text, DateAdjustment, + ConceptSetSelection, CriteriaGroup, template, constants @@ -56,6 +58,14 @@ define([ self.Criteria.Gender(ko.observableArray()); }, }, + { + ...constants.deviceAttributes.addGenderCS, + selected: false, + action: function () { + if (self.Criteria.GenderCS() == null) + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.deviceAttributes.addStartDate, selected: false, @@ -95,6 +105,14 @@ define([ self.Criteria.DeviceType(ko.observableArray()); }, }, + { + ...constants.deviceAttributes.addTypeCS, + selected: false, + action: function () { + if (self.Criteria.DeviceTypeCS() == null) + self.Criteria.DeviceTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.deviceAttributes.addVisit, selected: false, @@ -103,6 +121,14 @@ define([ self.Criteria.VisitType(ko.observableArray()); }, }, + { + ...constants.deviceAttributes.addVisitCS, + selected: false, + action: function () { + if (self.Criteria.VisitTypeCS() == null) + self.Criteria.VisitTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.deviceAttributes.addUniqueId, selected: false, @@ -143,6 +169,14 @@ define([ self.Criteria.ProviderSpecialty(ko.observableArray()); }, }, + { + ...constants.deviceAttributes.addProviderSpecialtyCS, + selected: false, + action: function () { + if (self.Criteria.ProviderSpecialtyCS() == null) + self.Criteria.ProviderSpecialtyCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.deviceAttributes.addNested, selected: false, diff --git a/js/components/cohortbuilder/components/DeviceExposureTemplate.html b/js/components/cohortbuilder/components/DeviceExposureTemplate.html index c8882a68f..de0bfa4f7 100644 --- a/js/components/cohortbuilder/components/DeviceExposureTemplate.html +++ b/js/components/cohortbuilder/components/DeviceExposureTemplate.html @@ -10,7 +10,7 @@ + defaultName: ko.i18n('components.conditionDevice.anyDevice', 'Any Device')">
    @@ -78,6 +78,18 @@ + + + +
    + + + + +
    + + @@ -101,7 +113,7 @@ + params="conceptSetId: DeviceSourceConcept(), conceptSets: $component.expression.ConceptSets, defaultName: ko.i18n('components.conditionDevice.anyDevice', 'Any Device')">
    @@ -124,6 +136,18 @@ + + + +
    + + + + +
    + + @@ -137,6 +161,18 @@ + + + +
    + + + + +
    + + @@ -146,6 +182,18 @@ + + + +
    + + + + +
    + + diff --git a/js/components/cohortbuilder/components/DoseEra.js b/js/components/cohortbuilder/components/DoseEra.js index 9fede622c..ded07ef9b 100644 --- a/js/components/cohortbuilder/components/DoseEra.js +++ b/js/components/cohortbuilder/components/DoseEra.js @@ -4,10 +4,11 @@ define([ "../utils", "../InputTypes/Range", "../InputTypes/DateAdjustment", + "../InputTypes/ConceptSetSelection", "../CriteriaGroup", "text!./DoseEraTemplate.html", "../const", -], function (ko, options, utils, Range, DateAdjustment, CriteriaGroup, template, constants) { +], function (ko, options, utils, Range, DateAdjustment, ConceptSetSelection, CriteriaGroup, template, constants) { function DoseEraViewModel(params) { var self = this; self.expression = ko.utils.unwrapObservable(params.expression); @@ -45,6 +46,14 @@ define([ self.Criteria.Gender(ko.observableArray()); }, }, + { + ...constants.doseAttributes.addGenderCS, + selected: false, + action: function () { + if (self.Criteria.GenderCS() == null) + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.doseAttributes.addStartDate, selected: false, @@ -84,6 +93,14 @@ define([ self.Criteria.Unit(ko.observableArray()); }, }, + { + ...constants.doseAttributes.addUnitCS, + selected: false, + action: function () { + if (self.Criteria.UnitCS() == null) + self.Criteria.UnitCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.doseAttributes.addLength, selected: false, diff --git a/js/components/cohortbuilder/components/DoseEraTemplate.html b/js/components/cohortbuilder/components/DoseEraTemplate.html index 165394abb..6dd8e4018 100644 --- a/js/components/cohortbuilder/components/DoseEraTemplate.html +++ b/js/components/cohortbuilder/components/DoseEraTemplate.html @@ -9,7 +9,7 @@ + defaultName: ko.i18n('components.conditionDose.anyDrug', 'Any Drug')">
    @@ -74,6 +74,18 @@
    + + + +
    + + + + +
    + + @@ -120,7 +132,20 @@ - + + + + +
    + + + + +
    + + + + defaultName: ko.i18n('components.conditionDrug.anyDrug', 'Any Drug')">
    @@ -118,6 +118,18 @@
    + + + +
    + + + + +
    + + diff --git a/js/components/cohortbuilder/components/DrugExposure.js b/js/components/cohortbuilder/components/DrugExposure.js index e1674c2c1..02962de48 100644 --- a/js/components/cohortbuilder/components/DrugExposure.js +++ b/js/components/cohortbuilder/components/DrugExposure.js @@ -4,6 +4,7 @@ define([ "../utils", "../InputTypes/Range", "../InputTypes/DateAdjustment", + "../InputTypes/ConceptSetSelection", "../InputTypes/Text", "../CriteriaGroup", "text!./DrugExposureTemplate.html", @@ -14,6 +15,7 @@ define([ utils, Range, DateAdjustment, + ConceptSetSelection, Text, CriteriaGroup, template, @@ -55,6 +57,14 @@ define([ self.Criteria.Gender(ko.observableArray()); }, }, + { + ...constants.drugexposureAttributes.addGenderCS, + selected: false, + action: function () { + if (self.Criteria.GenderCS() == null) + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.drugexposureAttributes.addStartDate, selected: false, @@ -94,6 +104,14 @@ define([ self.Criteria.DrugType(ko.observableArray()); }, }, + { + ...constants.drugexposureAttributes.addTypeCS, + selected: false, + action: function () { + if (self.Criteria.DrugTypeCS() == null) + self.Criteria.DrugTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.drugexposureAttributes.addVisit, selected: false, @@ -102,6 +120,14 @@ define([ self.Criteria.VisitType(ko.observableArray()); }, }, + { + ...constants.drugexposureAttributes.addVisitCS, + selected: false, + action: function () { + if (self.Criteria.VisitTypeCS() == null) + self.Criteria.VisitTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.drugexposureAttributes.addStopReason, selected: false, @@ -158,6 +184,14 @@ define([ self.Criteria.RouteConcept(ko.observableArray()); }, }, + { + ...constants.drugexposureAttributes.addRouteCS, + selected: false, + action: function () { + if (self.Criteria.RouteConceptCS() == null) + self.Criteria.RouteConceptCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.drugexposureAttributes.addEffective, selected: false, @@ -178,6 +212,14 @@ define([ self.Criteria.DoseUnit(ko.observableArray()); }, }, + { + ...constants.drugexposureAttributes.addUnitCS, + selected: false, + action: function () { + if (self.Criteria.DoseUnitCS() == null) + self.Criteria.DoseUnitCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.drugexposureAttributes.addLotNumber, selected: false, @@ -206,6 +248,14 @@ define([ self.Criteria.ProviderSpecialty(ko.observableArray()); }, }, + { + ...constants.drugexposureAttributes.addProviderSpecialtyCS, + selected: false, + action: function () { + if (self.Criteria.ProviderSpecialtyCS() == null) + self.Criteria.ProviderSpecialtyCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.drugexposureAttributes.addNested, selected: false, diff --git a/js/components/cohortbuilder/components/DrugExposureTemplate.html b/js/components/cohortbuilder/components/DrugExposureTemplate.html index 45d7bb539..c1211a8cb 100644 --- a/js/components/cohortbuilder/components/DrugExposureTemplate.html +++ b/js/components/cohortbuilder/components/DrugExposureTemplate.html @@ -10,7 +10,7 @@ + defaultName: ko.i18n('components.conditionDrugExposure.anyDrug', 'Any Drug')">
    @@ -81,6 +81,18 @@ + + + +
    + + + + +
    + + @@ -127,6 +139,18 @@ + + + +
    + + + + +
    + + @@ -146,6 +170,18 @@ + + + +
    + + + + +
    + + @@ -163,7 +199,7 @@ data-bind="text: ko.i18n('components.conditionDrugExposure.conditionDrugExposureText_13', 'Drug Source Concept is:')"> + params="conceptSetId: DrugSourceConcept(), conceptSets: $component.expression.ConceptSets, defaultName: ko.i18n('components.conditionDrugExposure.anyDrug', 'Any Drug')">
    @@ -189,20 +225,18 @@ - @@ -216,6 +250,18 @@ + + + +
    + + + + +
    + + @@ -226,6 +272,18 @@ + + + +
    + + + + +
    + + diff --git a/js/components/cohortbuilder/components/LocationRegionTemplate.html b/js/components/cohortbuilder/components/LocationRegionTemplate.html index ce27ec047..f4d43d93f 100644 --- a/js/components/cohortbuilder/components/LocationRegionTemplate.html +++ b/js/components/cohortbuilder/components/LocationRegionTemplate.html @@ -8,7 +8,7 @@ - +
    diff --git a/js/components/cohortbuilder/components/Measurement.js b/js/components/cohortbuilder/components/Measurement.js index d98991d00..741d04170 100644 --- a/js/components/cohortbuilder/components/Measurement.js +++ b/js/components/cohortbuilder/components/Measurement.js @@ -4,10 +4,11 @@ define([ "../utils", "../InputTypes/Range", "../InputTypes/DateAdjustment", + "../InputTypes/ConceptSetSelection", "../CriteriaGroup", "text!./MeasurementTemplate.html", "../const" -], function (ko, options, utils, Range, DateAdjustment, CriteriaGroup, template, constants) { +], function (ko, options, utils, Range, DateAdjustment, ConceptSetSelection, CriteriaGroup, template, constants) { function MeasurementViewModel(params) { var self = this; @@ -38,6 +39,14 @@ define([ self.Criteria.Gender(ko.observableArray()); }, }, + { + ...constants.measurementAttributes.addGenderCS, + selected: false, + action: function () { + if (self.Criteria.GenderCS() == null) + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.measurementAttributes.addDate, selected: false, @@ -65,6 +74,14 @@ define([ self.Criteria.MeasurementType(ko.observableArray()); }, }, + { + ...constants.measurementAttributes.addTypeCS, + selected: false, + action: function () { + if (self.Criteria.MeasurementTypeCS() == null) + self.Criteria.MeasurementTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.measurementAttributes.addVisit, selected: false, @@ -73,6 +90,14 @@ define([ self.Criteria.VisitType(ko.observableArray()); }, }, + { + ...constants.measurementAttributes.addVisitCS, + selected: false, + action: function () { + if (self.Criteria.VisitTypeCS() == null) + self.Criteria.VisitTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.measurementAttributes.addOperator, selected: false, @@ -81,6 +106,14 @@ define([ self.Criteria.Operator(ko.observableArray()); }, }, + { + ...constants.measurementAttributes.addOperatorCS, + selected: false, + action: function () { + if (self.Criteria.OperatorCS() == null) + self.Criteria.OperatorCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.measurementAttributes.addValue, selected: false, @@ -97,6 +130,14 @@ define([ self.Criteria.ValueAsConcept(ko.observableArray()); }, }, + { + ...constants.measurementAttributes.addValueAsConceptCS, + selected: false, + action: function () { + if (self.Criteria.ValueAsConceptCS() == null) + self.Criteria.ValueAsConceptCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.measurementAttributes.addUnit, selected: false, @@ -105,6 +146,14 @@ define([ self.Criteria.Unit(ko.observableArray()); }, }, + { + ...constants.measurementAttributes.addUnitCS, + selected: false, + action: function () { + if (self.Criteria.UnitCS() == null) + self.Criteria.UnitCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.measurementAttributes.addAbnormal, selected: false, @@ -152,6 +201,14 @@ define([ self.Criteria.ProviderSpecialty(ko.observableArray()); }, }, + { + ...constants.measurementAttributes.addProviderSpecialtyCS, + selected: false, + action: function () { + if (self.Criteria.ProviderSpecialtyCS() == null) + self.Criteria.ProviderSpecialtyCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.measurementAttributes.addSourceConcept, selected: false, diff --git a/js/components/cohortbuilder/components/MeasurementTemplate.html b/js/components/cohortbuilder/components/MeasurementTemplate.html index 59f426932..b1edd4dec 100644 --- a/js/components/cohortbuilder/components/MeasurementTemplate.html +++ b/js/components/cohortbuilder/components/MeasurementTemplate.html @@ -7,7 +7,7 @@ - @@ -57,6 +57,18 @@ + + + +
    + + + + +
    + + @@ -65,6 +77,18 @@ + + + +
    + + + + +
    + + @@ -80,6 +104,18 @@ + + + +
    + + + + +
    + + @@ -88,6 +124,18 @@ + + + +
    + + + + +
    + + @@ -134,7 +182,7 @@ + defaultName: ko.i18n('components.conditionMeasurement.anyMeasurement', 'Any Measurement')">
    @@ -156,20 +204,18 @@ - @@ -180,6 +226,18 @@ + + + +
    + + + + +
    + + @@ -188,6 +246,18 @@ + + + +
    + + + + +
    + + diff --git a/js/components/cohortbuilder/components/Observation.js b/js/components/cohortbuilder/components/Observation.js index 975b50637..b697cad22 100644 --- a/js/components/cohortbuilder/components/Observation.js +++ b/js/components/cohortbuilder/components/Observation.js @@ -4,11 +4,12 @@ define([ "../utils", "../InputTypes/Range", "../InputTypes/DateAdjustment", + "../InputTypes/ConceptSetSelection", "../InputTypes/Text", "../CriteriaGroup", "text!./ObservationTemplate.html", "../const" -], function (ko, options, utils, Range, DateAdjustment, Text, CriteriaGroup, template, constants) { +], function (ko, options, utils, Range, DateAdjustment, ConceptSetSelection, Text, CriteriaGroup, template, constants) { function ObservationViewModel(params) { var self = this; self.addActions = [ @@ -34,6 +35,14 @@ define([ self.Criteria.Gender(ko.observableArray()); }, }, + { + ...constants.observationAttributes.addGenderCS, + selected: false, + action: function () { + if (self.Criteria.GenderCS() == null) + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.observationAttributes.addDate, selected: false, @@ -61,6 +70,14 @@ define([ self.Criteria.ObservationType(ko.observableArray()); }, }, + { + ...constants.observationAttributes.addTypeCS, + selected: false, + action: function () { + if (self.Criteria.ObservationTypeCS() == null) + self.Criteria.ObservationTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.observationAttributes.addVisit, selected: false, @@ -69,6 +86,14 @@ define([ self.Criteria.VisitType(ko.observableArray()); }, }, + { + ...constants.observationAttributes.addVisitCS, + selected: false, + action: function () { + if (self.Criteria.VisitTypeCS() == null) + self.Criteria.VisitTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.observationAttributes.addValue, selected: false, @@ -101,6 +126,14 @@ define([ self.Criteria.ValueAsConcept(ko.observableArray()); }, }, + { + ...constants.observationAttributes.addValueAsConceptCS, + selected: false, + action: function () { + if (self.Criteria.ValueAsConceptCS() == null) + self.Criteria.ValueAsConceptCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.observationAttributes.addQualifier, selected: false, @@ -109,6 +142,14 @@ define([ self.Criteria.Qualifier(ko.observableArray()); }, }, + { + ...constants.observationAttributes.addQualifierCS, + selected: false, + action: function () { + if (self.Criteria.QualifierCS() == null) + self.Criteria.QualifierCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.observationAttributes.addUnit, selected: false, @@ -117,6 +158,14 @@ define([ self.Criteria.Unit(ko.observableArray()); }, }, + { + ...constants.observationAttributes.addUnitCS, + selected: false, + action: function () { + if (self.Criteria.UnitCS() == null) + self.Criteria.UnitCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.observationAttributes.addSourceConcept, selected: false, @@ -133,6 +182,14 @@ define([ self.Criteria.ProviderSpecialty(ko.observableArray()); }, }, + { + ...constants.observationAttributes.addProviderSpecialtyCS, + selected: false, + action: function () { + if (self.Criteria.ProviderSpecialtyCS() == null) + self.Criteria.ProviderSpecialtyCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.observationAttributes.addNested, selected: false, diff --git a/js/components/cohortbuilder/components/ObservationPeriod.js b/js/components/cohortbuilder/components/ObservationPeriod.js index a1d4229af..5722a3a56 100644 --- a/js/components/cohortbuilder/components/ObservationPeriod.js +++ b/js/components/cohortbuilder/components/ObservationPeriod.js @@ -3,11 +3,12 @@ define([ "../options", "../InputTypes/Range", "../InputTypes/DateAdjustment", + "../InputTypes/ConceptSetSelection", "../InputTypes/Period", "../CriteriaGroup", "text!./ObservationPeriodTemplate.html", "../const" -], function (ko, options, Range, DateAdjustment, Period, CriteriaGroup, template, constants) { +], function (ko, options, Range, DateAdjustment, ConceptSetSelection, Period, CriteriaGroup, template, constants) { function ObservationPeriodViewModel(params) { var self = this; @@ -86,6 +87,14 @@ define([ self.Criteria.PeriodType(ko.observableArray()); }, }, + { + ...constants.observationPeriodAttributes.addTypeCS, + selected: false, + action: function () { + if (self.Criteria.PeriodTypeCS() == null) + self.Criteria.PeriodTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.observationPeriodAttributes.addLength, selected: false, diff --git a/js/components/cohortbuilder/components/ObservationPeriodTemplate.html b/js/components/cohortbuilder/components/ObservationPeriodTemplate.html index 29161da04..40566f3d7 100644 --- a/js/components/cohortbuilder/components/ObservationPeriodTemplate.html +++ b/js/components/cohortbuilder/components/ObservationPeriodTemplate.html @@ -77,6 +77,18 @@ + + + +
    + + + + +
    + + diff --git a/js/components/cohortbuilder/components/ObservationTemplate.html b/js/components/cohortbuilder/components/ObservationTemplate.html index c86740763..6d04700d2 100644 --- a/js/components/cohortbuilder/components/ObservationTemplate.html +++ b/js/components/cohortbuilder/components/ObservationTemplate.html @@ -10,7 +10,7 @@ + defaultName: ko.i18n('components.conditionObservation.anyObservation', 'Any Observation')">
    @@ -52,7 +52,6 @@ - @@ -62,6 +61,18 @@ + + + +
    + + + + +
    + + @@ -88,6 +99,18 @@
    + + + +
    + + + + +
    + + @@ -98,6 +121,18 @@ + + + +
    + + + + +
    + + @@ -108,6 +143,18 @@ + + + +
    + + + + +
    + + @@ -115,7 +162,7 @@ + defaultName: ko.i18n('components.conditionObservation.anyObservation', 'Any Observation')"> @@ -137,20 +184,18 @@ - @@ -161,6 +206,18 @@ + + + +
    + + + + +
    + + @@ -169,6 +226,18 @@ + + + +
    + + + + +
    + + diff --git a/js/components/cohortbuilder/components/PayerPlanPeriod.js b/js/components/cohortbuilder/components/PayerPlanPeriod.js index 3709dfb4c..e80a3f71b 100644 --- a/js/components/cohortbuilder/components/PayerPlanPeriod.js +++ b/js/components/cohortbuilder/components/PayerPlanPeriod.js @@ -3,11 +3,12 @@ define([ "../options", "../InputTypes/Range", "../InputTypes/DateAdjustment", + "../InputTypes/ConceptSetSelection", "../InputTypes/Period", "../CriteriaGroup", "text!./PayerPlanPeriodTemplate.html", "../const" -], function (ko, options, Range, DateAdjustment, Period, CriteriaGroup, template, constants) { +], function (ko, options, Range, DateAdjustment, ConceptSetSelection, Period, CriteriaGroup, template, constants) { function PayerPlanPeriodViewModel(params) { var self = this; @@ -94,6 +95,14 @@ define([ self.Criteria.Gender(ko.observableArray()); }, }, + { + ...constants.payerPlanAttributes.addGenderCS, + selected: false, + action: function () { + if (self.Criteria.GenderCS() == null) + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.payerPlanAttributes.addPayerConcept, selected: false, diff --git a/js/components/cohortbuilder/components/PayerPlanPeriodTemplate.html b/js/components/cohortbuilder/components/PayerPlanPeriodTemplate.html index a0ff1f774..e7a3410b3 100644 --- a/js/components/cohortbuilder/components/PayerPlanPeriodTemplate.html +++ b/js/components/cohortbuilder/components/PayerPlanPeriodTemplate.html @@ -79,6 +79,18 @@ + + + +
    + + + + +
    + + @@ -109,7 +121,7 @@
    -
    @@ -120,7 +132,7 @@
    -
    @@ -131,7 +143,7 @@
    -
    @@ -142,7 +154,7 @@
    -
    @@ -153,7 +165,7 @@
    -
    @@ -164,7 +176,7 @@
    -
    @@ -175,7 +187,7 @@
    -
    @@ -186,8 +198,8 @@
    - +
    diff --git a/js/components/cohortbuilder/components/ProcedureOccurrence.js b/js/components/cohortbuilder/components/ProcedureOccurrence.js index 8b317bc89..9e5647584 100644 --- a/js/components/cohortbuilder/components/ProcedureOccurrence.js +++ b/js/components/cohortbuilder/components/ProcedureOccurrence.js @@ -4,6 +4,7 @@ define([ "../utils", "../InputTypes/Range", "../InputTypes/DateAdjustment", + "../InputTypes/ConceptSetSelection", "../CriteriaGroup", "text!./ProcedureOccurrenceTemplate.html", "../const", @@ -13,6 +14,7 @@ define([ utils, Range, DateAdjustment, + ConceptSetSelection, CriteriaGroup, template, constants @@ -42,6 +44,14 @@ define([ self.Criteria.Gender(ko.observableArray()); }, }, + { + ...constants.occurrenceAttributes.addGenderCS, + selected: false, + action: function () { + if (self.Criteria.GenderCS() == null) + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.procedureOccurrenceAttributes.addDate, selected: false, @@ -69,6 +79,14 @@ define([ self.Criteria.ProcedureType(ko.observableArray()); }, }, + { + ...constants.procedureOccurrenceAttributes.addTypeCS, + selected: false, + action: function () { + if (self.Criteria.ProcedureTypeCS() == null) + self.Criteria.ProcedureTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.procedureOccurrenceAttributes.addVisit, selected: false, @@ -77,6 +95,14 @@ define([ self.Criteria.VisitType(ko.observableArray()); }, }, + { + ...constants.procedureOccurrenceAttributes.addVisitCS, + selected: false, + action: function () { + if (self.Criteria.VisitTypeCS() == null) + self.Criteria.VisitTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.procedureOccurrenceAttributes.addModifier, selected: false, @@ -85,6 +111,14 @@ define([ self.Criteria.Modifier(ko.observableArray()); }, }, + { + ...constants.procedureOccurrenceAttributes.addModifierCS, + selected: false, + action: function () { + if (self.Criteria.ModifierCS() == null) + self.Criteria.ModifierCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.procedureOccurrenceAttributes.addQuantity, selected: false, @@ -113,6 +147,14 @@ define([ self.Criteria.ProviderSpecialty(ko.observableArray()); }, }, + { + ...constants.procedureOccurrenceAttributes.addProviderSpecialtyCS, + selected: false, + action: function () { + if (self.Criteria.ProviderSpecialtyCS() == null) + self.Criteria.ProviderSpecialtyCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.procedureOccurrenceAttributes.addNested, selected: false, diff --git a/js/components/cohortbuilder/components/ProcedureOccurrenceTemplate.html b/js/components/cohortbuilder/components/ProcedureOccurrenceTemplate.html index 8b1495076..8fe4372e5 100644 --- a/js/components/cohortbuilder/components/ProcedureOccurrenceTemplate.html +++ b/js/components/cohortbuilder/components/ProcedureOccurrenceTemplate.html @@ -10,7 +10,7 @@ + defaultName: ko.i18n('components.conditionProcedureOccurrence.anyProcedure', 'Any Procedure')"> @@ -64,6 +64,18 @@ + + + +
    + + + + +
    + + @@ -71,6 +83,18 @@ + + + +
    + + + + +
    + + @@ -84,8 +108,8 @@
    - +
    @@ -106,20 +130,18 @@ - @@ -130,6 +152,18 @@ + + + +
    + + + + +
    + + @@ -138,6 +172,18 @@ + + + +
    + + + + +
    + + diff --git a/js/components/cohortbuilder/components/Specimen.js b/js/components/cohortbuilder/components/Specimen.js index f32b46eae..0761684e9 100644 --- a/js/components/cohortbuilder/components/Specimen.js +++ b/js/components/cohortbuilder/components/Specimen.js @@ -4,11 +4,12 @@ define([ "../utils", "../InputTypes/Range", "../InputTypes/DateAdjustment", + "../InputTypes/ConceptSetSelection", "../InputTypes/Text", "../CriteriaGroup", "text!./SpecimenTemplate.html", "../const" -], function (ko, options, utils, Range, DateAdjustment, Text, CriteriaGroup, template, constants) { +], function (ko, options, utils, Range, DateAdjustment, ConceptSetSelection, Text, CriteriaGroup, template, constants) { function SpecimenViewModel(params) { var self = this; self.expression = ko.utils.unwrapObservable(params.expression); @@ -38,6 +39,14 @@ define([ self.Criteria.Gender(ko.observableArray()); }, }, + { + ...constants.specimenAttributes.addGenderCS, + selected: false, + action: function () { + if (self.Criteria.GenderCS() == null) + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.specimenAttributes.addDate, selected: false, @@ -65,6 +74,14 @@ define([ self.Criteria.SpecimenType(ko.observableArray()); }, }, + { + ...constants.specimenAttributes.addTypeCS, + selected: false, + action: function () { + if (self.Criteria.SpecimenTypeCS() == null) + self.Criteria.SpecimenTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.specimenAttributes.addQuantity, selected: false, @@ -85,6 +102,14 @@ define([ self.Criteria.Unit(ko.observableArray()); }, }, + { + ...constants.specimenAttributes.addUnitCS, + selected: false, + action: function () { + if (self.Criteria.UnitCS() == null) + self.Criteria.UnitCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.specimenAttributes.addAnatomicSite, selected: false, @@ -93,6 +118,14 @@ define([ self.Criteria.AnatomicSite(ko.observableArray()); }, }, + { + ...constants.specimenAttributes.addAnatomicSiteCS, + selected: false, + action: function () { + if (self.Criteria.AnatomicSiteCS() == null) + self.Criteria.AnatomicSiteCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.specimenAttributes.addDiseaseStatus, selected: false, @@ -101,6 +134,14 @@ define([ self.Criteria.DiseaseStatus(ko.observableArray()); }, }, + { + ...constants.specimenAttributes.addDiseaseStatusCS, + selected: false, + action: function () { + if (self.Criteria.DiseaseStatusCS() == null) + self.Criteria.DiseaseStatusCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.specimenAttributes.addSourceId, selected: false, diff --git a/js/components/cohortbuilder/components/SpecimenTemplate.html b/js/components/cohortbuilder/components/SpecimenTemplate.html index 6280bf455..e5930e1dd 100644 --- a/js/components/cohortbuilder/components/SpecimenTemplate.html +++ b/js/components/cohortbuilder/components/SpecimenTemplate.html @@ -9,7 +9,7 @@ - @@ -52,7 +52,6 @@ - @@ -62,6 +61,18 @@ + + + +
    + + + + +
    + + @@ -80,6 +91,18 @@ + + + +
    + + + + +
    + + @@ -90,6 +113,18 @@ + + + +
    + + + + +
    + + @@ -100,6 +135,18 @@ + + + +
    + + + + +
    + + @@ -126,6 +173,18 @@ + + + +
    + + + + +
    + + diff --git a/js/components/cohortbuilder/components/VisitDetail.js b/js/components/cohortbuilder/components/VisitDetail.js index 88899cab2..3ea184d1c 100644 --- a/js/components/cohortbuilder/components/VisitDetail.js +++ b/js/components/cohortbuilder/components/VisitDetail.js @@ -40,11 +40,11 @@ define([ }, }, { - ...constants.visitDetailAttributes.addGender, + ...constants.visitDetailAttributes.addGenderCS, selected: false, action: function () { if (self.Criteria.GenderCS() == null) - self.Criteria.GenderCS(new ConceptSetSelection()); + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); }, }, { @@ -79,11 +79,11 @@ define([ }, }, { - ...constants.visitDetailAttributes.addType, + ...constants.visitDetailAttributes.addTypeCS, selected: false, action: function () { if (self.Criteria.VisitDetailTypeCS() == null) - self.Criteria.VisitDetailTypeCS(new ConceptSetSelection()); + self.Criteria.VisitDetailTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); }, }, { @@ -103,19 +103,19 @@ define([ }, }, { - ...constants.visitDetailAttributes.addProviderSpecialty, + ...constants.visitDetailAttributes.addProviderSpecialtyCS, selected: false, action: function () { if (self.Criteria.ProviderSpecialtyCS() == null) - self.Criteria.ProviderSpecialtyCS(new ConceptSetSelection()); + self.Criteria.ProviderSpecialtyCS(new ConceptSetSelection({}, self.expression.ConceptSets)); }, }, { - ...constants.visitDetailAttributes.addPlaceService, + ...constants.visitDetailAttributes.addPlaceServiceCS, selected: false, action: function () { if (self.Criteria.PlaceOfServiceCS() == null) - self.Criteria.PlaceOfServiceCS(new ConceptSetSelection()); + self.Criteria.PlaceOfServiceCS(new ConceptSetSelection({}, self.expression.ConceptSets)); }, }, { diff --git a/js/components/cohortbuilder/components/VisitDetailTemplate.html b/js/components/cohortbuilder/components/VisitDetailTemplate.html index 7c7dfc865..ceb9163db 100644 --- a/js/components/cohortbuilder/components/VisitDetailTemplate.html +++ b/js/components/cohortbuilder/components/VisitDetailTemplate.html @@ -10,7 +10,7 @@ - +
    @@ -68,12 +68,11 @@
    - + - +
    @@ -84,16 +83,15 @@ - +
    - + @@ -110,11 +108,11 @@
    - + - +
    @@ -122,12 +120,11 @@
    - + - +
    @@ -135,10 +132,10 @@
    - + - +
    @@ -149,28 +146,15 @@ - - - - - - - -
    - - - +
    - - + diff --git a/js/components/cohortbuilder/components/VisitOccurrence.js b/js/components/cohortbuilder/components/VisitOccurrence.js index b0cdce90e..c81c090a5 100644 --- a/js/components/cohortbuilder/components/VisitOccurrence.js +++ b/js/components/cohortbuilder/components/VisitOccurrence.js @@ -5,6 +5,7 @@ define([ "../utils", "../InputTypes/Range", "../InputTypes/DateAdjustment", + "../InputTypes/ConceptSetSelection", "../CriteriaGroup", "text!./VisitOccurrenceTemplate.html", "../const", @@ -15,6 +16,7 @@ define([ utils, Range, DateAdjustment, + ConceptSetSelection, CriteriaGroup, template, constants @@ -44,6 +46,14 @@ define([ self.Criteria.Gender(ko.observableArray()); }, }, + { + ...constants.visitAttributes.addGenderCS, + selected: false, + action: function () { + if (self.Criteria.GenderCS() == null) + self.Criteria.GenderCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.visitAttributes.addStartDate, selected: false, @@ -83,6 +93,14 @@ define([ self.Criteria.VisitType(ko.observableArray()); }, }, + { + ...constants.visitAttributes.addTypeCS, + selected: false, + action: function () { + if (self.Criteria.VisitTypeCS() == null) + self.Criteria.VisitTypeCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.visitAttributes.addLength, selected: false, @@ -107,6 +125,14 @@ define([ self.Criteria.ProviderSpecialty(ko.observableArray()); }, }, + { + ...constants.visitAttributes.addProviderSpecialtyCS, + selected: false, + action: function () { + if (self.Criteria.ProviderSpecialtyCS() == null) + self.Criteria.ProviderSpecialtyCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.visitAttributes.addPlaceService, selected: false, @@ -115,6 +141,14 @@ define([ self.Criteria.PlaceOfService(ko.observableArray()); }, }, + { + ...constants.visitAttributes.addPlaceServiceCS, + selected: false, + action: function () { + if (self.Criteria.PlaceOfServiceCS() == null) + self.Criteria.PlaceOfServiceCS(new ConceptSetSelection({}, self.expression.ConceptSets)); + }, + }, { ...constants.visitAttributes.addPlaceServiceLocation, selected: false, diff --git a/js/components/cohortbuilder/components/VisitOccurrenceTemplate.html b/js/components/cohortbuilder/components/VisitOccurrenceTemplate.html index 8d01f05bd..9cd2e181d 100644 --- a/js/components/cohortbuilder/components/VisitOccurrenceTemplate.html +++ b/js/components/cohortbuilder/components/VisitOccurrenceTemplate.html @@ -10,7 +10,7 @@ - +
    @@ -76,6 +76,18 @@ + + + +
    + + + + +
    + + @@ -83,7 +95,7 @@ -
    @@ -117,6 +129,18 @@ + + + +
    + + + + +
    + + @@ -130,6 +154,18 @@ + + + +
    + + + + +
    + + @@ -142,26 +178,27 @@ - - + +
    - - - + + + +
    - - + +
    + data-bind="text: ko.i18n('components.conditionVisit.conditionSpecimeText_11', 'with a Place of Service located in:')"> - +
    diff --git a/js/components/cohortbuilder/const.js b/js/components/cohortbuilder/const.js index 92cb8edf9..8f7bc4f8f 100644 --- a/js/components/cohortbuilder/const.js +++ b/js/components/cohortbuilder/const.js @@ -410,6 +410,68 @@ define(["knockout"], function (ko) { descriptionDemographic: 'const.eventsList.addGender.desc_demographic', defaultDescriptionDemographic: 'Filter events based on Gender.', }, + addGenderCS: { + titleEra: 'const.eventsList.addGenderCS.title_era', + defaultTitleEra: 'Add Gender Criteria', + descriptionEra: 'const.eventsList.addGenderCS.desc_era', + defaultDescriptionEra: 'Filter Condition Eras based on Gender.', + titleOccurrence: 'const.eventsList.addGenderCS.title_occurrence', + defaultTitleOccurrence: 'Add Gender', + descriptionOccurrence: 'const.eventsList.addGenderCS.desc_occurrence', + defaultDescriptionOccurrence: 'Filter Condition Occurrences based on Gender.', + titleDeath: 'const.eventsList.addGenderCS.title_death', + defaultTitleDeath: 'Add Gender Criteria', + descriptionDeath: 'const.eventsList.addGenderCS.desc_death', + defaultDescriptionDeath: 'Filter Deaths based on Gender.', + titleDevice: 'const.eventsList.addGenderCS.title_deviceexposure', + defaultTitleDevice: 'Add Gender Criteria', + descriptionDevice: 'const.eventsList.addGenderCS.desc_deviceexposure', + defaultDescriptionDevice: 'Filter Device Exposures based on Gender.', + titleDose: 'const.eventsList.addGenderCS.title_dose', + defaultTitleDose: 'Add Gender Criteria', + descriptionDose: 'const.eventsList.addGenderCS.desc_dose', + defaultDescriptionDose: 'Filter Drug Eras based on Gender.', + titleDrug: 'const.eventsList.addGenderCS.title_drug', + defaultTitleDrug: 'Add Gender Criteria', + descriptionDrug: 'const.eventsList.addGenderCS.desc_drug', + defaultDescriptionDrug: 'Filter Drug Eras based on Gender.', + titleDrugexposure: 'const.eventsList.addGenderCS.title_drugexposure', + defaultTitleDrugexposure: 'Add Gender Criteria', + descriptionDrugexposure: 'const.eventsList.addGenderCS.desc_drugexposure', + defaultDescriptionDrugexposure: 'Filter Drug Exposures based on Gender.', + titleMeasurement: 'const.eventsList.addGenderCS.title_measurement', + defaultTitleMeasurement: 'Add Gender Criteria', + descriptionMeasurement: 'const.eventsList.addGenderCS.desc_measurement', + defaultDescriptionMeasurement: 'Filter Measurements based on Gender.', + titleObservation: 'const.eventsList.addGenderCS.title_observation', + defaultTitleObservation: 'Add Gender Criteria', + descriptionObservation: 'const.eventsList.addGenderCS.desc_observation', + defaultDescriptionObservation: 'Filter Observations based on Gender.', + titlePayerplan: 'const.eventsList.addGenderCS.title_payerplan', + defaultTitlePayerplan: 'Add Gender Criteria', + descriptionPayerplan: 'const.eventsList.addGenderCS.desc_payerplan', + defaultDescriptionPayerplan: 'Filter Payer Plan Periods based on Gender.', + titleProcedureoccurrence: 'const.eventsList.addGenderCS.title_procedureoccurrence', + defaultTitleProcedureoccurrence: 'Add Gender Criteria', + descriptionProcedureoccurrence: 'const.eventsList.addGenderCS.desc_procedureoccurrence', + defaultDescriptionProcedureoccurrence: 'Filter Procedure Occurrences based on Gender.', + titleSpecimen: 'const.eventsList.addGenderCS.title_specimen', + defaultTitleSpecimen: 'Add Gender Criteria', + descriptionSpecimen: 'const.eventsList.addGenderCS.desc_specimen', + defaultDescriptionSpecimen: 'Filter specimens based on Gender.', + titleVisit: 'const.eventsList.addGenderCS.title_visit', + defaultTitleVisit: 'Add Gender Criteria', + descriptionVisit: 'const.eventsList.addGenderCS.desc_visit', + defaultDescriptionVisit: 'Filter Visit Occurrences based on Gender.', + titleVisitdetail: 'const.eventsList.addGenderCS.title_visitDetail', + defaultTitleVisitdetail: 'Add Gender Criteria', + descriptionVisitdetail: 'const.eventsList.addGenderCS.desc_visitDetail', + defaultDescriptionVisitdetail: 'Filter Visit Detail based on Gender.', + titleDemographic: 'const.eventsList.addGenderCS.title_demographic', + defaultTitleDemographic: 'Add Gender Criteria', + descriptionDemographic: 'const.eventsList.addGenderCS.desc_demographic', + defaultDescriptionDemographic: 'Filter events based on Gender.', + }, addDate:{ titleDeath: 'const.eventsList.addDate.title_death', defaultTitleDeath: 'Add Death Date Criteria', @@ -552,6 +614,28 @@ define(["knockout"], function (ko) { descriptionSpecimen: 'const.eventsList.addUnit.desc_specimen', defaultDescriptionSpecimen: 'Filter Specimens by Unit.', }, + addUnitCS: { + titleDose: 'const.eventsList.addUnitCS.title_dose', + defaultTitleDose: 'Add Dose Unit Concept Set Criteria', + descriptionDose: 'const.eventsList.addUnitCS.desc_dose', + defaultDescriptionDose: 'Filter Dose Eras by the Unit.', + titleDrugexposure: 'const.eventsList.addUnitCS.title_drugexposure', + defaultTitleDrugexposure: 'Add Dose Unit Concept Set Criteria', + descriptionDrugexposure: 'const.eventsList.addUnitCS.desc_drugexposure', + defaultDescriptionDrugexposure: 'Filter Drug Exposures by Dose Unit.', + titleMeasurement: 'const.eventsList.addUnitCS.title_measurement', + defaultTitleMeasurement: 'Add Unit Concept Set Criteria', + descriptionMeasurement: 'const.eventsList.addUnitCS.desc_measurement', + defaultDescriptionMeasurement: 'Filter Measurements by the Unit.', + titleObservation: 'const.eventsList.addUnitCS.title_observation', + defaultTitleObservation: 'Add Unit Concept Set Criteria', + descriptionObservation: 'const.eventsList.addUnitCS.desc_observation', + defaultDescriptionObservation: 'Filter Observations by Unit.', + titleSpecimen: 'const.eventsList.addUnitCS.title_specimen', + defaultTitleSpecimen: 'Add Unit Concept Set Criteria', + descriptionSpecimen: 'const.eventsList.addUnitCS.desc_specimen', + defaultDescriptionSpecimen: 'Filter Specimens by Unit.', + }, addConditonCount: { titleEra: 'const.eventsList.addConditonCount.title_era', defaultTitleEra: 'Add Era Conditon Count Criteria', @@ -714,6 +798,52 @@ define(["knockout"], function (ko) { descriptionVisitdetail: 'const.eventsList.addType.desc_visitDetail', defaultDescriptionVisitdetail: 'Filter Visit details by the Visit Type.', }, + addTypeCS: { + titleOccurrence: 'const.eventsList.addTypeCS.title_occurrence', + defaultTitleOccurrence: 'Add Condition Type Concept Set', + descriptionOccurrence: 'const.eventsList.addTypeCS.desc_occurrence', + defaultDescriptionOccurrence: 'Filter Condition Occurrences by the Condition Type.', + titleDeath: 'const.eventsList.addTypeCS.title_death', + defaultTitleDeath: 'Add Death Type Concept Set Criteria', + descriptionDeath: 'const.eventsList.addTypeCS.desc_death', + defaultDescriptionDeath: 'Filter by Death Type.', + titleDevice: 'const.eventsList.addTypeCS.title_deviceexposure', + defaultTitleDevice: 'Add Device Type Concept Set Criteria', + descriptionDevice: 'const.eventsList.addTypeCS.desc_deviceexposure', + defaultDescriptionDevice: 'Filter Device Exposures by the Exposure Type.', + titleDrugexposure: 'const.eventsList.addTypeCS.title_drugexposure', + defaultTitleDrugexposure: 'Add Drug Type Concept Set Criteria', + descriptionDrugexposure: 'const.eventsList.addTypeCS.desc_drugexposure', + defaultDescriptionDrugexposure: 'Filter Drug Exposures by the Drug Type.', + titleMeasurement: 'const.eventsList.addTypeCS.title_measurement', + defaultTitleMeasurement: 'Add Measurement Type Concept Set Criteria', + descriptionMeasurement: 'const.eventsList.addTypeCS.desc_measurement', + defaultDescriptionMeasurement: 'Filter Measurements by the Measurement Type.', + titleObservation: 'const.eventsList.addTypeCS.title_observation', + defaultTitleObservation: 'Add Observation Type Concept Set Criteria', + descriptionObservation: 'const.eventsList.addTypeCS.desc_observation', + defaultDescriptionObservation: 'Filter Observations by the Type.', + titleObservationperiod: 'const.eventsList.addTypeCS.title_observationperiod', + defaultTitleObservationperiod: 'Add Period Type Concept Set Criteria', + descriptionObservationperiod: 'const.eventsList.addTypeCS.desc_observationperiod', + defaultDescriptionObservationperiod: 'Filter Obsevation Periods by Type.', + titleProcedureoccurrence: 'const.eventsList.addTypeCS.title_procedureoccurrence', + defaultTitleProcedureoccurrence: 'Add Procedure Type Concept Set Criteria', + descriptionProcedureoccurrence: 'const.eventsList.addTypeCS.desc_procedureoccurrence', + defaultDescriptionProcedureoccurrence: 'Filter Procedure Occurrences by the Procedure Type.', + titleSpecimen: 'const.eventsList.addTypeCS.title_specimen', + defaultTitleSpecimen: 'Add Specimen Type Concept Set Criteria', + descriptionSpecimen: 'const.eventsList.addTypeCS.desc_specimen', + defaultDescriptionSpecimen: 'Filter Specimen by the Type.', + titleVisit: 'const.eventsList.addTypeCS.title_visit', + defaultTitleVisit: 'Add Visit Type Concept Set Criteria', + descriptionVisit: 'const.eventsList.addTypeCS.desc_visit', + defaultDescriptionVisit: 'Filter Visit Occurrences by the Visit Type.', + titleVisitdetail: 'const.eventsList.addTypeCS.title_visitDetail', + defaultTitleVisitdetail: 'Add Visit detail Type Concept Set Criteria', + descriptionVisitdetail: 'const.eventsList.addTypeCS.desc_visitDetail', + defaultDescriptionVisitdetail: 'Filter Visit details by the Visit Type.', + }, addUserDefined: { titleObservationperiod: 'const.eventsList.addUserDefined.title_observationperiod', defaultTitleObservationperiod: '"Specify Start and End Dates', @@ -798,20 +928,66 @@ define(["knockout"], function (ko) { descriptionVisitdetail: 'const.eventsList.addProviderSpecialty.desc_visitDetail', defaultDescriptionVisitdetail: 'Filter Visit detail based on provider specialty.', }, + addProviderSpecialtyCS: { + titleOccurrence: 'const.eventsList.addProviderSpecialtyCS.title_occurrence', + defaultTitleOccurrence: 'Add Provider Specialty Concept Set', + descriptionOccurrence: 'const.eventsList.addProviderSpecialtyCS.desc_occurrence', + defaultDescriptionOccurrence: 'Filter Condition Occurrences based on provider specialty Concept Set.', + titleDevice: 'const.eventsList.addProviderSpecialtyCS.title_deviceexposure', + defaultTitleDevice: 'Add Provider Specialty Concept Set Criteria', + descriptionDevice: 'const.eventsList.addProviderSpecialtyCS.desc_deviceexposure', + defaultDescriptionDevice: 'Filter Device Exposures based on provider specialty.', + titleDrugexposure: 'const.eventsList.addProviderSpecialtyCS.title_drugexposure', + defaultTitleDrugexposure: 'Add Provider Specialty Concept Set Criteria', + descriptionDrugexposure: 'const.eventsList.addProviderSpecialtyCS.desc_drugexposure', + defaultDescriptionDrugexposure: 'Filter Drug Exposures based on provider specialty.', + titleMeasurement: 'const.eventsList.addProviderSpecialtyCS.title_measurement', + defaultTitleMeasurement: 'Add Provider Specialty Concept Set Criteria', + descriptionMeasurement: 'const.eventsList.addProviderSpecialtyCS.desc_measurement', + defaultDescriptionMeasurement: 'Filter Measurements based on provider specialty.', + titleObservation: 'const.eventsList.addProviderSpecialtyCS.title_observation', + defaultTitleObservation: 'Add Provider Specialty Concept Set Criteria', + descriptionObservation: 'const.eventsList.addProviderSpecialtyCS.desc_observation', + defaultDescriptionObservation: 'Filter Observations based on provider specialty.', + titleProcedureoccurrence: 'const.eventsList.addProviderSpecialtyCS.title_procedureoccurrence', + defaultTitleProcedureoccurrence: 'Add Provider Specialty Concept Set Criteria', + descriptionProcedureoccurrence: 'const.eventsList.addProviderSpecialtyCS.desc_procedureoccurrence', + defaultDescriptionProcedureoccurrence: 'Filter Procedure Occurrences based on provider specialty.', + titleVisit: 'const.eventsList.addProviderSpecialtyCS.title_visit', + defaultTitleVisit: 'Add Provider Specialty Concept Set Criteria', + descriptionVisit: 'const.eventsList.addProviderSpecialtyCS.desc_visit', + defaultDescriptionVisit: 'Filter Visit Occurrences based on provider specialty.', + titleVisitdetail: 'const.eventsList.addProviderSpecialtyCS.title_visitDetail', + defaultTitleVisitdetail: 'Add Provider Specialty Concept Set Criteria', + descriptionVisitdetail: 'const.eventsList.addProviderSpecialtyCS.desc_visitDetail', + defaultDescriptionVisitdetail: 'Filter Visit detail based on provider specialty.', + }, addConditionStatus: { titleOccurrence: 'const.eventsList.addConditionStatus.title_occurrence', defaultTitleOccurrence: 'Add Condition Status', descriptionOccurrence: 'const.eventsList.addConditionStatus.desc_occurrence', defaultDescriptionOccurrence: 'Filter Condition Occurrences based on condition status.', }, + addConditionStatusCS: { + titleOccurrence: 'const.eventsList.addConditionStatusCS.title_occurrence', + defaultTitleOccurrence: 'Add Condition Status Concept Set', + descriptionOccurrence: 'const.eventsList.addConditionStatus.desc_occurrence', + defaultDescriptionOccurrence: 'Filter Condition Occurrences based on condition status.', + }, addPlaceService: { titleVisit: 'const.eventsList.addPlaceService.title_visit', defaultTitleVisit: 'Add Place of Service Criteria', descriptionVisit: 'const.eventsList.addPlaceService.desc_visit', defaultDescriptionVisit: 'Filter Visit Occurrences based on Place of Service.', - titleVisitdetail: 'const.eventsList.addPlaceService.title_visitDetail', - defaultTitleVisitdetail: 'Add Place of Service Criteria', - descriptionVisitdetail: 'const.eventsList.addPlaceService.desc_visitDetail', + }, + addPlaceServiceCS: { + titleVisit: 'const.eventsList.addPlaceServiceCS.title_visit', + defaultTitleVisit: 'Add Place of Service Concept Set Criteria', + descriptionVisit: 'const.eventsList.addPlaceServiceCS.desc_visit', + defaultDescriptionVisit: 'Filter Visit Occurrences based on Place of Service.', + titleVisitdetail: 'const.eventsList.addPlaceServiceCS.title_visitDetail', + defaultTitleVisitdetail: 'Add Place of Service Concept Set Criteria', + descriptionVisitdetail: 'const.eventsList.addPlaceServiceCS.desc_visitDetail', defaultDescriptionVisitdetail: 'Filter Visit detail based on Place of Service.', }, addPlaceServiceLocation: { @@ -876,6 +1052,12 @@ define(["knockout"], function (ko) { descriptionDrugexposure: 'const.eventsList.addRoute.desc_drugexposure', defaultDescriptionDrugexposure: 'Filter Drug Exposures by Route.' }, + addRouteCS: { + titleDrugexposure: 'const.eventsList.addRouteCS.title_drugexposure', + defaultTitleDrugexposure: 'Add Route Concept Set Criteria', + descriptionDrugexposure: 'const.eventsList.addRouteCS.desc_drugexposure', + defaultDescriptionDrugexposure: 'Filter Drug Exposures by Route.' + }, addEffective: { titleDrugexposure: 'const.eventsList.addEffective.title_drugexposure', defaultTitleDrugexposure: 'Add Effective Dose Criteria', @@ -900,6 +1082,12 @@ define(["knockout"], function (ko) { descriptionMeasurement: 'const.eventsList.addOperator.desc_measurement', defaultDescriptionMeasurement: 'Filter Measurements by Operator.' }, + addOperatorCS: { + titleMeasurement: 'const.eventsList.addOperatorCS.title_measurement', + defaultTitleMeasurement: 'Add Operator Concept Set Criteria', + descriptionMeasurement: 'const.eventsList.addOperatorCS.desc_measurement', + defaultDescriptionMeasurement: 'Filter Measurements by Operator.' + }, addValueAsString: { titleObservation: 'const.eventsList.addValueAsString.title_observation', defaultTitleObservation: 'Add Value As String Criteria', @@ -916,6 +1104,16 @@ define(["knockout"], function (ko) { descriptionObservation: 'const.eventsList.addValueAsConcept.desc_observation', defaultDescriptionObservation: 'Filter Observations by the Value As Concept.', }, + addValueAsConceptCS: { + titleMeasurement: 'const.eventsList.addValueAsConceptCS.title_measurement', + defaultTitleMeasurement: 'Add Value as Concept Set Criteria', + descriptionMeasurement: 'const.eventsList.addValueAsConceptCS.desc_measurement', + defaultDescriptionMeasurement: 'Filter Measurements by Value as Concept.', + titleObservation: 'const.eventsList.addValueAsConceptCS.title_observation', + defaultTitleObservation: 'Add Value as Concept Set Criteria', + descriptionObservation: 'const.eventsList.addValueAsConceptCS.desc_observation', + defaultDescriptionObservation: 'Filter Observations by the Value As Concept.', + }, addAbnormal: { titleMeasurement: 'const.eventsList.addAbnormal.title_measurement', defaultTitleMeasurement: 'Add Abnormal Result Criteria', @@ -928,6 +1126,12 @@ define(["knockout"], function (ko) { descriptionObservation: 'const.eventsList.addQualifier.desc_observation', defaultDescriptionObservation: 'Filter Observations by Qualifier.', }, + addQualifierCS: { + titleObservation: 'const.eventsList.addQualifierCS.title_observation', + defaultTitleObservation: 'Add Qualifier Concept Set Criteria', + descriptionObservation: 'const.eventsList.addQualifierCS.desc_observation', + defaultDescriptionObservation: 'Filter Observations by Qualifier.', + }, addRangeLow: { titleMeasurement: 'const.eventsList.addRangeLow.title_measurement', defaultTitleMeasurement: 'Add Low Range Criteria', @@ -1006,18 +1210,36 @@ define(["knockout"], function (ko) { descriptionProcedureoccurrence: 'const.eventsList.addModifier.desc_procedureoccurrence', defaultDescriptionProcedureoccurrence: 'Filter Procedure Occurrences by the Modifier.', }, + addModifierCS: { + titleProcedureoccurrence: 'const.eventsList.addModifierCS.title_procedureoccurrence', + defaultTitleProcedureoccurrence: 'Add Modifier Concept Set Criteria', + descriptionProcedureoccurrence: 'const.eventsList.addModifierCS.desc_procedureoccurrence', + defaultDescriptionProcedureoccurrence: 'Filter Procedure Occurrences by the Modifier.', + }, addAnatomicSite: { titleSpecimen: 'const.eventsList.addAnatomicSite.title_specimen', defaultTitleSpecimen: 'Add Anatomic Site Criteria', descriptionSpecimen: 'const.eventsList.addAnatomicSite.desc_specimen', defaultDescriptionSpecimen: 'Filter Specimens by the Anatomic Site.', }, + addAnatomicSiteCS: { + titleSpecimen: 'const.eventsList.addAnatomicSiteCS.title_specimen', + defaultTitleSpecimen: 'Add Anatomic Site Concept Set Criteria', + descriptionSpecimen: 'const.eventsList.addAnatomicSiteCS.desc_specimen', + defaultDescriptionSpecimen: 'Filter Specimens by the Anatomic Site.', + }, addDiseaseStatus: { titleSpecimen: 'const.eventsList.addDiseaseStatus.title_specimen', defaultTitleSpecimen: 'Add Disease Status Criteria', descriptionSpecimen: 'const.eventsList.addDiseaseStatus.desc_specimen', defaultDescriptionSpecimen: 'Filter Specimens by the Disease Status.', }, + addDiseaseStatusCS: { + titleSpecimen: 'const.eventsList.addDiseaseStatusCS.title_specimen', + defaultTitleSpecimen: 'Add Disease Status Concept Set Criteria', + descriptionSpecimen: 'const.eventsList.addDiseaseStatusCS.desc_specimen', + defaultDescriptionSpecimen: 'Filter Specimens by the Disease Status.', + }, addSourceId: { titleSpecimen: 'const.eventsList.addSourceId.title_specimen', defaultTitleSpecimen: 'Add Source ID Criteria', @@ -1030,12 +1252,24 @@ define(["knockout"], function (ko) { descriptionDemographic: 'const.eventsList.addRace.desc_demographic', defaultDescriptionDemographic: 'Filter events based on Race.', }, + addRaceCS: { + titleDemographic: 'const.eventsList.addRaceCS.title_demographic', + defaultTitleDemographic: 'Add Race Concept Set Criteria', + descriptionDemographic: 'const.eventsList.addRace.desc_demographic', + defaultDescriptionDemographic: 'Filter events based on Race.', + }, addEthnicity: { titleDemographic: 'const.eventsList.addEthnicity.title_demographic', defaultTitleDemographic: 'Add Ethnicity Criteria', descriptionDemographic: 'const.eventsList.addEthnicity.desc_demographic', defaultDescriptionDemographic: 'Filter events based on Ethnicity.', }, + addEthnicityCS: { + titleDemographic: 'const.eventsList.addEthnicityCS.title_demographic', + defaultTitleDemographic: 'Add Ethnicity Concept Set Criteria', + descriptionDemographic: 'const.eventsList.addEthnicity.desc_demographic', + defaultDescriptionDemographic: 'Filter events based on Ethnicity.', + }, addGroup: { title: 'const.eventsList.addGroup.title', defaultTitle: 'Add Group', @@ -1064,6 +1298,32 @@ define(["knockout"], function (ko) { descriptionGroup: 'const.eventsList.addLocationRegion.desc_group', defaultDescriptionGroup: 'Find patients within geographical area.', }, + addVisitCS: { + titleOccurrence: 'const.eventsList.addVisitCS.title_occurrence', + defaultTitleOccurrence: 'Add Visit Occurrence Concept Set', + descriptionOccurrence: 'const.eventsList.addVisitCS.desc_occurrence', + defaultDescriptionOccurrence: 'Filter Condition Occurrences based on visit occurrence of diagnosis.', + titleDevice: 'const.eventsList.addVisitCS.title_deviceexposure', + defaultTitleDevice: 'Add Visit Concept Set Criteria', + descriptionDevice: 'const.eventsList.addVisitCS.desc_deviceexposure', + defaultDescriptionDevice: 'Filter Device Exposures based on visit occurrence of exposure.', + titleDrugexposure: 'const.eventsList.addVisitCS.title_drugexposure', + defaultTitleDrugexposure: 'Add Visit Concept Set Criteria', + descriptionDrugexposure: 'const.eventsList.addVisitCS.desc_drugexposure', + defaultDescriptionDrugexposure: 'Filter Drug Exposures based on visit occurrence of drug exposure.', + titleMeasurement: 'const.eventsList.addVisitCS.title_measurement', + defaultTitleMeasurement: 'Add Visit Concept Set Criteria', + descriptionMeasurement: 'const.eventsList.addVisitCS.desc_measurement', + defaultDescriptionMeasurement: 'Filter Measurements based on visit occurrence of measurement.', + titleObservation: 'const.eventsList.addVisitCS.title_observation', + defaultTitleObservation: 'Add Visit Concept Set Criteria', + descriptionObservation: 'const.eventsList.addVisitCS.desc_observation', + defaultDescriptionObservation: 'Filter Observations based on visit occurrence of observation.', + titleProcedureoccurrence: 'const.eventsList.addVisitCS.title_procedureoccurrence', + defaultTitleProcedureoccurrence: 'Add Visit Concept Set Criteria', + descriptionProcedureoccurrence: 'const.eventsList.addVisitCS.desc_procedureoccurrence', + defaultDescriptionProcedureoccurrence: 'Filter Procedure Occurrences based on visit occurrence of procedure.', + }, }; @@ -1149,6 +1409,7 @@ define(["knockout"], function (ko) { 'addAgeAtEnd', "addDateAdjustment", 'addGender', + 'addGenderCS', 'addStartDate', 'addEndDate', 'addConditonCount', @@ -1159,23 +1420,30 @@ define(["knockout"], function (ko) { 'addFirstDiagnosis', 'addAge', 'addGender', + 'addGenderCS', 'addConditionStatus', + 'addConditionStatusCS', 'addStartDate', 'addEndDate', 'addDateAdjustment', 'addType', + 'addTypeCS', 'addVisit', + 'addVisitCS', 'addStopReason', 'addSourceConcept', 'addProviderSpecialty', + 'addProviderSpecialtyCS', 'addNested', ]; const deathAttributesList = [ 'addDateAdjustment', 'addAge', 'addGender', + 'addGenderCS', 'addDate', 'addType', + 'addTypeCS', 'addSourceConcept', 'addNested', ]; @@ -1184,14 +1452,18 @@ define(["knockout"], function (ko) { 'addFirstDiagnosis', 'addAge', 'addGender', + 'addGenderCS', 'addStartDate', 'addEndDate', 'addType', + 'addTypeCS', 'addVisit', + 'addVisitCS', 'addUniqueId', 'addQuantity', - 'addSourceConcept', 'addProviderSpecialty', + 'addSourceConcept', + 'addProviderSpecialtyCS', 'addNested', ]; const doseAttributesList = [ @@ -1200,9 +1472,11 @@ define(["knockout"], function (ko) { 'addAgeAtStart', 'addAgeAtEnd', 'addGender', + 'addGenderCS', 'addStartDate', 'addEndDate', 'addUnit', + 'addUnitCS', 'addLength', 'addValue', 'addNested', @@ -1213,6 +1487,7 @@ define(["knockout"], function (ko) { 'addAgeAtStart', 'addAgeAtEnd', 'addGender', + 'addGenderCS', 'addStartDate', 'addEndDate', 'addLength', @@ -1224,19 +1499,26 @@ define(["knockout"], function (ko) { 'addFirstDiagnosis', 'addAge', 'addGender', + 'addGenderCS', 'addDate', 'addType', + 'addTypeCS', 'addVisit', + 'addVisitCS', 'addOperator', + 'addOperatorCS', 'addValue', 'addValueAsConcept', + 'addValueAsConceptCS', 'addUnit', + 'addUnitCS', 'addAbnormal', 'addRangeLow', 'addRangeHigh', 'addRangeLowRatio', 'addRangeHighRatio', 'addProviderSpecialty', + 'addProviderSpecialtyCS', 'addSourceConcept', 'addNested' ]; @@ -1245,20 +1527,26 @@ define(["knockout"], function (ko) { 'addFirstDiagnosis', 'addAge', 'addGender', + 'addGenderCS', 'addStartDate', 'addEndDate', 'addType', + 'addTypeCS', 'addVisit', + 'addVisitCS', 'addStopReason', 'addRefills', 'addQuantity', 'addDaysSupply', 'addRoute', + 'addRouteCS', 'addEffective', 'addUnit', + 'addUnitCS', 'addLotNumber', 'addSource', 'addProviderSpecialty', + 'addProviderSpecialtyCS', 'addNested', ]; const observationAttributesList = [ @@ -1266,16 +1554,23 @@ define(["knockout"], function (ko) { 'addFirstDiagnosis', 'addAge', 'addGender', + 'addGenderCS', 'addDate', 'addType', + 'addTypeCS', 'addVisit', + 'addVisitCS', 'addValue', 'addValueAsString', 'addValueAsConcept', + 'addValueAsConceptCS', 'addQualifier', + 'addQualifierCS', 'addUnit', + 'addUnitCS', 'addSourceConcept', 'addProviderSpecialty', + 'addProviderSpecialtyCS', 'addNested', ]; const observationPeriodAttributesList = [ @@ -1287,6 +1582,7 @@ define(["knockout"], function (ko) { 'addStartDate', 'addEndDate', 'addType', + 'addTypeCS', 'addLength', 'addNested', ]; @@ -1297,6 +1593,7 @@ define(["knockout"], function (ko) { 'addAgeAtEnd', 'addLength', 'addGender', + 'addGenderCS', 'addUserDefined', 'addStartDate', 'addEndDate', @@ -1314,69 +1611,84 @@ define(["knockout"], function (ko) { 'addDateAdjustment', 'addFirstDiagnosis', 'addVisit', + 'addVisitCS', 'addGender', + 'addGenderCS', 'addAge', 'addType', + 'addTypeCS', 'addSourceConcept', 'addProviderSpecialty', + 'addProviderSpecialtyCS', 'addDate', 'addQuantity', 'addModifier', + 'addModifierCS', 'addNested', ]; const specimenAttributesList = [ 'addDateAdjustment', 'addFirstDiagnosis', 'addGender', + 'addGenderCS', 'addNested', 'addAge', 'addType', + 'addTypeCS', 'addDate', 'addQuantity', 'addUnit', + 'addUnitCS', 'addAnatomicSite', + 'addAnatomicSiteCS', 'addDiseaseStatus', + 'addDiseaseStatusCS', 'addSourceId', ]; const visitAttributesList = [ 'addDateAdjustment', 'addFirstDiagnosis', 'addGender', + 'addGenderCS', 'addStartDate', 'addEndDate', 'addLength', 'addAge', 'addType', + 'addTypeCS', 'addSourceConcept', 'addProviderSpecialty', + 'addProviderSpecialtyCS', 'addPlaceService', + 'addPlaceServiceCS', 'addPlaceServiceLocation', - 'addPlaceServiceDistance', 'addNested', ]; const visitDetailAttributesList = [ 'addDateAdjustment', 'addFirstDiagnosis', - 'addGender', + 'addGenderCS', 'addStartDate', 'addEndDate', 'addLength', 'addAge', - 'addType', + 'addTypeCS', 'addSourceConcept', - 'addProviderSpecialty', - 'addPlaceService', + 'addProviderSpecialtyCS', + 'addPlaceServiceCS', 'addPlaceServiceLocation', - 'addPlaceServiceDistance', 'addNested', ]; const demographicAttributesList = [ 'addAge', 'addGender', + 'addGenderCS', 'addStartDate', 'addEndDate', 'addRace', + 'addRaceCS', 'addEthnicity', + 'addEthnicityCS', ]; const windowedpAttributesList = [ 'addDemographic', diff --git a/js/components/cohortbuilder/utils.js b/js/components/cohortbuilder/utils.js index 3db6af990..4e5589d79 100644 --- a/js/components/cohortbuilder/utils.js +++ b/js/components/cohortbuilder/utils.js @@ -4,9 +4,7 @@ function( ){ function getCriteriaComponent(data) { - if (data.hasOwnProperty("Person")) - return "person-criteria"; - else if (data.hasOwnProperty("ConditionOccurrence")) + if (data.hasOwnProperty("ConditionOccurrence")) return "condition-occurrence-criteria"; else if (data.hasOwnProperty("ConditionEra")) return "condition-era-criteria"; diff --git a/js/components/cohortdefinitionviewer/components/CohortExpressionViewer.js b/js/components/cohortdefinitionviewer/components/CohortExpressionViewer.js index a285cde1b..49b4d0973 100644 --- a/js/components/cohortdefinitionviewer/components/CohortExpressionViewer.js +++ b/js/components/cohortdefinitionviewer/components/CohortExpressionViewer.js @@ -31,6 +31,8 @@ define(['knockout', 'components/cohortbuilder/options', 'text!./CohortExpression return "observation-criteria-viewer"; else if (data.hasOwnProperty("VisitOccurrence")) return "visit-occurrence-criteria-viewer"; + else if (data.hasOwnProperty("VisitDetail")) + return "visit-detail-criteria-viewer"; else if (data.hasOwnProperty("DeviceExposure")) return "device-exposure-criteria-viewer"; else if (data.hasOwnProperty("Measurement")) diff --git a/js/components/cohortdefinitionviewer/components/ConceptSetReference.js b/js/components/cohortdefinitionviewer/components/ConceptSetReference.js index 11884e011..36b97f97e 100644 --- a/js/components/cohortdefinitionviewer/components/ConceptSetReference.js +++ b/js/components/cohortdefinitionviewer/components/ConceptSetReference.js @@ -16,7 +16,7 @@ define(['knockout','text!./ConceptSetReferenceTemplate.html'], function (ko, tem self.referenceId = ko.pureComputed(function () { var calculatedRefId = ""; - var selectedConceptSet = self.conceptSets().filter(function(item) { return item.id == ko.utils.unwrapObservable(self.conceptSetId); })[0]; + var selectedConceptSet = self.conceptSets().find(function(item) { return item.id == ko.utils.unwrapObservable(self.conceptSetId); }); if (selectedConceptSet) { calculatedRefId = (self.sortedConceptSets().indexOf(selectedConceptSet) + 1) + ""; @@ -25,7 +25,7 @@ define(['knockout','text!./ConceptSetReferenceTemplate.html'], function (ko, tem }); self.codesetName = ko.pureComputed(function () { - var selectedConceptSet = self.conceptSets().filter(function (item) { return item.id == ko.utils.unwrapObservable(self.conceptSetId)})[0]; + var selectedConceptSet = self.conceptSets().find(function (item) { return item.id == ko.utils.unwrapObservable(self.conceptSetId)}); if (selectedConceptSet) { return ko.utils.unwrapObservable(selectedConceptSet.name); diff --git a/js/components/cohortdefinitionviewer/components/ConditionEra.js b/js/components/cohortdefinitionviewer/components/ConditionEra.js index ca6420cc0..1015cd25b 100644 --- a/js/components/cohortdefinitionviewer/components/ConditionEra.js +++ b/js/components/cohortdefinitionviewer/components/ConditionEra.js @@ -1,4 +1,4 @@ -define(['knockout', 'components/cohortbuilder/options', 'components/cohortbuilder/InputTypes/Range', 'text!./ConditionEraTemplate.html'], function (ko, options, Range, template) { +define(['knockout', 'components/cohortbuilder/options', "components/cohortbuilder/utils", 'text!./ConditionEraTemplate.html'], function (ko, options, utils, template) { function ConditionEraViewModel(params) { @@ -7,15 +7,16 @@ define(['knockout', 'components/cohortbuilder/options', 'components/cohortbuilde self.Criteria = params.criteria.ConditionEra; self.options = options; - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; + self.indexMessage = ko.pureComputed(() => { + var anyCondition = ko.i18n('components.conditionEra.anyConditionButton', 'Any Condition'); + var message = ko.i18n('components.conditionEra.returnText_1', 'The index date refers to the condition era of'); + var conceptSetName = utils.getConceptSetName( + self.Criteria.CodesetId, + self.expression.ConceptSets, + anyCondition() + ); + return `${message()} ${conceptSetName}.`; + }); } // return compoonent definition diff --git a/js/components/cohortdefinitionviewer/components/ConditionEraTemplate.html b/js/components/cohortdefinitionviewer/components/ConditionEraTemplate.html index 52ebcbffc..e3cc42405 100644 --- a/js/components/cohortdefinitionviewer/components/ConditionEraTemplate.html +++ b/js/components/cohortdefinitionviewer/components/ConditionEraTemplate.html @@ -2,10 +2,15 @@
      + +
    • + + +
    • +
    • - +
    • @@ -45,8 +50,17 @@ + +
    • + + + +
    • + - +
    diff --git a/js/components/cohortdefinitionviewer/components/ConditionOccurrence.js b/js/components/cohortdefinitionviewer/components/ConditionOccurrence.js index 2f2ead08b..760899e7e 100644 --- a/js/components/cohortdefinitionviewer/components/ConditionOccurrence.js +++ b/js/components/cohortdefinitionviewer/components/ConditionOccurrence.js @@ -1,4 +1,5 @@ -define(['knockout', 'components/cohortbuilder/options', 'components/cohortbuilder/InputTypes/Range', 'components/cohortbuilder/InputTypes/Text', 'text!./ConditionOccurrenceTemplate.html'], function (ko, options, Range, Text, template) { +define(['knockout', 'components/cohortbuilder/options', "components/cohortbuilder/utils", 'text!./ConditionOccurrenceTemplate.html' +], function (ko, options, utils, template) { function ConditionOccurrenceViewModel(params) { @@ -7,15 +8,17 @@ define(['knockout', 'components/cohortbuilder/options', 'components/cohortbuilde self.Criteria = params.criteria.ConditionOccurrence; self.options = options; - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; + self.indexMessage = ko.i18nformat( + 'components.conditionOccurrence.indexDataText', + 'The index date refers to the condition occurrence of <%= conceptSetName %>.', + { + conceptSetName: ko.pureComputed(() => utils.getConceptSetName( + self.Criteria.CodesetId, + self.expression.ConceptSets, + ko.i18n('components.conditionOccurrence.anyCondition', 'Any Condition') + )), + } + ); } // return compoonent definition diff --git a/js/components/cohortdefinitionviewer/components/ConditionOccurrenceTemplate.html b/js/components/cohortdefinitionviewer/components/ConditionOccurrenceTemplate.html index 8c882c11e..dfb2400ee 100644 --- a/js/components/cohortdefinitionviewer/components/ConditionOccurrenceTemplate.html +++ b/js/components/cohortdefinitionviewer/components/ConditionOccurrenceTemplate.html @@ -2,6 +2,12 @@
      + +
    • + + +
    • +
    • @@ -20,11 +26,35 @@ data-bind="text: ko.i18n('components.conditionOccurrence.conditionOccurrenceText_4', 'occurrence end is:')">
    • + +
    • + with a condition status of: + +
    • + + +
    • + + + + +
    • +
    • + +
    • + + + + +
    • +
    • @@ -45,18 +75,45 @@
    • + +
    • + + + + +
    • +
    • + +
    • + + + + +
    • +
    • + +
    • + + + + +
    • + - +
    diff --git a/js/components/cohortdefinitionviewer/components/CriteriaGroup.js b/js/components/cohortdefinitionviewer/components/CriteriaGroup.js index 417ad5001..79e4fe53d 100644 --- a/js/components/cohortdefinitionviewer/components/CriteriaGroup.js +++ b/js/components/cohortdefinitionviewer/components/CriteriaGroup.js @@ -1,4 +1,5 @@ -define(['knockout','components/cohortbuilder/CriteriaTypes','components/cohortbuilder/CriteriaGroup','components/cohortbuilder/AdditionalCriteria','components/cohortbuilder/options', 'text!./CriteriaGroupTemplate.html'], function (ko, criteriaTypes, CriteriaGroup, AdditionalCriteria, options, template) { +define(['knockout','components/cohortbuilder/options', 'text!./CriteriaGroupTemplate.html' +], function (ko, options, template) { function CriteriaGroupViewModel(params) { var self = this; @@ -7,13 +8,11 @@ define(['knockout','components/cohortbuilder/CriteriaTypes','components/cohortbu self.group = params.group; self.parentGroup = params.parentGroup; self.options = options; - self.selectedFragment = params.selectedFragment; + self.indexMessage = params.indexMessage; self.getCriteriaComponent = function (data) { - if (data.hasOwnProperty("Person")) - return "person-criteria"; - else if (data.hasOwnProperty("ConditionOccurrence")) + if (data.hasOwnProperty("ConditionOccurrence")) return "condition-occurrence-criteria-viewer"; else if (data.hasOwnProperty("ConditionEra")) return "condition-era-criteria-viewer"; @@ -29,6 +28,8 @@ define(['knockout','components/cohortbuilder/CriteriaTypes','components/cohortbu return "procedure-occurrence-criteria-viewer"; else if (data.hasOwnProperty("VisitOccurrence")) return "visit-occurrence-criteria-viewer"; + else if (data.hasOwnProperty("VisitDetail")) + return "visit-detail-criteria-viewer"; else if (data.hasOwnProperty("Observation")) return "observation-criteria-viewer"; else if (data.hasOwnProperty("DeviceExposure")) diff --git a/js/components/cohortdefinitionviewer/components/CriteriaGroupTemplate.html b/js/components/cohortdefinitionviewer/components/CriteriaGroupTemplate.html index 120d4d844..13d331907 100644 --- a/js/components/cohortdefinitionviewer/components/CriteriaGroupTemplate.html +++ b/js/components/cohortdefinitionviewer/components/CriteriaGroupTemplate.html @@ -20,7 +20,7 @@ - +
  • @@ -49,6 +49,7 @@
    +
    diff --git a/js/components/cohortdefinitionviewer/components/DateAdjustment.js b/js/components/cohortdefinitionviewer/components/DateAdjustment.js new file mode 100644 index 000000000..13991a29b --- /dev/null +++ b/js/components/cohortdefinitionviewer/components/DateAdjustment.js @@ -0,0 +1,25 @@ +define(["knockout", "text!./DateAdjustmentTemplate.html", "components/cohortbuilder/InputTypes/DateAdjustment"], function ( + ko, + componentTemplate, + DateAdjustment + ) { + function DateAdjustmentViewModel(params) { + var self = this; + self.DateAdjustment = params.DateAdjustment; + self.getFieldName = function(field) { + switch (field) { + case DateAdjustment.START_DATE: + return ko.i18n('options.startDate', 'start date'); + case DateAdjustment.END_DATE: + return ko.i18n('options.endDate', 'end date'); + } + } + } + + // return compoonent definition + return { + viewModel: DateAdjustmentViewModel, + template: componentTemplate, + }; + }); + \ No newline at end of file diff --git a/js/components/cohortdefinitionviewer/components/DateAdjustmentTemplate.html b/js/components/cohortdefinitionviewer/components/DateAdjustmentTemplate.html new file mode 100644 index 000000000..c6303c53c --- /dev/null +++ b/js/components/cohortdefinitionviewer/components/DateAdjustmentTemplate.html @@ -0,0 +1,4 @@ +starting with ++ days and ending with + ++ days \ No newline at end of file diff --git a/js/components/cohortdefinitionviewer/components/Death.js b/js/components/cohortdefinitionviewer/components/Death.js index d7d686fe1..c8e4dd7b3 100644 --- a/js/components/cohortdefinitionviewer/components/Death.js +++ b/js/components/cohortdefinitionviewer/components/Death.js @@ -1,4 +1,5 @@ -define(['knockout','components/cohortbuilder/options','components/cohortbuilder/InputTypes/Range','components/cohortbuilder/InputTypes/Text', 'text!./DeathTemplate.html'], function (ko, options, Range, Text, template) { +define(['knockout','components/cohortbuilder/options','components/cohortbuilder/utils', 'text!./DeathTemplate.html' +], function (ko, options, utils, template) { function DeathViewModel(params) { var self = this; @@ -7,15 +8,17 @@ define(['knockout','components/cohortbuilder/options','components/cohortbuilder/ self.Criteria = params.criteria.Death; self.options = options; - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; + self.indexMessage = ko.i18nformat( + 'components.conditionDeath.indexDataText', + 'The index date refers to the death event of <%= conceptSetName %>.', + { + conceptSetName: ko.pureComputed(() => utils.getConceptSetName( + self.Criteria.CodesetId, + self.expression.ConceptSets, + ko.i18n('components.conditionDeath.anyDeath', 'Any Death') + )) + } + ); } diff --git a/js/components/cohortdefinitionviewer/components/DeathTemplate.html b/js/components/cohortdefinitionviewer/components/DeathTemplate.html index 2f2cc18b0..f299337ad 100644 --- a/js/components/cohortdefinitionviewer/components/DeathTemplate.html +++ b/js/components/cohortdefinitionviewer/components/DeathTemplate.html @@ -2,16 +2,31 @@
      + +
    • + + +
    • +
    • -
    • - - -
    • +
    • + + +
    • + + +
    • + + + + +
    • @@ -28,8 +43,17 @@
    • + +
    • + + + + +
    • + - +
    diff --git a/js/components/cohortdefinitionviewer/components/DemographicCriteria.js b/js/components/cohortdefinitionviewer/components/DemographicCriteria.js index c56182c46..9a6b76992 100644 --- a/js/components/cohortdefinitionviewer/components/DemographicCriteria.js +++ b/js/components/cohortdefinitionviewer/components/DemographicCriteria.js @@ -1,11 +1,12 @@ -define(['knockout', 'text!./DemographicCriteriaTemplate.html', -], function (ko, template) { +define(['knockout','components/cohortbuilder/options', 'text!./DemographicCriteriaTemplate.html', +], function (ko, options, template) { function DemographicCriteriaViewModel(params) { var self = this; self.Criteria = params.criteria; - + self.expression = ko.utils.unwrapObservable(params.expression); + self.options = options; } // return compoonent definition diff --git a/js/components/cohortdefinitionviewer/components/DemographicCriteriaTemplate.html b/js/components/cohortdefinitionviewer/components/DemographicCriteriaTemplate.html index 58cce4fe1..bf4ef64fb 100644 --- a/js/components/cohortdefinitionviewer/components/DemographicCriteriaTemplate.html +++ b/js/components/cohortdefinitionviewer/components/DemographicCriteriaTemplate.html @@ -10,16 +10,43 @@ + +
  • + + + + +
  • +
  • + +
  • + + + + +
  • +
  • + +
  • + + + + +
  • +
  • diff --git a/js/components/cohortdefinitionviewer/components/DeviceExposure.js b/js/components/cohortdefinitionviewer/components/DeviceExposure.js index 619c18ced..54d0c4c55 100644 --- a/js/components/cohortdefinitionviewer/components/DeviceExposure.js +++ b/js/components/cohortdefinitionviewer/components/DeviceExposure.js @@ -1,4 +1,5 @@ -define(['knockout','components/cohortbuilder/options','components/cohortbuilder/InputTypes/Range','components/cohortbuilder/InputTypes/Text', 'text!./DeviceExposureTemplate.html'], function (ko, options, Range, Text, template) { +define(['knockout','components/cohortbuilder/options','components/cohortbuilder/utils', 'text!./DeviceExposureTemplate.html' +], function (ko, options, utils, template) { function DeviceExposureViewModel(params) { var self = this; @@ -7,15 +8,17 @@ define(['knockout','components/cohortbuilder/options','components/cohortbuilder/ self.Criteria = params.criteria.DeviceExposure; self.options = options; - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; + self.indexMessage = ko.i18nformat( + 'components.conditionDevice.indexDataText', + 'The index date refers to the device exposure of <%= conceptSetName %>.', + { + conceptSetName: ko.pureComputed(() => utils.getConceptSetName( + self.Criteria.CodesetId, + self.expression.ConceptSets, + ko.i18n('components.conditionDevice.anyDevice', 'Any Device') + )) + } + ); } diff --git a/js/components/cohortdefinitionviewer/components/DeviceExposureTemplate.html b/js/components/cohortdefinitionviewer/components/DeviceExposureTemplate.html index afd85cdd0..5b5ff264f 100644 --- a/js/components/cohortdefinitionviewer/components/DeviceExposureTemplate.html +++ b/js/components/cohortdefinitionviewer/components/DeviceExposureTemplate.html @@ -2,6 +2,12 @@
      + +
    • + + +
    • +
    • @@ -22,6 +28,15 @@
    • + +
    • + + + + +
    • +
    • @@ -42,18 +57,45 @@
    • + +
    • + + + + +
    • +
    • + +
    • + + + + +
    • +
    • + +
    • + + + + +
    • + - +
    diff --git a/js/components/cohortdefinitionviewer/components/DoseEra.js b/js/components/cohortdefinitionviewer/components/DoseEra.js index a6105ff0b..f6bb5621e 100644 --- a/js/components/cohortdefinitionviewer/components/DoseEra.js +++ b/js/components/cohortdefinitionviewer/components/DoseEra.js @@ -1,4 +1,5 @@ -define(['knockout','components/cohortbuilder/options','components/cohortbuilder/InputTypes/Range', 'text!./DoseEraTemplate.html'], function (ko, options, Range, template) { +define(['knockout', 'components/cohortbuilder/options', 'components/cohortbuilder/utils', 'text!./DoseEraTemplate.html' +], function (ko, options, utils, template) { function DoseEraViewModel(params) { @@ -7,15 +8,17 @@ define(['knockout','components/cohortbuilder/options','components/cohortbuilder/ self.Criteria = params.criteria.DoseEra; self.options = options; - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; + self.indexMessage = ko.i18nformat( + 'components.conditionDose.indexDataText', + 'The index date refers to the dose era of <%= conceptSetName %>.', + { + conceptSetName: ko.pureComputed(() => utils.getConceptSetName( + self.Criteria.CodesetId, + self.expression.ConceptSets, + ko.i18n('components.conditionDose.anyDoseEra', 'Any Dose Era') + )) + } + ); } diff --git a/js/components/cohortdefinitionviewer/components/DoseEraTemplate.html b/js/components/cohortdefinitionviewer/components/DoseEraTemplate.html index f524174b2..94e78962b 100644 --- a/js/components/cohortdefinitionviewer/components/DoseEraTemplate.html +++ b/js/components/cohortdefinitionviewer/components/DoseEraTemplate.html @@ -2,6 +2,12 @@
      + +
    • + + +
    • +
    • @@ -22,6 +28,15 @@
    • + +
    • + + + + +
    • +
    • @@ -47,8 +62,17 @@
    • + +
    • + + + + +
    • + - +
    diff --git a/js/components/cohortdefinitionviewer/components/DrugEra.js b/js/components/cohortdefinitionviewer/components/DrugEra.js index ff8da2415..5c4e6a584 100644 --- a/js/components/cohortdefinitionviewer/components/DrugEra.js +++ b/js/components/cohortdefinitionviewer/components/DrugEra.js @@ -7,15 +7,17 @@ define(['knockout','components/cohortbuilder/options','components/cohortbuilder/ self.Criteria = params.criteria.DrugEra; self.options = options; - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; + self.indexMessage = ko.i18nformat( + 'components.conditionDrug.indexDataText', + 'The index date refers to the drug era of <%= conceptSetName %>.', + { + conceptSetName: ko.pureComputed(() => utils.getConceptSetName( + self.Criteria.CodesetId, + self.expression.ConceptSets, + ko.i18n('components.conditionDrug.anyDrug', 'Any Drug') + )) + } + ); } diff --git a/js/components/cohortdefinitionviewer/components/DrugEraTemplate.html b/js/components/cohortdefinitionviewer/components/DrugEraTemplate.html index 7089bdea4..66af2164d 100644 --- a/js/components/cohortdefinitionviewer/components/DrugEraTemplate.html +++ b/js/components/cohortdefinitionviewer/components/DrugEraTemplate.html @@ -2,6 +2,12 @@
      + +
    • + + +
    • +
    • @@ -47,8 +53,17 @@
    • + +
    • + + + + +
    • + - +
    diff --git a/js/components/cohortdefinitionviewer/components/DrugExposure.js b/js/components/cohortdefinitionviewer/components/DrugExposure.js index 2d939ac28..7766bb1c4 100644 --- a/js/components/cohortdefinitionviewer/components/DrugExposure.js +++ b/js/components/cohortdefinitionviewer/components/DrugExposure.js @@ -7,15 +7,17 @@ define(['knockout','components/cohortbuilder/options','components/cohortbuilder/ self.Criteria = params.criteria.DrugExposure; self.options = options; - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; + self.indexMessage = ko.i18nformat( + 'components.conditionDrugExposure.indexDataText', + 'The index date refers to the drug exposure of <%= conceptSetName %>.', + { + conceptSetName: ko.pureComputed(() => utils.getConceptSetName( + self.Criteria.CodesetId, + self.expression.ConceptSets, + ko.i18n('components.conditionDrugExposure.anyDrug', 'Any Drug') + )) + } + ); } diff --git a/js/components/cohortdefinitionviewer/components/DrugExposureTemplate.html b/js/components/cohortdefinitionviewer/components/DrugExposureTemplate.html index d2b4903a7..e04997ffe 100644 --- a/js/components/cohortdefinitionviewer/components/DrugExposureTemplate.html +++ b/js/components/cohortdefinitionviewer/components/DrugExposureTemplate.html @@ -2,6 +2,12 @@
      + +
    • + + +
    • +
    • @@ -22,6 +28,15 @@
    • + +
    • + + + + +
    • +
    • @@ -47,6 +62,15 @@
    • + +
    • + + + + +
    • +
    • @@ -57,6 +81,15 @@
    • + +
    • + + + + +
    • +
    • @@ -77,18 +110,45 @@
    • + +
    • + + + + +
    • +
    • + +
    • + + + + +
    • +
    • + +
    • + + + + +
    • +
    - + diff --git a/js/components/cohortdefinitionviewer/components/Measurement.js b/js/components/cohortdefinitionviewer/components/Measurement.js index e3fb42bb6..c426d7857 100644 --- a/js/components/cohortdefinitionviewer/components/Measurement.js +++ b/js/components/cohortdefinitionviewer/components/Measurement.js @@ -1,4 +1,5 @@ -define(['knockout','components/cohortbuilder/options','components/cohortbuilder/InputTypes/Range','components/cohortbuilder/InputTypes/Text', 'text!./MeasurementTemplate.html'], function (ko, options, Range, Text, template) { +define(['knockout','components/cohortbuilder/options','components/cohortbuilder/utils', 'text!./MeasurementTemplate.html' +], function (ko, options, utils, template) { function MeasurementViewModel(params) { var self = this; @@ -7,15 +8,14 @@ define(['knockout','components/cohortbuilder/options','components/cohortbuilder/ self.Criteria = params.criteria.Measurement; self.options = options; - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; + self.indexMessage = ko.pureComputed(() => { + var conceptSetName = utils.getConceptSetName( + self.Criteria.CodesetId, + self.expression.ConceptSets, + "" + ); + return `${conceptSetName}.`; + }); } diff --git a/js/components/cohortdefinitionviewer/components/MeasurementTemplate.html b/js/components/cohortdefinitionviewer/components/MeasurementTemplate.html index 85670c600..da349cadb 100644 --- a/js/components/cohortdefinitionviewer/components/MeasurementTemplate.html +++ b/js/components/cohortdefinitionviewer/components/MeasurementTemplate.html @@ -2,6 +2,12 @@
      + +
    • + + +
    • +
    • @@ -17,11 +23,29 @@
    • + +
    • + + + + +
    • +
    • + +
    • + + + + +
    • +
    • @@ -30,13 +54,31 @@
    • -
    • + + + +
    • + + + + +
    • + +
    • + + + + +
    • +
    • @@ -77,18 +119,45 @@
    • + +
    • + + + + +
    • +
    • + +
    • + + + + +
    • +
    • + +
    • + + + + +
    • + - +
    diff --git a/js/components/cohortdefinitionviewer/components/Observation.js b/js/components/cohortdefinitionviewer/components/Observation.js index e5e2bef9d..78502a5d9 100644 --- a/js/components/cohortdefinitionviewer/components/Observation.js +++ b/js/components/cohortdefinitionviewer/components/Observation.js @@ -1,4 +1,5 @@ -define(['knockout','components/cohortbuilder/options','components/cohortbuilder/InputTypes/Range','components/cohortbuilder/InputTypes/Text', 'text!./ObservationTemplate.html'], function (ko, options, Range, Text, template) { +define(['knockout', 'components/cohortbuilder/options', 'components/cohortbuilder/utils', 'text!./ObservationTemplate.html' +], function (ko, options, utils, template) { function ObservationViewModel(params) { var self = this; @@ -7,16 +8,17 @@ define(['knockout','components/cohortbuilder/options','components/cohortbuilder/ self.Criteria = params.criteria.Observation; self.options = options; - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; - + self.indexMessage = ko.i18nformat( + 'components.conditionObservation.indexDataText', + 'The index date refers to the observation of <%= conceptSetName %>.', + { + conceptSetName: ko.pureComputed(() => utils.getConceptSetName( + self.Criteria.CodesetId, + self.expression.ConceptSets, + ko.i18n('components.conditionObservation.anyObservation', 'Any Observation') + )) + } + ); } // return compoonent definition diff --git a/js/components/cohortdefinitionviewer/components/ObservationPeriod.js b/js/components/cohortdefinitionviewer/components/ObservationPeriod.js index d55f75d8b..5c9a15ce5 100644 --- a/js/components/cohortdefinitionviewer/components/ObservationPeriod.js +++ b/js/components/cohortdefinitionviewer/components/ObservationPeriod.js @@ -1,4 +1,5 @@ -define(['knockout','components/cohortbuilder/options','components/cohortbuilder/InputTypes/Range', 'text!./ObservationPeriodTemplate.html'], function (ko, options, Range, template) { +define(['knockout','components/cohortbuilder/options','components/cohortbuilder/utils', 'text!./ObservationPeriodTemplate.html' +], function (ko, options, utils, template) { function ObservationPeriodViewModel(params) { var self = this; @@ -6,17 +7,6 @@ define(['knockout','components/cohortbuilder/options','components/cohortbuilder/ self.expression = ko.utils.unwrapObservable(params.expression); self.Criteria = params.criteria.ObservationPeriod; self.options = options; - - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; - } // return compoonent definition diff --git a/js/components/cohortdefinitionviewer/components/ObservationPeriodTemplate.html b/js/components/cohortdefinitionviewer/components/ObservationPeriodTemplate.html index d1115c3bd..58c542e1d 100644 --- a/js/components/cohortdefinitionviewer/components/ObservationPeriodTemplate.html +++ b/js/components/cohortdefinitionviewer/components/ObservationPeriodTemplate.html @@ -1,6 +1,12 @@
      + +
    • + + +
    • +
    • @@ -26,6 +32,15 @@
    • + +
    • + + + + +
    • +
    • @@ -42,7 +57,7 @@
    • - +
    diff --git a/js/components/cohortdefinitionviewer/components/ObservationTemplate.html b/js/components/cohortdefinitionviewer/components/ObservationTemplate.html index 76c93ca45..092be2418 100644 --- a/js/components/cohortdefinitionviewer/components/ObservationTemplate.html +++ b/js/components/cohortdefinitionviewer/components/ObservationTemplate.html @@ -1,8 +1,13 @@ -
      + +
    • + + +
    • +
    • @@ -18,6 +23,15 @@
    • + +
    • + + + + +
    • +
    • @@ -31,18 +45,45 @@
    • -
    • + + + +
    • + + + + +
    • + +
    • + + + + +
    • +
    • + +
    • + + + + +
    • +
    • @@ -53,15 +94,42 @@
    • + +
    • + + + + +
    • +
    • + +
    • + + + + +
    • + +
    • + +
    • + +
    • - -
    • + + + + + diff --git a/js/components/cohortdefinitionviewer/components/PayerPlanPeriod.js b/js/components/cohortdefinitionviewer/components/PayerPlanPeriod.js index a187b6680..6321aedcc 100644 --- a/js/components/cohortdefinitionviewer/components/PayerPlanPeriod.js +++ b/js/components/cohortdefinitionviewer/components/PayerPlanPeriod.js @@ -1,4 +1,5 @@ -define(['knockout','components/cohortbuilder/options','components/cohortbuilder/InputTypes/Range', 'text!./PayerPlanPeriodTemplate.html'], function (ko, options, Range, template) { +define(['knockout','components/cohortbuilder/options', 'text!./PayerPlanPeriodTemplate.html' +], function (ko, options, template) { function PayerPlanPeriodViewModel(params) { var self = this; @@ -6,15 +7,7 @@ define(['knockout','components/cohortbuilder/options','components/cohortbuilder/ self.Criteria = params.criteria.PayerPlanPeriod; self.options = options; - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; + self.indexMessage = ko.i18n('components.conditionPayerPlanPeriod.indexDataText', 'The index date refers to the payer plan period.'); } return { diff --git a/js/components/cohortdefinitionviewer/components/PayerPlanPeriodTemplate.html b/js/components/cohortdefinitionviewer/components/PayerPlanPeriodTemplate.html index 9885b0508..db4dfed97 100644 --- a/js/components/cohortdefinitionviewer/components/PayerPlanPeriodTemplate.html +++ b/js/components/cohortdefinitionviewer/components/PayerPlanPeriodTemplate.html @@ -2,6 +2,12 @@
        + +
      • + + +
      • +
      • @@ -42,6 +48,15 @@
      • + +
      • + + + + +
      • +
      • @@ -81,7 +96,7 @@
      • - +
      \ No newline at end of file diff --git a/js/components/cohortdefinitionviewer/components/ProcedureOccurrence.js b/js/components/cohortdefinitionviewer/components/ProcedureOccurrence.js index 11c735577..7809620c8 100644 --- a/js/components/cohortdefinitionviewer/components/ProcedureOccurrence.js +++ b/js/components/cohortdefinitionviewer/components/ProcedureOccurrence.js @@ -1,4 +1,5 @@ -define(['knockout','components/cohortbuilder/options','components/cohortbuilder/InputTypes/Range','components/cohortbuilder/InputTypes/Text', 'text!./ProcedureOccurrenceTemplate.html'], function (ko, options, Range, Text, template) { +define(['knockout', 'components/cohortbuilder/options', 'components/cohortbuilder/utils', 'text!./ProcedureOccurrenceTemplate.html' +], function (ko, options, utils, template) { function ProcedureOccurrenceViewModel(params) { var self = this; @@ -7,15 +8,17 @@ define(['knockout','components/cohortbuilder/options','components/cohortbuilder/ self.Criteria = params.criteria.ProcedureOccurrence; self.options = options; - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; + self.indexMessage = ko.i18nformat( + 'components.conditionProcedureOccurrence.indexDataText', + 'The index date refers to the procedure of <%= conceptSetName %>.', + { + conceptSetName: ko.pureComputed(() => utils.getConceptSetName( + self.Criteria.CodesetId, + self.expression.ConceptSets, + ko.i18n('components.conditionProcedureOccurrence.anyProcedure', 'Any Procedure') + )) + } + ); } diff --git a/js/components/cohortdefinitionviewer/components/ProcedureOccurrenceTemplate.html b/js/components/cohortdefinitionviewer/components/ProcedureOccurrenceTemplate.html index 0b6e3bb2f..1b56a1296 100644 --- a/js/components/cohortdefinitionviewer/components/ProcedureOccurrenceTemplate.html +++ b/js/components/cohortdefinitionviewer/components/ProcedureOccurrenceTemplate.html @@ -2,6 +2,12 @@
        + +
      • + + +
      • +
      • @@ -17,11 +23,30 @@
      • + +
      • +
        + + + + +
      • +
      • + +
      • + + + + +
      • +
      • @@ -42,18 +67,45 @@
      • + +
      • + + + + +
      • +
      • + +
      • + + + + +
      • +
      • + +
      • + + + + +
      • + - +
      diff --git a/js/components/cohortdefinitionviewer/components/Specimen.js b/js/components/cohortdefinitionviewer/components/Specimen.js index 15ffdb5a2..df4cd8fb4 100644 --- a/js/components/cohortdefinitionviewer/components/Specimen.js +++ b/js/components/cohortdefinitionviewer/components/Specimen.js @@ -1,4 +1,5 @@ -define(['knockout','components/cohortbuilder/options','components/cohortbuilder/InputTypes/Range','components/cohortbuilder/InputTypes/Text', 'text!./SpecimenTemplate.html'], function (ko, options, Range, Text, template) { +define(['knockout','components/cohortbuilder/options','components/cohortbuilder/utils', 'text!./SpecimenTemplate.html' +], function (ko, options, utils, template) { function SpecimenViewModel(params) { var self = this; @@ -6,15 +7,17 @@ define(['knockout','components/cohortbuilder/options','components/cohortbuilder/ self.Criteria = params.criteria.Specimen; self.options = options; - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; + self.indexMessage = ko.i18nformat( + 'components.conditionSpecimen.indexDataText', + 'The index date refers to the specimen of <%= conceptSetName %>.', + { + conceptSetName: ko.pureComputed(() => utils.getConceptSetName( + self.Criteria.CodesetId, + self.expression.ConceptSets, + ko.i18n('components.conditionSpecimen.anySpecimen', 'Any Specimen') + )) + } + ); } diff --git a/js/components/cohortdefinitionviewer/components/SpecimenTemplate.html b/js/components/cohortdefinitionviewer/components/SpecimenTemplate.html index 33345a392..860358df8 100644 --- a/js/components/cohortdefinitionviewer/components/SpecimenTemplate.html +++ b/js/components/cohortdefinitionviewer/components/SpecimenTemplate.html @@ -2,6 +2,12 @@
        + +
      • + + +
      • +
      • @@ -18,6 +24,15 @@
      • + +
      • + + + + +
      • +
      • @@ -28,16 +43,43 @@
      • + +
      • + + + + +
      • +
      • + +
      • + + + + +
      • +
      • + +
      • + + + + +
      • +
      • @@ -53,8 +95,17 @@
      • + +
      • + + + + +
      • + - +
      diff --git a/js/components/cohortdefinitionviewer/components/VisitDetail.js b/js/components/cohortdefinitionviewer/components/VisitDetail.js new file mode 100644 index 000000000..0e19c228f --- /dev/null +++ b/js/components/cohortdefinitionviewer/components/VisitDetail.js @@ -0,0 +1,36 @@ +define([ + "knockout", + 'components/cohortbuilder/options', + "components/cohortbuilder/utils", + "text!./VisitDetailTemplate.html", +], function ( + ko, + options, + utils, + template +) { + function VisitDetailViewModel(params) { + var self = this; + self.expression = ko.utils.unwrapObservable(params.expression); + self.Criteria = params.criteria.VisitDetail; + self.options = options; + + self.indexMessage = ko.i18nformat( + 'components.conditionVisitDetail.indexDataText', + 'The index date refers to the visit detail of <%= conceptSetName %>.', + { + conceptSetName: ko.pureComputed(() => utils.getConceptSetName( + self.Criteria.CodesetId, + self.expression.ConceptSets, + ko.i18n('components.conditionVisitDetail.anyVisitDetail', 'Any Visit Detail') + )) + } + ); + } + + // return component definition + return { + viewModel: VisitDetailViewModel, + template: template, + }; +}); diff --git a/js/components/cohortdefinitionviewer/components/VisitDetailTemplate.html b/js/components/cohortdefinitionviewer/components/VisitDetailTemplate.html new file mode 100644 index 000000000..1c1abe775 --- /dev/null +++ b/js/components/cohortdefinitionviewer/components/VisitDetailTemplate.html @@ -0,0 +1,98 @@ + + + + +
        + +
      • + + +
      • + + +
      • + +
      • + + +
      • + + +
      • + + +
      • + + +
      • + + +
      • + + + +
      • + + +
      • + + +
      • + + +
      • + + +
      • + + +
      • + + +
      • + + +
      • + + + +
      • + + +
      • + + + +
      • + + +
      • + + + +
      • + + +
      • + + +
      • + + + + +
      + diff --git a/js/components/cohortdefinitionviewer/components/VisitOccurrence.js b/js/components/cohortdefinitionviewer/components/VisitOccurrence.js index 084451681..c682d4c76 100644 --- a/js/components/cohortdefinitionviewer/components/VisitOccurrence.js +++ b/js/components/cohortdefinitionviewer/components/VisitOccurrence.js @@ -7,15 +7,17 @@ define(['knockout','components/cohortbuilder/options','components/cohortbuilder/ self.Criteria = params.criteria.VisitOccurrence; self.options = options; - self.getCodesetName = function(codesetId, defaultName) { - if (codesetId != null) - { - var selectedConceptSet = self.expression.ConceptSets().filter(function (item) { return item.id == codesetId })[0]; - return ko.utils.unwrapObservable(selectedConceptSet.name); - } - else - return defaultName; - }; + self.indexMessage = ko.i18nformat( + 'components.conditionVisit.indexDataText', + 'The index date refers to the visit of <%= conceptSetName %>.', + { + conceptSetName: ko.pureComputed(() => utils.getConceptSetName( + self.Criteria.CodesetId, + self.expression.ConceptSets, + ko.i18n('components.conditionVisit.anyVisit', 'Any Visit') + )) + } + ); } diff --git a/js/components/cohortdefinitionviewer/components/VisitOccurrenceTemplate.html b/js/components/cohortdefinitionviewer/components/VisitOccurrenceTemplate.html index b225426fb..3c5a33fb1 100644 --- a/js/components/cohortdefinitionviewer/components/VisitOccurrenceTemplate.html +++ b/js/components/cohortdefinitionviewer/components/VisitOccurrenceTemplate.html @@ -3,6 +3,12 @@
        + +
      • + + +
      • +
      • @@ -23,6 +29,15 @@
      • + +
      • + + + + +
      • +
      • @@ -43,23 +58,51 @@
      • + +
      • + + + + +
      • +
      • - - + +
      • + + + + +
      • + + +
      • + + +
      • + + +
      • + + + + +
      • - -
      • - -
      • + +
      diff --git a/js/components/cohortdefinitionviewer/main.js b/js/components/cohortdefinitionviewer/main.js index 9dfa719cd..5aa6ed430 100644 --- a/js/components/cohortdefinitionviewer/main.js +++ b/js/components/cohortdefinitionviewer/main.js @@ -30,6 +30,9 @@ define(function (require, exports) { var observation = require('./components/Observation'); ko.components.register('observation-criteria-viewer', observation); + var visitDetail = require('./components/VisitDetail'); + ko.components.register('visit-detail-criteria-viewer', visitDetail); + var visitOccurrence = require('./components/VisitOccurrence'); ko.components.register('visit-occurrence-criteria-viewer', visitOccurrence); @@ -71,10 +74,13 @@ define(function (require, exports) { var conceptList = require('./components/ConceptList'); ko.components.register('concept-list-viewer',conceptList); - + var conceptSetReference = require('./components/ConceptSetReference'); ko.components.register('conceptset-reference',conceptSetReference); + var dateAdjustment = require('./components/DateAdjustment'); + ko.components.register('date-adjustment-viewer', dateAdjustment); + var conceptSetViewer = require('./components/ConceptSetViewer'); commonUtils.build('conceptset-viewer', conceptSetViewer.viewModel, conceptSetViewer.template); diff --git a/js/components/conceptAddBox/concept-add-box.js b/js/components/conceptAddBox/concept-add-box.js index d0ea0fab3..06ee5536f 100644 --- a/js/components/conceptAddBox/concept-add-box.js +++ b/js/components/conceptAddBox/concept-add-box.js @@ -44,6 +44,7 @@ define([ this.noPreview = params.noPreview || false; this.conceptsToAdd = params.concepts; this.canSelectSource = params.canSelectSource || false; + this.overrideHandleAddToConceptSet = params.overrideHandleAddToConceptSet; this.isAdded = ko.observable(false); this.defaultSelectionOptions = { includeDescendants: ko.observable(false), @@ -142,6 +143,10 @@ define([ } handleSubmit() { + if (this.overrideHandleAddToConceptSet) { + const items = CommonUtils.buildConceptSetItems(this.conceptsToAdd(), this.selectionOptions()); + this.overrideHandleAddToConceptSet(items); + } else { clearTimeout(this.messageTimeout); this.isSuccessMessageVisible(true); this.messageTimeout = setTimeout(() => { @@ -157,6 +162,20 @@ define([ sharedState.activeConceptSet(conceptSet); + const filterSource = localStorage?.getItem('filter-source') || null; + const filterData = JSON.parse(localStorage?.getItem('filter-data') || null); + const datasAdded = JSON.parse(localStorage?.getItem('data-add-selected-concept') || null) || []; + const dataSearch = { filterData, filterSource } + const payloadAdd = this.conceptsToAdd().map(item => { + return { + "searchData": dataSearch, + "vocabularyVersion": sharedState.currentVocabularyVersion(), + "conceptId": item.CONCEPT_ID + } + }) + + localStorage.setItem('data-add-selected-concept', JSON.stringify([...datasAdded, ...payloadAdd])) + // if concepts were previewed, then they already built and can have individual option flags! if (this.previewConcepts().length > 0) { if (!conceptSet.current()) { @@ -172,6 +191,7 @@ define([ CommonUtils.clearConceptsSelectionState(this.conceptsToAdd()); this.selectionOptions(this.defaultSelectionOptions); } + } toggleSelectionOption(option) { const options = this.selectionOptions(); @@ -184,7 +204,6 @@ define([ setActiveConceptSet(conceptSet) { this.activeConceptSet(conceptSet); } - } return CommonUtils.build('concept-add-box', ConceptAddBox, view); diff --git a/js/components/conceptset/ConceptSetStore.js b/js/components/conceptset/ConceptSetStore.js index b3dfa0685..fa51862c0 100644 --- a/js/components/conceptset/ConceptSetStore.js +++ b/js/components/conceptset/ConceptSetStore.js @@ -29,9 +29,9 @@ define([ const increment = () => currentValue++; const value = () => currentValue; - return {increment, value}; + return { increment, value }; } - + class ConceptSetStore extends AutoBind() { constructor(props = {}) { @@ -41,7 +41,7 @@ define([ this.current = ko.observable(); this.expression = ko.pureComputed(() => this.current() && this.current().expression); this.currentConceptSetExpressionJson = ko.pureComputed(() => commonUtils.syntaxHighlight(this.expression())); - + // the concepts in the expression // concepts can appear more than once, the index object will keep the list of different items for each concept. this.selectedConceptsIndex = ko.pureComputed(() => { @@ -50,12 +50,12 @@ define([ const itemArr = result[item.concept.CONCEPT_ID] || []; itemArr.push(item); result[item.concept.CONCEPT_ID] = itemArr; - return result; - }, {}); + return result; + }, {}); return index || {}; }); this.currentConceptIdentifierList = ko.pureComputed(() => Object.keys(this.selectedConceptsIndex()).join(',')); - + // the included conceptIds (from resolveConceptSetExpression, these are guarteed to be unique) this.conceptSetInclusionIdentifiers = ko.observableArray(); this.currentIncludedConceptIdentifierList = ko.pureComputed(() => (this.conceptSetInclusionIdentifiers() || []).join(',')); @@ -66,27 +66,28 @@ define([ () => this.includedConcepts() && this.includedConcepts() .reduce((result, item) => { result[item.CONCEPT_ID] = item; - return result; - }, {}) + return result; + }, {}) ); - + // the included source codes (from loadSourceCodes) this.includedSourcecodes = ko.observableArray([]); // the recommended concepts (from loadRecommended) this.recommendedConcepts = ko.observableArray([]); this.isRecommendedAvailable = ko.observable(true); - + + // loading state of individual aspects of the concept set store this.resolvingConceptSetExpression = ko.observable(false); this.loadingSourceCodes = ko.observable(false); this.loadingIncluded = ko.observable(false); this.loadingRecommended = ko.observable(false); - + // metadata about this store this.source = props.source || "unnamed"; this.title = props.title || "unnamed"; - + this.resolveCount = counter(); // handle out of order resolves this.observer = ko.pureComputed(() => ko.toJSON(this.current() && this.current().expression.items())) @@ -104,75 +105,79 @@ define([ this.recommendedConcepts(null); this.conceptSetInclusionIdentifiers(null); } - - clearIncluded() { - ['includedConcepts', 'includedSourcecodes', 'recommendedConcepts', 'conceptSetInclusionIdentifiers'] - .forEach(key => this[key](null)); + + clearIncluded() { + ['includedConcepts', 'includedSourcecodes', 'recommendedConcepts', 'conceptSetInclusionIdentifiers'] + .forEach(key => this[key](null)); ['loadingIncluded', 'loadingSourceCodes', 'loadingRecommended'] .forEach(key => this[key](true)); - } - - async resolveConceptSetExpression() { - this.clearIncluded(); - if (this.current()) { - this.resolvingConceptSetExpression(true); - this.resolveCount.increment(); - const currentResolve = this.resolveCount.value(); - const conceptSetExpression = this.current().expression; - const identfiers = await vocabularyService.resolveConceptSetExpression(conceptSetExpression) - if (currentResolve != this.resolveCount.value()) { - return Promise.reject(constants.RESOLVE_OUT_OF_ORDER); - } - this.conceptSetInclusionIdentifiers(identfiers); - this.resolvingConceptSetExpression(false); - return identfiers; - } else { - return null; - } - } - - async refresh(mode) { - this.currentConseptSetTab(mode); - if (this.resolvingConceptSetExpression() || this.conceptSetInclusionIdentifiers() == null) // do nothing + } + + async resolveConceptSetExpression() { + this.clearIncluded(); + if (this.current()) { + this.resolvingConceptSetExpression(true); + this.resolveCount.increment(); + const currentResolve = this.resolveCount.value(); + const conceptSetExpression = this.current().expression; + const identfiers = await vocabularyService.resolveConceptSetExpression(conceptSetExpression) + if (currentResolve != this.resolveCount.value()) { + return Promise.reject(constants.RESOLVE_OUT_OF_ORDER); + } + this.conceptSetInclusionIdentifiers(identfiers); + this.resolvingConceptSetExpression(false); + return identfiers; + } else { + return null; + } + } + + async refresh(mode) { + this.currentConseptSetTab(mode); + if (this.resolvingConceptSetExpression() || this.conceptSetInclusionIdentifiers() == null) // do nothing return false; - switch (mode) { - case ViewMode.INCLUDED: - this.includedConcepts() == null && await this.loadIncluded(); - break; - case ViewMode.SOURCECODES: - this.includedSourcecodes() == null && await this.loadSourceCodes(); - break; + switch (mode) { + case ViewMode.INCLUDED: + this.includedConcepts() == null && await this.loadIncluded(); + break; + case ViewMode.SOURCECODES: + this.includedSourcecodes() == null && await this.loadSourceCodes(); + break; case ViewMode.RECOMMEND: this.recommendedConcepts() == null && await this.loadRecommended(); - } - } - + break; + case ViewMode.MAPPINGS: + this.includedConcepts() == null && await this.loadIncluded(); + break; + } + } + removeItemsByIndex(idxList) { - const newItems = this.current().expression.items().filter((i,idx) => !idxList.includes(idx)); + const newItems = this.current().expression.items().filter((i, idx) => !idxList.includes(idx)); this.current().expression.items(newItems); } - - async loadIncluded() { - const conceptIds = this.conceptSetInclusionIdentifiers(); - try { - this.loadingIncluded(true); - const response = await vocabularyService.getConceptsById(conceptIds); - await vocabularyService.loadDensity(response.data); - this.includedConcepts((response.data || []).map(item => ({ - ...item, - ANCESTORS: null, - isSelected: ko.observable(false) - }))); - } catch (err) { - console.error(err); - } finally { - this.loadingIncluded(false); - } - } - + + async loadIncluded() { + const conceptIds = this.conceptSetInclusionIdentifiers(); + try { + this.loadingIncluded(true); + const response = await vocabularyService.getConceptsById(conceptIds); + await vocabularyService.loadDensity(response.data); + this.includedConcepts((response.data || []).map(item => ({ + ...item, + ANCESTORS: null, + isSelected: ko.observable(false) + }))); + } catch (err) { + console.error(err); + } finally { + this.loadingIncluded(false); + } + } + async loadSourceCodes() { this.loadingSourceCodes(true); - this.includedConcepts() == null && await this.loadIncluded(); + this.includedConcepts() == null && await this.loadIncluded(); // load mapped let concepts = this.includedConcepts(); const identifiers = concepts.map(c => c.CONCEPT_ID); @@ -180,7 +185,7 @@ define([ const data = await vocabularyService.getMappedConceptsById(identifiers); await vocabularyService.loadDensity(data); const normalizedData = data.map(item => ({ - ...item, + ...item, isSelected: ko.observable(false), })) this.includedSourcecodes(normalizedData); @@ -201,11 +206,11 @@ define([ try { const data = await vocabularyService.getRecommendedConceptsById(identifiers); const includedSet = new Set(this.conceptSetInclusionIdentifiers()); - const excludedSet = new Set(this.current().expression.items().filter(i => i.isExcluded()).map(i=>i.concept.CONCEPT_ID)); + const excludedSet = new Set(this.current().expression.items().filter(i => i.isExcluded()).map(i => i.concept.CONCEPT_ID)); const filtered = data.filter(f => !(includedSet.has(f.CONCEPT_ID) || excludedSet.has(f.CONCEPT_ID))); - await vocabularyService.loadDensity(filtered); + await vocabularyService.loadDensity(filtered); const normalizedData = filtered.map(item => ({ - ...item, + ...item, isSelected: ko.observable(false), })) this.recommendedConcepts(normalizedData); @@ -215,7 +220,7 @@ define([ this.isRecommendedAvailable(false); this.recommendedConcepts([]); } else { - throw(err); + throw (err); } } finally { this.loadingRecommended(false); @@ -224,43 +229,43 @@ define([ async exportConceptSet(prefixFields = {}) { - function formatBoolean (b) { return b ? "TRUE" : "FALSE"} + function formatBoolean(b) { return b ? "TRUE" : "FALSE" } - function conceptCols(c) { + function conceptCols(c) { return { - "Concept ID": c.CONCEPT_ID, - "Concept Code": c.CONCEPT_CODE, + "Concept ID": c.CONCEPT_ID, + "Concept Code": c.CONCEPT_CODE, "Concept Name": c.CONCEPT_NAME, - "Domain": c.DOMAIN_ID, - "Vocabulary": c.VOCABULARY_ID, + "Domain": c.DOMAIN_ID, + "Vocabulary": c.VOCABULARY_ID, "Standard Concept": c.STANDARD_CONCEPT, "Valid Start Date": momentApi.formatDateTimeWithFormat(c.VALID_START_DATE, momentApi.ISO_DATE_FORMAT), "Valid End Date": momentApi.formatDateTimeWithFormat(c.VALID_END_DATE, momentApi.ISO_DATE_FORMAT), }; } - - function itemSettings(i) { + + function itemSettings(i) { return { - "Exclude": formatBoolean(ko.unwrap(i.isExcluded)), + "Exclude": formatBoolean(ko.unwrap(i.isExcluded)), "Descendants": formatBoolean(ko.unwrap(i.includeDescendants)), "Mapped": formatBoolean(ko.unwrap(i.includeMapped)) }; } // setup the left-most columns of result CSV - const firstColumns = {...prefixFields, "Concept Set ID": this.current().id, "Name": this.current().name()}; + const firstColumns = { ...prefixFields, "Concept Set ID": this.current().id, "Name": this.current().name() }; // fetch included and source codes this.includedConcepts() == null && await this.loadIncluded(); this.includedSourcecodes() == null && await this.loadSourceCodes(); - const expressionRows = this.expression().items().map((item) => ({...firstColumns, ...conceptCols(item.concept), ...itemSettings(item)})); + const expressionRows = this.expression().items().map((item) => ({ ...firstColumns, ...conceptCols(item.concept), ...itemSettings(item) })); const expressionCsv = csvUtils.toCsv(expressionRows); - const includedRows = this.includedConcepts().map((ic) => ({...firstColumns, ...conceptCols(ic)})); + const includedRows = this.includedConcepts().map((ic) => ({ ...firstColumns, ...conceptCols(ic) })); const includedCsv = csvUtils.toCsv(includedRows); - const mappedRows = this.includedSourcecodes().map((ic) => ({...firstColumns, ...conceptCols(ic)})); + const mappedRows = this.includedSourcecodes().map((ic) => ({ ...firstColumns, ...conceptCols(ic) })); const mappedCsv = csvUtils.toCsv(mappedRows); const zip = new JSZip(); @@ -268,24 +273,24 @@ define([ zip.file("includedConcepts.csv", includedCsv); zip.file("mappedConcepts.csv", mappedCsv); - const zipFile = await zip.generateAsync({type:"blob", compression: "DEFLATE"}); + const zipFile = await zip.generateAsync({ type: "blob", compression: "DEFLATE" }); saveAs(zipFile, `${this.current().name()}.zip`); - - } - + + } + static activeStores() { const activeKeys = Object.keys(constants.ConceptSetSources).filter(key => !!this.getStore(key).current() && this.getStore(key).isEditable()); return activeKeys.map(k => ConceptSetStore.getStore(k)); } - + static getStore(source) { return registry[source]; } - + static sourceKeys() { return constants.ConceptSetSources; } - + // convienience getters static featureAnalysis() { return ConceptSetStore.getStore(constants.ConceptSetSources.featureAnalysis); @@ -294,7 +299,7 @@ define([ static repository() { return ConceptSetStore.getStore(constants.ConceptSetSources.repository); } - + static cohortDefinition() { return ConceptSetStore.getStore(constants.ConceptSetSources.cohortDefinition); } @@ -302,23 +307,23 @@ define([ static characterization() { return ConceptSetStore.getStore(constants.ConceptSetSources.characterization); } - + static incidenceRates() { return ConceptSetStore.getStore(constants.ConceptSetSources.incidenceRates); - } + } } - + // define a registry to contain individual stores const registry = {}; - + // Define ConceptSetStore for each module with conceptSet tab Object.keys(constants.ConceptSetSources).forEach(k => { - registry[k] = new ConceptSetStore({source: k}); + registry[k] = new ConceptSetStore({ source: k }); // freeze the object to prevent any changes to the observable references, which should not be changed. Object.freeze(registry[k]); }); - - return ConceptSetStore; - + + return ConceptSetStore; + }); diff --git a/js/components/conceptset/const.js b/js/components/conceptset/const.js index e8e6292ff..7f3c3410c 100644 --- a/js/components/conceptset/const.js +++ b/js/components/conceptset/const.js @@ -14,6 +14,8 @@ define([ RECOMMEND: 'recommend', EXPORT: 'conceptset-export', IMPORT: 'conceptset-import', + ANNOTATION: 'annotation', + MAPPINGS: 'resolve-mappings' }; const ConceptSetSources = { diff --git a/js/components/faceted-datatable.js b/js/components/faceted-datatable.js index 74f963074..a9ba55cd2 100644 --- a/js/components/faceted-datatable.js +++ b/js/components/faceted-datatable.js @@ -68,7 +68,52 @@ define(['knockout', 'text!./faceted-datatable.html', 'crossfilter', 'utils/Commo self.outsideFilters = (params.outsideFilters || ko.observable()).extend({notify: 'always'}); + self.setDataLocalStorage = (data, nameItem) => { + const filterArrayString = localStorage.getItem(nameItem) + let filterArrayObj = filterArrayString? JSON.parse(filterArrayString): [] + + if(!data?.selected()){ + filterArrayObj.push({title:data.facet.caption(), value:`${data.key} (${data.value})`,key:data.key}) + }else{ + filterArrayObj = filterArrayObj.filter((item)=> item.key !== data.key) + } + localStorage.setItem(nameItem, JSON.stringify(filterArrayObj)) + } + + self.setDataObjectLocalStorage = (data, nameItem) => { + const filterObjString = localStorage.getItem(nameItem) + let filterObj = filterObjString ? JSON.parse(filterObjString): {} + let newFilterObj = {} + + if(!data?.selected()){ + const dataPush = { title: data.facet.caption(), value: `${data.key} (${data.value})`, key: data.key }; + newFilterObj.filterColumns = filterObj['filterColumns'] ? [...filterObj['filterColumns'], dataPush] : [dataPush] + newFilterObj = { ...filterObj, filterColumns : newFilterObj.filterColumns }; + }else{ + newFilterObj.filterColumns = filterObj['filterColumns'].filter((item)=> item.key !== data.key); + newFilterObj = { ...filterObj, filterColumns : newFilterObj.filterColumns }; + } + localStorage.setItem(nameItem, JSON.stringify(newFilterObj)) + } + self.updateFilters = function (data, event) { + const currentPath = window.location?.href; + if (currentPath?.includes('/conceptset/')) { + if (currentPath?.includes('/included-sourcecodes')) { + localStorage.setItem('filter-source', 'Included Source Codes'); + } else if (currentPath?.includes('/included')) { + localStorage.setItem('filter-source', 'Included Concepts'); + } + self.setDataLocalStorage(data, 'filter-data'); + } + const isAddConcept = currentPath?.split('?').reduce((prev, curr) => prev || curr.includes('search'), false) && + currentPath?.split('?').reduce((prev, curr) => prev || curr.includes('query'), false) || + currentPath?.includes('/concept/') + + if (isAddConcept) { + localStorage.setItem('filter-source', 'Search'); + self.setDataObjectLocalStorage(data, 'filter-data') + } var facet = data.facet; data.selected(!data.selected()); if (data.selected()) { diff --git a/js/components/multi-select.html b/js/components/multi-select.html index d2f6a68de..cbdd6d990 100644 --- a/js/components/multi-select.html +++ b/js/components/multi-select.html @@ -1,8 +1,8 @@
      - + - +
      \ No newline at end of file diff --git a/js/components/multi-select.js b/js/components/multi-select.js index 389a204a3..62b39a860 100644 --- a/js/components/multi-select.js +++ b/js/components/multi-select.js @@ -14,6 +14,7 @@ define( self.multiple = params.multiple; self.options = params.options; + self.disable = (params.disable || false); self.selectedValues = ko.observableArray(params.selectedValues && params.selectedValues()); self.selectedValue = params.selectedValue; self.selectedTextFormat = params.selectedTextFormat || 'count > 2'; diff --git a/js/components/reports/classes/Treemap.js b/js/components/reports/classes/Treemap.js index 5a471d3d5..7e4a4d5c3 100644 --- a/js/components/reports/classes/Treemap.js +++ b/js/components/reports/classes/Treemap.js @@ -146,10 +146,10 @@ define([ data }) { const normalizedData = atlascharts.chart.normalizeDataframe(ChartUtils.normalizeArray(data, true)); + let tableData = []; if (!normalizedData.empty) { let distinctConceptIds = new Set([]); - let tableData = []; // Make the values unique per https://github.com/OHDSI/Atlas/issues/913 normalizedData.conceptPath.forEach((d, i) => { if (!distinctConceptIds.has(normalizedData.conceptId[i])) { @@ -165,14 +165,13 @@ define([ }); } }); - this.tableData(tableData); this.treeData(normalizedData); - - return { - data - }; } + this.tableData(tableData); + return { + data + }; } getData() { diff --git a/js/components/reports/reportDrilldown.js b/js/components/reports/reportDrilldown.js index 9fb21448a..19e65e5ea 100644 --- a/js/components/reports/reportDrilldown.js +++ b/js/components/reports/reportDrilldown.js @@ -234,7 +234,7 @@ define([ this.chartFormats.frequencyDistribution.yMax = yScaleMax; this.chartFormats.frequencyDistribution.xLabel = ko.pureComputed(function () { return ko.i18n('dataSources.drilldown.chartFormat.frequencyDistribution.xLabel1', 'Count ("x" or more ')() + - report() + + report + ko.i18n('dataSources.drilldown.chartFormat.frequencyDistribution.xLabel2', 's)')(); }); this.chartFormats.frequencyDistribution.ticks = Math.min(5, frequencyHistogram.INTERVALS); diff --git a/js/pages/characterizations/components/feature-analyses/fa-view-edit/fa-design.html b/js/pages/characterizations/components/feature-analyses/fa-view-edit/fa-design.html index c8737eb7c..c0a308f82 100644 --- a/js/pages/characterizations/components/feature-analyses/fa-view-edit/fa-design.html +++ b/js/pages/characterizations/components/feature-analyses/fa-view-edit/fa-design.html @@ -16,13 +16,13 @@ -
      +
      @@ -32,6 +32,7 @@ data-bind=" if: $component.canEdit() || $component.data().type() === $component.featureTypes.PRESET, css: classes({ element: 'nav-pill', extra: $component.data().type() === $component.featureTypes.PRESET ? 'active' : null }), + attr: { disabled: $component.canEdit() ? null : true }, click: setType.bind(null, $component.featureTypes.PRESET) "> @@ -44,6 +45,7 @@ data-bind=" if: ($component.canEdit() && !$component.featureId()) || $component.data().type() === $component.featureTypes.CRITERIA_SET, css: classes({ element: 'nav-pill', extra: $component.data().type() === $component.featureTypes.CRITERIA_SET ? 'active' : null }), + attr: { disabled: $component.canEdit() ? null : true }, click: setType.bind(null, $component.featureTypes.CRITERIA_SET) "> @@ -52,6 +54,7 @@ data-bind=" if: ($component.canEdit() && !$component.featureId()) || $component.data().type() === $component.featureTypes.CUSTOM_FE, css: classes({ element: 'nav-pill', extra: $component.data().type() === $component.featureTypes.CUSTOM_FE ? 'active' : null }), + attr: { disabled: $component.canEdit() ? null : true }, click: setType.bind(null, $component.featureTypes.CUSTOM_FE) "> @@ -63,27 +66,27 @@
      - + - +
      -
      -
      - - -
      - -
      - -
      -
      - -
      -
      - -
      -
      - -
      +
      +
      +
      + + +
      + +
      + +
      +
      + +
      +
      + +
      +
      + +
      +
      @@ -146,7 +151,7 @@ SELECT covariate_id, covariate_name, concept_id, sum_value, average_value FROM ( ) diff --git a/js/pages/characterizations/components/feature-analyses/feature-analysis-view-edit.html b/js/pages/characterizations/components/feature-analyses/feature-analysis-view-edit.html index 85d579285..f3872bed6 100644 --- a/js/pages/characterizations/components/feature-analyses/feature-analysis-view-edit.html +++ b/js/pages/characterizations/components/feature-analyses/feature-analysis-view-edit.html @@ -38,7 +38,7 @@
      - diff --git a/js/pages/cohort-definitions/cohort-definition-manager.css b/js/pages/cohort-definitions/cohort-definition-manager.css index 838705a78..d1fa8dfdb 100644 --- a/js/pages/cohort-definitions/cohort-definition-manager.css +++ b/js/pages/cohort-definitions/cohort-definition-manager.css @@ -100,11 +100,21 @@ .only-results-checkbox { } table.sources-table td.generation-buttons-column { - text-align: right; + text-align: left; } table.sources-table thead th, table.sources-table tbody tr:first-child td { padding: 4px 10px; } +table.sources-table tr { + transition: background-color 0.3s ease; /* Smooth transition for hover effect */ +} +table.sources-table tr:hover { + background-color: #f2f2f2; +} +table.sources-table tr:hover td { + border-top: solid 1px #ccc; + border-bottom: solid 1px #ccc; +} table.sources-table tbody td { padding: 0 10px 4px; } diff --git a/js/pages/cohort-definitions/cohort-definition-manager.js b/js/pages/cohort-definitions/cohort-definition-manager.js index 99f13fdc5..64241936d 100644 --- a/js/pages/cohort-definitions/cohort-definition-manager.js +++ b/js/pages/cohort-definitions/cohort-definition-manager.js @@ -583,6 +583,10 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', this.sourcesTableOptions = commonUtils.getTableOptions('S'); this.sourcesColumns = [{ + sortable: false, + className: 'generation-buttons-column', + render: () => `` + }, { title: `${ko.i18n('cohortDefinitions.cohortDefinitionManager.panels.sourceName', 'Source Name')()}`, data: 'name' }, { @@ -600,10 +604,6 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', }, { title: ko.i18n('cohortDefinitions.cohortDefinitionManager.panels.generationDuration', 'Generation Duration'), data: 'executionDuration' - }, { - sortable: false, - className: 'generation-buttons-column', - render: () => `` }]; this.stopping = ko.pureComputed(() => this.cohortDefinitionSourceInfo().reduce((acc, target) => ({...acc, [target.sourceKey]: ko.observable(false)}), {})); diff --git a/js/pages/concept-sets/components/tabs/conceptset-annotation.html b/js/pages/concept-sets/components/tabs/conceptset-annotation.html new file mode 100644 index 000000000..17b93b858 --- /dev/null +++ b/js/pages/concept-sets/components/tabs/conceptset-annotation.html @@ -0,0 +1,15 @@ + +
      + + +
      \ No newline at end of file diff --git a/js/pages/concept-sets/components/tabs/conceptset-annotation.js b/js/pages/concept-sets/components/tabs/conceptset-annotation.js new file mode 100644 index 000000000..a9dc6116b --- /dev/null +++ b/js/pages/concept-sets/components/tabs/conceptset-annotation.js @@ -0,0 +1,175 @@ +define([ + 'knockout', + 'text!./conceptset-annotation.html', + 'components/Component', + 'utils/AutoBind', + 'utils/CommonUtils', + 'services/AuthAPI', + 'faceted-datatable', + 'less!./conceptset-annotation.less', + ], function ( + ko, + view, + Component, + AutoBind, + commonUtils, + authApi, + ) { + class ConceptsetAnnotation extends AutoBind(Component) { + constructor(params) { + super(params); + this.isLoading = ko.observable(true); + this.data = ko.observable(); + this.getList = params.getList; + this.delete = params.delete; + this.canDeleteAnnotations = params.canDeleteAnnotations; + + const { pageLength, lengthMenu } = commonUtils.getTableOptions('M'); + this.pageLength = params.pageLength || pageLength; + this.lengthMenu = params.lengthMenu || lengthMenu; + + this.columns = ko.computed(() => { + let cols = [ + { + title: ko.i18n('columns.conceptID', 'Concept Id'), + data: 'conceptId', + }, + { + title: ko.i18n('columns.searchData', 'Search Data'), + className: this.classes('tbl-col', 'search-data'), + render: (d, t, r) => { + if (r.searchData === null || r.searchData === undefined || !r.searchData) { + return 'N/A'; + } else { + return `

      ${r.searchData}

      ` + } + }, + sortable: false + }, + { + title: ko.i18n('columns.vocabularyVersion', 'Vocabulary Version'), + data: 'vocabularyVersion', + render: (d, t, r) => { + if (r.vocabularyVersion === null || r.vocabularyVersion === undefined || !r.vocabularyVersion) { + return 'N/A'; + } else { + return `

      ${r.vocabularyVersion}

      ` + } + }, + sortable: false + }, + { + title: ko.i18n('columns.conceptSetVersion', 'Concept Set Version'), + data: 'conceptSetVersion', + render: (d, t, r) => { + if (r.conceptSetVersion === null || r.conceptSetVersion === undefined || !r.conceptSetVersion) { + return 'N/A'; + } else { + return `

      ${r.conceptSetVersion}

      ` + } + }, + sortable: false + }, + { + title: ko.i18n('columns.createdBy', 'Created By'), + data: 'createdBy', + render: (d, t, r) => { + if (r.createdBy === null || r.createdBy === undefined || !r.createdBy) { + return 'N/A'; + } else { + return `

      ${r.createdBy}

      ` + } + }, + sortable: false + }, + { + title: ko.i18n('columns.createdDate', 'Created Date'), + render: (d, t, r) => { + if (r.createdDate === null || r.createdDate === undefined) { + return 'N/A'; + } else { + return `

      ${r.createdDate}

      ` + } + }, + sortable: false + }, + { + title: ko.i18n('columns.originConceptSets', 'Origin Concept Sets'), + render: (d, t, r) => { + if (r.copiedFromConceptSetIds === null || r.copiedFromConceptSetIds === undefined) { + return 'N/A'; + } else { + return `

      ${r.copiedFromConceptSetIds}

      ` + } + }, + sortable: false + } + ]; + + if (this.canDeleteAnnotations()) { + cols.push({ + title: ko.i18n('columns.action', 'Action'), + sortable: false, + render: function () { + return ``; + } + }); + } + return cols; + }); + + this.loadData(); + } + + objectMap(obj) { + const newObject = {}; + const keysNotToParse = ['createdBy', 'createdDate', 'vocabularyVersion', 'conceptSetVersion', 'copiedFromConceptSetIds', 'searchData']; + Object.keys(obj).forEach((key) => { + if (typeof obj[key] === 'string' && !keysNotToParse.includes(key)) { + newObject[key] = JSON.parse(obj[key] || null); + } else { + newObject[key] = obj[key]; + } + }); + return newObject; + } + + async onRowClick(d, e){ + try { + const { id } = d; + if(e.target.className === 'deleteIcon fa fa-trash') { + const res = await this.delete(id); + if(res){ + this.loadData(); + } + } + } catch (ex) { + console.log(ex); + } finally { + this.isLoading(false); + } + } + + handleConvertData(arr){ + const newDatas = []; + (arr || []).forEach(item => { + newDatas.push(this.objectMap(item)) + }) + return newDatas; + } + + async loadData() { + this.isLoading(true); + try { + const data = await this.getList(); + this.data(this.handleConvertData(data.data)); + } catch (ex) { + console.log(ex); + } finally { + this.isLoading(false); + } + } + + } + return commonUtils.build('conceptset-annotation', ConceptsetAnnotation, view); +}); \ No newline at end of file diff --git a/js/pages/concept-sets/components/tabs/conceptset-annotation.less b/js/pages/concept-sets/components/tabs/conceptset-annotation.less new file mode 100644 index 000000000..47164968a --- /dev/null +++ b/js/pages/concept-sets/components/tabs/conceptset-annotation.less @@ -0,0 +1,23 @@ +.conceptset-annotation { + + &__tbl-col { + &--search-data { + min-width: 40%; + } + &--concept-data{ + max-width: 500px; + text-overflow: ellipsis; + white-space: nowrap; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + } + } +} + +.deleteIcon { + color: #d9534f; + cursor: pointer; + min-width: 30px; +} \ No newline at end of file diff --git a/js/pages/concept-sets/components/tabs/conceptset-expression.js b/js/pages/concept-sets/components/tabs/conceptset-expression.js index 992d03779..e827124fb 100644 --- a/js/pages/concept-sets/components/tabs/conceptset-expression.js +++ b/js/pages/concept-sets/components/tabs/conceptset-expression.js @@ -36,6 +36,7 @@ define([ }); this.datatableLanguage = ko.i18n('datatable.language'); + this.currentConceptSetId = ko.observable(params.router.routerParams().conceptSetId); this.data = ko.pureComputed(() => this.conceptSetItems().map((item, idx) => ({ ...item, idx, isSelected: ko.observable() }))); @@ -113,7 +114,34 @@ define([ removeConceptsFromConceptSet() { const idxForRemoval = this.data().filter(concept => concept.isSelected()).map(item => item.idx); - this.conceptSetStore.removeItemsByIndex(idxForRemoval); + + const removeItems = this.data().filter(concept => concept.isSelected()); + const datasAdded = JSON.parse(localStorage.getItem('data-add-selected-concept') || null) || []; + const datasDeleted = JSON.parse(localStorage.getItem('data-remove-selected-concept') || null) || []; + + const datasRemove = []; + const payloadRemove = removeItems.map(item => { + if((datasAdded.map(item => item.conceptId)).includes(item.concept.CONCEPT_ID)){ + datasRemove.push(item.concept.CONCEPT_ID); + return null; + } + return { + "searchData": "", + "relatedConcepts": "", + "conceptHierarchy": "", + "conceptSetData": { id: this.currentConceptSetId(), name: this.conceptSetStore.current().name()}, + "conceptData": item, + "conceptId": item.concept.CONCEPT_ID + } + }); + + const dataRemoveSelected = [...datasDeleted, ...payloadRemove].filter((item, i, arr) => item && arr.indexOf(item) === i); + localStorage.setItem('data-remove-selected-concept', JSON.stringify(dataRemoveSelected)); + if(datasRemove?.length){ + const newAddDatas = datasAdded.filter(data => !datasRemove.includes(data.conceptId)); + localStorage.setItem('data-add-selected-concept', JSON.stringify(newAddDatas)); + } + this.conceptSetStore.removeItemsByIndex(idxForRemoval); } async selectAllConceptSetItems(key, areAllSelected) { diff --git a/js/pages/concept-sets/components/tabs/manual-mapping.html b/js/pages/concept-sets/components/tabs/manual-mapping.html new file mode 100644 index 000000000..4ce758d21 --- /dev/null +++ b/js/pages/concept-sets/components/tabs/manual-mapping.html @@ -0,0 +1,114 @@ +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      + +
      + +
      +
      +
      +
      +
      + +
      +
      + +
      + +
      +
      +
      + + + +
      +
      +
      +
      + +
      +
      +
      + + + +
      + +
      \ No newline at end of file diff --git a/js/pages/concept-sets/components/tabs/manual-mapping.js b/js/pages/concept-sets/components/tabs/manual-mapping.js new file mode 100644 index 000000000..94c8c53a1 --- /dev/null +++ b/js/pages/concept-sets/components/tabs/manual-mapping.js @@ -0,0 +1,286 @@ +define([ + 'knockout', + 'text!./manual-mapping.html', + 'components/Component', + 'utils/AutoBind', + 'utils/CommonUtils', + 'atlas-state', + 'const', + 'utils/Renderers', + 'services/MomentAPI', + 'components/conceptset/utils', + 'services/Vocabulary', + 'less!./manual-mapping.less', + 'components/conceptAddBox/concept-add-box', + 'components/dataSourceSelect', + 'components/conceptAddBox/preview/conceptset-expression-preview', + 'components/conceptAddBox/preview/included-preview', + 'components/conceptAddBox/preview/included-preview-badge', + 'components/conceptAddBox/preview/included-sourcecodes-preview', + 'components/conceptset/included-sourcecodes', +], function ( + ko, + view, + Component, + AutoBind, + commonUtils, + sharedState, + globalConstants, + renderers, + momentApi, + conceptSetUtils, + vocabularyService, +) { + + class ManualMapping extends AutoBind(Component) { + constructor(params) { + super(params); + this.loading = params.loading; + this.tableOptions = params.tableOptions || commonUtils.getTableOptions('M'); + + + this.currentConcept = params.currentConcept; + this.conceptSetStore = params.conceptSetStore; + + this.standardConceptsWithCounterparts = params.standardConceptsWithCounterparts; + this.resultConceptSetItems = params.resultConceptSetItems; + this.showManualMappingModal = params.showManualMappingModal; + this.addedSourceCodesViaManualMapping = params.addedSourceCodesViaManualMapping; + + this.nonStandardConceptsForCurrentStandard = ko.observableArray([]); + this.loadingNonStandardConceptsForCurrentStandard = ko.observable(false); + + this.includedSourcecodes = ko.observableArray([]); + this.loadingIncludedSourceCodes = ko.observable(false); + + + this.relatedSourcecodesColumns = globalConstants.getRelatedSourcecodesColumns(sharedState, { canEditCurrentConceptSet: this.canEdit }, + (data, selected) => { + const conceptIds = data.map(c => c.CONCEPT_ID); + ko.utils.arrayForEach(this.includedSourcecodes(), c => conceptIds.indexOf(c.CONCEPT_ID) > -1 && c.isSelected(selected)); + this.includedSourcecodes.valueHasMutated(); + }); + + + this.relatedSourcecodesColumns.forEach(column => { + if (column.data === 'CONCEPT_NAME') { + column.render = (s, p, d) => { + var valid = d.INVALID_REASON_CAPTION == 'Invalid' ? 'invalid' : ''; + return '' + d.CONCEPT_NAME + ''; + }; + } + }); + + this.relatedSourcecodesOptions = globalConstants.relatedSourcecodesOptions; + + this.buttonTooltip = conceptSetUtils.getPermissionsText(true, "test"); + + const getNonStandardConceptsColumns = (context, commonUtils, selectAllFn) => [ + { + title: '', + orderable: false, + searchable: false, + className: 'text-center', + render: () => renderers.renderCheckbox('isSelected', context.canEditCurrentConceptSet()), + renderSelectAll: context.canEditCurrentConceptSet(), + selectAll: selectAllFn + }, + { + title: ko.i18n('columns.id', 'Id'), + data: 'CONCEPT_ID' + }, + { + title: ko.i18n('columns.code', 'Code'), + data: 'CONCEPT_CODE' + }, + { + title: ko.i18n('columns.name', 'Name'), + render: function (s, p, d) { + return p === 'display' + ? '
      ' + d.CONCEPT_NAME + '
      ' + : d.CONCEPT_NAME; + } + }, + { + title: ko.i18n('columns.class', 'Class'), + data: 'CONCEPT_CLASS_ID' + }, + { + title: ko.i18n('columns.standardConceptCaption', 'Standard Concept Caption'), + data: 'STANDARD_CONCEPT_CAPTION', + visible: false + }, + { + title: ko.i18n('columns.validStartDate', 'Valid Start Date'), + render: (s, type, d) => type === "sort" ? +d['VALID_START_DATE'] : + momentApi.formatDateTimeWithFormat(d['VALID_START_DATE'], momentApi.DATE_FORMAT), + visible: false + }, + { + title: ko.i18n('columns.validEndDate', 'Valid End Date'), + render: (s, type, d) => type === "sort" ? +d['VALID_END_DATE'] : + momentApi.formatDateTimeWithFormat(d['VALID_END_DATE'], momentApi.DATE_FORMAT), + visible: false + }, + { + title: ko.i18n('columns.domain', 'Domain'), + data: 'DOMAIN_ID' + }, + { + title: ko.i18n('columns.vocabulary', 'Vocabulary'), + data: 'VOCABULARY_ID' + }, + ]; + + this.nonStandardConceptsColumns = getNonStandardConceptsColumns({ + canEditCurrentConceptSet: this.canEdit + }, + commonUtils, + (data, selected) => { + const conceptIds = data.map(c => c.CONCEPT_ID); + ko.utils.arrayForEach(this.nonStandardConceptsForCurrentStandard(), c => conceptIds.indexOf(c.CONCEPT_ID) > -1 && c.isSelected(selected)); + this.nonStandardConceptsForCurrentStandard.valueHasMutated(); + }); + + this.overrideHandleAddToConceptSet = (selectedItemsToAdd) => { this.overrideAddToConceptSet(selectedItemsToAdd); }; + + this.loadNonStandardForCurrentStandard(); + this.loadIncludedSourceCodes(); + } + + + overrideAddToConceptSet(selectedItemsToAdd) { + console.log("overrideAddToConceptSet"); + this.addedSourceCodesViaManualMapping(selectedItemsToAdd); + } + + async loadNonStandardForCurrentStandard() { + let standardConceptId = this.currentConcept.CONCEPT_ID; + if (!standardConceptId) { + console.warn("No standard concept available to process."); + return; + } + try { + this.loadingNonStandardConceptsForCurrentStandard(true); + let relatedNonStandardConceptsIds = this.currentConcept.mapped_from; + let relatedNonStandardConcepts = this.resultConceptSetItems().filter(item => item.concept.STANDARD_CONCEPT === 'N' && relatedNonStandardConceptsIds.some(id => item.concept.CONCEPT_ID === id)); + const formattedConcepts = relatedNonStandardConcepts.map(concept => { + return { + ...concept.concept, + isSelected: ko.observable(false), + isExcluded: ko.observable(false), + includeDescendants: ko.observable(false), + includeMapped: ko.observable(false) + }; + }); + this.nonStandardConceptsForCurrentStandard(formattedConcepts); // Update the observable array with filtered related non-standard concepts + } catch (err) { + console.error("Error loading non-standard concepts for the standard concept ID: " + standardConceptId, err); + this.nonStandardConceptsForCurrentStandard([]); // Handle error by providing an empty array + } finally { + this.loadingNonStandardConceptsForCurrentStandard(false); + } + } + + formatDate(date) { + return momentApi.formatDateTimeWithFormat(date, momentApi.ISO_DATE_FORMAT); + } + + + deepClone(obj) { + return JSON.parse(JSON.stringify(obj)); + } + + mapAllSelectedToGivenStandardConcept() { + + if (!this.currentConcept || typeof this.currentConcept.CONCEPT_ID === 'undefined') { + console.warn("No valid standard concept available to map."); + return; + } + + let standardConceptId = this.currentConcept.CONCEPT_ID; + + const selectedNonStandardConcepts = this.nonStandardConceptsForCurrentStandard().filter(item => item.isSelected()); + + if (selectedNonStandardConcepts.length === 0) { + console.warn("No selected non-standard concepts to map."); + return; + } + + // Remove the selected non-standard concepts from resultConceptSetItems + let resultConceptSetItemsWithoutMappedNonStandardConcepts = this.resultConceptSetItems().filter(item => !selectedNonStandardConcepts.some(selectedConcept => selectedConcept.CONCEPT_ID === item.concept.CONCEPT_ID)); + this.resultConceptSetItems(resultConceptSetItemsWithoutMappedNonStandardConcepts); + + //Remove selected concepts from mapped_from of remaining standard concepts + this.standardConceptsWithCounterparts().forEach(standardConcept => { + const clonedData = this.deepClone(standardConcept.mapped_from); + standardConcept.mapped_from = clonedData.filter( + id => !selectedNonStandardConcepts.map(concept => concept.CONCEPT_ID).includes(id) + ); + }); + + // Add current standard concept to resultConceptSetItems if not already present + if (!this.resultConceptSetItems().some(item => item.concept.CONCEPT_ID === standardConceptId)) { + this.resultConceptSetItems().push({ + concept: this.currentConcept, + isSelected: ko.observable(false), + isExcluded: ko.observable(false), + includeDescendants: ko.observable(false), + includeMapped: ko.observable(false) + }); + } + + // Optionally remove the current standard concept from standardConceptsWithCounterparts + let standardConceptsWithCounterpartsWithoutCurrentMappedConcept = this.standardConceptsWithCounterparts().filter(item => item.CONCEPT_ID !== standardConceptId); + this.standardConceptsWithCounterparts(standardConceptsWithCounterpartsWithoutCurrentMappedConcept); + + this.showManualMappingModal(false); + console.log("Mapping complete: Selected non-standard concepts have been mapped to the current standard concept."); + } + + + async loadIncludedSourceCodes() { + this.loadingIncludedSourceCodes(true); + + let standardConceptId = this.currentConcept.CONCEPT_ID; + const selectedNonStandardConcepts = this.nonStandardConceptsForCurrentStandard().filter(item => item.isSelected()); + + let allConceptsIds = [ + this.currentConcept.CONCEPT_ID, + ...selectedNonStandardConcepts.map(concept => concept.CONCEPT_ID) + ]; + try { + const data = await vocabularyService.getMappedConceptsById(allConceptsIds); + await vocabularyService.loadDensity(data); + const normalizedData = data.map(item => ({ + ...item, + isSelected: ko.observable(false), + })) + this.includedSourcecodes(normalizedData); + return data; + } catch (err) { + console.error(err); + } finally { + this.loadingIncludedSourceCodes(false); + } + } + + isEnabledMapAllSelectedToGivenStandardConcept() { + return true; + } + + canAddConcepts() { + return true; + } + + getSelectedIncludedSourcecodes() { + return ko.unwrap(this.includedSourcecodes) && commonUtils.getSelectedConcepts(this.includedSourcecodes); + } + + canEdit() { + return true; + } + } + + return commonUtils.build('manual-mapping', ManualMapping, view); +}); \ No newline at end of file diff --git a/js/pages/concept-sets/components/tabs/manual-mapping.less b/js/pages/concept-sets/components/tabs/manual-mapping.less new file mode 100644 index 000000000..aaddca9a8 --- /dev/null +++ b/js/pages/concept-sets/components/tabs/manual-mapping.less @@ -0,0 +1,23 @@ +.concept-add-container { + margin-left: 0.5rem; + + .concept-add-box-container { + display: inline-flex; + } +} +.configureSourceTable tbody td, .configureSourceTable th { + padding-top: 0.75rem; + padding-bottom: 0.75rem; + cursor: default; + padding-left: 7px; + padding-right: 7px; + font-size: 11px; + border-bottom: 1px solid #ccc; + vertical-align: top; + line-height: 16px; +} + +.configureSourceTable tr td th { + padding: 2px 5px; + user-select: none; +} \ No newline at end of file diff --git a/js/pages/concept-sets/components/tabs/resolve-mappings.html b/js/pages/concept-sets/components/tabs/resolve-mappings.html new file mode 100644 index 000000000..8382196e3 --- /dev/null +++ b/js/pages/concept-sets/components/tabs/resolve-mappings.html @@ -0,0 +1,130 @@ + + +
      +
      +
      + +
      + +
      + +
      +
      +
      + + + +
      +
      +
      +
      + +
      + +
      + + + + + + + + + + +
      +
      + + +
      + +
      + + + + + + + +
      + +
      + + +
      + +
      +
      +
      +
      +

      Warning

      +

      + +
      +
      +
      \ No newline at end of file diff --git a/js/pages/concept-sets/components/tabs/resolve-mappings.js b/js/pages/concept-sets/components/tabs/resolve-mappings.js new file mode 100644 index 000000000..dbbed3fb0 --- /dev/null +++ b/js/pages/concept-sets/components/tabs/resolve-mappings.js @@ -0,0 +1,447 @@ +define([ + 'knockout', + 'text!./resolve-mappings.html', + 'components/Component', + 'utils/AutoBind', + 'utils/CommonUtils', + 'atlas-state', + 'const', + 'services/ConceptSet', + 'components/conceptset/utils', + 'utils/Renderers', + 'services/MomentAPI', + 'services/http', + 'less!./resolve-mappings.less', + './manual-mapping', + 'components/conceptAddBox/concept-add-box', + 'components/dataSourceSelect', + 'components/conceptAddBox/preview/conceptset-expression-preview', + 'components/conceptAddBox/preview/included-preview', + 'components/conceptAddBox/preview/included-preview-badge', + 'components/conceptAddBox/preview/included-sourcecodes-preview', + +], function ( + ko, + view, + Component, + AutoBind, + commonUtils, + sharedState, + globalConstants, + conceptSetService, + conceptSetUtils, + renderers, + MomentApi, + httpService, +) { + + class ResolveConceptSetMappings extends AutoBind(Component) { + constructor(params) { + super(params); + this.loading = params.loading; + this.canEdit = params.canEdit; + this.tableOptions = params.tableOptions || commonUtils.getTableOptions('M'); + const tableOptions = this.tableOptions; + this.conceptSetStore = params.conceptSetStore; + + this.showPreviewModal = ko.observable(false); + + + this.previewConcepts = ko.observableArray(); + this.previewTabsParams = ko.observable({ + tabs: [ + { + title: ko.i18n('components.conceptAddBox.previewModal.tabs.concepts', 'Concepts'), + key: 'expression', + componentName: 'conceptset-expression-preview', + componentParams: { + tableOptions, + conceptSetItems: this.previewConcepts + }, + }, + { + title: ko.i18n('cs.manager.tabs.includedConcepts', 'Included Concepts'), + key: 'included', + componentName: 'conceptset-list-included-preview', + componentParams: { + tableOptions, + previewConcepts: this.previewConcepts + }, + hasBadge: true, + }, + { + title: ko.i18n('cs.manager.tabs.includedSourceCodes', 'Source Codes'), + key: 'included-sourcecodes', + componentName: 'conceptset-list-included-sourcecodes-preview', + componentParams: { + tableOptions, + previewConcepts: this.previewConcepts + }, + } + ] + }); + + this.commonUtils = commonUtils; + this.datatableLanguage = ko.i18n('datatable.language'); + this.conceptSetItems = ko.pureComputed(() => (this.conceptSetStore.current() && this.conceptSetStore.current().expression.items()) || []); + + this.initialIncludedConcepts = ko.observableArray([]); + this.initialStandardConceptsWithCounterparts = ko.observableArray([]); + this.resultConceptSetItems = ko.observableArray([]); + this.loadingResultConceptSetItems = ko.observable(false); + + this.standardConceptsWithCounterparts = ko.observableArray([]); + this.loadingStandardConceptsWithCounterparts = ko.observable(false); + + this.isResolveButtonEnabled = ko.pureComputed(() => { + const mappings = this.standardConceptsWithCounterparts(); + return mappings.some(concept => { + return concept.mapped_from && concept.mapped_from.length === 1 && mappings.filter(m => m.mapped_from && m.mapped_from.includes(concept.mapped_from[0])).length === 1; + }); + }); + + this.addedSourceCodesViaManualMapping = ko.observableArray([]); + this.enrichStandardWithCounterpartsAndResultsWithSourceCodes = this.enrichStandardWithCounterpartsAndResultsWithSourceCodes.bind(this); + this.addedSourceCodesViaManualMapping.subscribe((addedSourceCodes) => { + this.enrichStandardWithCounterpartsAndResultsWithSourceCodes(addedSourceCodes); + }); + + /** MANUAL MAPPING VARIABLES */ + this.showManualMappingModal = ko.observable(false); + this.manualMappingModalParams = ko.observable(null); + this.standardConceptsWithCounterpartsRowClick = (conceptRowData) => { + this.openManualMapping(conceptRowData); + } + + this.openManualMapping = (conceptRowData) => { + this.manualMappingModalParams( + { + currentConcept: conceptRowData, + tableOptions: this.tableOptions, + conceptSetStore: this.conceptSetStore, + standardConceptsWithCounterparts: this.standardConceptsWithCounterparts, + resultConceptSetItems: this.resultConceptSetItems, + showManualMappingModal: this.showManualMappingModal, + addedSourceCodesViaManualMapping: this.addedSourceCodesViaManualMapping, + } + ); + this.showManualMappingModal(true); + } + + this.canEditCurrentConceptSet = params.canEdit; + this.resultConceptSetColumns = [ + { + data: 'concept.CONCEPT_ID', + }, + { + data: 'concept.CONCEPT_CODE', + }, + { + render: commonUtils.renderBoundLink, + }, + { + data: 'concept.DOMAIN_ID', + }, + { + data: 'concept.STANDARD_CONCEPT', + visible: false, + }, + { + data: 'concept.STANDARD_CONCEPT_CAPTION', + }, + ]; + + this.buttonTooltip = conceptSetUtils.getPermissionsText(true, "test"); + + this.standardConceptsColumns = [ + { + title: ko.i18n('columns.id', 'Id'), + data: 'CONCEPT_ID' + }, + { + title: ko.i18n('columns.code', 'Code'), + data: 'CONCEPT_CODE' + }, + { + title: ko.i18n('columns.name', 'Name'), + data: 'CONCEPT_NAME', + render: function (s, p, d) { + var valid = d.INVALID_REASON_CAPTION == 'Invalid' ? 'invalid' : ''; + if (p === 'display') { + return '' + d.CONCEPT_NAME + ''; + } + return d.CONCEPT_NAME; + } + }, + { + title: ko.i18n('columns.class', 'Class'), + data: 'CONCEPT_CLASS_ID' + }, + { + title: ko.i18n('columns.standardConceptCaption', 'Standard Concept Caption'), + data: 'STANDARD_CONCEPT_CAPTION', + visible: false + }, + { + title: ko.i18n('columns.validStartDate', 'Valid Start Date'), + render: (s, type, d) => type === "sort" ? +d['VALID_START_DATE'] : + MomentApi.formatDateTimeWithFormat(d['VALID_START_DATE'], MomentApi.DATE_FORMAT), + visible: false + }, + { + title: ko.i18n('columns.validEndDate', 'Valid End Date'), + render: (s, type, d) => type === "sort" ? +d['VALID_END_DATE'] : + MomentApi.formatDateTimeWithFormat(d['VALID_END_DATE'], MomentApi.DATE_FORMAT), + visible: false + }, + { + title: ko.i18n('columns.domain', 'Domain'), + data: 'DOMAIN_ID' + }, + { + title: ko.i18n('columns.vocabulary', 'Vocabulary'), + data: 'VOCABULARY_ID' + }, + { + title: ko.i18n('columns.mappedNonStandardCount', 'Mapped Non-Standard Count'), + data: function (item) { + return ko.computed(() => { + return this.countMappedNonStandard(item); + }).peek(); + }.bind(this) + }, + ]; + + this.includedSourcecodes = this.conceptSetStore.includedSourcecodes; + + this.showWarningModal = ko.observable(false); + this.warningModalMessage = ko.observable(''); + this.closeWarningModal = () => { + this.showWarningModal(false); + }; + + this.loadInitialIncludedConcepts(); //detached copy of the initial concept set, used for resetting to the initial state and first fill of resultConceptSetItems + this.loadResultConceptSetItems(); //detached copy of the initialIncludedConcepts + this.loadStandardWithCounterparts(); + } + + countMappedNonStandard(concept) { + if (concept.mapped_from) { + return concept.mapped_from.length.toString(); + } + return "0"; + } + + deepClone(item) { + return JSON.parse(JSON.stringify(item)); + } + + loadInitialIncludedConcepts() { + const uniqueItemsMap = new Map(); + this.conceptSetItems().forEach((item) => { + const conceptId = item.concept.CONCEPT_ID; + if (!uniqueItemsMap.has(conceptId)) { + const clonedItem = this.deepClone(item); + uniqueItemsMap.set(conceptId, { + ...clonedItem, + idx: uniqueItemsMap.size, // idx will correspond to the index in the map + isSelected: ko.observable(false), + isExcluded: ko.observable(false), + includeDescendants: ko.observable(false), + includeMapped: ko.observable(false), + }); + } + }); + this.initialIncludedConcepts(Array.from(uniqueItemsMap.values())); + } + + async loadResultConceptSetItems() { + try { + this.loadingResultConceptSetItems(true); + const uniqueItemsMap = new Map(); + this.initialIncludedConcepts().forEach((item) => { + const conceptId = item.concept.CONCEPT_ID; + if (!uniqueItemsMap.has(conceptId)) { + const clonedItem = this.deepClone(item); + uniqueItemsMap.set(conceptId, { + ...clonedItem, + idx: uniqueItemsMap.size, // idx will correspond to the index in the map + isSelected: ko.observable(false), + isExcluded: ko.observable(false), + includeDescendants: ko.observable(false), + includeMapped: ko.observable(false), + }); + } + }); + this.resultConceptSetItems(Array.from(uniqueItemsMap.values())); + } catch (err) { + console.error("Error loading result concept set items:", err); + } finally { + this.loadingResultConceptSetItems(false); + } + } + + async enrichStandardWithCounterpartsAndResultsWithSourceCodes(addedSourceCodes) { + try { + this.loadingStandardConceptsWithCounterparts(true); + this.loadingResultConceptSetItems(true); + let standardForNonStandardWithMappings = await this.loadRelatedStandardConceptsWithMapping(addedSourceCodes); + let currentStandardsWithCounterparts = this.standardConceptsWithCounterparts(); + let combinedListOfStandardsWithMappings = [ + ...standardForNonStandardWithMappings, + ...currentStandardsWithCounterparts.filter(item => !standardForNonStandardWithMappings || standardForNonStandardWithMappings.length === 0 || standardForNonStandardWithMappings.some(existingItem => existingItem.CONCEPT_ID !== item.CONCEPT_ID))]; + + this.standardConceptsWithCounterparts(combinedListOfStandardsWithMappings); + + //add to current results + let currentResults = this.resultConceptSetItems(); + + let addedConcepts = addedSourceCodes.map(sc => { + return { + ...sc, + isSelected: ko.observable(false), + isExcluded: ko.observable(sc.isExcluded), + includeDescendants: ko.observable(sc.isExcluded), + includeMapped: ko.observable(sc.isExcluded), + } + }); + + let combinedResultItems = [ + ...currentResults, + ...addedConcepts, + ]; + this.resultConceptSetItems(combinedResultItems); + } catch (err) { + console.error("Error enriching standard counterparts:", err); + } finally { + this.loadingStandardConceptsWithCounterparts(false); + this.loadingResultConceptSetItems(false); + } + } + + async loadStandardWithCounterparts() { + try { + this.loadingStandardConceptsWithCounterparts(true); + let standardForNonStandardWithMappings = await this.loadRelatedStandardConceptsWithMapping(this.initialIncludedConcepts()); + // Set the data avoiding duplicates + this.standardConceptsWithCounterparts(standardForNonStandardWithMappings); + this.initialStandardConceptsWithCounterparts(this.deepClone(standardForNonStandardWithMappings)); + } catch (err) { + console.error("Error loading standard counterparts:", err); + } finally { + this.loadingStandardConceptsWithCounterparts(false); + } + } + + async loadRelatedStandardConceptsWithMapping(concepts) { + const nonStandardConcepts = concepts.filter(c => c.concept.STANDARD_CONCEPT !== 'S' && c.concept.STANDARD_CONCEPT !== 'C'); + let nonStandardConceptsIds = nonStandardConcepts.map(concept => concept.concept.CONCEPT_ID); + const { data: relatedStandardMappedConcepts } = await httpService.doPost(sharedState.vocabularyUrl() + 'related-standard', nonStandardConceptsIds); + + const relatedStandardMappedConceptsWithFlags = relatedStandardMappedConcepts.map(concept => { + return { + ...concept, + isSelected: ko.observable(false), + isExcluded: ko.observable(false), + includeDescendants: ko.observable(false), + includeMapped: ko.observable(false), + } + }); + + return Array.from(relatedStandardMappedConceptsWithFlags); + } + + renderCheckbox(field, clickable = true) { + return ``; + } + + getSelectedConcepts() { + return ko.unwrap(this.includedSourcecodes) && commonUtils.getSelectedConcepts(this.includedSourcecodes); + } + + resolveOneToOneMappings() { + const conceptIdsToRemoveFromStandard = new Set(); + let showWarning = false; + const updatedItems = this.resultConceptSetItems().map((item) => { + if (item.concept.STANDARD_CONCEPT === 'N') { + const mappedStandardConcepts = this.standardConceptsWithCounterparts().filter(m => m.mapped_from && m.mapped_from.some(mappedId => mappedId === item.concept.CONCEPT_ID)); + if (mappedStandardConcepts.length > 1) { + showWarning = true; + return item; + } + if (mappedStandardConcepts.length === 1) { + if (mappedStandardConcepts[0].mapped_from.length === 1) { + conceptIdsToRemoveFromStandard.add(mappedStandardConcepts[0].CONCEPT_ID); + return { + ...item, + concept: mappedStandardConcepts[0], + isSelected: ko.observable(false), + isExcluded: ko.observable(false), + includeDescendants: ko.observable(false), + includeMapped: ko.observable(false), + }; + } + } + } + return item; + }); + + const unmappableStandardConceptsToRetain = this.standardConceptsWithCounterparts().filter(mapping => + !conceptIdsToRemoveFromStandard.has(mapping.CONCEPT_ID) + ); + + this.standardConceptsWithCounterparts(unmappableStandardConceptsToRetain); + this.resultConceptSetItems(updatedItems); + + if (showWarning) { + this.warningModalMessage('Encountered non-standard concepts mapped to multiple standard concepts. Please resolve those mappings manually.'); + this.showWarningModal(true); + } + } + + handlePreview() { + let itemsForPreview = this.resultConceptSetItems().map(item => { + return { + ...item, + isExcluded: ko.observable(false), + includeDescendants: ko.observable(false), + includeMapped: ko.observable(false), + } + }) + this.previewConcepts(itemsForPreview); + this.showPreviewModal(true); + } + + handleSubmit() { + let itemsForPreview = this.resultConceptSetItems().map(item => { + return { + ...item, + isExcluded: ko.observable(false), + includeDescendants: ko.observable(false), + includeMapped: ko.observable(false), + } + }) + this.conceptSetStore.current().expression.items(itemsForPreview); + } + + isPreviewAvailable() { + return true; + } + + resetToInitialState() { + this.loadResultConceptSetItems(); + let initialConcepts = this.initialStandardConceptsWithCounterparts().map(initialConcept => { + return { + ...this.deepClone(initialConcept), + isSelected: ko.observable(false), + isExcluded: ko.observable(false), + includeDescendants: ko.observable(false), + includeMapped: ko.observable(false), + }; + }); + this.standardConceptsWithCounterparts(initialConcepts); + } + } + + return commonUtils.build('resolve-mappings', ResolveConceptSetMappings, view); +}); \ No newline at end of file diff --git a/js/pages/concept-sets/components/tabs/resolve-mappings.less b/js/pages/concept-sets/components/tabs/resolve-mappings.less new file mode 100644 index 000000000..f9e30b8d6 --- /dev/null +++ b/js/pages/concept-sets/components/tabs/resolve-mappings.less @@ -0,0 +1,19 @@ +div[data-bind*="visible: showWarningModal"] { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(0, 0, 0, 0.7); + z-index: 1000; +} + +.warning-modal-content { + background-color: white; + padding: 20px; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); +} \ No newline at end of file diff --git a/js/pages/concept-sets/conceptset-manager.js b/js/pages/concept-sets/conceptset-manager.js index 7402046ed..d371fcdf6 100644 --- a/js/pages/concept-sets/conceptset-manager.js +++ b/js/pages/concept-sets/conceptset-manager.js @@ -3,8 +3,8 @@ define([ 'text!./conceptset-manager.html', 'pages/Page', 'utils/AutoBind', - 'utils/CommonUtils', - 'appConfig', + 'utils/CommonUtils', + 'appConfig', './const', 'const', 'components/conceptset/utils', @@ -44,7 +44,9 @@ define([ 'components/authorship', 'components/name-validation', 'components/ac-access-denied', - 'components/versions/versions' + 'components/versions/versions', + './components/tabs/conceptset-annotation', + './components/tabs/resolve-mappings' ], function ( ko, view, @@ -147,6 +149,14 @@ define([ } return this.conceptSetStore.current() && authApi.isPermittedDeleteConceptset(this.conceptSetStore.current().id); }); + + this.canDeleteAnnotations = ko.pureComputed(() => { + if (!config.userAuthenticationEnabled) { + return true; + } + return this.conceptSetStore.current() && authApi.isPermittedConceptSetAnnotationsDelete(this.conceptSetStore.current().id); + }); + this.canOptimize = ko.computed(() => { return ( this.currentConceptSet() @@ -313,6 +323,16 @@ define([ }, hidden: () => !!this.previewVersion() }, + { + title: ko.i18n('cs.manager.tabs.annotation', 'Annotation'), + key: ViewMode.ANNOTATION, + componentName: 'conceptset-annotation', + componentParams: { + getList: () => this.currentConceptSet().id ? conceptSetService.getConceptSetAnnotation(this.currentConceptSet().id) : [], + delete: (annotationId) => annotationId ? conceptSetService.deleteConceptSetAnnotation(this.currentConceptSet().id, annotationId) : null, + canDeleteAnnotations: this.canDeleteAnnotations, + } + }, { title: ko.i18n('cs.manager.tabs.versions', 'Versions'), key: ViewMode.VERSIONS, @@ -327,10 +347,23 @@ define([ hasBadge: true, preload: true, }, + { + title: ko.i18n('cs.manager.tabs.resolve-mappings', 'Resolve mappings'), + key: ViewMode.MAPPINGS, + componentName: 'resolve-mappings', + componentParams: { + ...params, + canEdit: this.canEdit, + conceptSetStore: this.conceptSetStore, + selectedSource: this.selectedSource, + loading: this.conceptSetStore.loadingIncluded, + }, + }, ]; this.selectedTab = ko.observable(0); this.activeUtility = ko.observable(""); + this.newConceptSetIdForCopyAnnotations = ko.observable(0); GlobalPermissionService.decorateComponent(this, { entityTypeGetter: () => entityType.CONCEPT_SET, @@ -469,6 +502,33 @@ define([ this.conceptSetCaption.dispose(); } + removeDataFilterStorage(){ + localStorage.removeItem('filter-data'); + localStorage.removeItem('filter-source'); + localStorage.removeItem('data-remove-selected-concept'); + localStorage.removeItem('data-add-selected-concept'); + } + + objectMap(obj) { + const newObject = {}; + Object.keys(obj).forEach((key) => { + if(typeof obj[key] === 'object'){ + newObject[key] = JSON.stringify(obj[key]); + }else{ + newObject[key] = obj[key]; + } + }); + return newObject; + } + + handleConvertDataToString(arr){ + const newDatas = []; + (arr || []).forEach(item => { + newDatas.push(this.objectMap(item)) + }) + return newDatas; + } + async saveConceptSet(conceptSet, nameElementId) { if (this.previewVersion() && !confirm(ko.i18n('common.savePreviewWarning', 'Save as current version?')())) { return; @@ -487,11 +547,24 @@ define([ this.raiseConceptSetNameProblem(ko.i18n('cs.manager.csAlreadyExistsMessage', 'A concept set with this name already exists. Please choose a different name.')(), nameElementId); } else { const savedConceptSet = await conceptSetService.saveConceptSet(conceptSet); + const savedVersions = await this.versionsParams()?.getList(); + let latestSavedVersion = 1; + + if (savedVersions && Array.isArray(savedVersions)) { + latestSavedVersion = savedVersions.reduce((max, obj) => Math.max(max, obj.version), 1); + } + + let annotationDataToAdd = JSON.parse(localStorage?.getItem('data-add-selected-concept') || null) || []; + const enrichedAnnotationDataToAdd = annotationDataToAdd.map(item => ({...item, "conceptSetVersion": latestSavedVersion})); + await conceptSetService.saveConceptSetItems(savedConceptSet.data.id, conceptSetItems); + await conceptSetService.saveConceptSetAnnotation(savedConceptSet.data.id, { newAnnotation: this.handleConvertDataToString(enrichedAnnotationDataToAdd), removeAnnotation: this.handleConvertDataToString(JSON.parse(localStorage?.getItem('data-remove-selected-concept') || null) || [])}); + this.removeDataFilterStorage(); const current = this.conceptSetStore.current(); current.modifiedBy = savedConceptSet.data.modifiedBy; current.modifiedDate = savedConceptSet.data.modifiedDate; + this.newConceptSetIdForCopyAnnotations(savedConceptSet.data.id); this.conceptSetStore.current(current); this.previewVersion(null); @@ -533,11 +606,17 @@ define([ } async copy() { + let sourceConceptSetId = this.currentConceptSet().id; const responseWithName = await conceptSetService.getCopyName(this.currentConceptSet().id); this.currentConceptSet().name(responseWithName.copyName); this.currentConceptSet().id = 0; this.currentConceptSetDirtyFlag().reset(); await this.saveConceptSet(this.currentConceptSet(), "#txtConceptSetName"); + let copyAnnotationsRequest = { + sourceConceptSetId: sourceConceptSetId, + targetConceptSetId: this.newConceptSetIdForCopyAnnotations(), + }; + await conceptSetService.copyAnnotations(copyAnnotationsRequest); } async optimize() { diff --git a/js/pages/concept-sets/const.js b/js/pages/concept-sets/const.js index 87b99eb2b..93559f03a 100644 --- a/js/pages/concept-sets/const.js +++ b/js/pages/concept-sets/const.js @@ -10,6 +10,8 @@ define( RECOMMEND: conceptSetConstants.ViewMode.RECOMMEND, EXPORT: conceptSetConstants.ViewMode.EXPORT, IMPORT: conceptSetConstants.ViewMode.IMPORT, + ANNOTATION: conceptSetConstants.ViewMode.ANNOTATION, + MAPPINGS: conceptSetConstants.ViewMode.MAPPINGS, EXPLORE: 'explore', COMPARE: 'compare', VERSIONS: 'versions', diff --git a/js/pages/configuration/configuration.js b/js/pages/configuration/configuration.js index a13723d96..6aa4919d7 100644 --- a/js/pages/configuration/configuration.js +++ b/js/pages/configuration/configuration.js @@ -14,7 +14,7 @@ define([ 'services/job/jobDetail', 'services/CacheAPI', 'less!./configuration.less', - 'components/heading' + 'components/heading', ], function ( ko, view, @@ -27,11 +27,11 @@ define([ sharedState, constants, jobDetailsService, - {PollService}, + { PollService }, jobDetail, - cacheApi, + cacheApi ) { - class Configuration extends AutoBind(Page) { + class Configuration extends AutoBind(Page) { constructor(params) { super(params); this.config = config; @@ -44,16 +44,38 @@ define([ this.sources = sharedState.sources; this.priorityOptions = [ - {id: 'session', name: ko.i18n('configuration.priorityOptions.session', 'Current Session')}, - {id: 'application', name: ko.i18n('configuration.priorityOptions.application', 'Whole Application')}, + { + id: 'session', + name: ko.i18n( + 'configuration.priorityOptions.session', + 'Current Session' + ), + }, + { + id: 'application', + name: ko.i18n( + 'configuration.priorityOptions.application', + 'Whole Application' + ), + }, ]; this.isAuthenticated = authApi.isAuthenticated; - this.initializationCompleted = ko.pureComputed(() => sharedState.appInitializationStatus() === constants.applicationStatuses.running || - sharedState.appInitializationStatus() === constants.applicationStatuses.noSourcesAvailable); + this.initializationCompleted = ko.pureComputed( + () => + sharedState.appInitializationStatus() === + constants.applicationStatuses.running || + sharedState.appInitializationStatus() === + constants.applicationStatuses.noSourcesAvailable + ); this.hasSourceAccess = authApi.hasSourceAccess; this.hasPageAccess = ko.pureComputed(() => { - return (config.userAuthenticationEnabled && this.isAuthenticated() && authApi.isPermittedEditConfiguration()) || !config.userAuthenticationEnabled; + return ( + (config.userAuthenticationEnabled && + this.isAuthenticated() && + authApi.isPermittedEditConfiguration()) || + !config.userAuthenticationEnabled + ); }); this.canReadRoles = ko.pureComputed(() => { return this.isAuthenticated() && authApi.isPermittedReadRoles(); @@ -62,26 +84,42 @@ define([ if (!config.userAuthenticationEnabled) { return false; } else { - return (config.userAuthenticationEnabled && this.isAuthenticated() && authApi.isPermittedCreateSource()); + return ( + config.userAuthenticationEnabled && + this.isAuthenticated() && + authApi.isPermittedCreateSource() + ); } }); this.canChangePriority = ko.pureComputed(() => { if (!config.userAuthenticationEnabled) { return false; } else { - return (config.userAuthenticationEnabled && this.isAuthenticated() && authApi.isPermittedEditSourcePriority()) + return ( + config.userAuthenticationEnabled && + this.isAuthenticated() && + authApi.isPermittedEditSourcePriority() + ); } }); - this.canImport = ko.pureComputed(() => this.isAuthenticated() && authApi.isPermittedImportUsers()); - this.canManageTags = ko.pureComputed(() => this.isAuthenticated() && authApi.isPermittedTagsManagement()); + this.canImport = ko.pureComputed( + () => this.isAuthenticated() && authApi.isPermittedImportUsers() + ); + this.canManageTags = ko.pureComputed( + () => this.isAuthenticated() && authApi.isPermittedTagsManagement() + ); this.canClearServerCache = ko.pureComputed(() => { - return config.userAuthenticationEnabled && this.isAuthenticated() && authApi.isPermittedClearServerCache() + return ( + config.userAuthenticationEnabled && + this.isAuthenticated() && + authApi.isPermittedClearServerCache() + ); }); this.intervalId = PollService.add({ callback: () => this.checkJobs(), - interval: config.pollInterval + interval: config.pollInterval, }); } @@ -95,18 +133,22 @@ define([ async checkJobs() { const notifications = await jobDetailsService.listRefreshCacheJobs(); - const jobs = notifications.data.map(n => { - const job = new jobDetail(); - job.status(n.status); - job.executionId = n.executionId; - return job; + const jobs = notifications.data.map((n) => { + const job = new jobDetail(); + job.status(n.status); + job.executionId = n.executionId; + return job; }); - jobs.forEach(job => { + jobs.forEach((job) => { let source = this.getSource(job); if (source && (job.isComplete() || job.isFailed())) { this.sourceJobs.delete(job.executionId); - source.refreshState(job.isComplete() ? sourceApi.buttonCheckState.success : sourceApi.buttonCheckState.failed); + source.refreshState( + job.isComplete() + ? sourceApi.buttonCheckState.success + : sourceApi.buttonCheckState.failed + ); } }); } @@ -119,52 +161,99 @@ define([ } canReadSource(source) { - if (!config.userAuthenticationEnabled) { - return false; - } else { - return (config.userAuthenticationEnabled && this.isAuthenticated() && authApi.isPermittedReadSource(source.sourceKey)); - } + if (!config.userAuthenticationEnabled) { + return false; + } else { + return ( + config.userAuthenticationEnabled && + this.isAuthenticated() && + authApi.isPermittedReadSource(source.sourceKey) + ); + } } - canCheckConnection(source) { - if (!config.userAuthenticationEnabled) { - return false; - } else { - return (config.userAuthenticationEnabled && this.isAuthenticated() && authApi.isPermittedCheckSourceConnection(source.sourceKey)); - } + canCheckConnection(source) { + if (!config.userAuthenticationEnabled) { + return false; + } else { + return ( + config.userAuthenticationEnabled && + this.isAuthenticated() && + authApi.isPermittedCheckSourceConnection(source.sourceKey) + ); + } } canRefreshSourceCache(source) { if (!config.userAuthenticationEnabled) { return false; } else { - return (config.userAuthenticationEnabled && this.isAuthenticated() && authApi.hasSourceAccess(source.sourceKey) && source.hasResults - && (source.hasVocabulary || source.hasCDM)); + return ( + config.userAuthenticationEnabled && + this.isAuthenticated() && + authApi.hasSourceAccess(source.sourceKey) && + source.hasResults && + (source.hasVocabulary || source.hasCDM) + ); } } - clearLocalStorageCache() { - localStorage.clear(); - - alert(ko.unwrap(ko.i18n('configuration.alerts.clearLocalCache', 'Local Storage has been cleared. Please refresh the page to reload configuration information.'))) - }; - - clearServerCache() { - if (confirm(ko.unwrap(ko.i18n('configuration.confirms.clearServerCache', 'Are you sure you want to clear the server cache?')))) { - cacheApi.clearCache().then(() => { + clearLocalStorageCache() { + localStorage.clear(); + + alert( + ko.unwrap( + ko.i18n( + 'configuration.alerts.clearLocalCache', + 'Local Storage has been cleared. Please refresh the page to reload configuration information.' + ) + ) + ); + } - alert(ko.unwrap(ko.i18n('configuration.alerts.clearServerCache', 'Server cache has been cleared.'))); - }); + clearServerCache() { + if ( + confirm( + ko.unwrap( + ko.i18n( + 'configuration.confirms.clearServerCache', + 'Are you sure you want to clear the server cache?' + ) + ) + ) + ) { + cacheApi.clearCache().then( + () => { + alert( + ko.unwrap( + ko.i18n( + 'configuration.alerts.clearServerCache', + 'Server cache has been cleared.' + ) + ) + ); + }, + (error) => { + alert( + ko.unwrap( + ko.i18n( + 'configuration.alerts.clearServerCacheError', + 'There was an error! The server cache has NOT been cleared.' + ) + ) + ); + } + ); } - }; + } - newSource() { + newSource() { commonUtils.routeTo('/source/0'); - }; + } - selectSource(source) { - document.location = "#/source/" + source.sourceId; - }; + selectSource(source) { + document.location = '#/source/' + source.sourceId; + } async updateSourceDaimonPriority(sourceKey, daimonType) { if (sharedState.priorityScope() !== 'application') { @@ -174,51 +263,91 @@ define([ try { await sourceApi.updateSourceDaimonPriority(sourceKey, daimonType); await sourceApi.initSourcesConfig(); - } catch(err) { - alert(ko.unwrap(ko.i18n('configuration.alerts.failUpdatePrioritySourceDaimon', 'Failed to update priority source daimon'))); - + } catch (err) { + alert( + ko.unwrap( + ko.i18n( + 'configuration.alerts.failUpdatePrioritySourceDaimon', + 'Failed to update priority source daimon' + ) + ) + ); } this.isInProgress(false); } + async updateCurrentVocabularyVersion(sourceKey) { + try { + const result = await sourceApi.getVocabularyInfo(sourceKey); + if (result && result.data && result.data.version != null) { + sharedState.currentVocabularyVersion(result.data.version); + return result.data.version; + } else { + throw new Error('Vocabulary info response does not contain version'); + } + } catch (err) { + alert(ko.unwrap(ko.i18n('configuration.alerts.failUpdateCurrentVocabVersion', 'Failed to update current vocabulary version'))); + } + } + updateVocabPriority() { var newVocabUrl = sharedState.vocabularyUrl(); - var selectedSource = sharedState.sources().find((item) => { return item.vocabularyUrl === newVocabUrl; }); - sharedState.priorityScope() === 'application' && sharedState.defaultVocabularyUrl(newVocabUrl); + var newCurrentVocabularyVersion = sharedState.currentVocabularyVersion(); + var selectedSource = sharedState.sources().find((item) => { + return item.vocabularyUrl === newVocabUrl; + }); + sharedState.priorityScope() === 'application' && + sharedState.defaultVocabularyUrl(newVocabUrl) && + sharedState.defaultVocabularyVersion(newCurrentVocabularyVersion); + this.updateSourceDaimonPriority(selectedSource.sourceKey, 'Vocabulary'); + this.updateCurrentVocabularyVersion(selectedSource.sourceKey); return true; - }; + } updateEvidencePriority() { var newEvidenceUrl = sharedState.evidenceUrl(); - var selectedSource = sharedState.sources().find((item) => { return item.evidenceUrl === newEvidenceUrl; }); - sharedState.priorityScope() === 'application' && sharedState.defaultEvidenceUrl(newEvidenceUrl); + var selectedSource = sharedState.sources().find((item) => { + return item.evidenceUrl === newEvidenceUrl; + }); + sharedState.priorityScope() === 'application' && + sharedState.defaultEvidenceUrl(newEvidenceUrl); this.updateSourceDaimonPriority(selectedSource.sourceKey, 'CEM'); return true; - }; + } updateResultsPriority() { var newResultsUrl = sharedState.resultsUrl(); - var selectedSource = sharedState.sources().find((item) => { return item.resultsUrl === newResultsUrl; }); - sharedState.priorityScope() === 'application' && sharedState.defaultResultsUrl(newResultsUrl); + var selectedSource = sharedState.sources().find((item) => { + return item.resultsUrl === newResultsUrl; + }); + sharedState.priorityScope() === 'application' && + sharedState.defaultResultsUrl(newResultsUrl); this.updateSourceDaimonPriority(selectedSource.sourceKey, 'Results'); return true; - }; + } checkSourceConnection(source) { - sourceApi.checkSourceConnection(source.sourceKey) - .then( ({ data }) => - source.connectionCheck(data.sourceId === undefined ? - sourceApi.buttonCheckState.failed : sourceApi.buttonCheckState.success)) - .catch(() => {source.connectionCheck(sourceApi.buttonCheckState.failed);}); + sourceApi + .checkSourceConnection(source.sourceKey) + .then(({ data }) => + source.connectionCheck( + data.sourceId === undefined + ? sourceApi.buttonCheckState.failed + : sourceApi.buttonCheckState.success + ) + ) + .catch(() => { + source.connectionCheck(sourceApi.buttonCheckState.failed); + }); source.connectionCheck(sourceApi.buttonCheckState.checking); - }; + } async refreshSourceCache(source) { try { source.refreshState(sourceApi.buttonCheckState.checking); const { data } = await sourceApi.refreshSourceCache(source.sourceKey); - if(data.executionId === undefined) { + if (data.executionId === undefined) { source.refreshState(sourceApi.buttonCheckState.failed); } else { jobDetailsService.createJob(data); @@ -231,8 +360,8 @@ define([ } getRefreshCacheButtonStyles(source) { - return this.getButtonStyles(source.refreshState()) - }; + return this.getButtonStyles(source.refreshState()); + } getCheckButtonStyles(source) { return this.getButtonStyles(source.connectionCheck()); @@ -241,7 +370,7 @@ define([ getButtonStyles(sourceState) { let iconClass = 'fa-caret-right'; let buttonClass = 'btn-primary'; - switch(sourceState) { + switch (sourceState) { case sourceApi.buttonCheckState.success: buttonClass = 'btn-success'; iconClass = 'fa-check-square'; @@ -258,16 +387,19 @@ define([ return { iconClass, buttonClass, - } + }; } runDiagnostics() { - const startTime = performance.now(); // get the list of isPermitted functions, except the literal isPermitted for (const key in authApi) { - if (typeof authApi[key] === 'function' && key.startsWith('isPermitted') && key != 'isPermitted') { + if ( + typeof authApi[key] === 'function' && + key.startsWith('isPermitted') && + key != 'isPermitted' + ) { authApi[key](); // Invoke the function } } @@ -276,7 +408,6 @@ define([ const elapsedTime = endTime - startTime; console.log(`Script execution time: ${elapsedTime} milliseconds`); - } } diff --git a/js/pages/incidence-rates/components/iranalysis/components/results.html b/js/pages/incidence-rates/components/iranalysis/components/results.html index 665fb77a4..8a2464901 100644 --- a/js/pages/incidence-rates/components/iranalysis/components/results.html +++ b/js/pages/incidence-rates/components/iranalysis/components/results.html @@ -142,15 +142,24 @@

      + + + + - - diff --git a/js/pages/incidence-rates/components/iranalysis/components/results.js b/js/pages/incidence-rates/components/iranalysis/components/results.js index e77519f9f..3e53862ed 100644 --- a/js/pages/incidence-rates/components/iranalysis/components/results.js +++ b/js/pages/incidence-rates/components/iranalysis/components/results.js @@ -38,13 +38,7 @@ define([ this.selectedSourceId = sharedState.IRAnalysis.selectedSourceId; this.selectedSourceId.subscribe(() => this.expandSelectedSource()); this.hasSourceAccess = authApi.hasSourceAccess; - this.generationSources = ko.computed(() => params.sources().map(s => ({ - ...s.source, - disabled: this.isInProgress(s) || !this.hasSourceAccess(s.source.sourceKey), - disabledReason: this.isInProgress(s) - ? ko.i18n('ir.results.generationInProgress', 'Generation is in progress')() - : !this.hasSourceAccess(s.source.sourceKey) ? ko.i18n('ir.results.accessDenied', 'Access denied')() : null, - }))); + this.execute = params.execute; this.cancelExecution = params.cancelExecution; this.stoppingSources = params.stoppingSources; @@ -116,6 +110,10 @@ define([ this.showOnlySourcesWithResults = ko.observable(false); this.sourcesTableOptions = commonUtils.getTableOptions('S'); this.sourcesColumns = [{ + sortable: false, + className: 'generation-buttons-column', + render: () => `` + }, { title: ko.i18n('cohortDefinitions.cohortDefinitionManager.panels.sourceName', 'Source Name'), render: (s,p,d) => `${d.source.sourceName}` }, { @@ -144,10 +142,6 @@ define([ }, { title: ko.i18n('ir.results.duration', 'Duration'), render: (s,p,d) => d.info() ? `${this.msToTime(d.info().executionInfo.executionDuration)}` : `n/a` - }, { - sortable: false, - className: 'generation-buttons-column', - render: () => `` }]; } @@ -165,6 +159,16 @@ define([ return (sourceItem.info() && constants.isInProgress(sourceItem.info().executionInfo.status)); } + isStopping(sourceItem) { + return ko.pureComputed(() => { + if (sourceItem.info() && Object.keys(this.stoppingSources()).length > 0) { + return(this.stoppingSources()[sourceItem.source.sourceKey]); + } else { + return(false); + } + }); + } + isSummaryLoading(sourceItem) { return sourceItem.source && this.loadingSummary && this.loadingSummary().find(sourceKey => sourceKey === sourceItem.source.sourceKey); } diff --git a/js/pages/incidence-rates/components/iranalysis/components/results.less b/js/pages/incidence-rates/components/iranalysis/components/results.less index fb054e202..e899ed5f7 100644 --- a/js/pages/incidence-rates/components/iranalysis/components/results.less +++ b/js/pages/incidence-rates/components/iranalysis/components/results.less @@ -25,8 +25,18 @@ tbody tr td { padding: 0 10px 4px; } + tr { + transition: background-color 0.3s ease; /* Smooth transition for hover effect */ + } + tr:hover { + background-color: #f2f2f2; + } + tr:hover td { + border-top: solid 1px #ccc; + border-bottom: solid 1px #ccc; + } .generation-buttons-column { - text-align: right; + text-align: left; } .generation-buttons { white-space: nowrap; diff --git a/js/pages/profiles/profile-manager.js b/js/pages/profiles/profile-manager.js index b3fc9f4dc..6affbfbbc 100644 --- a/js/pages/profiles/profile-manager.js +++ b/js/pages/profiles/profile-manager.js @@ -205,7 +205,7 @@ define([ return (_.chain(this.conceptSets()) .map(function (ids, conceptSetName) { if (_.includes(ids, d.conceptId)) - return ' ' + conceptSetName; + return conceptSetName + " (Concept Set)"; }) .compact() .value() diff --git a/js/pages/vocabulary/components/search.js b/js/pages/vocabulary/components/search.js index 1f17c040d..25a1203ee 100644 --- a/js/pages/vocabulary/components/search.js +++ b/js/pages/vocabulary/components/search.js @@ -369,6 +369,15 @@ define([ } async executeSearch() { + const filterObjString = localStorage.getItem('filter-data') + let filterObj = filterObjString ? JSON.parse(filterObjString): {} + + filterObj = { + ...filterObj, + searchText: this.currentSearch() + } + localStorage.setItem('filter-data', JSON.stringify(filterObj)) + if (!this.currentSearch() && !this.showAdvanced()) { this.data([]); return; diff --git a/js/services/AuthAPI.js b/js/services/AuthAPI.js index cd88bbb32..082377ba2 100644 --- a/js/services/AuthAPI.js +++ b/js/services/AuthAPI.js @@ -506,6 +506,10 @@ define(function(require, exports) { return isPermitted(`tag:management`); }; + const isPermittedConceptSetAnnotationsDelete = function (conceptSetId) { + return isPermitted('conceptset:' + conceptSetId + ':annotation:*:delete'); + }; + const isPermittedRunAs = () => isPermitted('user:runas:post'); const isPermittedViewDataSourceReport = sourceKey => isPermitted(`cdmresults:${sourceKey}:*:get`); @@ -634,6 +638,8 @@ define(function(require, exports) { isPermittedViewDataSourceReport, isPermittedViewDataSourceReportDetails, + isPermittedConceptSetAnnotationsDelete, + loadUserInfo, TOKEN_HEADER, runAs, diff --git a/js/services/CacheAPI.js b/js/services/CacheAPI.js index 7f03b6273..b2a8f9ab1 100644 --- a/js/services/CacheAPI.js +++ b/js/services/CacheAPI.js @@ -16,4 +16,4 @@ define(function(require, exports) { return { clearCache, }; -}); \ No newline at end of file +}); diff --git a/js/services/ConceptSet.js b/js/services/ConceptSet.js index 435d050db..77e290942 100644 --- a/js/services/ConceptSet.js +++ b/js/services/ConceptSet.js @@ -74,6 +74,21 @@ define(function (require) { .catch(authApi.handleAccessDenied); } + function saveConceptSetAnnotation(id, conceptSetItems) { + return httpService.doPut(config.api.url + 'conceptset/' + id + '/annotation', conceptSetItems) + .catch(authApi.handleAccessDenied); + } + + function getConceptSetAnnotation(conceptSetId) { + return httpService.doGet(config.webAPIRoot + 'conceptset/' + (conceptSetId || '-1') + '/annotation') + .catch(authApi.handleAccessDenied); + } + + function deleteConceptSetAnnotation(conceptSetId, annotationId) { + return httpService.doDelete(config.webAPIRoot + 'conceptset/' + (conceptSetId || '-1') +'/annotation/' + (annotationId || '-1')) + .catch(authApi.handleAccessDenied); + } + function getConceptSet(conceptSetId) { return httpService.doGet(config.webAPIRoot + 'conceptset/' + (conceptSetId || '-1')) .catch(authApi.handleAccessDenied); @@ -84,6 +99,12 @@ define(function (require) { .then(({ data }) => data); } + function copyAnnotations(copyAnnotationsRequest) { + return httpService + .doPost(`${config.webAPIRoot}conceptset/copy-annotations`, copyAnnotationsRequest) + .then(({ data }) => data); + } + function runDiagnostics(conceptSet) { return httpService .doPost(`${config.webAPIRoot}conceptset/check`, conceptSet) @@ -129,6 +150,7 @@ define(function (require) { lookupIdentifiers, getInclusionCount, getCopyName, + copyAnnotations, getConceptSet, getGenerationInfo, deleteConceptSet, @@ -140,7 +162,10 @@ define(function (require) { getVersion, getVersionExpression, updateVersion, - copyVersion + copyVersion, + saveConceptSetAnnotation, + getConceptSetAnnotation, + deleteConceptSetAnnotation }; return api; diff --git a/js/services/SourceAPI.js b/js/services/SourceAPI.js index 8aa5dbe4c..d52e14a04 100644 --- a/js/services/SourceAPI.js +++ b/js/services/SourceAPI.js @@ -161,6 +161,7 @@ define(function (require, exports) { success: function (info) { source.version(info.version); source.dialect(info.dialect); + sharedState.currentVocabularyVersion() || sharedState.defaultVocabularyVersion(info.version); }, error: function (err) { source.version('unknown'); @@ -185,8 +186,13 @@ define(function (require, exports) { return httpService.doGet(config.webAPIRoot + 'source/connection/' + sourceKey) } - function refreshSourceCache(sourceKey) { - return httpService.doGet(config.webAPIRoot + 'cdmresults/' + sourceKey + '/refreshCache'); + async function refreshSourceCache(sourceKey) { + await httpService.doPost( + config.webAPIRoot + 'cdmresults/' + sourceKey + '/clearCache' + ); + return await httpService.doGet( + config.webAPIRoot + 'cdmresults/' + sourceKey + '/refreshCache' + ); } function updateSourceDaimonPriority(sourceKey, daimonType) { @@ -204,6 +210,9 @@ define(function (require, exports) { function getResultsUrl(sourceKey) { return config.api.url + 'cdmresults/' + sourceKey + '/'; } + function getVocabularyInfo(sourceKey) { + return httpService.doGet(config.webAPIRoot + 'vocabulary/' + sourceKey + '/info'); + } var api = { getSources: getSources, @@ -217,6 +226,7 @@ define(function (require, exports) { buttonCheckState: buttonCheckState, setSharedStateSources: setSharedStateSources, updateSourceDaimonPriority, + getVocabularyInfo }; return api; diff --git a/js/utils/CommonUtils.js b/js/utils/CommonUtils.js index f3940a945..aeb25dfbc 100644 --- a/js/utils/CommonUtils.js +++ b/js/utils/CommonUtils.js @@ -261,5 +261,6 @@ define([ isNameCharactersValid, isNameLengthValid, getTableOptions, + getConceptLinkClass, }; }); \ No newline at end of file