From 81ad1f0f1dd6cc6ea95ff7384c021d4f60822b3f Mon Sep 17 00:00:00 2001 From: Chris Knoll Date: Sun, 8 Nov 2020 22:23:48 -0500 Subject: [PATCH 1/5] Added simplified cohort sample UI. --- .../cohort-definition-manager.css | 33 +- .../cohort-definition-manager.html | 189 +- .../cohort-definition-manager.js | 1658 +++++++++++------ js/pages/cohort-definitions/routes.js | 84 +- js/services/Sample.js | 53 + js/settings.js | 1 - 6 files changed, 1431 insertions(+), 587 deletions(-) create mode 100644 js/services/Sample.js diff --git a/js/pages/cohort-definitions/cohort-definition-manager.css b/js/pages/cohort-definitions/cohort-definition-manager.css index a4f0e9175..db5777dc3 100644 --- a/js/pages/cohort-definitions/cohort-definition-manager.css +++ b/js/pages/cohort-definitions/cohort-definition-manager.css @@ -56,4 +56,35 @@ .cohort-conceptset-button-pane .btn-success.disabled { color: #f3f3f3; -} \ No newline at end of file +} + +.sampleCreatingForm label { + font-weight: bold !important; +} + +.myCustomInputError { + border-color: #a94442 +} + +.myCustomInputSuccess { + border-color: #3c763d +} + +.myCustomTextError { + color: #a94442 !important +} + +.myCustomTextSuccess { + color: #3c763d !important +} + +.sample-list.fa-trash { + color: red; + cursor: pointer; +} + +.sample-list.fa-refresh { + color: #265a88; + cursor: pointer; +} + \ No newline at end of file diff --git a/js/pages/cohort-definitions/cohort-definition-manager.html b/js/pages/cohort-definitions/cohort-definition-manager.html index c3c8dfe75..815e7d1d4 100644 --- a/js/pages/cohort-definitions/cohort-definition-manager.html +++ b/js/pages/cohort-definitions/cohort-definition-manager.html @@ -26,7 +26,7 @@ - @@ -44,6 +44,10 @@ Generation +
  • + Samples +
  • +
  • Reporting
  • @@ -354,8 +358,189 @@

    Appendix 1: Concept Set Definitions

    + +
    +
    +
    +
    + Sample Selections +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + +
    + +
    + +
    + +
    + + + +
    +
    +
    + Cohort Not Generated +
    +
    + This cohort has not been generated in the data source you selected. Please return to the generation tab to generate the cohort before accessing samples. +
    +
    +
    +
    +
    + +
    +
    + *Mandatory fields + + + Sample name cannot be empty +
    +
    + + + Number of patients must be a positive integer +
    + +
    + +
    +
    + +
    +
    + + Age must be a non-negative integer +
    +
    + +
    +
    + +
    + First and second age must be non-negative integers and not equal +
    +
    + +
    + +
    + +
    +
    + +
    +
    + +
    +
    + + + + + + +
    +
    + - + \ No newline at end of file diff --git a/js/pages/cohort-definitions/cohort-definition-manager.js b/js/pages/cohort-definitions/cohort-definition-manager.js index 59ddea809..8b4c972d2 100644 --- a/js/pages/cohort-definitions/cohort-definition-manager.js +++ b/js/pages/cohort-definitions/cohort-definition-manager.js @@ -16,6 +16,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', 'atlas-state', 'clipboard', 'd3', + 'services/Sample', 'services/Jobs', 'services/job/jobDetail', 'services/JobDetailsService', @@ -76,6 +77,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', sharedState, clipboard, d3, + sampleService, jobService, jobDetail, jobDetailsService, @@ -108,6 +110,81 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', return (textA < textB) ? -1 : (textA > textB) ? 1 : 0; } + function gender(code) { + if(code==8507) return 'Male' + if(code==8532) return 'Female' + else return "Other" + } + + function mapSampleListData(originalData) { + return originalData.map(el => { + let selectionCriteria; + let mode; + if(el.age) { + switch (el.age.mode) { + case 'between': + mode = 'Between' + break; + case 'notBetween': + mode = 'Not between' + break; + case 'lessThan': + mode= 'Less than' + break; + case 'lessThanOrEqual': + mode= 'Less than or equal' + break; + case 'equalTo': + mode= 'Equal' + break; + case 'greaterThan': + mode= 'Greater than' + break; + case 'greaterThanOrEqual': + mode= 'Greater than or equal' + break; + default: + break; + } + } else { + mode = '' + } + if((mode=='Between'||mode=='Not between')&& el.age) { + selectionCriteria = `${mode} ${el.age.min} and ${el.age.max}` + } else if(el.age) { + selectionCriteria = `${mode} ${el.age.value}` + } else { + selectionCriteria = 'Random age' + } + if(el.gender.otherNonBinary&&el.gender.conceptIds.length==2) { + selectionCriteria =`Mix, ${selectionCriteria}` + } else { + if (el.gender.otherNonBinary) { + selectionCriteria = `Other, ${selectionCriteria}` + } + if (el.gender.conceptIds[0]) { + selectionCriteria = `${gender(el.gender.conceptIds[0])}, ${selectionCriteria}` + } + if (el.gender.conceptIds[1]) { + selectionCriteria = `${gender(el.gender.conceptIds[1])}, ${selectionCriteria}` + } + } + const sampleId = el.id; + const sampleName = el.name || '' + const patientCounts = el.size + const createdBy = el.createdBy && el.createdBy.name || '' + const createdOn = new Date(el.createdDate).toLocaleString() + return { + sampleId, + sampleName, + selectionCriteria, + patientCounts, + createdBy, + createdOn, + } + }) + } + class CohortDefinitionManager extends AutoBind(Clipboard(Page)) { constructor(params) { super(params); @@ -123,7 +200,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', this.cohortDefinitionSourceInfo = sharedState.CohortDefinition.sourceInfo; this.dirtyFlag = sharedState.CohortDefinition.dirtyFlag; this.conceptSets = ko.computed(() => this.currentCohortDefinition() && this.currentCohortDefinition().expression().ConceptSets); - this.conceptSetStore = ConceptSetStore.getStore(ConceptSetStore.sourceKeys().cohortDefinition); + this.conceptSetStore = ConceptSetStore.getStore(ConceptSetStore.sourceKeys().cohortDefinition); this.sourceAnalysesStatus = {}; this.reportCohortDefinitionId = ko.observable(); this.reportSourceKey = ko.observable(); @@ -136,11 +213,206 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', this.service = cohortDefinitionService; this.defaultName = globalConstants.newEntityNames.cohortDefinition; this.isReportGenerating = ko.observable(false); - this.cdmSources = ko.computed(() => { + + // sample states + this.showSampleCreatingModal = ko.observable(false); + this.sampleSourceKey = ko.observable(); + this.isSampleGenerating = ko.observable(false); + this.isLoadingSampleData = ko.observable(false); + this.cohortDefinitionIdOnRoute=ko.observable(); + // new sample state + this.newSampleCreatingLoader = ko.observable(false); + this.sampleName=ko.observable(''); + this.patientCount=ko.observable(); + this.sampleAgeType = ko.observable(''); + this.isAgeRange =ko.observable(false); + this.firstAge = ko.observable(); + this.secondAge = ko.observable(); + this.isMaleSample=ko.observable(false); + this.isFeMaleSample=ko.observable(false); + this.isOtherGenderSample=ko.observable(false); + //error state + this.isAgeRangeError = ko.observable(); + this.firstAgeError = ko.observable(); + this.sampleNameError=ko.observable(); + this.patientCountError=ko.observable(); + //reset sample state after closing + this.trackSub( this.showSampleCreatingModal.subscribe(val =>{ + if(!val) this.resetSampleForm(); + }) + ); + + //sampleSourceKey changes => get list of samples + this.trackSub(this.sampleSourceKey.subscribe(val => { + const cohortId = this.currentCohortDefinition()? + this.currentCohortDefinition().id(): + this.cohortDefinitionIdOnRoute() + if(!val) { + history.pushState(null, '', `#/cohortdefinition/${cohortId}/samples`) + return + }; + history.pushState(null, '', `#/cohortdefinition/${cohortId}/samples/${val}`) + this.getSampleList(cohortId) + })); + + //validation input value + this.trackSub(this.sampleAgeType.subscribe(val => { + this.isAgeRange(val=='between'||val=='notBetween') + })); + + this.trackSub(this.isAgeRange.subscribe(val => { + this.firstAgeError(undefined) + this.isAgeRangeError(undefined) + this.firstAge(null) + this.secondAge(null) + })); + + this.trackSub(this.secondAge.subscribe(val => { + let secondAge; + if(val!=null) { + secondAge = Number(val) + } else { + secondAge == val + } + if(secondAge==null&&this.firstAge()==null) { + this.isAgeRangeError(undefined) + this.firstAgeError(undefined) + return + } + if (this.isAgeRange()) { + if(!Number.isInteger(secondAge)||secondAge<0||!this.firstAge()||!secondAge||secondAge==this.firstAge()) { + this.isAgeRangeError(true) + } else { + this.isAgeRangeError(false) + } + } else { + this.isAgeRangeError(undefined) + } + })); + + this.trackSub(this.firstAge.subscribe(val => { + let firstAge; + if(val!=null) { + firstAge = Number(val) + } else { + firstAge == val + } + if(firstAge==null&&this.secondAge()==null) { + this.isAgeRangeError(undefined) + this.firstAgeError(undefined) + return + } + + if(this.isAgeRange()) { + if(!Number.isInteger(firstAge)||!this.secondAge()||!this.secondAge()||firstAge<0||firstAge==this.secondAge()) { + this.isAgeRangeError(true) + } else { + this.isAgeRangeError(false) + } + } + + if(!this.isAgeRange()) { + if(!Number.isInteger(firstAge)||firstAge<0) { + this.firstAgeError(true) + } else { + this.firstAgeError(false) + } + } + })); + + this.trackSub(this.sampleName.subscribe(val =>{ + if(!val.trim()) { + this.sampleNameError(true) + } else { + this.sampleNameError(false) + } + })); + + this.trackSub(this.patientCount.subscribe(val =>{ + if(!val||!Number.isInteger(Number(val))||Number(val)<=0) { + this.patientCountError(true) + } else { + this.patientCountError(false) + } + })); + + //sample list + this.sampleListCols = [ + { + title: 'Sample Id', + data:'sampleId', + visible: false, + }, + { + title: 'Sample name', + render: datatableUtils.getLinkFormatter(d => ({ + label: d['sampleName'], + linkish: true, + })), + }, + { + title: 'Number of patients', + data: 'patientCounts', + }, + { + title: 'Selection criteria', + data: 'selectionCriteria', + }, + { + title: 'Created by', + data: 'createdBy', + }, + { + title: 'Created on', + data: 'createdOn' + }, + { + title: 'Action', + sortable: false, + render: function() {return ` `} + } + ] + this.sampleList = ko.observableArray() + + // Sample data table + this.sampleCols = [ + { + title: 'Person ID', + render: datatableUtils.getLinkFormatter(d => ({ + label: d['personId'], + linkish: true, + })), + }, + { + title: 'Gender', + data: 'gender', + }, + { + title: 'Age at index', + data: 'ageIndex', + } + ]; + + this.selectedSampleId = ko.observable(''); + this.selectedSampleName = ko.observable(''); + this.sampleDataLoading = ko.observable(false); + this.sampleData =ko.observableArray([]); + + this.isCohortGenerated = ko.pureComputed(() => { + const sourceInfo = this.cohortDefinitionSourceInfo().find(d => d.sourceKey == this.sampleSourceKey()); + if (sourceInfo&&this.getStatusMessage(sourceInfo) == 'COMPLETE') { + return true; + } + return false; + }); + + //end of sample states + + this.cdmSources = ko.pureComputed(() => { return sharedState.sources().filter((source) => commonUtils.hasCDM(source) && authApi.hasSourceAccess(source.sourceKey)); }); - this.cohortDefinitionCaption = ko.computed(() => { + this.cohortDefinitionCaption = ko.pureComputed(() => { if (this.currentCohortDefinition()) { if (this.currentCohortDefinition().id() === 0 || this.currentCohortDefinition().id() === null) { return this.defaultName; @@ -149,20 +421,20 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', } } }); - this.isNameFilled = ko.computed(() => { + this.isNameFilled = ko.pureComputed(() => { return this.currentCohortDefinition() && this.currentCohortDefinition().name() && this.currentCohortDefinition().name().trim(); }); - this.isNameCharactersValid = ko.computed(() => { + this.isNameCharactersValid = ko.pureComputed(() => { return this.isNameFilled() && commonUtils.isNameCharactersValid(this.currentCohortDefinition().name()); }); - this.isNameLengthValid = ko.computed(() => { + this.isNameLengthValid = ko.pureComputed(() => { return this.isNameFilled() && commonUtils.isNameLengthValid(this.currentCohortDefinition().name()); }); - this.isDefaultName = ko.computed(() => { + this.isDefaultName = ko.pureComputed(() => { return this.isNameFilled() && this.currentCohortDefinition().name().trim() === this.defaultName; }); - this.isNameCorrect = ko.computed(() => { + this.isNameCorrect = ko.pureComputed(() => { return this.isNameFilled() && !this.isDefaultName() && this.isNameCharactersValid() && this.isNameLengthValid(); }); this.isAuthenticated = ko.pureComputed(() => { @@ -242,7 +514,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', this.sourcecodes = ko.observable(); this.conceptLoading = ko.observable(false); this.conceptSetName = ko.observable(); - this.tabPath = ko.computed(() => { + this.tabPath = ko.pureComputed(() => { var path = this.tabMode(); if (path === 'export') { path += '/' + this.exportTabMode(); @@ -319,7 +591,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', this.selectedSource = ko.observable(); this.selectedReportSource = ko.observable(); - this.sortedConceptSets = ko.computed((d) => { + this.sortedConceptSets = ko.pureComputed((d) => { if (this.currentCohortDefinition() != null) { var clone = this.currentCohortDefinition().expression().ConceptSets().slice(0); return clone.sort(conceptSetSorter); @@ -382,7 +654,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', }, ] }; - + this.stopping = ko.pureComputed(() => this.cohortDefinitionSourceInfo().reduce((acc, target) => ({...acc, [target.sourceKey]: ko.observable(false)}), {})); this.isSourceStopping = (source) => this.stopping()[source.sourceKey]; @@ -443,7 +715,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', }).length > 0; }); - this.cohortDefinitionLink = ko.computed(() => { + this.cohortDefinitionLink = ko.pureComputed(() => { if (this.currentCohortDefinition()) { return commonUtils.normalizeUrl(this.config.api.url, "cohortdefinition", this.currentCohortDefinition().id()); } @@ -471,7 +743,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', } }); - this.reportingState = ko.computed(() => { + this.reportingState = ko.pureComputed(() => { // require a data source selection if (this.reportSourceKey() == undefined) { this.generateReportsEnabled(false); @@ -546,7 +818,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', return "unknown_cohort_report_state"; }); - this.showReportNameDropdown = ko.computed(() => { + this.showReportNameDropdown = ko.pureComputed(() => { return this.reportSourceKey() != undefined && this.reportingState() != 'checking_status' && this.reportingState() != 'cohort_not_generated' && @@ -608,14 +880,15 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', this.analysisTypesOpened = ko.observable(false); this.currentConceptSet = this.conceptSetStore.current; - this.reportSourceKeySub = this.reportSourceKey.subscribe(source => { + this.trackSub(this.reportSourceKey.subscribe(source => { PollService.stop(this.pollId); this.reportReportName(null); this.reportingSourceStatusAvailable(false); this.reportingAvailableReports.removeAll(); const cd = this.currentCohortDefinition(); source && this.startPolling(cd, source); - }); + })); + this.reportsManagerComponentParams = { reportSourceKey: this.reportSourceKey, @@ -623,10 +896,19 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', PermissionService.decorateComponent(this, { entityTypeGetter: () => entityType.COHORT_DEFINITION, entityIdGetter: () => this.currentCohortDefinition().id(), - createdByUsernameGetter: () => this.currentCohortDefinition() && this.currentCohortDefinition().createdBy() + createdByUsernameGetter: () => this.currentCohortDefinition() && this.currentCohortDefinition().createdBy() && this.currentCohortDefinition().createdBy().login }); +// todo: look into if this subscription is necessary + this.trackSub(this.tabMode.subscribe(mode => { + if(mode&&this.currentCohortDefinition()&&mode!=='samples') { + const cohortId = this.currentCohortDefinition().id() + // use push state to prevent the component to re-render + history.pushState(null, '', `#/cohortdefinition/${cohortId}`) + } + })); + this.pollForInfoPeriodically(); this.subscriptions.push( @@ -658,676 +940,900 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', } } - delete () { - if (!confirm("Delete cohort definition? Warning: deletion can not be undone!")) - return; + delete () { + if (!confirm("Delete cohort definition? Warning: deletion can not be undone!")) + return; - this.isDeleting(true); - - // reset view after save - cohortDefinitionService.deleteCohortDefinition(this.currentCohortDefinition().id()). - then( (result) => { - this.currentCohortDefinition(null); - PollService.stop(this.pollTimeoutId); - this.dirtyFlag().reset(); - this.close(); - }, (error) => { - console.log("Error: " + error); - if(error.status == 409) { - alert("Cohort definition cannot be deleted because it is referenced in some analysis"); - this.isDeleting(false); - } else { - authApi.handleAccessDenied(error); - } - }); - } + this.isDeleting(true); + + // reset view after save + cohortDefinitionService.deleteCohortDefinition(this.currentCohortDefinition().id()). + then( (result) => { + this.currentCohortDefinition(null); + PollService.stop(this.pollTimeoutId); + this.dirtyFlag().reset(); + this.close(); + }, (error) => { + console.log("Error: " + error); + if(error.status == 409) { + alert("Cohort definition cannot be deleted because it is referenced in some analysis"); + this.isDeleting(false); + } else { + authApi.handleAccessDenied(error); + } + }); + } - async save () { - this.isSaving(true); + async save () { + this.sampleSourceKey(null) + this.isSaving(true); - let cohortDefinitionName = this.currentCohortDefinition().name(); - this.currentCohortDefinition().name(cohortDefinitionName.trim()); + let cohortDefinitionName = this.currentCohortDefinition().name(); + this.currentCohortDefinition().name(cohortDefinitionName.trim()); - // Next check to see that a cohort definition with this name does not already exist - // in the database. Also pass the id so we can make sure that the - // current Cohort Definition is excluded in this check. + // Next check to see that a cohort definition with this name does not already exist + // in the database. Also pass the id so we can make sure that the + // current Cohort Definition is excluded in this check. - try { - const results = await cohortDefinitionService.exists(this.currentCohortDefinition().name(), this.currentCohortDefinition().id()); - if (results > 0) { - alert('A cohort definition with this name already exists. Please choose a different name.'); - } else { - this.conceptSetStore.clear(); + try { + const results = await cohortDefinitionService.exists(this.currentCohortDefinition().name(), this.currentCohortDefinition().id()); + if (results > 0) { + alert('A cohort definition with this name already exists. Please choose a different name.'); + } else { + this.conceptSetStore.clear(); - // If we are saving a new cohort definition (id === 0) then clear - // the id field before saving - if (this.currentCohortDefinition().id() === "0") { - this.currentCohortDefinition().id(undefined); - } - var definition = ko.toJS(this.currentCohortDefinition()); - // reset view after save - const savedDefinition = await cohortDefinitionService.saveCohortDefinition(definition); - definition = new CohortDefinition(savedDefinition); - const redirectWhenComplete = definition.id() != this.currentCohortDefinition().id(); - this.currentCohortDefinition(definition); - if (redirectWhenComplete) { - commonUtils.routeTo(constants.paths.details(definition.id())); - } + // If we are saving a new cohort definition (id === 0) then clear + // the id field before saving + if (this.currentCohortDefinition().id() === "0") { + this.currentCohortDefinition().id(undefined); + } + var definition = ko.toJS(this.currentCohortDefinition()); + // reset view after save + const savedDefinition = await cohortDefinitionService.saveCohortDefinition(definition); + definition = new CohortDefinition(savedDefinition); + const redirectWhenComplete = definition.id() != this.currentCohortDefinition().id(); + this.currentCohortDefinition(definition); + if (redirectWhenComplete) { + commonUtils.routeTo(constants.paths.details(definition.id())); } - sharedState.CohortDefinition.lastUpdatedId(this.currentCohortDefinition().id()); - } catch (e) { - alert('An error occurred while attempting to save a cohort definition.'); - } finally { - this.isSaving(false); } + sharedState.CohortDefinition.lastUpdatedId(this.currentCohortDefinition().id()); + } catch (e) { + alert('An error occurred while attempting to save a cohort definition.'); + } finally { + this.isSaving(false); } + } - close () { - if (this.dirtyFlag().isDirty() && !confirm("Your cohort changes are not saved. Would you like to continue?")) { - return; - } else { - this.conceptSetStore.clear(); - this.currentCohortDefinition(null); - this.dirtyFlag().reset(); - this.reportCohortDefinitionId(null); - this.reportReportName(null); - this.reportSourceKey(null); - commonUtils.routeTo('/cohortdefinitions'); - } + close () { + if (this.dirtyFlag().isDirty() && !confirm("Your cohort changes are not saved. Would you like to continue?")) { + return; + } else { + this.conceptSetStore.clear(); + this.currentCohortDefinition(null); + this.dirtyFlag().reset(); + this.reportCohortDefinitionId(null); + this.reportReportName(null); + this.reportSourceKey(null); + commonUtils.routeTo('/cohortdefinitions'); } + } - async copy () { - this.isCopying(true); - // reset view after save - try { - const result = await cohortDefinitionService.copyCohortDefinition(this.currentCohortDefinition().id()); - PollService.stop(this.pollTimeoutId); - document.location = "#/cohortdefinition/" + result.id; - } finally { - this.isCopying(false); - } + async copy () { + this.isCopying(true); + // reset view after save + try { + const result = await cohortDefinitionService.copyCohortDefinition(this.currentCohortDefinition().id()); + PollService.stop(this.pollTimeoutId); + document.location = "#/cohortdefinition/" + result.id; + } finally { + this.isCopying(false); } + } - isSourceRunning(source) { - if (source) { - switch (source.status()) { - case 'COMPLETE': - return false; - break; - case 'n/a': - return false; - break; - default: - return true; - } - } else { - return false; + isSourceRunning(source) { + if (source) { + switch (source.status()) { + case 'COMPLETE': + return false; + break; + case 'n/a': + return false; + break; + default: + return true; } + } else { + return false; } + } - isCancelDisabled(source) { - return this.isSourceStopping(source)() || this.isProcessingByAnother(source); - } + isCancelDisabled(source) { + return this.isSourceStopping(source)() || this.isProcessingByAnother(source); + } - isProcessingByAnother(source) { - return !this.isMine(source) && source.status() !== "COMPLETE" && source.status() !== "FAILED"; - } + isProcessingByAnother(source) { + return !this.isMine(source) && source.status() !== "COMPLETE" && source.status() !== "FAILED"; + } - isMine(source) { - return source.createdBy() ? authApi.subject() === source.createdBy() : false; - } + isMine(source) { + return source.createdBy() ? authApi.subject() === source.createdBy() : false; + } - async exportSql({ expression = {} } = {}) { + async exportSql({ expression = {} } = {}) { - return await this.service.getSql(ko.toJS(expression, pruneJSON)); - } + return await this.service.getSql(ko.toJS(expression, pruneJSON)); + } - getSourceKeyInfo (sourceKey) { - return this.cohortDefinitionSourceInfo().filter((d) => { - return d.sourceKey == sourceKey - })[0]; - } + getSourceKeyInfo (sourceKey) { + return this.cohortDefinitionSourceInfo().filter((d) => { + return d.sourceKey == sourceKey + })[0]; + } - getSourceId (sourceKey) { - return sharedState.sources().find(source => source.sourceKey === sourceKey).sourceId; - } + getSourceId (sourceKey) { + return sharedState.sources().find(source => source.sourceKey === sourceKey).sourceId; + } - generateCohort (source) { - this.stopping()[source.sourceKey](false); - this.getSourceKeyInfo(source.sourceKey).status(globalConstants.generationStatuses.PENDING); - this.getSourceKeyInfo(source.sourceKey).createdBy(authApi.subject()); - if (this.selectedSource() && this.selectedSource().sourceId === source.sourceId) { - this.toggleCohortReport(null); - } - cohortDefinitionService.generate(this.currentCohortDefinition().id(), source.sourceKey) - .catch(this.authApi.handleAccessDenied) - .then(({data}) => { - jobDetailsService.createJob(data); - }); + generateCohort (source) { + this.stopping()[source.sourceKey](false); + this.getSourceKeyInfo(source.sourceKey).status(globalConstants.generationStatuses.PENDING); + this.getSourceKeyInfo(source.sourceKey).createdBy(authApi.subject()); + if (this.selectedSource() && this.selectedSource().sourceId === source.sourceId) { + this.toggleCohortReport(null); } + cohortDefinitionService.generate(this.currentCohortDefinition().id(), source.sourceKey) + .catch(this.authApi.handleAccessDenied) + .then(({data}) => { + jobDetailsService.createJob(data); + }); + } - cancelGenerate (source) { - this.stopping()[source.sourceKey](true); - cohortDefinitionService.cancelGenerate(this.currentCohortDefinition().id(), source.sourceKey); - }; + cancelGenerate (source) { + this.stopping()[source.sourceKey](true); + cohortDefinitionService.cancelGenerate(this.currentCohortDefinition().id(), source.sourceKey); + }; - hasCDM (source) { - for (var d = 0; d < source.daimons.length; d++) { - if (source.daimons[d].daimonType == 'CDM') { - return true; - } + hasCDM (source) { + for (var d = 0; d < source.daimons.length; d++) { + if (source.daimons[d].daimonType == 'CDM') { + return true; } - return false; } + return false; + } - hasResults (source) { - for (var d = 0; d < source.daimons.length; d++) { - if (source.daimons[d].daimonType == 'Results') { - return true; - } + hasResults (source) { + for (var d = 0; d < source.daimons.length; d++) { + if (source.daimons[d].daimonType == 'Results') { + return true; } - return false; } + return false; + } - removeConceptSet(id) { - this.currentCohortDefinition().expression().ConceptSets.remove( - function (item) { - return item.id === id; - } - ); - } + removeConceptSet(id) { + this.currentCohortDefinition().expression().ConceptSets.remove( + function (item) { + return item.id === id; + } + ); + } - removeInclusionRule(name) { - this.currentCohortDefinition().expression().InclusionRules.remove( - (item) => item.name() === name - ); - } + removeInclusionRule(name) { + this.currentCohortDefinition().expression().InclusionRules.remove( + (item) => item.name() === name + ); + } - fixConceptSet(warning) { - if (warning.type === 'ConceptSetWarning' && warning.conceptSetId >= 0) { - this.removeConceptSet(warning.conceptSetId); - } else if (warning.type === 'IncompleteRuleWarning' && warning.ruleName) { - this.removeInclusionRule(warning.ruleName); - } + fixConceptSet(warning) { + if (warning.type === 'ConceptSetWarning' && warning.conceptSetId >= 0) { + this.removeConceptSet(warning.conceptSetId); + } else if (warning.type === 'IncompleteRuleWarning' && warning.ruleName) { + this.removeInclusionRule(warning.ruleName); } + } - diagnose() { - if (this.currentCohortDefinition()) { - return cohortDefinitionService.runDiagnostics(this.currentCohortDefinition().expression()); - } + diagnose() { + if (this.currentCohortDefinition()) { + return cohortDefinitionService.runDiagnostics(this.currentCohortDefinition().expression()); } + } - showSaveConceptSet () { - this.newConceptSetName(this.currentConceptSet().name()); - this.saveConceptSetShow(true); - }; + showSaveConceptSet () { + this.newConceptSetName(this.currentConceptSet().name()); + this.saveConceptSetShow(true); + }; - saveConceptSet () { - this.saveConceptSetShow(false); - var conceptSet = { - id: 0, - name: this.newConceptSetName() - }; - var conceptSetItems = conceptSetUitls.toRepositoryConceptSetItems(this.selectedConcepts()); - conceptSetService.saveConceptSet(conceptSet) - .then((data) => { - const conceptSetId = data.data.id; - return conceptSetService.saveConceptSetItems(conceptSetId, conceptSetItems); - }); + saveConceptSet () { + this.saveConceptSetShow(false); + var conceptSet = { + id: 0, + name: this.newConceptSetName() }; - - viewReport (sourceKey, reportName) { - // TODO: Should we prevent running an analysis on an unsaved cohort definition? - if (this.currentCohortDefinition().id() > 0) { - this.reportCohortDefinitionId(this.currentCohortDefinition().id()); - this.reportReportName(reportName); - this.reportSourceKey(sourceKey); - this.reportTriggerRun(true); - } + var conceptSetItems = conceptSetUitls.toRepositoryConceptSetItems(this.selectedConcepts()); + conceptSetService.saveConceptSet(conceptSet) + .then((data) => { + const conceptSetId = data.data.id; + return conceptSetService.saveConceptSetItems(conceptSetId, conceptSetItems); + }); + }; + + viewReport (sourceKey, reportName) { + // TODO: Should we prevent running an analysis on an unsaved cohort definition? + if (this.currentCohortDefinition().id() > 0) { + this.reportCohortDefinitionId(this.currentCohortDefinition().id()); + this.reportReportName(reportName); + this.reportSourceKey(sourceKey); + this.reportTriggerRun(true); } + } - onRouterParamsChanged(params) { - let { cohortDefinitionId, conceptSetId, selectedSourceId, mode = 'definition', sourceKey } = params; - // cohortDefinitionId can be undefined in case of following links fron notifications - // when another tab of the same cohort definition is selected - if (!cohortDefinitionId && this.currentCohortDefinition()) { - cohortDefinitionId = this.currentCohortDefinition().id(); - } - this.tabMode(mode); - if (!this.checkifDataLoaded(cohortDefinitionId, conceptSetId, sourceKey)) { - this.prepareCohortDefinition(cohortDefinitionId, conceptSetId, selectedSourceId, sourceKey); - } else if (selectedSourceId) { - let source = this.sharedState.sources().find(s => s.sourceId === selectedSourceId) - if (source) { - let selectedSourceInfo = this.cohortDefinitionSourceInfo().find(s => s.sourceKey === source.sourceKey) - this.expandSelectedSection(selectedSourceInfo); - } - } else { - this.selectedReportSource(null); - } + onRouterParamsChanged(params) { + let { cohortDefinitionId, conceptSetId, selectedSourceId, mode = 'definition', sourceKey, sampleId } = params; + this.cohortDefinitionIdOnRoute(cohortDefinitionId) + // cohortDefinitionId can be undefined in case of following links fron notifications + // when another tab of the same cohort definition is selected + if (!cohortDefinitionId && this.currentCohortDefinition()) { + cohortDefinitionId = this.currentCohortDefinition().id(); } - - expandSelectedSection(item) { - this.selectedReportSource(item); + this.tabMode(mode); + if(sourceKey) { + this.sampleSourceKey(sourceKey) } - - getSourceInfo(source) { - const info = this.currentCohortDefinitionInfo(); - for (var i = 0; i < info.length; i++) { - if (info[i].id.sourceId == source.sourceId) { - return info[i]; - } - } + if(sampleId) { + this.selectedSampleId(sampleId) + this.fetchSampleData({sampleId, sourceKey, cohortDefinitionId}) } - - getCDSI(source, sourceInfo) { - let cdsi = {}; - cdsi.name = source.sourceName; - cdsi.sourceKey = source.sourceKey; - if (sourceInfo != null) { - cdsi.isValid = ko.observable(sourceInfo.isValid); - cdsi.isCanceled = ko.observable(sourceInfo.isCanceled); - cdsi.sourceId = sourceInfo.id.sourceId; - cdsi.status = ko.observable(sourceInfo.status); - const date = new Date(sourceInfo.startTime); - cdsi.startTime = ko.observable(momentApi.formatDateTime(date)); - cdsi.executionDuration = ko.observable(momentApi.formatDuration(sourceInfo.executionDuration)); - const commaFormatted = d3.format(","); - if (sourceInfo.personCount == null) { - cdsi.personCount = ko.observable('...'); - } else { - cdsi.personCount = ko.observable(commaFormatted(sourceInfo.personCount)); - } - if (sourceInfo.recordCount) { - cdsi.recordCount = ko.observable(commaFormatted(sourceInfo.recordCount)); - } else { - cdsi.recordCount = ko.observable('...'); - } - cdsi.failMessage = ko.observable(sourceInfo.failMessage); - cdsi.createdBy = ko.observable(sourceInfo.createdBy); - } else { - cdsi.isValid = ko.observable(false); - cdsi.isCanceled = ko.observable(false); - cdsi.status = ko.observable('n/a'); - cdsi.startTime = ko.observable('n/a'); - cdsi.executionDuration = ko.observable('n/a'); - cdsi.personCount = ko.observable('n/a'); - cdsi.recordCount = ko.observable('n/a'); - cdsi.failMessage = ko.observable(null); - cdsi.createdBy = ko.observable(null); + if (!this.checkifDataLoaded(cohortDefinitionId, conceptSetId, sourceKey)) { + this.prepareCohortDefinition(cohortDefinitionId, conceptSetId, selectedSourceId, sourceKey); + } else if (selectedSourceId) { + let source = this.sharedState.sources().find(s => s.sourceId === selectedSourceId) + if (source) { + let selectedSourceInfo = this.cohortDefinitionSourceInfo().find(s => s.sourceKey === source.sourceKey) + this.expandSelectedSection(selectedSourceInfo); } - return cdsi; + } else { + this.selectedReportSource(null); } + } - async loadRequiredData(conceptSetId, selectedSourceId, sourceKey) { - if (this.currentCohortDefinition()) { - try { - // now that we have required information lets compile them into data objects for our view - const cdmSources = sharedState.sources().filter(commonUtils.hasCDM); - let results = []; - let selectedSourceInfo = null; - for (let s = 0; s < cdmSources.length; s++) { - const source = cdmSources[s]; - this.sourceAnalysesStatus[source.sourceKey] = ko.observable({ - ready: false, - checking: false - }); - const sourceInfo = this.getSourceInfo(source); - let cdsi = this.getCDSI(source, sourceInfo); - results.push(cdsi); - - if (selectedSourceId && source.sourceId === selectedSourceId) { - selectedSourceInfo = cdsi; - } - } - this.cohortDefinitionSourceInfo(results); - - if (selectedSourceInfo) { - this.expandSelectedSection(selectedSourceInfo); - } + expandSelectedSection(item) { + this.selectedReportSource(item); + } - if (conceptSetId != null) { - await this.loadConceptSet(conceptSetId); - return; - } else { - this.reportSourceKey(sourceKey); - } - } catch(er) { - console.error(er); - } + getSourceInfo(source) { + const info = this.currentCohortDefinitionInfo(); + for (var i = 0; i < info.length; i++) { + if (info[i].id.sourceId == source.sourceId) { + return info[i]; } } + } - - async prepareCohortDefinition(cohortDefinitionId, conceptSetId, selectedSourceId, sourceKey) { - this.isLoading(true); - if(parseInt(cohortDefinitionId) === 0) { - this.setNewCohortDefinition(); + getCDSI(source, sourceInfo) { + let cdsi = {}; + cdsi.name = source.sourceName; + cdsi.sourceKey = source.sourceKey; + if (sourceInfo != null) { + cdsi.isValid = ko.observable(sourceInfo.isValid); + cdsi.isCanceled = ko.observable(sourceInfo.isCanceled); + cdsi.sourceId = sourceInfo.id.sourceId; + cdsi.status = ko.observable(sourceInfo.status); + const date = new Date(sourceInfo.startTime); + cdsi.startTime = ko.observable(momentApi.formatDateTime(date)); + cdsi.executionDuration = ko.observable(momentApi.formatDuration(sourceInfo.executionDuration)); + const commaFormatted = d3.format(","); + if (sourceInfo.personCount == null) { + cdsi.personCount = ko.observable('...'); + } else { + cdsi.personCount = ko.observable(commaFormatted(sourceInfo.personCount)); + } + if (sourceInfo.recordCount) { + cdsi.recordCount = ko.observable(commaFormatted(sourceInfo.recordCount)); } else { - await this.loadExistingCohortDefinition(cohortDefinitionId); + cdsi.recordCount = ko.observable('...'); } - await this.loadRequiredData(conceptSetId, selectedSourceId, sourceKey); - this.isLoading(false); + cdsi.failMessage = ko.observable(sourceInfo.failMessage); + cdsi.createdBy = ko.observable(sourceInfo.createdBy); + } else { + cdsi.isValid = ko.observable(false); + cdsi.isCanceled = ko.observable(false); + cdsi.status = ko.observable('n/a'); + cdsi.startTime = ko.observable('n/a'); + cdsi.executionDuration = ko.observable('n/a'); + cdsi.personCount = ko.observable('n/a'); + cdsi.recordCount = ko.observable('n/a'); + cdsi.failMessage = ko.observable(null); + cdsi.createdBy = ko.observable(null); } + return cdsi; + } - setNewCohortDefinition() { - this.currentCohortDefinition(new CohortDefinition({ id: 0, name: 'New Cohort Definition' })); - this.currentCohortDefinitionInfo([]); + async loadRequiredData(conceptSetId, selectedSourceId, sourceKey) { + if (this.currentCohortDefinition()) { + try { + // now that we have required information lets compile them into data objects for our view + const cdmSources = sharedState.sources().filter(commonUtils.hasCDM); + let results = []; + let selectedSourceInfo = null; + for (let s = 0; s < cdmSources.length; s++) { + const source = cdmSources[s]; + this.sourceAnalysesStatus[source.sourceKey] = ko.observable({ + ready: false, + checking: false + }); + const sourceInfo = this.getSourceInfo(source); + let cdsi = this.getCDSI(source, sourceInfo); + results.push(cdsi); - } + if (selectedSourceId && source.sourceId === selectedSourceId) { + selectedSourceInfo = cdsi; + } + } + this.cohortDefinitionSourceInfo(results); - async loadExistingCohortDefinition(id) { - try { - const cohortDefinition = await cohortDefinitionService.getCohortDefinition(id); - const generationInfo = await cohortDefinitionService.getInfo(id); - this.currentCohortDefinition(new CohortDefinition(cohortDefinition)); - this.currentCohortDefinitionInfo(generationInfo); - } catch (err) { - console.error(err); - } - } + if (selectedSourceInfo) { + this.expandSelectedSection(selectedSourceInfo); + } - checkifDataLoaded(cohortDefinitionId, conceptSetId, sourceKey) { - if (this.currentCohortDefinition() && this.currentCohortDefinition().id() == cohortDefinitionId) { - if (this.currentConceptSet() && this.currentConceptSet().id == conceptSetId) { - this.reportSourceKey(sourceKey); - return true; - } else if (conceptSetId != null) { - this.loadConceptSet(conceptSetId); - return true; + if (conceptSetId != null) { + await this.loadConceptSet(conceptSetId); + return; } else { this.reportSourceKey(sourceKey); - return true; } + } catch(er) { + console.error(er); } - return false; } + } - loadConceptSet(conceptSetId) { - this.conceptSetStore.current(this.conceptSets()().find(item => item.id == conceptSetId)); - this.conceptSetStore.isEditable(this.canEdit()); - commonUtils.routeTo(`/cohortdefinition/${this.currentCohortDefinition().id()}/conceptsets/`); - } - - reload () { - if (this.modifiedJSON.length > 0) { - var updatedExpression = JSON.parse(this.modifiedJSON); - this.currentCohortDefinition().expression(new CohortExpression(updatedExpression)); - } + + async prepareCohortDefinition(cohortDefinitionId, conceptSetId, selectedSourceId, sourceKey) { + this.isLoading(true); + if(parseInt(cohortDefinitionId) === 0) { + this.setNewCohortDefinition(); + } else { + await this.loadExistingCohortDefinition(cohortDefinitionId); } + await this.loadRequiredData(conceptSetId, selectedSourceId, sourceKey); + this.isLoading(false); + } + + setNewCohortDefinition() { + this.currentCohortDefinition(new CohortDefinition({ id: 0, name: 'New Cohort Definition' })); + this.currentCohortDefinitionInfo([]); + + } - exportConceptSetsCSV () { - return FileService.loadZip(`${config.api.url}cohortdefinition/${this.currentCohortDefinition().id()}/export/conceptset`, - `cohortdefinition-conceptsets-${this.currentCohortDefinition().id()}.zip`); + async loadExistingCohortDefinition(id) { + try { + const cohortDefinition = await cohortDefinitionService.getCohortDefinition(id); + const generationInfo = await cohortDefinitionService.getInfo(id); + this.currentCohortDefinition(new CohortDefinition(cohortDefinition)); + this.currentCohortDefinitionInfo(generationInfo); + } catch (err) { + console.error(err); } + } - toggleCohortReport(item) { - if (this.selectedReportSource() && this.selectedReportSource().sourceKey === item.sourceKey) { - this.selectedReportSource(null); - commonUtils.routeTo('/cohortdefinition/' + this.currentCohortDefinition().id() + '/generation'); + checkifDataLoaded(cohortDefinitionId, conceptSetId, sourceKey) { + if (this.currentCohortDefinition() && this.currentCohortDefinition().id() == cohortDefinitionId) { + if (this.currentConceptSet() && this.currentConceptSet().id == conceptSetId) { + this.reportSourceKey(sourceKey); + return true; + } else if (conceptSetId != null) { + this.loadConceptSet(conceptSetId); + return true; } else { - this.selectedReportSource(item); - commonUtils.routeTo('/cohortdefinition/' + this.currentCohortDefinition().id() + '/generation/' + item.sourceId); + this.reportSourceKey(sourceKey); + return true; } } + return false; + } - selectTab(key) { - this.tabMode(key); - return commonUtils.routeTo('/cohortdefinition/' + this.currentCohortDefinition().id() + '/' + key); + loadConceptSet(conceptSetId) { + this.conceptSetStore.current(this.conceptSets()().find(item => item.id == conceptSetId)); + this.conceptSetStore.isEditable(this.canEdit()); + commonUtils.routeTo(`/cohortdefinition/${this.currentCohortDefinition().id()}/conceptsets/`); + } + + reload () { + if (this.modifiedJSON.length > 0) { + var updatedExpression = JSON.parse(this.modifiedJSON); + this.currentCohortDefinition().expression(new CohortExpression(updatedExpression)); } + } - getStatusMessage (info) { - if (info.status() === "COMPLETE" && !info.isValid()) - return !info.isCanceled() ? "FAILED" : "CANCELED"; - else - // replace 'COMPLETE' with 'COMPLETED' to match other complete statuses - return info.status() === 'COMPLETE' ? 'COMPLETED' : info.status(); - } + exportConceptSetsCSV () { + return FileService.loadZip(`${config.api.url}cohortdefinition/${this.currentCohortDefinition().id()}/export/conceptset`, + `cohortdefinition-conceptsets-${this.currentCohortDefinition().id()}.zip`); + } - getStatusTemplate(item) { - return item.status === 'FAILED' ? 'failed-status-tmpl' : 'success-status-tmpl'; + toggleCohortReport(item) { + if (this.selectedReportSource() && this.selectedReportSource().sourceKey === item.sourceKey) { + this.selectedReportSource(null); + commonUtils.routeTo('/cohortdefinition/' + this.currentCohortDefinition().id() + '/generation'); + } else { + this.selectedReportSource(item); + commonUtils.routeTo('/cohortdefinition/' + this.currentCohortDefinition().id() + '/generation/' + item.sourceId); } + } - showExitMessage(sourceKey) { - const info = this.cohortDefinitionSourceInfo().find(i => i.sourceKey === sourceKey) || { failMessage: 'Failed without any message' }; - this.exitMessage(info.failMessage); - this.isExitMessageShown(true); - } + selectTab(key) { + this.tabMode(key); + return commonUtils.routeTo('/cohortdefinition/' + this.currentCohortDefinition().id() + '/' + key); + } - async generateAnalyses ({ descr, duration, analysisIdentifiers, runHeraclesHeel, periods, rollupUtilizationVisit, rollupUtilizationDrug }) { - if (!confirm(`This will run ${descr} and may take about ${duration}. Are you sure?`)) { - return; - } + getStatusMessage (info) { + if (info.status() === "COMPLETE" && !info.isValid()) + return !info.isCanceled() ? "FAILED" : "CANCELED"; + else + // replace 'COMPLETE' with 'COMPLETED' to match other complete statuses + return info.status() === 'COMPLETE' ? 'COMPLETED' : info.status(); + } - this.generateReportsEnabled(false); - analysisIdentifiers = _.uniq(analysisIdentifiers); - var cohortDefinitionId = this.currentCohortDefinition().id(); - var cohortJob = {}; - - cohortJob.jobName = `HERACLES_COHORT_${cohortDefinitionId}_${this.reportSourceKey()}`; - cohortJob.sourceKey = this.reportSourceKey(); - cohortJob.smallCellCount = 5; - cohortJob.cohortDefinitionIds = []; - cohortJob.cohortDefinitionIds.push(cohortDefinitionId); - cohortJob.analysisIds = analysisIdentifiers; - cohortJob.runHeraclesHeel = runHeraclesHeel; - cohortJob.cohortPeriodOnly = false; - - // set concepts - cohortJob.conditionConceptIds = []; - cohortJob.drugConceptIds = []; - cohortJob.procedureConceptIds = []; - cohortJob.observationConceptIds = []; - cohortJob.measurementConceptIds = []; - - cohortJob.periods = periods; - - cohortJob.rollupUtilizationVisit = rollupUtilizationVisit; - cohortJob.rollupUtilizationDrug = rollupUtilizationDrug; - - this.createReportJobFailed(false); - try { - this.isReportGenerating(true); - const { data } = await cohortDefinitionService.getCohortAnalyses(cohortJob); - jobDetailsService.createJob(data); - } catch (err) { - this.createReportJobFailed(true); - const { status, data } = err; - const createReportJobErrorPackage = { - status, - error: data.payload, - }; - this.createReportJobError(JSON.stringify(createReportJobErrorPackage)); - - // reset button to allow generation attempt - this.generateReportsEnabled(true); - this.generateButtonCaption('Generate'); - } - await this.queryHeraclesJob(this.currentCohortDefinition(), this.reportSourceKey()); - this.isReportGenerating(false); - } + getStatusTemplate(item) { + return item.status === 'FAILED' ? 'failed-status-tmpl' : 'success-status-tmpl'; + } - generateQuickAnalysis () { - this.generateAnalyses({ - descr: 'minimal analyses set to provide a quick overview of the cohort', - duration: '10 minutes', - analysisIdentifiers: cohortReportingService.getQuickAnalysisIdentifiers(), - runHeraclesHeel: false - }); + showExitMessage(sourceKey) { + const info = this.cohortDefinitionSourceInfo().find(i => i.sourceKey === sourceKey) || { failMessage: 'Failed without any message' }; + this.exitMessage(info.failMessage); + this.isExitMessageShown(true); + } + + async generateAnalyses ({ descr, duration, analysisIdentifiers, runHeraclesHeel, periods, rollupUtilizationVisit, rollupUtilizationDrug }) { + if (!confirm(`This will run ${descr} and may take about ${duration}. Are you sure?`)) { + return; } - selectHealthcareAnalyses () { - this.showUtilizationToRunModal(true); + this.generateReportsEnabled(false); + analysisIdentifiers = _.uniq(analysisIdentifiers); + var cohortDefinitionId = this.currentCohortDefinition().id(); + var cohortJob = {}; + + cohortJob.jobName = `HERACLES_COHORT_${cohortDefinitionId}_${this.reportSourceKey()}`; + cohortJob.sourceKey = this.reportSourceKey(); + cohortJob.smallCellCount = 5; + cohortJob.cohortDefinitionIds = []; + cohortJob.cohortDefinitionIds.push(cohortDefinitionId); + cohortJob.analysisIds = analysisIdentifiers; + cohortJob.runHeraclesHeel = runHeraclesHeel; + cohortJob.cohortPeriodOnly = false; + + // set concepts + cohortJob.conditionConceptIds = []; + cohortJob.drugConceptIds = []; + cohortJob.procedureConceptIds = []; + cohortJob.observationConceptIds = []; + cohortJob.measurementConceptIds = []; + + cohortJob.periods = periods; + + cohortJob.rollupUtilizationVisit = rollupUtilizationVisit; + cohortJob.rollupUtilizationDrug = rollupUtilizationDrug; + + this.createReportJobFailed(false); + try { + this.isReportGenerating(true); + const { data } = await cohortDefinitionService.getCohortAnalyses(cohortJob); + jobDetailsService.createJob(data); + } catch (err) { + this.createReportJobFailed(true); + const { status, data } = err; + const createReportJobErrorPackage = { + status, + error: data.payload, + }; + this.createReportJobError(JSON.stringify(createReportJobErrorPackage)); + + // reset button to allow generation attempt + this.generateReportsEnabled(true); + this.generateButtonCaption('Generate'); } + await this.queryHeraclesJob(this.currentCohortDefinition(), this.reportSourceKey()); + this.isReportGenerating(false); + } - generateHealthcareAnalyses () { - const analysisIds = this.utilReportOptions.reports.selectedOptions().reduce((acc, ids) => [...acc, ...ids], []); - this.generateAnalyses({ - descr: 'the Cost and Utilization analyses', - duration: '10-45 minutes', - analysisIdentifiers: analysisIds, - runHeraclesHeel: false, - periods: this.utilReportOptions.periods.selectedOptions(), - ...this.utilReportOptions.rollups.selectedOptions().reduce((acc, current) => { acc[current] = true; return acc }, {}), - }); + generateQuickAnalysis () { + this.generateAnalyses({ + descr: 'minimal analyses set to provide a quick overview of the cohort', + duration: '10 minutes', + analysisIdentifiers: cohortReportingService.getQuickAnalysisIdentifiers(), + runHeraclesHeel: false + }); + } - this.showUtilizationToRunModal(false); - }; + selectHealthcareAnalyses () { + this.showUtilizationToRunModal(true); + } - generateAllAnalyses () { - this.generateAnalyses({ - descr: 'all analyses', - duration: '60-90 minutes', - analysisIdentifiers: cohortReportingService.getAnalysisIdentifiers(), - runHeraclesHeel: true - }); - }; + generateHealthcareAnalyses () { + const analysisIds = this.utilReportOptions.reports.selectedOptions().reduce((acc, ids) => [...acc, ...ids], []); + this.generateAnalyses({ + descr: 'the Cost and Utilization analyses', + duration: '10-45 minutes', + analysisIdentifiers: analysisIds, + runHeraclesHeel: false, + periods: this.utilReportOptions.periods.selectedOptions(), + ...this.utilReportOptions.rollups.selectedOptions().reduce((acc, current) => { acc[current] = true; return acc }, {}), + }); - // dispose subscriptions / cleanup computed observables (non-pureComputeds) - dispose () { - super.dispose(); - this.cohortDefinitionLink.dispose(); - this.cohortDefinitionCaption.dispose(); - this.tabPath.dispose(); - this.sortedConceptSets.dispose(); - this.reportingState.dispose(); - this.showReportNameDropdown.dispose(); - this.reportSourceKeySub.dispose(); - PollService.stop(this.pollId); + this.showUtilizationToRunModal(false); + }; + + generateAllAnalyses () { + this.generateAnalyses({ + descr: 'all analyses', + duration: '60-90 minutes', + analysisIdentifiers: cohortReportingService.getAnalysisIdentifiers(), + runHeraclesHeel: true + }); + }; + + // track subscriptions + trackSub(sub) { + this.subscriptions.push(sub); + } + + // dispose subscriptions / cleanup computed observables (non-pureComputeds) + dispose () { + super.dispose(); + PollService.stop(this.pollId); + } + + getCriteriaIndexComponent (data) { + data = ko.utils.unwrapObservable(data); + if (!data) return; + if (data.hasOwnProperty("ConditionOccurrence")) + return "condition-occurrence-criteria-viewer"; + else if (data.hasOwnProperty("ConditionEra")) + return "condition-era-criteria-viewer"; + else if (data.hasOwnProperty("DrugExposure")) + return "drug-exposure-criteria-viewer"; + else if (data.hasOwnProperty("DrugEra")) + return "drug-era-criteria-viewer"; + else if (data.hasOwnProperty("DoseEra")) + return "dose-era-criteria-viewer"; + else if (data.hasOwnProperty("ProcedureOccurrence")) + return "procedure-occurrence-criteria-viewer"; + else if (data.hasOwnProperty("Observation")) + return "observation-criteria-viewer"; + else if (data.hasOwnProperty("VisitOccurrence")) + return "visit-occurrence-criteria-viewer"; + else if (data.hasOwnProperty("DeviceExposure")) + return "device-exposure-criteria-viewer"; + else if (data.hasOwnProperty("Measurement")) + return "measurement-criteria-viewer"; + else if (data.hasOwnProperty("Specimen")) + return "specimen-criteria-viewer"; + else if (data.hasOwnProperty("ObservationPeriod")) + return "observation-period-criteria-viewer"; + else if (data.hasOwnProperty("PayerPlanPeriod")) + return "payer-plan-period-criteria-viewer"; + else if (data.hasOwnProperty("Death")) + return "death-criteria-viewer"; + else if (data.hasOwnProperty("LocationRegion")) + return "location-region-viewer"; + else + return "unknownCriteriaType"; + }; + + copyExpressionToClipboard () { + this.copyToClipboard('#btnCopyExpressionClipboard', '#copyExpressionToClipboardMessage'); + } + + copyIdentifierListToClipboard () { + this.copyToClipboard('#btnCopyIdentifierListClipboard', '#copyIdentifierListMessage'); + } + + copyIncludedConceptIdentifierListToClipboard () { + this.copyToClipboard('#btnCopyIncludedConceptIdentifierListClipboard', '#copyIncludedConceptIdentifierListMessage'); + } + + copyTextViewToClipboard() { + let columns = [ + { + title: 'Concept Id', + data: 'CONCEPT_ID' + }, + { + title: 'Concept Name', + data: 'CONCEPT_NAME' + }, + { + title: 'Domain', + data: 'DOMAIN_ID' + }, + { + title: 'Vocabulary', + data: 'VOCABULARY_ID' } + ]; + let setsText = ''; + this.sortedConceptSets().forEach((set) => { + setsText += '\n' + set.name() + '\n'; + columns.forEach((c) => { + setsText += c.title + '\t'; + }); + setsText += 'Excluded\tDescendants\tMapped' + '\n'; + set.expression.items().forEach((item) => { + columns.forEach((c) => { + setsText += item.concept[c.data] + '\t'; + }); + setsText += (item.isExcluded() ? 'YES' : 'NO') + '\t'; + setsText += (item.includeDescendants() ? 'YES' : 'NO') + '\t'; + setsText += (item.includeMapped() ? 'YES' : 'NO') + '\n'; + }); + }); + this.copyToClipboard('#btnCopyTextViewClipboard', '#copyTextViewMessage', setsText); + } - getCriteriaIndexComponent (data) { - data = ko.utils.unwrapObservable(data); - if (!data) return; - if (data.hasOwnProperty("ConditionOccurrence")) - return "condition-occurrence-criteria-viewer"; - else if (data.hasOwnProperty("ConditionEra")) - return "condition-era-criteria-viewer"; - else if (data.hasOwnProperty("DrugExposure")) - return "drug-exposure-criteria-viewer"; - else if (data.hasOwnProperty("DrugEra")) - return "drug-era-criteria-viewer"; - else if (data.hasOwnProperty("DoseEra")) - return "dose-era-criteria-viewer"; - else if (data.hasOwnProperty("ProcedureOccurrence")) - return "procedure-occurrence-criteria-viewer"; - else if (data.hasOwnProperty("Observation")) - return "observation-criteria-viewer"; - else if (data.hasOwnProperty("VisitOccurrence")) - return "visit-occurrence-criteria-viewer"; - else if (data.hasOwnProperty("DeviceExposure")) - return "device-exposure-criteria-viewer"; - else if (data.hasOwnProperty("Measurement")) - return "measurement-criteria-viewer"; - else if (data.hasOwnProperty("Specimen")) - return "specimen-criteria-viewer"; - else if (data.hasOwnProperty("ObservationPeriod")) - return "observation-period-criteria-viewer"; - else if (data.hasOwnProperty("PayerPlanPeriod")) - return "payer-plan-period-criteria-viewer"; - else if (data.hasOwnProperty("Death")) - return "death-criteria-viewer"; - else if (data.hasOwnProperty("LocationRegion")) - return "location-region-viewer"; - else - return "unknownCriteriaType"; - }; + copyCohortExpressionJSONToClipboard () { + this.copyToClipboard('#btnCopyExpressionJSONClipboard', '#copyCohortExpressionJSONMessage'); + } - copyExpressionToClipboard () { - this.copyToClipboard('#btnCopyExpressionClipboard', '#copyExpressionToClipboardMessage'); + getExpressionJson() { + if (!this.currentCohortDefinition()) { + return ko.toJSON(null); } + return ko.toJSON(this.currentCohortDefinition().expression(), (key, value) => { + // UseEventEnd is a speical case: always include this key in the result. + if (value === 0 || value || ['UseEventEnd'].indexOf(key) > -1) { + return value; + } else { + return; + } + }, 2); + } - copyIdentifierListToClipboard () { - this.copyToClipboard('#btnCopyIdentifierListClipboard', '#copyIdentifierListMessage'); + setExpressionJson(value) { + this.modifiedJSON = value; + } + + getAuthorship() { + const cohortDef = this.currentCohortDefinition(); + const createdDate = commonUtils.formatDateForAuthorship(cohortDef.createdDate); + const modifiedDate = commonUtils.formatDateForAuthorship(cohortDef.modifiedDate); + return { + createdBy: cohortDef.createdBy() ? cohortDef.createdBy().name : '', + createdDate: createdDate, + modifiedBy: cohortDef.modifiedBy() ? cohortDef.modifiedBy().name : '', + modifiedDate: modifiedDate, } + } - copyIncludedConceptIdentifierListToClipboard () { - this.copyToClipboard('#btnCopyIncludedConceptIdentifierListClipboard', '#copyIncludedConceptIdentifierListMessage'); + async refreshPrintFriendly() { + this.printFriendlyLoading(true); + try { + const printFriendlyHtml = await cohortDefinitionService.getCohortPrintFriendly(ko.toJS(this.currentCohortDefinition().expression())); + this.printFriendlyHtml(printFriendlyHtml.data); + } catch(error) { + console.error("Problem loading print-friendly output.", error); + }finally { + this.printFriendlyLoading(false); } + } - copyTextViewToClipboard() { - let columns = [ - { - title: 'Concept Id', - data: 'CONCEPT_ID' - }, - { - title: 'Concept Name', - data: 'CONCEPT_NAME' - }, - { - title: 'Domain', - data: 'DOMAIN_ID' - }, - { - title: 'Vocabulary', - data: 'VOCABULARY_ID' + // samples methods + clickSampleTab() { + this.tabMode('samples'); + const cohortId = this.currentCohortDefinition().id(); + history.pushState(null, '', `#/cohortdefinition/${cohortId}/samples`); + } + addNewSample() { + this.showSampleCreatingModal(true); + } + + validateSampleForm() { + // if a mandotory field is not yet filled at all, it should be error + if(this.sampleNameError()==undefined) this.sampleNameError(true); + if(this.patientCountError()==undefined) this.patientCountError(true); + if(!this.isAgeRange()) { + // not-madatory field + if(this.firstAgeError()==undefined) this.firstAgeError(false); + if(!this.firstAgeError()&&!this.sampleNameError()&&!this.patientCountError()) { + return true; } - ]; - let setsText = ''; - this.sortedConceptSets().forEach((set) => { - setsText += '\n' + set.name() + '\n'; - columns.forEach((c) => { - setsText += c.title + '\t'; - }); - setsText += 'Excluded\tDescendants\tMapped' + '\n'; - set.expression.items().forEach((item) => { - columns.forEach((c) => { - setsText += item.concept[c.data] + '\t'; - }); - setsText += (item.isExcluded() ? 'YES' : 'NO') + '\t'; - setsText += (item.includeDescendants() ? 'YES' : 'NO') + '\t'; - setsText += (item.includeMapped() ? 'YES' : 'NO') + '\n'; - }); - }); - this.copyToClipboard('#btnCopyTextViewClipboard', '#copyTextViewMessage', setsText); + return false; + } else { + // not madatory field + if(this.isAgeRangeError()==undefined) this.isAgeRangeError(false) + if(!this.isAgeRangeError()&&!this.sampleNameError()&&!this.patientCountError()) { + return true; + } + return false; } + } + + resetSampleForm() { + this.sampleName('') + this.patientCount('') + this.sampleAgeType('lessThan') + this.firstAge(null); + this.secondAge(null); + this.isMaleSample(false); + this.isFeMaleSample(false); + this.isOtherGenderSample(false); + + this.isAgeRangeError(undefined); + this.firstAgeError(undefined); + this.sampleNameError(undefined); + this.patientCountError(undefined); + this.isAgeRange(false); + } - copyCohortExpressionJSONToClipboard () { - this.copyToClipboard('#btnCopyExpressionJSONClipboard', '#copyCohortExpressionJSONMessage'); + createNewSample() { + const allValidated = this.validateSampleForm() + if(!allValidated) return; + // create Sample + const cohortDefinitionId =this.currentCohortDefinition().id(); + const sourceKey=this.sampleSourceKey() + const name = this.sampleName(); + const size = Number(this.patientCount()); + const ageMode = this.sampleAgeType(); + let conceptIds = []; + let otherNonBinary = false; + const selectAllGender = !this.isMaleSample()&&!this.isFeMaleSample()&&!this.isOtherGenderSample() + if(this.isMaleSample()||selectAllGender) { + conceptIds.push(8507); + } + if(this.isFeMaleSample()||selectAllGender) { + conceptIds.push(8532); + } + if(this.isOtherGenderSample()||selectAllGender) { + otherNonBinary = true; } - getExpressionJson() { - if (!this.currentCohortDefinition()) { - return ko.toJSON(null); + const firstAge = Number(this.firstAge()); + const secondAge = Number(this.secondAge()); + let age; + if(this.firstAge()==null&&this.secondAge()==null) { + age = null; + } else { + age = { + value: this.isAgeRange()?null:firstAge, + mode: ageMode, + min:this.isAgeRange()? firstAge { - // UseEventEnd is a speical case: always include this key in the result. - if (value === 0 || value || ['UseEventEnd'].indexOf(key) > -1) { - return value; - } else { - return; - } - }, 2); } - setExpressionJson(value) { - this.modifiedJSON = value; - } + const payload = { + name, + size, + age, + gender: {otherNonBinary, conceptIds} + }; + this.newSampleCreatingLoader(true); + sampleService.createSample(payload, {cohortDefinitionId, sourceKey}) + .then(res => { + if(res.ok) { + const newData= mapSampleListData([res.data]); + this.sampleList.unshift(...newData); + this.showSampleCreatingModal(false); + } + //close pop-up + }) + .catch((error) => { + console.error(error); + alert('Error when creating sample, please try again later'); + }) + .finally(() => { + this.newSampleCreatingLoader(false); + }) + } - getAuthorship() { - const cohortDef = this.currentCohortDefinition(); - const createdDate = commonUtils.formatDateForAuthorship(cohortDef.createdDate); - const modifiedDate = commonUtils.formatDateForAuthorship(cohortDef.modifiedDate); - return { - createdBy: cohortDef.createdBy() ? cohortDef.createdBy().name : '', - createdDate: createdDate, - modifiedBy: cohortDef.modifiedBy() ? cohortDef.modifiedBy().name : '', - modifiedDate: modifiedDate, + getSampleList(cohortId) { + this.isLoadingSampleData(true); + const cohortDefinitionId= cohortId || this.currentCohortDefinition().id(); + // if (cohortDefinitionId==0) return + const sourceKey=this.sampleSourceKey(); + sampleService.getSampleList({cohortDefinitionId, sourceKey}) + .then(res => { + if(res.generationStatus!="COMPLETE") { + this.sampleSourceKey(null); + alert('Cohort should be generated before creating samples'); + return; } - } + const sampleListData = mapSampleListData(res.samples); + this.sampleList(sampleListData); + }) + .catch(error=>{ + console.error(error); + alert('Error when fetching sample list, please try again later'); + }) + .finally(() => { + this.isLoadingSampleData(false) + }) + } - async refreshPrintFriendly() { - this.printFriendlyLoading(true); - try { - const printFriendlyHtml = await cohortDefinitionService.getCohortPrintFriendly(ko.toJS(this.currentCohortDefinition().expression())); - this.printFriendlyHtml(printFriendlyHtml.data); - } catch(error) { - console.error("Problem loading print-friendly output.", error); - }finally { - this.printFriendlyLoading(false); + onSampleListRowClick(d, e) { + // find index of click + const {sampleId} = d; + const rowIndex = this.sampleList().findIndex(el=>el.sampleId == sampleId); + + const cohortDefinitionId= this.currentCohortDefinition().id(); + const sourceKey=this.sampleSourceKey(); + if(e.target.className=='sample-list fa fa-trash') { + // todo: close existing sample + if (this.selectedSampleId() == sampleId) { + this.sampleData([]); + this.selectedSampleId(null); + commonUtils.routeTo(`/cohortdefinition/${cohortDefinitionId}/samples/${sourceKey}/`); } + sampleService.deleteSample({cohortDefinitionId, sourceKey, sampleId}) + .then(res=>{ + if(res.ok) { + this.sampleList.splice(rowIndex, 1); + } + }) + .catch(() => { + alert('Error when deleting sample, please try again later'); + }) + } else if (e.target.className == 'sample-list fa fa-refresh') { + this.sampleDataLoading(false); + this.refreshSample({sampleId, sourceKey, cohortDefinitionId}) + .then(res => { + this.showSampleDataTable(res.elements); + }) + .finally(() => { + this.sampleDataLoading(false); + }); + } else { + this.fetchSampleData({sampleId, sourceKey, cohortDefinitionId}); } + } + + fetchSampleData({sampleId, sourceKey, cohortDefinitionId}) { + this.sampleDataLoading(true) + sampleService.getSample({ cohortDefinitionId, sourceKey, sampleId }) + .then(res=>{ + this.selectedSampleId(sampleId); + this.selectedSampleName(res.name); + this.showSampleDataTable(res.elements); + history.pushState(null, '', `#/cohortdefinition/${cohortDefinitionId}/samples/${sourceKey}/${sampleId}`); + }) + .catch(error => { + console.error(error); + alert('Error when fetching sample data, please try again later'); + }) + .finally(() => { + this.sampleDataLoading(false); + }) + } + + refreshSample({sampleId, sourceKey, cohortDefinitionId}) { + sampleService.refreshSample({cohortDefinitionId, sourceKey, sampleId}) + .then(res=>{ + if(res.ok) { + console.log("todo, refresh result table with new sample"); + } + }) + .catch(() => { + alert('Error when refreshing sample, please try again later'); + }) + } + + onSampleDataClick(d) { + const sampleId = this.selectedSampleId(); + const cohortDefinitionId= this.currentCohortDefinition().id(); + const sourceKey=this.sampleSourceKey(); + window.open(`#/profiles/${sourceKey}/${d.personId}/${cohortDefinitionId}/${sampleId}`); + } + + showSampleDataTable(sample) { + const transformedSampleData = sample.map(el => ({ + personId: el.personId, + gender: gender(el.genderConceptId), + ageIndex: el.age + })); + + this.sampleData(transformedSampleData); + } } return commonUtils.build('cohort-definition-manager', CohortDefinitionManager, view); diff --git a/js/pages/cohort-definitions/routes.js b/js/pages/cohort-definitions/routes.js index 28bf6fbf2..c46cb4755 100644 --- a/js/pages/cohort-definitions/routes.js +++ b/js/pages/cohort-definitions/routes.js @@ -14,7 +14,77 @@ define( router.setCurrentView('cohort-definitions'); }); }), - '/cohortdefinition/:cohortDefinitionId/conceptsets/:conceptSetId/:mode': new AuthorizedRoute((cohortDefinitionId, conceptSetId, mode) => { + + '/cohortdefinition/:cohortDefinitionId/samples': new AuthorizedRoute( + cohortDefinitionId => { + require([ + 'components/conceptset/ConceptSetStore', + 'components/cohortbuilder/CohortDefinition', + 'components/atlas.cohort-editor', + './cohort-definitions', + './cohort-definition-manager', + 'components/cohort-definition-browser', + 'conceptset-editor', + './components/reporting/cost-utilization/report-manager', + 'components/explore-cohort', + ], function() { + // not re-render component if it was rendered already + router.setCurrentView('cohort-definition-manager', { + cohortDefinitionId, + mode: 'samples', + }) + sharedState.CohortDefinition.mode('samples') + }) + } + ), + + '/cohortdefinition/:cohortDefinitionId/samples/:sourceKey': new AuthorizedRoute( + (cohortDefinitionId, sourceKey) => { + require([ + 'components/cohortbuilder/CohortDefinition', + 'components/atlas.cohort-editor', + './cohort-definitions', + './cohort-definition-manager', + 'components/cohort-definition-browser', + 'conceptset-editor', + './components/reporting/cost-utilization/report-manager', + 'components/explore-cohort', + ], function() { + router.setCurrentView('cohort-definition-manager', { + cohortDefinitionId, + sourceKey, + mode: 'samples', + }) + sharedState.CohortDefinition.mode('samples') + }) + } + ), + + '/cohortdefinition/:cohortDefinitionId/samples/:sourceKey/:sampleId': new AuthorizedRoute( + (cohortDefinitionId, sourceKey, sampleId) => { + require([ + 'components/cohortbuilder/CohortDefinition', + 'components/atlas.cohort-editor', + './cohort-definitions', + './cohort-definition-manager', + 'components/cohort-definition-browser', + 'conceptset-editor', + './components/reporting/cost-utilization/report-manager', + 'components/explore-cohort', + ], function() { + router.setCurrentView('cohort-definition-manager', { + cohortDefinitionId, + sampleId, + sourceKey, + mode: 'samples', + }) + sharedState.CohortDefinition.mode('samples') + }) + } + ), + + '/cohortdefinition/:cohortDefinitionId/conceptsets/:conceptSetId/:mode': new AuthorizedRoute( + (cohortDefinitionId, conceptSetId, mode) => { require([ 'components/conceptset/ConceptSetStore', 'components/cohortbuilder/CohortDefinition', @@ -24,10 +94,10 @@ define( 'components/cohort-definition-browser', 'conceptset-editor', './components/reporting/cost-utilization/report-manager', - 'explore-cohort', - ], function (ConceptSetStore) { - sharedState.CohortDefinition.mode('conceptsets'); - sharedState.activeConceptSet(ConceptSetStore.cohortDefinition()); + 'components/explore-cohort', + ], function(ConceptSetStore) { + sharedState.CohortDefinition.mode('conceptsets') + sharedState.activeConceptSet(ConceptSetStore.cohortDefinition()) router.setCurrentView('cohort-definition-manager', { cohortDefinitionId, mode: 'conceptsets', @@ -44,9 +114,9 @@ define( 'components/cohort-definition-browser', 'conceptset-editor', './components/reporting/cost-utilization/report-manager', - 'explore-cohort', + 'components/explore-cohort', 'components/conceptset/concept-modal', - ], function () { + ], function() { // Determine the view to show on the cohort manager screen based on the path path = path.split("/"); let view = 'definition'; diff --git a/js/services/Sample.js b/js/services/Sample.js new file mode 100644 index 000000000..f29623c7a --- /dev/null +++ b/js/services/Sample.js @@ -0,0 +1,53 @@ +define(['services/http', 'appConfig'], function(httpService, config) { + function createSample(payload, { cohortDefinitionId, sourceKey }) { + return httpService + .doPost( + `${config.webAPIRoot}cohortsample/${cohortDefinitionId}/${sourceKey}`, + { + ...payload, + } + ) + .catch(error => { + console.log(error) + }) + } + + function getSampleList({ cohortDefinitionId, sourceKey }) { + return httpService + .doGet( + `${config.webAPIRoot}cohortsample/${cohortDefinitionId}/${sourceKey}` + ) + .then(res => res.data) + } + + function getSample({ cohortDefinitionId, sourceKey, sampleId }) { + return httpService + .doGet( + `${config.webAPIRoot}cohortsample/${cohortDefinitionId}/${sourceKey}/${sampleId}` + ) + .then(res => res.data) + } + + function refreshSample({ cohortDefinitionId, sourceKey, sampleId }) { + return httpService + .doPost( + `${config.webAPIRoot}cohortsample/${cohortDefinitionId}/${sourceKey}/${sampleId}/refresh`, + null + ) + .then(res => res.data) + } + + function deleteSample({ cohortDefinitionId, sourceKey, sampleId }) { + return httpService.doDelete( + `${config.webAPIRoot}cohortsample/${cohortDefinitionId}/${sourceKey}/${sampleId}` + ) + } + + return { + createSample, + getSampleList, + getSample, + deleteSample, + refreshSample + } +}) diff --git a/js/settings.js b/js/settings.js index fa7b24e84..6f2a71bbc 100644 --- a/js/settings.js +++ b/js/settings.js @@ -171,7 +171,6 @@ const settingsObject = { "conceptset-modal": "components/conceptsetmodal/conceptSetSaveModal", "user-bar": "components/userbar/user-bar", "faceted-datatable": "components/faceted-datatable", - "explore-cohort": "components/explore-cohort", "r-manager": "components/r-manager", "home": "components/home", "welcome": "components/welcome", From 326577c8c22b71cd0accfa27723057e8c27f65a4 Mon Sep 17 00:00:00 2001 From: Chris Knoll Date: Mon, 9 Nov 2020 11:09:25 -0500 Subject: [PATCH 2/5] Fix refresh UI redraw. --- js/pages/cohort-definitions/cohort-definition-manager.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/js/pages/cohort-definitions/cohort-definition-manager.js b/js/pages/cohort-definitions/cohort-definition-manager.js index 8b4c972d2..8abfe4b3a 100644 --- a/js/pages/cohort-definitions/cohort-definition-manager.js +++ b/js/pages/cohort-definitions/cohort-definition-manager.js @@ -1775,7 +1775,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', alert('Error when deleting sample, please try again later'); }) } else if (e.target.className == 'sample-list fa fa-refresh') { - this.sampleDataLoading(false); + this.sampleDataLoading(true); this.refreshSample({sampleId, sourceKey, cohortDefinitionId}) .then(res => { this.showSampleDataTable(res.elements); @@ -1807,12 +1807,7 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html', } refreshSample({sampleId, sourceKey, cohortDefinitionId}) { - sampleService.refreshSample({cohortDefinitionId, sourceKey, sampleId}) - .then(res=>{ - if(res.ok) { - console.log("todo, refresh result table with new sample"); - } - }) + return sampleService.refreshSample({cohortDefinitionId, sourceKey, sampleId}) .catch(() => { alert('Error when refreshing sample, please try again later'); }) From b615abc715248a4dd2909a78cc77c66933383079 Mon Sep 17 00:00:00 2001 From: Chris Knoll Date: Wed, 11 Nov 2020 16:13:00 -0500 Subject: [PATCH 3/5] Simplified sample form validation. --- .../cohort-definition-manager.html | 35 +++-- .../cohort-definition-manager.js | 130 ++---------------- 2 files changed, 27 insertions(+), 138 deletions(-) diff --git a/js/pages/cohort-definitions/cohort-definition-manager.html b/js/pages/cohort-definitions/cohort-definition-manager.html index 815e7d1d4..8e77ed440 100644 --- a/js/pages/cohort-definitions/cohort-definition-manager.html +++ b/js/pages/cohort-definitions/cohort-definition-manager.html @@ -449,27 +449,26 @@

    Appendix 1: Concept Set Definitions

    isFeMaleSample: isFeMaleSample, isOtherGenderSample: isOtherGenderSample, createNewSample: createNewSample, - newSampleCreatingLoader: newSampleCreatingLoader + newSampleCreatingLoader: newSampleCreatingLoader, + isSampleFormValid: isSampleFormValid } - id='sampleCreatingModal' - }" - > + }">
    *Mandatory fields - Sample name cannot be empty + Sample name cannot be empty
    -
    - - - Number of patients must be a positive integer +
    + + + Number of patients must be a positive integer
    @@ -530,7 +527,7 @@

    Appendix 1: Concept Set Definitions

    - +