diff --git a/index.html b/index.html index 2f8659dab..40850a3c9 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@ + diff --git a/js/app.js b/js/app.js index 062513c42..23311fdd1 100644 --- a/js/app.js +++ b/js/app.js @@ -244,7 +244,8 @@ define(['jquery', 'knockout', 'ohdsi.util', 'appConfig', 'webapi/AuthAPI', 'atla }); }, '/estimation/:cohortComparisonId:': function (cohortComparisonId) { - require(['cohort-comparison-manager', 'cohort-definition-browser', 'components/atlas.cohort-editor', 'cohort-comparison-print-friendly', 'cohort-comparison-r-code', 'cohort-comparison-multi-r-code'], function () { + require(['cohort-comparison-manager', 'cohort-definition-browser', 'cohort-comparison-spec-editor', 'cohort-comparison-results', + 'components/atlas.cohort-editor', 'cohort-comparison-print-friendly', 'cohort-comparison-r-code', 'cohort-comparison-multi-r-code'], function () { self.currentCohortComparisonId(+cohortComparisonId); self.componentParams = { currentCohortComparisonId: self.currentCohortComparisonId, diff --git a/js/components/cohort-comparison-browser.js b/js/components/cohort-comparison-browser.js index 4c5b16c02..cdb65c884 100644 --- a/js/components/cohort-comparison-browser.js +++ b/js/components/cohort-comparison-browser.js @@ -1,4 +1,4 @@ -define(['knockout', 'text!./cohort-comparison-browser.html', 'appConfig','cohortcomparison/ComparativeCohortAnalysis','faceted-datatable'], function (ko, view, config) { +define(['knockout', 'text!./cohort-comparison-browser.html', 'appConfig', 'webapi/ComparativeCohortAnalysisAPI', 'cohortcomparison/ComparativeCohortAnalysis', 'faceted-datatable'], function (ko, view, config, comparativeCohortAnalysisAPI) { function cohortComparisonBrowser(params) { var self = this; self.reference = ko.observableArray(); @@ -11,9 +11,9 @@ define(['knockout', 'text!./cohort-comparison-browser.html', 'appConfig','cohort 'caption': 'Last Modified', 'binding': function (o) { var daysSinceModification = (new Date().getTime() - new Date(o.modified).getTime()) / 1000 / 60 / 60 / 24; - if (daysSinceModification < .01) { + if (daysSinceModification < .01) { return 'Just Now'; - } else if (daysSinceModification < 1) { + } else if (daysSinceModification < 1) { return 'Within 24 Hours'; } else if (daysSinceModification < 7) { return 'This Week'; @@ -38,9 +38,9 @@ define(['knockout', 'text!./cohort-comparison-browser.html', 'appConfig','cohort }, { title: 'Name', - data: d => { - return '' + d.name + ''; - }, + data: d => { + return '' + d.name + ''; + }, }, { title: 'Created', @@ -56,19 +56,14 @@ define(['knockout', 'text!./cohort-comparison-browser.html', 'appConfig','cohort } ]; - self.newCohortComparison = function() { - document.location = '#/estimation/0' ; + self.newCohortComparison = function () { + document.location = '#/estimation/0'; } - + self.loading(true); - - $.ajax({ - url: config.api.url + 'comparativecohortanalysis', - method: 'GET', - success: function (d) { - self.loading(false); - self.reference(d); - } + comparativeCohortAnalysisAPI.getComparativeCohortAnalysisList().then(function (d) { + self.loading(false); + self.reference(d); }); } @@ -79,4 +74,4 @@ define(['knockout', 'text!./cohort-comparison-browser.html', 'appConfig','cohort ko.components.register('cohort-comparison-browser', component); return component; -}); \ No newline at end of file +}); diff --git a/js/components/cohort-comparison-manager.html b/js/components/cohort-comparison-manager.html index c6a86564e..2ab409d27 100644 --- a/js/components/cohort-comparison-manager.html +++ b/js/components/cohort-comparison-manager.html @@ -1,660 +1,78 @@
- Loading... + Loading...
-
- Population Level Effect Estimation -
- -
- -
- - - -
-
- - - -
-
-
- -
-
error loading selected results
-
- -
-
-
Available Data Sources
-
-
-
-
- - - - - - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- -
-
loading results
-
- -
-
- -
- -
-
- -
-
- -
-
- - - -
-
-
-
-
- The result of the analysis when comparing the rate of the outcome defined by the cohort () between the treatment cohort () and the comparator cohort () is . -
-
-
-
-
- - -
-
-
- - - - - - - - - - - - - - - - - -
DescriptionTreated PersonsComparator PersonsTreated ExposuresComparator Exposures
-
-
-
-
- -
-
-
-
- -
-
-
-
- -
- - -
-
-
-
-
-
-
-
- -
- - - - - -
-
The target cohort specified (#) was deleted. Please select a different cohort.
-
-
- -
- - - - - -
-
The comparator cohort specified (#) was deleted. Please select a different cohort.
-
-
- -
- - - - - -
-
The outcome cohort specified (#) was deleted. Please select a different cohort.
-
-
- -
- -
-
-
- -
- days from cohort start date -
-
-
- -
- days from - -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
    -
  • Demographics
  • -
      -
    • - Gender
    • -
    • - Age group (5-year bands)
    • -
    • - Index year
    • -
    • - Index month
    • -
    • - Race
    • -
    • - Ethnicity
    • -
    -
-
    -
  • Conditions
  • -
      -
    • - In prior 30d
    • -
    • - In prior 365d
    • -
    • - In prior 180d within inpatient setting
    • -
    • - All time prior
    • -
    • - Overlapping index date
    • -
    -
-
    -
  • Condition aggregation
  • -
      -
    • - SNOMED
    • -
    • - MedDRA
    • -
    -
-
    -
  • Drugs
  • -
      -
    • - In prior 30d
    • -
    • - In prior 365d
    • -
    • - All time prior
    • -
    • - Overlapping index date
    • -
    -
-
    -
  • Drug aggregation
  • -
      -
    • - Clinical Drug
    • -
    • - Ingredient
    • -
    • - ATC Class
    • -
    -
-
    -
  • Procedures
  • -
      -
    • - In prior 30d
    • -
    • - In prior 365d
    • -
    -
-
    -
  • Measurement
  • -
      -
    • - Existence in prior 30d
    • -
    • - Existence in prior 365d
    • -
    • - Count in prior 365d
    • -
    • - Has latest prior numeric value below normal range
    • -
    • - Has latest prior numeric value above normal range
    • -
    -
-
    -
  • Risk scores
  • -
      -
    • - Charlson
    • -
    • - CHADS2
    • -
    • - DCSI
    • -
    -
-
    -
  • - Concept counts (count of distinct conditions/procedures/visits in history)
  • -
-
    -
  • Interaction terms
  • -
      -
    • - By index year
    • -
    • - By index month
    • -
    -
-
-
- -
- - - - - -
-
The concept set specified (#) was deleted. Please select a different concept set.
-
-
- -
- - - - - -
-
The concept set specified (#) was deleted. Please select a different concept set.
-
-
- -
- - Trim Fraction (1-100%): -
-
-
- -
- - How many comparator patients do you want to select for each target patient (within a defined caliper)? - How many strata do you want to use? -
-
-
- -
- -
- -
-
-
-
- -
    -
  • Demographics
  • -
      -
    • - Gender
    • -
    • - Age group (5-year bands)
    • -
    • - Index year
    • -
    • - Index month
    • -
    • - Race
    • -
    • - Ethnicity
    • -
    -
-
    -
  • Conditions
  • -
      -
    • - In prior 30d
    • -
    • - In prior 365d
    • -
    • - In prior 180d within inpatient setting
    • -
    • - All time prior
    • -
    • - Overlapping index date
    • -
    -
-
    -
  • Condition aggregation
  • -
      -
    • - SNOMED
    • -
    • - MedDRA
    • -
    -
-
    -
  • Drugs
  • -
      -
    • - In prior 30d
    • -
    • - In prior 365d
    • -
    • - All time prior
    • -
    • - Overlapping index date
    • -
    -
-
    -
  • Drug aggregation
  • -
      -
    • - Clinical Drug
    • -
    • - Ingredient
    • -
    • - ATC Class
    • -
    -
-
    -
  • Procedures
  • -
      -
    • - In prior 30d
    • -
    • - In prior 365d
    • -
    -
-
    -
  • Measurement
  • -
      -
    • - Existence in prior 30d
    • -
    • - Existence in prior 365d
    • -
    • - Count in prior 365d
    • -
    • - Has latest prior numeric value below normal range
    • -
    • - Has latest prior numeric value above normal range
    • -
    -
-
    -
  • Risk scores
  • -
      -
    • - Charlson
    • -
    • - CHADS2
    • -
    • - DCSI
    • -
    -
-
    -
  • - Concept counts (count of distinct conditions/procedures/visits in history)
  • -
-
    -
  • Interaction terms
  • -
      -
    • - By index year
    • -
    • - By index month
    • -
    -
-
-
- -
- - - - - -
-
The concept set specified (#) was deleted. Please select a different concept set.
-
-
- -
- - - - - -
-
The concept set specified (#) was deleted. Please select a different concept set.
-
-
-
- -
- - - - - -
-
The concept set specified (#) was deleted. Please select a different concept set.
-
-
-
-
-
-
-
- -
-
- -
-
-
-
-

Multi-Analysis Approach

-    - -
-
-
-
- -
- -
-
-
-
-
- -
-
-
-
-
- - - - - +
+ +
+
+ +
+
+ + + diff --git a/js/components/cohort-comparison-manager.js b/js/components/cohort-comparison-manager.js index 5ab8b722c..4f988f357 100644 --- a/js/components/cohort-comparison-manager.js +++ b/js/components/cohort-comparison-manager.js @@ -1,16 +1,9 @@ define(['jquery', 'knockout', 'text!./cohort-comparison-manager.html', 'lodash', 'clipboard', 'webapi/CohortDefinitionAPI', 'appConfig', 'ohdsi.util', - 'cohortcomparison/ComparativeCohortAnalysis', 'cohortbuilder/options', - 'cohortbuilder/CohortDefinition', 'vocabularyprovider', - 'conceptsetbuilder/InputTypes/ConceptSet', - 'nvd3', 'databindings/d3ChartBinding', - 'css!./styles/nv.d3.min.css'], + 'cohortcomparison/ComparativeCohortAnalysis', 'webapi/ComparativeCohortAnalysisAPI'], function ($, ko, view, _, clipboard, cohortDefinitionAPI, config, ohdsiUtil, - ComparativeCohortAnalysis, options, CohortDefinition, vocabularyAPI, - ConceptSet) { + ComparativeCohortAnalysis, comparativeCohortAnalysisAPI) { function cohortComparisonManager(params) { - - var DEBUG = true; var self = this; self.cohortComparisonId = params.currentCohortComparisonId; self.cohortComparison = params.currentCohortComparison; @@ -18,512 +11,20 @@ define(['jquery', 'knockout', 'text!./cohort-comparison-manager.html', 'lodash', self.config = config; self.loading = ko.observable(true); - self.loadingExecution = ko.observable(false); - self.loadingExecutionFailure = ko.observable(false); - self.covariates = ko.observableArray(); - self.currentExecutionId = ko.observable(); - self.currentExecutionAuc = ko.observable(); - self.poppropdist = ko.observableArray(); - self.popprefdist = ko.observableArray(); - self.psmodeldist = ko.observableArray(); - self.attrition = ko.observableArray(); - self.sources = ko.observableArray(); - self.currentExecutionSourceName = ko.observable(); - self.sourceHistoryDisplay = {}; - self.sourceProcessingStatus = {}; - self.sourceExecutions = {}; - self.options = options; self.expressionMode = ko.observable('print'); - self.om = ko.observable(); - self.modifiedJSON = ""; - self.importJSON = ko.observable(); - self.expressionJSON = ko.pureComputed({ - read: function () { - return ko.toJSON(self.cohortComparison(), function (key, value) { - if (value === 0 || value) { - delete value.analysisId; - delete value.name; - return value; - } else { - return - } - }, 2); - }, - write: function (value) { - self.modifiedJSON = value; + self.tabMode = ko.observable('specification'); + self.isSaving = ko.observable(false); + + self.saveText = ko.pureComputed(function() { + if (self.isSaving()) { + return " Saving"; + } else { + return "Save"; } }); - // for new scatter balance chart - /* - self.chartObj = ko.observable(); - self.domElement = ko.observable(); - self.chartData = ko.observableArray(self.chartData && self.chartData() || []); - self.sharedCrossfilter = ko.observable(new ohdsiUtil.SharedCrossfilter([])); - window.scf = self.sharedCrossfilter(); - self.chartData.subscribe(function(recs) { - self.sharedCrossfilter().replaceData(recs); - }); - $(self.sharedCrossfilter()).on('filterEvt', function(evt, stuff) { - console.log("filter in sharedCrossfilter", stuff); - }); - $(self.sharedCrossfilter()).on('newData', function(evt, stuff) { - console.log("new data in sharedCrossfilter; shouldn't happen much", stuff); - }); - self.chartResolution = ko.observable(); // junk - self.jqEventSpace = params.jqEventSpace || {}; - self.fields = ko.observable([]); - self.chartObj.subscribe(function(chart) { - }); - self.ready = ko.computed(function() { - return self.chartObj() && - self.chartData().length && - self.domElement() && - self.pillMode() === 'balance'; - }); - self.chartOptions = chartOptions(); - self.ready.subscribe(function(ready) { - if (ready) { - initializeBalanceComponents(); - } - }); - var initializeBalanceComponents = _.once(function() { - var opts = _.merge(self.chartObj().defaultOptions, self.chartOptions); - var fields = _.map(opts, - (opt, name) => { - if (opt.isField) { - if (!(opt instanceof ohdsiUtil.Field)) { - opt = new ohdsiUtil.Field(name, opt, opts); - } - opt.bindParams({data:self.chartData()}, false); - } - return opts[name] = opt; - }); - self.fields(fields); - self.chartObj().chartSetup(self.domElement(), 460, 150, opts); - self.chartObj().render(self.chartData(), self.domElement(), 460, 150, opts); - self.sharedCrossfilter().dimField('xy', opts.xy); - self.pillMode.subscribe(function(pillMode) { - if (pillMode === 'balance') - self.chartObj().render(self.chartData(), self.domElement(), 460, 150, opts); - }); - //self.chartOptions.xy.accessor = self.chartOptions.xy.accessors.value; - }); - $(self.jqEventSpace).on('brush', function(evt, {empty, x1,x2,y1,y2} = {}) { - //console.log('brush event', arguments); - var xyFilt; - if (empty) { - xyFilt = null; - util.deleteState('filters.brush'); - } else { - xyFilt = ([x,y] = [], i) => { - return x >= x1 && - x <= x2 && - y >= y1 && - y <= y2; - }; - util.setState('filters.brush', {x1,x2,y1,y2}); - } - self.sharedCrossfilter().filter('xy', xyFilt, - {source:'brush', x1, x2, y1, y2, empty}); - }); - $(self.sharedCrossfilter()).on('filterEvt', - function(evt, {dimField, source, x1, x2, y1, y2, empty, waitForMore} = {}) { - if (source === 'brush') { - // scatter has already zoomed. - if (empty) { - self.chartObj().cp.x.setZoomScale(); - self.chartObj().cp.y.setZoomScale(); - } else { - self.chartObj().cp.x.setZoomScale([x1,x2]); - self.chartObj().cp.y.setZoomScale([y1,y2]); - } - self.chartObj().updateData(self.sharedCrossfilter().dimRecs('xy')); - } else { - if (!waitForMore || waitForMore === 'done') { - self.chartObj().updateData(self.sharedCrossfilter().filteredRecs()); - } - } - }); - */ - /* - function dataSetup(raw) { - var points = raw.map(d => ({ - x: Math.abs(d.beforeMatchingStdDiff), - y: Math.abs(d.afterMatchingStdDiff), - tooltip: d.covariateName - })); - return points; - } - */ - - // end for balance chart - - var initSources = config.api.sources.filter(s => s.hasCDM); - for (var i = 0; i < initSources.length; i++) { - self.sourceHistoryDisplay[initSources[i].sourceKey] = ko.observable(false); - self.sourceProcessingStatus[initSources[i].sourceKey] = ko.observable(false); - } - - self.sources(initSources); - - self.loadExecutions = function () { - // reset before load - $.each(self.sources(), function (i, s) { - if (!self.sourceExecutions[s.sourceKey]) { - self.sourceExecutions[s.sourceKey] = ko.observableArray(); - } else { - self.sourceExecutions[s.sourceKey].removeAll(); - } - }); - - ohdsiUtil.cachedAjax({ - url: config.api.url + 'comparativecohortanalysis/' + self.cohortComparisonId() + '/executions', - method: 'GET', - contentType: 'application/json', - success: function (response) { - - response = response.sort(function (a, b) { - return a.executed - b.executed; - }); - - $.each(response, function (i, d) { - var sourceKey = self.sources().filter(s => s.sourceId == d.sourceId)[0].sourceKey; - var executedTimestamp = new Date(d.executed); - d.executedCaption = executedTimestamp.toLocaleDateString() + ' ' + executedTimestamp.toLocaleTimeString(); - - var h = Math.floor(((d.duration % 31536000) % 86400) / 3600); - var m = Math.floor((((d.duration % 31536000) % 86400) % 3600) / 60); - var s = (((d.duration % 31536000) % 86400) % 3600) % 60; - - d.durationCaption = '' - if (h > 0) d.durationCaption += h + 'h '; - if (m > 0) d.durationCaption += m + 'm '; - if (s > 0) d.durationCaption += s + 's '; - - if (h == 0 && m == 0 && s == 0) { - d.durationCaption = 'n/a'; - } - - // this will ensure that if the last execution is still running that we don't allow additional executions to begin - if (d.executionStatus != 'COMPLETED') { - self.sourceProcessingStatus[sourceKey](true); - } else { - self.sourceProcessingStatus[sourceKey](false); - } - - self.sourceExecutions[sourceKey].push(d); - }); - } - }); - } - - self.loadExecutions(); - - self.toggleHistoryDisplay = function (sourceKey) { - self.sourceHistoryDisplay[sourceKey](!self.sourceHistoryDisplay[sourceKey]()); - } - - self.covariateColumns = [ - { - title: 'Id', - data: 'id' - }, - { - title: 'Name', - data: 'name' - }, - { - title: 'Coefficient', - data: function (d) { - return d3.round(d.value, 2); - } - }, - { - title: '|Coefficient|', - data: function (d) { - return d3.round(Math.abs(d.value), 2); - } - } - ]; - - self.covariateOptions = { - lengthMenu: [[10, -1], ['10', 'All']], - Facets: [ - { - 'caption': 'Value', - 'binding': function (o) { - if (o.value > 2) { - return '> 2'; - } else if (o.value < -2) { - return '< -2'; - } else { - return 'Other'; - } - } - }, - { - 'caption': 'Analysis', - 'binding': function (o) { - return o.name.split(/[:,=]/)[0]; - } - } - ] - }; - - var defaultTab = 'specification'; - self.tabMode = ko.observable(util.getState('cohortCompTab') || defaultTab); - self.tabMode.subscribe(function (tab) { - if (util.getState('cohortCompTab') === tab) - return; - if (!util.hasState('cohortCompTab') && tab === defaultTab) - return; - util.setState('cohortCompTab', tab); - }); - util.onStateChange('cohortCompTab', function (evt, { - val - } = {}) { - self.tabMode(val || defaultTab); - }); - - self.resultsMode = ko.observable('sources'); - self.pillMode = ko.observable('covariates'); - - self.pillMode.subscribe(function (d) { - window.setTimeout(function (d) { - window.dispatchEvent(new Event('resize')); - }, 1); - }) - - self.covariateSelected = function (d) { - console.log(d); - } - - self.closeExecution = function () { - self.resultsMode('sources'); - } - - self.viewLastExecution = function (source) { - var executionCount = self.sourceExecutions[source.sourceKey]().length - 1; - for (var e = executionCount; e >= 0; e--) { - var execution = self.sourceExecutions[source.sourceKey]()[e]; - if (execution.executionStatus == 'COMPLETED') { - self.executionSelected(execution); - break; - } - } - } - - self.executionSelected = function (d) { - self.loadingExecutionFailure(false); - self.resultsMode('execution'); - self.loadingExecution(true); - - var sourceName = self.sources().filter(s => s.sourceId == d.sourceId)[0].sourceName; - self.currentExecutionSourceName(sourceName); - - var p1 = ohdsiUtil.cachedAjax({ - url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/psmodel', - method: 'GET', - contentType: 'application/json', - success: function (response) { - self.currentExecutionId(d.executionId); - self.currentExecutionAuc(response.auc); - self.covariates(response.covariates); - } - }); - - var p2 = ohdsiUtil.cachedAjax({ - url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/attrition', - method: 'GET', - contentType: 'application/json', - success: function (response) { - self.attrition(response); - } - }); - - var p3 = ohdsiUtil.cachedAjax({ - url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/balance', - method: 'GET', - contentType: 'application/json', - success: function (response) { - - // self.chartData(response); - - nv.addGraph(function () { - var points = response.map(d => ({ - x: Math.abs(d.beforeMatchingStdDiff), - y: Math.abs(d.afterMatchingStdDiff), - tooltip: d.covariateName - })); - var data = [{ - key: 'Covariates', - values: points - }]; - - var balanceChart = nv.models.scatterChart() - .showDistX(true) - .showDistY(true) - .color( - d3.scaleOrdinal() - .range(d3.schemeCategory10) - ); - - balanceChart.tooltip.contentGenerator(function (d) { - return '
' + d.point.tooltip + '
Before Matching: ' + d.point.x + '
After Matching: ' + d.point.y + '
'; - }); - - //Axis settings - balanceChart.xAxis.tickFormat(d3.format('.02f')); - balanceChart.yAxis.tickFormat(d3.format('.02f')); - - d3.select('#balanceChart svg') - .datum(data) - .call(balanceChart); - - nv.utils.windowResize(balanceChart.update); - - return balanceChart; - }); - } - }); - - var p4 = ohdsiUtil.cachedAjax({ - url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/poppropdist', - method: 'GET', - contentType: 'application/json', - success: function (response) { - self.poppropdist(response); - - var data = [ - { - values: response.map(d => ({ - 'x': d.score, - 'y': d.comparator - })).sort(function (a, b) { - return a.x - b.x - }), - key: 'Comparator', - color: '#000088', - area: true - }, - { - values: response.map(d => ({ - 'x': d.score, - 'y': d.treatment - })).sort(function (a, b) { - return a.x - b.x - }), - key: 'Treatment', - color: '#880000', - area: true - } - ]; - - nv.addGraph(function () { - var matchedChart = nv.models.lineChart() - .useInteractiveGuideline(true) - .interpolate("basis"); - - matchedChart.duration(0); - - d3.select("#popdistChart svg") - .datum(data) - .call(matchedChart); - - nv.utils.windowResize(matchedChart.update); - return matchedChart; - }); - } - }); - - var p5 = ohdsiUtil.cachedAjax({ - url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/psmodelpropscore', - method: 'GET', - contentType: 'application/json', - success: function (response) { - var data = [ - { - values: response.map(d => ({ - 'x': d.score, - 'y': d.comparator - })).sort(function (a, b) { - return a.x - b.x - }), - key: 'Comparator', - color: '#000088', - area: true - }, - { - values: response.map(d => ({ - 'x': d.score, - 'y': d.treatment - })).sort(function (a, b) { - return a.x - b.x - }), - key: 'Treatment', - color: '#880000', - area: true - } - ]; - - - nv.addGraph(function () { - var modelChart = nv.models.lineChart() - .useInteractiveGuideline(true) - .interpolate("basis") - .showYAxis(false); - - modelChart.tooltip.enabled(false); - - modelChart.duration(0); - - d3.select("#psmodeldistChart svg") - .datum(data) - .call(modelChart); - - nv.utils.windowResize(modelChart.update); - return modelChart; - }); - } - }); - - var p6 = ohdsiUtil.cachedAjax({ - url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/om', - method: 'GET', - contentType: 'application/json', - success: function (response) { - response.forEach(function (r) { - r.caption = r.estimate + ' (' + d3.round(r.lower95, 2) + '-' + d3.round(r.upper95, 2) + ')'; - }); - self.om(response); - } - }); - - - Promise.all([p1, p2, p3, p4, p5]) - .then(results => { - self.loadingExecution(false); - }) - .catch(error => { - self.loadingExecution(false); - self.resultsMode('sources'); - self.loadingExecutionFailure(true); - }); - } - - self.cohortSelected = function (id) { - $('#modalCohortDefinition').modal('hide'); - cohortDefinitionAPI.getCohortDefinition(id).then(function (cohortDefinition) { - self.targetId(cohortDefinition.id); - self.targetCaption(cohortDefinition.name); - cohortDefinition.expression = JSON.parse(cohortDefinition.expression); - self.targetCohortDefinition(new CohortDefinition(cohortDefinition)); - }); - } - self.canSave = ko.pureComputed(function () { - return (self.cohortComparison().name() && self.cohortComparison().comparatorId() && self.cohortComparison().comparatorId() > 0 && self.cohortComparison().treatmentId() && self.cohortComparison().treatmentId() > 0 && self.cohortComparison().outcomeId() && self.cohortComparison().outcomeId() > 0 && self.cohortComparison().modelType && self.cohortComparison().modelType() > 0 && self.cohortComparisonDirtyFlag() && self.cohortComparisonDirtyFlag().isDirty()); + return (self.cohortComparison().name() && self.cohortComparison().targetComparator().comparatorId() && self.cohortComparison().targetComparator().comparatorId() > 0 && self.cohortComparison().targetComparator().targetId() && self.cohortComparison().targetComparator().targetId() > 0 && self.cohortComparison().outcome().outcomeId() && self.cohortComparison().outcome().outcomeId() > 0 && self.cohortComparison().analysis().modelType && self.cohortComparison().analysis().modelType() > 0 && self.cohortComparisonDirtyFlag() && self.cohortComparisonDirtyFlag().isDirty()); }); self.canDelete = ko.pureComputed(function () { @@ -534,174 +35,33 @@ define(['jquery', 'knockout', 'text!./cohort-comparison-manager.html', 'lodash', if (!confirm("Delete estimation specification? Warning: deletion can not be undone!")) return; - $.ajax({ - url: config.api.url + 'comparativecohortanalysis/' + self.cohortComparisonId(), - method: 'DELETE', - error: function (error) { - console.log("Error: " + error); - authApi.handleAccessDenied(error); - }, - success: function (data) { - document.location = "#/estimation" - } + comparativeCohortAnalysisAPI.deleteAnalysis(self.cohortComparisonId()).then(function (result) { + self.cohortComparisonId(null); + self.cohortComparison(null); + self.cohortComparison = params.currentCohortComparison; + self.cohortComparisonDirtyFlag(new ohdsiUtil.dirtyFlag(self.cohortComparison())); + document.location = "#/estimation" + }, function (error) { + console.log("Error: " + error); }); } self.save = function () { - - var cca = { - analysisId: self.cohortComparison().analysisId || null, - name: self.cohortComparison().name(), - treatmentId: self.cohortComparison().treatmentId(), - comparatorId: self.cohortComparison().comparatorId(), - outcomeId: self.cohortComparison().outcomeId(), - modelType: self.cohortComparison().modelType(), - timeAtRiskStart: self.cohortComparison().timeAtRiskStart(), - timeAtRiskEnd: self.cohortComparison().timeAtRiskEnd(), - addExposureDaysToEnd: self.cohortComparison().addExposureDaysToEnd(), - minimumWashoutPeriod: self.cohortComparison().minimumWashoutPeriod(), - minimumDaysAtRisk: self.cohortComparison().minimumDaysAtRisk(), - rmSubjectsInBothCohorts: self.cohortComparison().rmSubjectsInBothCohorts(), - rmPriorOutcomes: self.cohortComparison().rmPriorOutcomes(), - psAdjustment: self.cohortComparison().psAdjustment(), - psExclusionId: self.cohortComparison().psExclusionId(), - psInclusionId: self.cohortComparison().psInclusionId(), - psDemographics: self.cohortComparison().psDemographics() | 0, - psDemographicsGender: self.cohortComparison().psDemographicsGender() | 0, - psDemographicsRace: self.cohortComparison().psDemographicsRace() | 0, - psDemographicsEthnicity: self.cohortComparison().psDemographicsEthnicity() | 0, - psDemographicsAge: self.cohortComparison().psDemographicsAge() | 0, - psDemographicsYear: self.cohortComparison().psDemographicsYear() | 0, - psDemographicsMonth: self.cohortComparison().psDemographicsMonth() | 0, - psTrim: self.cohortComparison().psTrim(), - psTrimFraction: self.cohortComparison().psTrimFraction(), - psMatch: self.cohortComparison().psMatch(), - psMatchMaxRatio: self.cohortComparison().psMatchMaxRatio(), - psStrat: self.cohortComparison().psStrat() | 0, - psStratNumStrata: self.cohortComparison().psStratNumStrata(), - psConditionOcc: self.cohortComparison().psConditionOcc() | 0, - psConditionOcc365d: self.cohortComparison().psConditionOcc365d() | 0, - psConditionOcc30d: self.cohortComparison().psConditionOcc30d() | 0, - psConditionOccInpt180d: self.cohortComparison().psConditionOccInpt180d() | 0, - psConditionEra: self.cohortComparison().psConditionEra() | 0, - psConditionEraEver: self.cohortComparison().psConditionEraEver() | 0, - psConditionEraOverlap: self.cohortComparison().psConditionEraOverlap() | 0, - psConditionGroup: self.cohortComparison().psConditionGroup() | 0, - psConditionGroupMeddra: self.cohortComparison().psConditionGroupMeddra() | 0, - psConditionGroupSnomed: self.cohortComparison().psConditionGroupSnomed() | 0, - psDrugExposure: self.cohortComparison().psDrugExposure() | 0, - psDrugExposure365d: self.cohortComparison().psDrugExposure365d() | 0, - psDrugExposure30d: self.cohortComparison().psDrugExposure30d() | 0, - psDrugEra: self.cohortComparison().psDrugEra() | 0, - psDrugEra365d: self.cohortComparison().psDrugEra365d() | 0, - psDrugEra30d: self.cohortComparison().psDrugEra30d() | 0, - psDrugEraOverlap: self.cohortComparison().psDrugEraOverlap() | 0, - psDrugEraEver: self.cohortComparison().psDrugEraEver() | 0, - psDrugGroup: self.cohortComparison().psDrugGroup() | 0, - psProcedureOcc: self.cohortComparison().psProcedureOcc() | 0, - psProcedureOcc365d: self.cohortComparison().psProcedureOcc365d() | 0, - psProcedureOcc30d: self.cohortComparison().psProcedureOcc30d() | 0, - psProcedureGroup: self.cohortComparison().psProcedureGroup() | 0, - psObservation: self.cohortComparison().psObservation() | 0, - psObservation365d: self.cohortComparison().psObservation365d() | 0, - psObservation30d: self.cohortComparison().psObservation30d() | 0, - psObservationCount365d: self.cohortComparison().psObservationCount365d() | 0, - psMeasurement: self.cohortComparison().psMeasurement() | 0, - psMeasurement365d: self.cohortComparison().psMeasurement365d() | 0, - psMeasurement30d: self.cohortComparison().psMeasurement30d() | 0, - psMeasurementCount365d: self.cohortComparison().psMeasurementCount365d() | 0, - psMeasurementBelow: self.cohortComparison().psMeasurementBelow() | 0, - psMeasurementAbove: self.cohortComparison().psMeasurementAbove() | 0, - psConceptCounts: self.cohortComparison().psConceptCounts() | 0, - psRiskScores: self.cohortComparison().psRiskScores() | 0, - psRiskScoresCharlson: self.cohortComparison().psRiskScoresCharlson() | 0, - psRiskScoresDcsi: self.cohortComparison().psRiskScoresDcsi() | 0, - psRiskScoresChads2: self.cohortComparison().psRiskScoresChads2() | 0, - psRiskScoresChads2vasc: self.cohortComparison().psRiskScoresChads2vasc() | 0, - psInteractionYear: self.cohortComparison().psInteractionYear() | 0, - psInteractionMonth: self.cohortComparison().psInteractionMonth() | 0, - omCovariates: self.cohortComparison().omCovariates(), - omExclusionId: self.cohortComparison().omExclusionId(), - omInclusionId: self.cohortComparison().omInclusionId(), - omDemographics: self.cohortComparison().omDemographics() | 0, - omDemographicsGender: self.cohortComparison().omDemographicsGender() | 0, - omDemographicsRace: self.cohortComparison().omDemographicsRace() | 0, - omDemographicsEthnicity: self.cohortComparison().omDemographicsEthnicity() | 0, - omDemographicsAge: self.cohortComparison().omDemographicsAge() | 0, - omDemographicsYear: self.cohortComparison().omDemographicsYear() | 0, - omDemographicsMonth: self.cohortComparison().omDemographicsMonth() | 0, - omTrim: self.cohortComparison().omTrim(), - omTrimFraction: self.cohortComparison().omTrimFraction(), - omMatch: self.cohortComparison().omMatch(), - omMatchMaxRatio: self.cohortComparison().omMatchMaxRatio(), - omStrat: self.cohortComparison().omStrat() | 0, - omStratNumStrata: self.cohortComparison().omStratNumStrata(), - omConditionOcc: self.cohortComparison().omConditionOcc() | 0, - omConditionOcc365d: self.cohortComparison().omConditionOcc365d() | 0, - omConditionOcc30d: self.cohortComparison().omConditionOcc30d() | 0, - omConditionOccInpt180d: self.cohortComparison().omConditionOccInpt180d() | 0, - omConditionEra: self.cohortComparison().omConditionEra() | 0, - omConditionEraEver: self.cohortComparison().omConditionEraEver() | 0, - omConditionEraOverlap: self.cohortComparison().omConditionEraOverlap() | 0, - omConditionGroup: self.cohortComparison().omConditionGroup() | 0, - omConditionGroupMeddra: self.cohortComparison().omConditionGroupMeddra() | 0, - omConditionGroupSnomed: self.cohortComparison().omConditionGroupSnomed() | 0, - omDrugExposure: self.cohortComparison().omDrugExposure() | 0, - omDrugExposure365d: self.cohortComparison().omDrugExposure365d() | 0, - omDrugExposure30d: self.cohortComparison().omDrugExposure30d() | 0, - omDrugEra: self.cohortComparison().omDrugEra() | 0, - omDrugEra365d: self.cohortComparison().omDrugEra365d() | 0, - omDrugEra30d: self.cohortComparison().omDrugEra30d() | 0, - omDrugEraOverlap: self.cohortComparison().omDrugEraOverlap() | 0, - omDrugEraEver: self.cohortComparison().omDrugEraEver() | 0, - omDrugGroup: self.cohortComparison().omDrugGroup() | 0, - omProcedureOcc: self.cohortComparison().omProcedureOcc() | 0, - omProcedureOcc365d: self.cohortComparison().omProcedureOcc365d() | 0, - omProcedureOcc30d: self.cohortComparison().omProcedureOcc30d() | 0, - omProcedureGroup: self.cohortComparison().omProcedureGroup() | 0, - omObservation: self.cohortComparison().omObservation() | 0, - omObservation365d: self.cohortComparison().omObservation365d() | 0, - omObservation30d: self.cohortComparison().omObservation30d() | 0, - omObservationCount365d: self.cohortComparison().omObservationCount365d() | 0, - omMeasurement: self.cohortComparison().omMeasurement() | 0, - omMeasurement365d: self.cohortComparison().omMeasurement365d() | 0, - omMeasurement30d: self.cohortComparison().omMeasurement30d() | 0, - omMeasurementCount365d: self.cohortComparison().omMeasurementCount365d() | 0, - omMeasurementBelow: self.cohortComparison().omMeasurementBelow() | 0, - omMeasurementAbove: self.cohortComparison().omMeasurementAbove() | 0, - omConceptCounts: self.cohortComparison().omConceptCounts() | 0, - omRiskScores: self.cohortComparison().omRiskScores() | 0, - omRiskScoresCharlson: self.cohortComparison().omRiskScoresCharlson() | 0, - omRiskScoresDcsi: self.cohortComparison().omRiskScoresDcsi() | 0, - omRiskScoresChads2: self.cohortComparison().omRiskScoresChads2() | 0, - omRiskScoresChads2vasc: self.cohortComparison().omRiskScoresChads2vasc() | 0, - omInteractionYear: self.cohortComparison().omInteractionYear() | 0, - omInteractionMonth: self.cohortComparison().omInteractionMonth() | 0, - delCovariatesSmallCount: self.cohortComparison().delCovariatesSmallCount(), - negativeControlId: self.cohortComparison().negativeControlId() - }; - - var json = JSON.stringify(cca); - - var savePromise = ohdsiUtil.cachedAjax({ - method: self.cohortComparisonId() ? 'PUT' : 'POST', - url: config.api.url + 'comparativecohortanalysis/' + (self.cohortComparisonId() || ''), - contentType: 'application/json', - data: json, - dataType: 'json', - success: function (data) {} - }); - - savePromise.then(function (saveResult) { + self.isSaving(true); + comparativeCohortAnalysisAPI.saveAnalysis(self.cohortComparison()).then(function (saveResult) { var redirectWhenComplete = saveResult.analysisId != self.cohortComparison().analysisId; self.cohortComparisonId(saveResult.analysisId); self.cohortComparison().analysisId = saveResult.analysisId; - if (redirectWhenComplete) { - document.location = "#/estimation/" + self.cohortComparisonId(); - } - self.cohortComparisonDirtyFlag().reset(); - self.cohortComparison.valueHasMutated(); - console.log(saveResult); + setTimeout(function () { + self.cohortComparison(new ComparativeCohortAnalysis(saveResult)); + setTimeout(function () { + self.cohortComparisonDirtyFlag(new ohdsiUtil.dirtyFlag(self.cohortComparison())); + if (redirectWhenComplete) { + document.location = "#/estimation/" + self.cohortComparisonId(); + } + self.isSaving(false); + }, 0); + }, 0); }); } @@ -715,181 +75,6 @@ define(['jquery', 'knockout', 'text!./cohort-comparison-manager.html', 'lodash', document.location = '#/estimation'; } - self.conceptsetSelected = function (d) { - $('#modalConceptSet').modal('hide'); - vocabularyAPI.getConceptSetExpression(d.id).then(function (csExpression) { - self.targetId(d.id); - self.targetCaption(d.name); - var conceptSetData = new ConceptSet({ - id: d.id, - name: d.name, - expression: csExpression - }); - self.targetExpression.removeAll(); - self.targetExpression.push(conceptSetData); - - vocabularyAPI.getConceptSetExpressionSQL(csExpression).then( - function (data) { - self.targetConceptSetSQL(data); - }); - }); - } - - self.chooseTreatment = function () { - $('#modalCohortDefinition').modal('show'); - self.targetId = self.cohortComparison().treatmentId; - self.targetCaption = self.cohortComparison().treatmentCaption; - self.targetCohortDefinition = self.cohortComparison().treatmentCohortDefinition; - } - - self.chooseComparator = function () { - $('#modalCohortDefinition').modal('show'); - self.targetId = self.cohortComparison().comparatorId; - self.targetCaption = self.cohortComparison().comparatorCaption; - self.targetCohortDefinition = self.cohortComparison().comparatorCohortDefinition; - } - - self.chooseOutcome = function () { - $('#modalCohortDefinition').modal('show'); - self.targetId = self.cohortComparison().outcomeId; - self.targetCaption = self.cohortComparison().outcomeCaption; - self.targetCohortDefinition = self.cohortComparison().outcomeCohortDefinition; - } - - self.clearTreatment = function () { - self.cohortComparison().treatmentId(0); - self.cohortComparison().treatmentCaption(null); - self.cohortComparison().treatmentCohortDefinition(null); - } - - self.clearComparator = function () { - self.cohortComparison().comparatorId(0); - self.cohortComparison().comparatorCaption(null); - self.cohortComparison().comparatorCohortDefinition(null); - } - - self.clearOutcome = function () { - self.cohortComparison().outcomeId(0); - self.cohortComparison().outcomeCaption(null); - self.cohortComparison().outcomeCohortDefinition(null); - } - - self.clearPsExclusion = function () { - self.cohortComparison().psExclusionId(0); - self.cohortComparison().psExclusionCaption(null); - self.cohortComparison().psExclusionConceptSet.removeAll(); - self.cohortComparison().psExclusionConceptSetSQL(null); - } - - self.clearPsInclusion = function () { - self.cohortComparison().psInclusionId(0); - self.cohortComparison().psInclusionCaption(null); - self.cohortComparison().psInclusionConceptSet.removeAll(); - self.cohortComparison().psInclusionConceptSetSQL(null); - } - - self.clearOmExclusion = function () { - self.cohortComparison().omExclusionId(0); - self.cohortComparison().omExclusionCaption(null); - self.cohortComparison().omExclusionConceptSet.removeAll(); - self.cohortComparison().omExclusionConceptSetSQL(null); - } - - self.clearOmInclusion = function () { - self.cohortComparison().omInclusionId(0); - self.cohortComparison().omInclusionCaption(null); - self.cohortComparison().omInclusionConceptSet.removeAll(); - self.cohortComparison().omInclusionConceptSetSQL(null); - } - - self.clearNegativeControl = function () { - self.cohortComparison().negativeControlId(0); - self.cohortComparison().negativeControlCaption(null); - self.cohortComparison().negativeControlConceptSet.removeAll(); - self.cohortComparison().negativeControlConceptSetSQL(null); - } - - self.choosePsExclusion = function () { - $('#modalConceptSet').modal('show'); - self.targetId = self.cohortComparison().psExclusionId; - self.targetCaption = self.cohortComparison().psExclusionCaption; - self.targetExpression = self.cohortComparison().psExclusionConceptSet; - self.targetConceptSetSQL = self.cohortComparison().psExclusionConceptSetSQL; - } - - self.choosePsInclusion = function () { - $('#modalConceptSet').modal('show'); - self.targetId = self.cohortComparison().psInclusionId; - self.targetCaption = self.cohortComparison().psInclusionCaption; - self.targetExpression = self.cohortComparison().psInclusionConceptSet; - self.targetConceptSetSQL = self.cohortComparison().psInclusionConceptSetSQL; - } - - self.chooseOmExclusion = function () { - $('#modalConceptSet').modal('show'); - self.targetId = self.cohortComparison().omExclusionId; - self.targetCaption = self.cohortComparison().omExclusionCaption; - self.targetExpression = self.cohortComparison().omExclusionConceptSet; - self.targetConceptSetSQL = self.cohortComparison().omExclusionConceptSetSQL; - } - - self.chooseOmInclusion = function () { - $('#modalConceptSet').modal('show'); - self.targetId = self.cohortComparison().omInclusionId; - self.targetCaption = self.cohortComparison().omInclusionCaption; - self.targetExpression = self.cohortComparison().omInclusionConceptSet; - self.targetConceptSetSQL = self.cohortComparison().omInclusionConceptSetSQL; - } - - self.chooseNegativeControl = function () { - $('#modalConceptSet').modal('show'); - self.targetId = self.cohortComparison().negativeControlId; - self.targetCaption = self.cohortComparison().negativeControlCaption; - self.targetExpression = self.cohortComparison().negativeControlConceptSet; - self.targetConceptSetSQL = self.cohortComparison().negativeControlConceptSetSQL; - } - - self.chooseConceptSet = function (conceptSetType, observable) { - self.targetObservable = observable; - $('#modalConceptSet').modal('show'); - } - - self.isHistoryVisible = function (d) { - return "fa fa-angle-double-down"; - } - - self.monitorJobExecution = function (jobExecutionId, sourceKey) { - setTimeout(function () { - ohdsiUtil.cachedAjax({ - url: config.api.url + 'job/execution/' + jobExecutionId, - method: 'GET', - contentType: 'application/json', - success: function (d) { - if (d.status == 'COMPLETED') { - completed = true; - self.sourceProcessingStatus[sourceKey](false); - self.loadExecutions(); - } else { - self.monitorJobExecution(jobExecutionId, sourceKey); - } - } - }); - }, 60000); - } - - self.executeCohortComparison = function (sourceKey) { - self.sourceProcessingStatus[sourceKey](true); - - var generatePromise = ohdsiUtil.cachedAjax({ - url: config.api.url + 'comparativecohortanalysis/' + self.cohortComparisonId() + '/execute/' + sourceKey, - method: 'GET', - contentType: 'application/json', - success: function (c, status, xhr) { - self.monitorJobExecution(c.executionId, sourceKey); - } - }); - }; - self.import = function () { if (self.importJSON().length > 0) { var updatedExpression = JSON.parse(self.importJSON()); @@ -918,37 +103,36 @@ define(['jquery', 'knockout', 'text!./cohort-comparison-manager.html', 'lodash', } self.newCohortComparison = function () { - self.cohortComparison(new ComparativeCohortAnalysis()); // The ComparativeCohortAnalysis module is pretty big - use the setTimeout({}, 0) // to allow the event loop to catch up. // http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful setTimeout(function () { - self.cohortComparisonDirtyFlag(new ohdsiUtil.dirtyFlag(self.cohortComparison())); + self.loading(false); + self.cohortComparison(new ComparativeCohortAnalysis()); + setTimeout(function () { + self.cohortComparisonDirtyFlag(new ohdsiUtil.dirtyFlag(self.cohortComparison())); + }, 0); }, 0); } self.loadCohortComparison = function () { // load cca - ohdsiUtil.cachedAjax({ - url: config.api.url + 'comparativecohortanalysis/' + self.cohortComparisonId(), - method: 'GET', - contentType: 'application/json', - success: function (comparativeCohortAnalysis) { + comparativeCohortAnalysisAPI.getComparativeCohortAnalysis(self.cohortComparisonId()).then(function (comparativeCohortAnalysis) { + setTimeout(function () { + self.loading(false); self.cohortComparison(new ComparativeCohortAnalysis(comparativeCohortAnalysis)); setTimeout(function () { self.cohortComparisonDirtyFlag(new ohdsiUtil.dirtyFlag(self.cohortComparison())); }, 0); - } + }, 0); }); } // startup actions if (self.cohortComparisonId() == 0 && self.cohortComparison() == null) { self.newCohortComparison(); - self.loading(false); } else if (self.cohortComparisonId() > 0 && self.cohortComparisonId() != (self.cohortComparison() && self.cohortComparison().analysisId)) { self.loadCohortComparison(); - self.loading(false); } else { // already loaded self.loading(false); @@ -962,217 +146,5 @@ define(['jquery', 'knockout', 'text!./cohort-comparison-manager.html', 'lodash', ko.components.register('cohort-comparison-manager', component); return component; - - function chartOptions() { - var junk = 1; - return { - x: { - value: d => { - return Math.abs(d.beforeMatchingStdDiff); - }, - label: 'Before matching StdDiff', - tooltipOrder: 1, - propName: 'beforeMatchingStdDiff', - isColumn: true, - colIdx: 1, - isField: true, - }, - beforeMatchingDirection: { - value: d => d.beforeMatchingStdDiff > 0 ? "positive" : d.beforeMatchingStdDiff < 0 ? "negative" : "0", - tooltipOrder: 1.5, - isField: true, - isFacet: true, - label: "Direction before matching", - }, - y: { - value: d => Math.abs(d.afterMatchingStdDiff), - label: "After matching StdDiff", - /* - format: d => { - var str = d.toString(); - var idx = str.indexOf('.'); - if (idx == -1) { - return d3.format('0%')(d); - } - - var precision = (str.length - (idx+1) - 2).toString(); - return d3.format('0.' + precision + '%')(d); - }, - */ - tooltipOrder: 2, - propName: 'afterMatchingStdDiff', - isColumn: true, - colIdx: 1, - isField: true, - }, - afterMatchingDirection: { - value: d => d.afterMatchingStdDiff > 0 ? "positive" : d.afterMatchingStdDiff < 0 ? "negative" : "0", - tooltipOrder: 2.5, - isField: true, - isFacet: true, - label: "Direction after matching", - }, - xy: { // for brushing - _accessors: { - value: { - func: function (d, allFields) { - return [ - allFields.x.accessors.value(d), - allFields.y.accessors.value(d)]; - }, - posParams: ['d', 'allFields'], - }, - }, - isField: true, - }, - /* - size: { - //value: d=>d.afterMatchingMeanTreated, - propName: 'afterMatchingMeanTreated', - //scale: d3.scale.log(), - label: "After matching mean treated", - tooltipOrder: 3, - isField: true, - _accessors: { - avg: { - posParams: ['data','allFields'], - func: (data, allFields) => { - return d3.mean(data.map(allFields.size.accessors.value)); - }, - }, - tooltip: { - posParams: ['d','allFields'], - func: (d, allFields) => { - return { - name: `After matching mean treated - (avg: ${round(allFields.size.accessors.avg(),4)})`, - value: round(allFields.size.accessors.value(d), 4), - } - }, - }, - range: { - func: () => [1,8], - }, - }, - }, - series: { - value: d => ['A','B','C','D'][Math.floor(Math.random() * 4)], - sortBy: d => d.afterMatchingStdDiff, - tooltipOrder: 5, - isField: true, - }, - color: { - _accessors: { - value: { - func: function(d,i,j,allFields,data,series) { - return allFields.series.value(d,i,j,data,series); - }, - posParams: ['d','i','j','allFields','data','series'], - }, - domain: { - func: function(data, series, allFields) { - return _.uniq(data.map(allFields.series.value)); - }, - posParams: ['data', 'series', 'allFields'], - }, - range: { - func: () => ['red', 'green', 'pink', 'blue'], - }, - }, - //value: d=>nthroot(d.coefficient, 7), - //value: d=>d.coefficient, - /* - ['NA','N/A','null','.'] - .indexOf(d.coefficient && - d.coefficient.toLowerCase() - .trim()) > -1 - ? 0 : d.coefficient || 0, // (set NA = 0) - * / - //label: "Coefficient", - label: "Nonsense series", - scale: d3.scale.ordinal(), - //range: ['#ef8a62','#ddd','#67a9cf'], - //range: ['red', 'green', 'pink', 'blue'], - isField: true, - //domainFuncNeedsExtent: true, - //domainFunc: (data, ext) => [ext[0], 0, ext[1]], - /* - rangeFunc: (layout, prop) => { - prop.scale.rangePoints( - ['#ef8a62','#ddd','#67a9cf']); - }, - domainFunc: (data, prop) => { - var vals = data.map(prop.value).sort(d3.ascending); - return vals; - var preScale = d3.scale.ordinal() - .domain(vals) - .rangePoints([-1, 0, 1]); - - - }, - * / - //range: ['red', 'yellow', 'blue'], - }, - shape: { - value: () => junk++ % 3, - label: "Random", - tooltipOrder: 4, - isField: true, - }, - CIup: { // support CI in both directions - value: d => d.upperBound, - value: d => y(d) - d.upperBoundDiff, - }, - */ - covariateName: { - propName: 'covariateName', - value: d => { - return d.covariateName.split(/:/).shift(); - }, - isColumn: true, - isFacet: true, - colIdx: 0, - tooltipOrder: 7, - label: 'Covariate Name', - isField: true, - }, - covariateValue: { - propName: 'covariateName', - value: d => d.covariateName.split(/:/).pop(), - isColumn: true, - colIdx: 0, - tooltipOrder: 8, - label: 'Covariate Value', - isField: true, - /* - _accessors: { - tooltip: { - posParams: ['d','allFields'], - func: (d, allFields) => { - return { - name: `Covariate value`, - value: allFields.covariateName.accessors.value(d).split(/:/).pop(), - } - }, - }, - }, - */ - }, - /* - conceptId: { - propName: 'conceptId', - isColumn: true, - isFacet: true, - colIdx: 3, - tooltipOrder: 5, - label: 'Concept ID', - needsValueFunc: true, // so ChartProps will make one - // even though this isn't a normal - // zoomScatter field - isField: true, - }, - */ - }; - } } ); diff --git a/js/components/cohort-comparison-multi-r-code.html b/js/components/cohort-comparison-multi-r-code.html index 5b6fa0e4b..62e8448a8 100644 --- a/js/components/cohort-comparison-multi-r-code.html +++ b/js/components/cohort-comparison-multi-r-code.html @@ -24,7 +24,7 @@ # Data extraction ---- # TODO: Insert your connection details here - connectionDetails <- createConnectionDetails(dbms = "postgresql", + connectionDetails <- DatabaseConnector::createConnectionDetails(dbms = "postgresql", server = "localhost/ohdsi", user = "joe", password = "supersecret") @@ -36,17 +36,17 @@ outputFolder <- "<insert your directory here>" maxCores <- 1 - targetCohortId <- - comparatorCohortId <- - outcomeCohortId <- - outcomeList <- c(outcomeCohortId) + targetCohortId <- + comparatorCohortId <- + outcomeCohortId <- + outcomeList <- c() # Default Prior & Control settings ---- - defaultPrior <- createPrior("laplace", + Cyclops::defaultPrior <- createPrior("laplace", exclude = c(0), useCrossValidation = TRUE) - defaultControl <- createControl(cvType = "auto", + Cyclops::defaultControl <- createControl(cvType = "auto", startingVariance = 0.01, noiseLevel = "quiet", tolerance = 2e-07, @@ -59,61 +59,61 @@ # The code below ASSUMES you are only running in your local network # where common cohort IDs have already been assigned in the cohort table. - # Get all Concept IDs for exclusion ---- - sql <- paste("") + # Get all Concept IDs for exclusion ---- + sql <- paste("") sql <- SqlRender::renderSql(sql, cdm_database_schema = cdmDatabaseSchema)$sql sql <- SqlRender::translateSql(sql, targetDialect = connectionDetails$dbms)$sql - connection <- connect(connectionDetails) - excludedConcepts <- querySql(connection, sql) + connection <- DatabaseConnector::connect(connectionDetails) + excludedConcepts <- DatabaseConnector::querySql(connection, sql) excludedConcepts <- excludedConcepts$CONCEPT_ID - + excludedConcepts <- c() - # Get all Concept IDs for inclusion ---- - sql <- paste("") + # Get all Concept IDs for inclusion ---- + sql <- paste("") sql <- SqlRender::renderSql(sql, cdm_database_schema = cdmDatabaseSchema)$sql sql <- SqlRender::translateSql(sql, targetDialect = connectionDetails$dbms)$sql - connection <- connect(connectionDetails) - includedConcepts <- querySql(connection, sql) + connection <- DatabaseConnector::connect(connectionDetails) + includedConcepts <- DatabaseConnector::querySql(connection, sql) includedConcepts <- includedConcepts$CONCEPT_ID - + includedConcepts <- c() - # Get all Concept IDs for exclusion in the outcome model ---- - sql <- paste("") + # Get all Concept IDs for exclusion in the outcome model ---- + sql <- paste("") sql <- SqlRender::renderSql(sql, cdm_database_schema = cdmDatabaseSchema)$sql sql <- SqlRender::translateSql(sql, targetDialect = connectionDetails$dbms)$sql - connection <- connect(connectionDetails) - omExcludedConcepts <- querySql(connection, sql) + connection <- DatabaseConnector::connect(connectionDetails) + omExcludedConcepts <- DatabaseConnector::querySql(connection, sql) omExcludedConcepts <- omExcludedConcepts$CONCEPT_ID - + omExcludedConcepts <- c() - # Get all Concept IDs for inclusion exclusion in the outcome model ---- - sql <- paste("") + # Get all Concept IDs for inclusion exclusion in the outcome model ---- + sql <- paste("") sql <- SqlRender::renderSql(sql, cdm_database_schema = cdmDatabaseSchema)$sql sql <- SqlRender::translateSql(sql, targetDialect = connectionDetails$dbms)$sql - connection <- connect(connectionDetails) - omIncludedConcepts <- querySql(connection, sql) + connection <- DatabaseConnector::connect(connectionDetails) + omIncludedConcepts <- DatabaseConnector::querySql(connection, sql) omIncludedConcepts <- omIncludedConcepts$CONCEPT_ID - + omIncludedConcepts <- c() - # Get all Concept IDs for empirical calibration ---- - sql <- paste("") + # Get all Concept IDs for empirical calibration ---- + sql <- paste("") sql <- SqlRender::renderSql(sql, cdm_database_schema = cdmDatabaseSchema)$sql sql <- SqlRender::translateSql(sql, targetDialect = connectionDetails$dbms)$sql - connection <- connect(connectionDetails) - negativeControlConcepts <- querySql(connection, sql) + connection <- DatabaseConnector::connect(connectionDetails) + negativeControlConcepts <- DatabaseConnector::querySql(connection, sql) negativeControlConcepts <- negativeControlConcepts$CONCEPT_ID - + negativeControlConcepts <- c() # Create drug comparator and outcome arguments by combining target + comparitor + outcome + negative controls ---- - dcos <- createDrugComparatorOutcomes(targetId = targetCohortId, + dcos <- CohortMethod::createDrugComparatorOutcomes(targetId = targetCohortId, comparatorId = comparatorCohortId, excludedCovariateConceptIds = excludedConcepts, includedCovariateConceptIds = includedConcepts, @@ -122,108 +122,110 @@ drugComparatorOutcomesList <- list(dcos) -
+
# Define which types of covariates must be constructed ---- - covariateSettings <- createCovariateSettings(useCovariateDemographics = , - useCovariateDemographicsGender = , - useCovariateDemographicsRace = , - useCovariateDemographicsEthnicity = , - useCovariateDemographicsAge = , - useCovariateDemographicsYear = , - useCovariateDemographicsMonth = , - useCovariateConditionOccurrence = , - useCovariateConditionOccurrence365d = , - useCovariateConditionOccurrence30d = , - useCovariateConditionOccurrenceInpt180d = , - useCovariateConditionEra = , - useCovariateConditionEraEver = , - useCovariateConditionEraOverlap = , - useCovariateConditionGroup = , - useCovariateConditionGroupMeddra = , - useCovariateConditionGroupSnomed = , - useCovariateDrugExposure = , - useCovariateDrugExposure365d = , - useCovariateDrugExposure30d = , - useCovariateDrugEra = , - useCovariateDrugEra365d = , - useCovariateDrugEra30d = , - useCovariateDrugEraOverlap = , - useCovariateDrugEraEver = , - useCovariateDrugGroup = , - useCovariateProcedureOccurrence = , - useCovariateProcedureOccurrence365d = , - useCovariateProcedureOccurrence30d = , - useCovariateProcedureGroup = , - useCovariateObservation = , - useCovariateObservation365d = , - useCovariateObservation30d = , - useCovariateObservationCount365d = , - useCovariateMeasurement = , - useCovariateMeasurement365d = , - useCovariateMeasurement30d = , - useCovariateMeasurementCount365d = , - useCovariateMeasurementBelow = , - useCovariateMeasurementAbove = , - useCovariateConceptCounts = , - useCovariateRiskScores = , - useCovariateRiskScoresCharlson = , - useCovariateRiskScoresDCSI = , - useCovariateRiskScoresCHADS2 = , - useCovariateRiskScoresCHADS2VASc = , - useCovariateInteractionYear = , - useCovariateInteractionMonth = , - excludedCovariateConceptIds = excludedConcepts, - includedCovariateConceptIds = includedConcepts, - deleteCovariatesSmallCount = ) + covariateSettings <- FeatureExtraction::createCovariateSettings(useCovariateDemographics = , + useCovariateDemographicsGender = , + useCovariateDemographicsRace = , + useCovariateDemographicsEthnicity = , + useCovariateDemographicsAge = , + useCovariateDemographicsYear = , + useCovariateDemographicsMonth = , + useCovariateConditionOccurrence = , + useCovariateConditionOccurrenceLongTerm = , + useCovariateConditionOccurrenceShortTerm = , + useCovariateConditionOccurrenceInptMediumTerm = , + useCovariateConditionEra = , + useCovariateConditionEraEver = , + useCovariateConditionEraOverlap = , + useCovariateConditionGroup = , + useCovariateConditionGroupMeddra = , + useCovariateConditionGroupSnomed = , + useCovariateDrugExposure = , + useCovariateDrugExposureLongTerm = , + useCovariateDrugExposureShortTerm = , + useCovariateDrugEra = , + useCovariateDrugEraLongTerm = , + useCovariateDrugEraShortTerm = , + useCovariateDrugEraOverlap = , + useCovariateDrugEraEver = , + useCovariateDrugGroup = , + useCovariateProcedureOccurrence = , + useCovariateProcedureOccurrenceLongTerm = , + useCovariateProcedureOccurrenceShortTerm = , + useCovariateProcedureGroup = , + useCovariateObservation = , + useCovariateObservationLongTerm = , + useCovariateObservationShortTerm = , + useCovariateObservationCountLongTerm = , + useCovariateMeasurement = , + useCovariateMeasurementLongTerm = , + useCovariateMeasurementShortTerm = , + useCovariateMeasurementCountLongTerm = , + useCovariateMeasurementBelow = , + useCovariateMeasurementAbove = , + useCovariateConceptCounts = , + useCovariateRiskScores = , + useCovariateRiskScoresCharlson = , + useCovariateRiskScoresDCSI = , + useCovariateRiskScoresCHADS2 = , + useCovariateRiskScoresCHADS2VASc = , + useCovariateInteractionYear = , + useCovariateInteractionMonth = , + deleteCovariatesSmallCount = , + longTermDays = 365, + mediumTermDays = 180, + shortTermDays = 30, + windowEndDays = 0)
- getDbCmDataArgs <- createGetDbCohortMethodDataArgs(washoutPeriod = , + getDbCmDataArgs <- CohortMethod::createGetDbCohortMethodDataArgs(washoutPeriod = , firstExposureOnly = FALSE, - removeDuplicateSubjects = , + removeDuplicateSubjects = , studyStartDate = "", studyEndDate = "", excludeDrugsFromCovariates = FALSE, - covariateSettings = covariateSettings) + covariateSettings = covariateSettings) - createStudyPopArgs <- createCreateStudyPopulationArgs(removeSubjectsWithPriorOutcome = , + createStudyPopArgs <- CohortMethod::createCreateStudyPopulationArgs(removeSubjectsWithPriorOutcome = , firstExposureOnly = FALSE, - washoutPeriod = , - removeDuplicateSubjects = , - minDaysAtRisk = , - riskWindowStart = , + washoutPeriod = , + removeDuplicateSubjects = , + minDaysAtRisk = , + riskWindowStart = , addExposureDaysToStart = FALSE, - riskWindowEnd = , - addExposureDaysToEnd = ) + riskWindowEnd = , + addExposureDaysToEnd = ) - fitOutcomeModelArgs1 <- createFitOutcomeModelArgs(useCovariates = TRUEFALSE, - modelType = , - stratified = TRUE,stratified = FALSE, + fitOutcomeModelArgs1 <- CohortMethod::createFitOutcomeModelArgs(useCovariates = TRUEFALSE, + modelType = , + stratified = TRUE,stratified = FALSE, includeCovariateIds = omIncludedConcepts, excludeCovariateIds = omExcludedConcepts, prior = defaultPrior, control = defaultControl) - createPsArgs1 <- createCreatePsArgs(control = defaultControl) # Using only defaults - trimByPsArgs1 <- createTrimByPsArgs(trimFraction = )createTrimByPsArgs() # Using only defaults - trimByPsToEquipoiseArgs1 <- createTrimByPsToEquipoiseArgs(bounds = c())createTrimByPsToEquipoiseArgs() # Using only defaults - matchOnPsArgs1 <- createMatchOnPsArgs(caliper = 0.25, caliperScale = "standardized", maxRatio = )createMatchOnPsArgs() # Using only defaults - stratifyByPsArgs1 <- createStratifyByPsArgs(strataPop, numberOfStrata = )createStratifyByPsArgs() # Using only defaults + createPsArgs1 <- CohortMethod::createCreatePsArgs(control = defaultControl) # Using only defaults + trimByPsArgs1 <- CohortMethod::createTrimByPsArgs(trimFraction = )CohortMethod::createTrimByPsArgs() # Using only defaults + trimByPsToEquipoiseArgs1 <- CohortMethod::createTrimByPsToEquipoiseArgs(bounds = c())CohortMethod::createTrimByPsToEquipoiseArgs() # Using only defaults + matchOnPsArgs1 <- CohortMethod::createMatchOnPsArgs(caliper = 0.25, caliperScale = "standardized", maxRatio = )CohortMethod::createMatchOnPsArgs() # Using only defaults + stratifyByPsArgs1 <- CohortMethod::createStratifyByPsArgs(numberOfStrata = )CohortMethod::createStratifyByPsArgs() # Using only defaults - cmAnalysis1 <- createCmAnalysis(analysisId = 1, + cmAnalysis1 <- CohortMethod::createCmAnalysis(analysisId = 1, description = "", getDbCohortMethodDataArgs = getDbCmDataArgs, createStudyPopArgs = createStudyPopArgs, - createPs = TRUEFALSE, + createPs = TRUEFALSE, createPsArgs = createPsArgs1, - trimByPs = TRUEFALSE, + trimByPs = TRUEFALSE, trimByPsArgs = trimByPsArgs1, - trimByPsToEquipoise = TRUEFALSE, + trimByPsToEquipoise = TRUEFALSE, trimByPsToEquipoiseArgs = trimByPsToEquipoiseArgs1, - matchOnPs = TRUEFALSE, + matchOnPs = TRUEFALSE, matchOnPsArgs = matchOnPsArgs1, - stratifyByPs = TRUEFALSE, + stratifyByPs = TRUEFALSE, stratifyByPsArgs = stratifyByPsArgs1, computeCovariateBalance = TRUE, fitOutcomeModel = TRUE, @@ -233,7 +235,7 @@ cmAnalysisList <- list(cmAnalysis1) # Run the analysis ---- - result <- runCmAnalyses(connectionDetails = connectionDetails, + result <- CohortMethod::runCmAnalyses(connectionDetails = connectionDetails, cdmDatabaseSchema = cdmDatabaseSchema, exposureDatabaseSchema = resultsDatabaseSchema, exposureTable = exposureTable, @@ -255,7 +257,7 @@ refitPsForEveryOutcome = FALSE) ## Summarize the results - analysisSummary <- summarizeAnalyses(result) + analysisSummary <- CohortMethod::summarizeAnalyses(result) head(analysisSummary) # Perform Empirical Calibration ---- @@ -275,27 +277,27 @@ hoiSubset <- hoiSubset[!is.na(hoiSubset$logRr) & hoiSubset$logRr != 0, ] if (nrow(negControlSubset) > 10) { - null <- fitMcmcNull(negControlSubset$logRr, negControlSubset$seLogRr) + null <- EmpiricalCalibration::fitMcmcNull(negControlSubset$logRr, negControlSubset$seLogRr) # View the empirical calibration plot with only negative controls - plotCalibrationEffect(negControlSubset$logRr, + EmpiricalCalibration::plotCalibrationEffect(negControlSubset$logRr, negControlSubset$seLogRr) # Save the empirical calibration plot with only negative controls plotName <- paste("calEffectNoHois_a",analysisId, "_t", drugComparatorOutcome$targetId, "_c", drugComparatorOutcome$comparatorId, ".png", sep = "") - plotCalibrationEffect(negControlSubset$logRr, + EmpiricalCalibration::plotCalibrationEffect(negControlSubset$logRr, negControlSubset$seLogRr, fileName = file.path(outputFolder, plotName)) # View the empirical calibration plot with negative controls and HOIs plotted - plotCalibrationEffect(negControlSubset$logRr, + EmpiricalCalibration::plotCalibrationEffect(negControlSubset$logRr, negControlSubset$seLogRr, hoiSubset$logRr, hoiSubset$seLogRr) # Save the empirical calibration plot with negative controls and HOIs plotted plotName <- paste("calEffect_a",analysisId, "_t", drugComparatorOutcome$targetId, "_c", drugComparatorOutcome$comparatorId, ".png", sep = "") - plotCalibrationEffect(negControlSubset$logRr, + EmpiricalCalibration::plotCalibrationEffect(negControlSubset$logRr, negControlSubset$seLogRr, hoiSubset$logRr, hoiSubset$seLogRr, @@ -368,21 +370,21 @@ ps <- readRDS(psFile) # Compute the area under the receiver-operator curve (AUC) for the propensity score model ---- - computePsAuc(ps) + CohortMethod::computePsAuc(ps) # Plot the propensity score distribution ---- - plotPs(ps, + CohortMethod::plotPs(ps, scale = "preference") # Save the propensity score distribution ---- plotName <- paste0("propensityScorePlot", outputImageSuffix); - plotPs(ps, + CohortMethod::plotPs(ps, scale = "preference", fileName = file.path(outputFolder, plotName)) # Inspect the propensity model ---- - propensityModel <- getPsModel(ps, cohortMethodData) + propensityModel <- CohortMethod::getPsModel(ps, cohortMethodData) head(propensityModel) @@ -393,27 +395,27 @@ strataPop <- readRDS(strataFile) # View PS With Population Trimmed By Percentile ---- - plotPs(strataPop, + CohortMethod::plotPs(strataPop, ps, scale = "preference") # Save PS With Population Trimmed By Percentile ---- plotName <- paste0("propensityScorePlotStrata", outputImageSuffix); - plotPs(strataPop, + CohortMethod::plotPs(strataPop, ps, scale = "preference", fileName = file.path(outputFolder, plotName)) # Get the attrition table and diagram for the strata pop ---- - getAttritionTable(strataPop) + CohortMethod::getAttritionTable(strataPop) # View the attrition diagram for the strata pop ---- - drawAttritionDiagram(strataPop) + CohortMethod::drawAttritionDiagram(strataPop) # Save the attrition diagram for the strata pop ---- plotName <- paste0("attritionDiagramStrata", outputImageSuffix); - drawAttritionDiagram(strataPop, + CohortMethod::drawAttritionDiagram(strataPop, fileName = file.path(outputFolder, plotName)) @@ -425,19 +427,19 @@ balance <- readRDS(balanceFile) # View the covariate balance scatter plot ---- - plotCovariateBalanceScatterPlot(balance) + CohortMethod::plotCovariateBalanceScatterPlot(balance) # Save the covariate balance scatter plot ---- plotName <- paste0("covBalScatter", outputImageSuffix); - plotCovariateBalanceScatterPlot(balance, + CohortMethod::plotCovariateBalanceScatterPlot(balance, fileName = file.path(outputFolder, plotName)) # View the plot of top variables ---- - plotCovariateBalanceOfTopVariables(balance) + CohortMethod::plotCovariateBalanceOfTopVariables(balance) # Save the plot of top variables ---- plotName <- paste0("covBalTop", outputImageSuffix); - plotCovariateBalanceOfTopVariables(balance, + CohortMethod::plotCovariateBalanceOfTopVariables(balance, fileName = file.path(outputFolder, plotName)) diff --git a/js/components/cohort-comparison-multi-r-code.js b/js/components/cohort-comparison-multi-r-code.js index 032d578a3..d794ab539 100644 --- a/js/components/cohort-comparison-multi-r-code.js +++ b/js/components/cohort-comparison-multi-r-code.js @@ -1,10 +1,10 @@ -define(['jquery', 'knockout', 'text!./cohort-comparison-multi-r-code.html', 'appConfig', 'cohortcomparison/ComparativeCohortAnalysis', 'vocabularyprovider', 'nvd3', 'css!./styles/nv.d3.min.css', 'prism', 'css!./styles/prism.css'], - function ($, ko, view, config, cohortComparison, vocabularyAPI, options) { +define(['jquery', 'knockout', 'text!./cohort-comparison-multi-r-code.html', 'appConfig', 'cohortcomparison/ComparativeCohortAnalysis', 'prism', 'css!./styles/prism.css'], + function ($, ko, view, config, cohortComparison) { function cohortComparisonMultiRCode(params) { var self = this; self.config = config; - self.cohortComparison = params.cohortComparison; - self.codeElementId = params.codeElementId || 'estimation-r-code'; + self.cohortComparison = params.cohortComparison; + self.codeElementId = params.codeElementId || 'estimation-r-code'; } var component = { @@ -14,4 +14,4 @@ define(['jquery', 'knockout', 'text!./cohort-comparison-multi-r-code.html', 'app ko.components.register('cohort-comparison-multi-r-code', component); return component; - }); \ No newline at end of file + }); diff --git a/js/components/cohort-comparison-print-friendly.html b/js/components/cohort-comparison-print-friendly.html index a815f5158..381fbc8c2 100644 --- a/js/components/cohort-comparison-print-friendly.html +++ b/js/components/cohort-comparison-print-friendly.html @@ -4,43 +4,43 @@

Research question


-To compare the risk of between and , we will estimate the population-level effect of exposure on the of the outcome during the period from days from cohort start date to days from . +To compare the risk of between and , we will estimate the population-level effect of exposure on the of the outcome during the period from days from cohort start date to days from .

Study Design:

This study will follow a retrospective, observational, comparative cohort design. We define ‘retrospective’ to mean the study will be conducted using data already collected prior to the start of the study. We define ‘observational’ to mean there is no intervention or treatment assignment imposed by the study. We define 'cohort' to mean a set of patients satisfying a one or more inclusion criteria for a duration of time. We define ‘comparative cohort design’ to mean the formal comparison between two cohorts, a target cohort and comparator cohort, for the risk of an outcome during a defined time period after cohort entry.

-In this study, we compare with for the of from days from cohort start date to days from . +In this study, we compare with for the of from days from cohort start date to days from .
-
+

-For both cohorts, we impose a requirement that patients must have at least days of continuous observation prior to cohort entry. +For both cohorts, we impose a requirement that patients must have at least days of continuous observation prior to cohort entry.
-
+

-For both cohorts, we impose a requirement that patients must have at least days of continuous observation after the time-at-risk start, days from cohort start date. +For both cohorts, we impose a requirement that patients must have at least days of continuous observation after the time-at-risk start, days from cohort start date.

-The overall study population could be considered to be patients who entered either the target cohort or comparator cohort. Patients were excluded from consideration is they qualified for both the target cohort and comparator cohort at any time in their record. +The overall study population could be considered to be patients who entered either the target cohort or comparator cohort. Patients were excluded from consideration is they qualified for both the target cohort and comparator cohort at any time in their record.
-
+

-Patients in the target and comparator cohorts are classified as having the outcome if an event among occurs with a start date during the time-at-risk of days from cohort start date to days from . +Patients in the target and comparator cohorts are classified as having the outcome if an event among occurs with a start date during the time-at-risk of days from cohort start date to days from .
-
+

-The rate of outcomes among patients in the target and comparator cohorts is determined by counting the number of outcome occurrences of during the time-at-risk of days from cohort start date to days from . +The rate of outcomes among patients in the target and comparator cohorts is determined by counting the number of outcome occurrences of during the time-at-risk of days from cohort start date to days from .
-
+

-The time-to-event of outcome among patients in the target and comparator cohorts is determined by calculating the number of days from the start of the time-at-risk window, days from cohort start date until the earliest event among 1) the first occurrence of the outcome, before days from , 2) the end of the time-at-risk window, days from , and 3) the end of the observation period that spans the time-at-risk start. +The time-to-event of outcome among patients in the target and comparator cohorts is determined by calculating the number of days from the start of the time-at-risk window, days from cohort start date until the earliest event among 1) the first occurrence of the outcome, before days from , 2) the end of the time-at-risk window, days from , and 3) the end of the observation period that spans the time-at-risk start.
-
+

Patients with prior to target or comparator cohort entry were excluded from consideration.
-
+

Propensity scores will be used as an analytic strategy to reduce potential confounding due to imbalance between the target and comparator cohorts in baseline covariates. The propensity score is the probability of a patient being classified in the target cohort vs. the comparator cohort, given a set of observed covariates. In this study, the propensity score is estimated for each patient, using the predicted probability from a regularized logistic regression model, fit with a Laplace prior (LASSO) and the regularization hyperparameter selected by optimizing the likelihood in a 10-fold cross validation, using a starting variance of 0.01 and a tolerance of 2e-7.
@@ -49,340 +49,340 @@

Study Design:



-
    +
    • Demographics
      • -
      • Gender
      • -
      • Age group (5-year bands)
      • -
      • Index year
      • -
      • Index month
      • -
      • Race
      • -
      • Ethnicity
      • +
      • Gender
      • +
      • Age group (5-year bands)
      • +
      • Index year
      • +
      • Index month
      • +
      • Race
      • +
      • Ethnicity
    -
      +
      • Conditions
        • -
        • In prior 30d
        • -
        • In prior 365d
        • -
        • In prior 180d within inpatient setting
        • -
        • All time prior
        • -
        • Overlapping index date
        • +
        • In prior 30d
        • +
        • In prior 365d
        • +
        • In prior 180d within inpatient setting
        • +
        • All time prior
        • +
        • Overlapping index date
      -
        +
        • Condition aggregation
          • -
          • SNOMED
          • -
          • MedDRA
          • +
          • SNOMED
          • +
          • MedDRA
        -
          +
          • Drugs
            • -
            • In prior 30d
            • -
            • In prior 365d
            • -
            • All time prior
            • -
            • Overlapping index date
            • +
            • In prior 30d
            • +
            • In prior 365d
            • +
            • All time prior
            • +
            • Overlapping index date
          -
            +
            • Drug aggregation
              • -
              • Clinical Drug
              • -
              • Ingredient
              • -
              • ATC Class
              • +
              • Clinical Drug
              • +
              • Ingredient
              • +
              • ATC Class
            -
              +
              • Procedures
                • -
                • In prior 30d
                • -
                • In prior 365d
                • +
                • In prior 30d
                • +
                • In prior 365d
              -
                +
                • Measurement
                  • -
                  • Existence in prior 30d
                  • -
                  • Existence in prior 365d
                  • -
                  • Count in prior 365d
                  • -
                  • Has latest prior numeric value below normal range
                  • -
                  • Has latest prior numeric value above normal range
                  • +
                  • Existence in prior 30d
                  • +
                  • Existence in prior 365d
                  • +
                  • Count in prior 365d
                  • +
                  • Has latest prior numeric value below normal range
                  • +
                  • Has latest prior numeric value above normal range
                -
                  +
                  • Risk scores
                    • -
                    • Charlson
                    • -
                    • CHADS2
                    • -
                    • DCSI
                    • +
                    • Charlson
                    • +
                    • CHADS2
                    • +
                    • DCSI
                    -
                  • Concept counts (count of distinct conditions/procedures/visits in history)
                  • +
                  • Concept counts (count of distinct conditions/procedures/visits in history)
                  -
                    +
                    • Interaction terms
                      • -
                      • By index year
                      • -
                      • By index month
                      • +
                      • By index year
                      • +
                      • By index month
-
+

-Specific covariates to be included in the propensity score model are labelled as detailed in Appendix 2. +Specific covariates to be included in the propensity score model are labelled as detailed in Appendix 2.
-
+

-Specific covariates to be excluded from the propensity score model are labelled as detailed in Appendix 2. +Specific covariates to be excluded from the propensity score model are labelled as detailed in Appendix 2.

All covariates that occur in fewer than 10 persons between the target and comparator cohorts combined will be excluded prior to model fitting.
-
- The propensity score estimates are used to restrict the cohorts through patient trimming. Patients are excluded if their predicted probability is less than % or greater than % of the propensity score distribution across both cohorts.Patients are excluded if their predicted probability is less than % or greater than % of the preference score. +
+ The propensity score estimates are used to restrict the cohorts through patient trimming. Patients are excluded if their predicted probability is less than % or greater than % of the propensity score distribution across both cohorts.Patients are excluded if their predicted probability is less than % or greater than % of the preference score.
-
+

-Patients in the target cohort will be matched to patients in the comparator cohort, using a maximum ratio of :1 matching, using a greedy matching algorithm, applying a caliper of 0.25 of the standard deviation of the propensity score distribution. +Patients in the target cohort will be matched to patients in the comparator cohort, using a maximum ratio of :1 matching, using a greedy matching algorithm, applying a caliper of 0.25 of the standard deviation of the propensity score distribution.
-
+

-The target cohort and comparator cohorts will be stratified into quantiles of the propensity score distrbution. +The target cohort and comparator cohorts will be stratified into quantiles of the propensity score distrbution.
-
+

-A multivariate conditional will be used to estimate the of in , relative to , adjusting for the following baseline covariates: +A multivariate conditional will be used to estimate the of in , relative to , adjusting for the following baseline covariates:

-
    +
    • Demographics
      • -
      • Gender
      • -
      • Age group (5-year bands)
      • -
      • Index year
      • -
      • Index month
      • -
      • Race
      • -
      • Ethnicity
      • +
      • Gender
      • +
      • Age group (5-year bands)
      • +
      • Index year
      • +
      • Index month
      • +
      • Race
      • +
      • Ethnicity
    -
      +
      • Conditions
        • -
        • In prior 30d
        • -
        • In prior 365d
        • -
        • In prior 180d within inpatient setting
        • -
        • All time prior
        • -
        • Overlapping index date
        • +
        • In prior 30d
        • +
        • In prior 365d
        • +
        • In prior 180d within inpatient setting
        • +
        • All time prior
        • +
        • Overlapping index date
      -
        +
        • Condition aggregation
          • -
          • SNOMED
          • -
          • MedDRA
          • +
          • SNOMED
          • +
          • MedDRA
        -
          +
          • Drugs
            • -
            • In prior 30d
            • -
            • In prior 365d
            • -
            • All time prior
            • -
            • Overlapping index date
            • +
            • In prior 30d
            • +
            • In prior 365d
            • +
            • All time prior
            • +
            • Overlapping index date
          -
            +
            • Drug aggregation
              • -
              • Clinical Drug
              • -
              • Ingredient
              • -
              • ATC Class
              • +
              • Clinical Drug
              • +
              • Ingredient
              • +
              • ATC Class
            -
              +
              • Procedures
                • -
                • In prior 30d
                • -
                • In prior 365d
                • +
                • In prior 30d
                • +
                • In prior 365d
              -
                +
                • Measurement
                  • -
                  • Existence in prior 30d
                  • -
                  • Existence in prior 365d
                  • -
                  • Count in prior 365d
                  • -
                  • Has latest prior numeric value below normal range
                  • -
                  • Has latest prior numeric value above normal range
                  • +
                  • Existence in prior 30d
                  • +
                  • Existence in prior 365d
                  • +
                  • Count in prior 365d
                  • +
                  • Has latest prior numeric value below normal range
                  • +
                  • Has latest prior numeric value above normal range
                -
                  +
                  • Risk scores
                    • -
                    • Charlson
                    • -
                    • CHADS2
                    • -
                    • DCSI
                    • +
                    • Charlson
                    • +
                    • CHADS2
                    • +
                    • DCSI
                    -
                  • Concept counts (count of distinct conditions/procedures/visits in history)
                  • +
                  • Concept counts (count of distinct conditions/procedures/visits in history)
                  -
                    +
                    • Interaction terms
                      • -
                      • By index year
                      • -
                      • By index month
                      • +
                      • By index year
                      • +
                      • By index month


A regularized regression model will be fit with a LaPlace prior (LASSO) for all covariates except for the cohort classifier (target vs. comparator); the regularization hyperparameter will be selected by optimizing the likelihood in a 10-fold cross validation, using a starting variance of 0.01 and a tolerance of 2e-7. -
+

-Specific covariates to be included in the outcome model are labelled as detailed in Appendix 2. +Specific covariates to be included in the outcome model are labelled as detailed in Appendix 2.
-
+

-Specific covariates to be excluded from the outcome model are labelled as detailed in Appendix 2. +Specific covariates to be excluded from the outcome model are labelled as detailed in Appendix 2.

Output and evaluation

Covariate balance will be summarized in tabular form by showing the mean value for all baseline covariates in the target and comparator cohort, with the associated standardized mean difference computed for each covariate.
-
+

Once the propensity score model is fit, we will plot the propensity score distribution of the target and comparator cohorts to evaluate the comparability of the two cohorts. The plot will be scaled to the preference score, normalizing for any imbalance in cohort size. The area under the Receiver Operating Characteric (ROC) curve (AUC) will be reported. The covariates selected within the propensity score model, with associated coefficients will also be reported. -
+

A plot showing the propensity score distributions for both cohorts after matching will be provided. Covariate balance will be evaluated by plotting the standardized mean difference of each covariate before propensity score matching against the standardized mean difference for each covariate after propensity score matching.
-
+

A plot showing the propensity score distributions for both cohorts after stratification will be provided, with each quantile cutpoint shown as a vertical line.

-An attrition diagram will be provided to detail the loss of patients from the original target cohort, , and comparator cohort to the subpopulations that remain after all design considerations have been applied. +An attrition diagram will be provided to detail the loss of patients from the original target cohort, , and comparator cohort to the subpopulations that remain after all design considerations have been applied.

-The final outcome model, a conditional model, will be summarized by providing the ratio and associated 95% confidence interval. The number of persons, amount of time-at-risk, and number of outcomes in each cohort will also be reported. +The final outcome model, a conditional model, will be summarized by providing the ratio and associated 95% confidence interval. The number of persons, amount of time-at-risk, and number of outcomes in each cohort will also be reported.
-
+

A Kaplan-Meier plot will be generated to characterize the contour of risk over time for the outcome of interest.
-
+

The covariates selected within the outcome model, with associated coefficients will be provided.
-
+

-Negative control outcomes will be used to evaluate the potential impact of residual systematic error in the study design, and to facilitate empirical calibration of the p-value for the outcomes of interest. Negative control outcomes are concepts known not to be associated with either the target or comparator group, such that we can assume the true relative risk should equal 1. The negative control outcomes used in this study are labelled as detailed in Appendix 2. For each negative control outcome, the study design described above will be implemented and the effect estimate will be recorded. The distribution of effect estimates across all negative control outcomes will be used to fit an empirical null distribution which models the observed residual systematic error. The empirical null distribution will then be applied to the target outcome of interest to calibrate the p-value. Empirical calibration serves as an important diagnostic tool to evaluate if the residual systematic error is sufficient to cast doubt on the accuracy of the unknown effect estimate. The calibration effect plot and calibration probability plots will be generated for review. We will report the traditional p-value and empirically calibrated p-value for each negative control, as well as the unknown outcomes of interest +Negative control outcomes will be used to evaluate the potential impact of residual systematic error in the study design, and to facilitate empirical calibration of the p-value for the outcomes of interest. Negative control outcomes are concepts known not to be associated with either the target or comparator group, such that we can assume the true relative risk should equal 1. The negative control outcomes used in this study are labelled as detailed in Appendix 2. For each negative control outcome, the study design described above will be implemented and the effect estimate will be recorded. The distribution of effect estimates across all negative control outcomes will be used to fit an empirical null distribution which models the observed residual systematic error. The empirical null distribution will then be applied to the target outcome of interest to calibrate the p-value. Empirical calibration serves as an important diagnostic tool to evaluate if the residual systematic error is sufficient to cast doubt on the accuracy of the unknown effect estimate. The calibration effect plot and calibration probability plots will be generated for review. We will report the traditional p-value and empirically calibrated p-value for each negative control, as well as the unknown outcomes of interest

Appendix 1: Cohort Definitions

-
+

- The target cohort is defined as: + The target cohort is defined as:
- +
-
+

- The comparator cohort is defined as: + The comparator cohort is defined as:
- +
-
+

The outcome is defined as:
- +

Appendix 2: Code List Definitions

-
+

- +
- +
-
+

- +
- +
-
+

- +
- +
-
+

- +
- +
-
+

- +
- +
diff --git a/js/components/cohort-comparison-print-friendly.js b/js/components/cohort-comparison-print-friendly.js index 52464746b..e895af1eb 100644 --- a/js/components/cohort-comparison-print-friendly.js +++ b/js/components/cohort-comparison-print-friendly.js @@ -1,10 +1,10 @@ -define(['jquery', 'knockout', 'text!./cohort-comparison-print-friendly.html', 'appConfig', 'cohortcomparison/ComparativeCohortAnalysis', 'nvd3', 'css!./styles/nv.d3.min.css'], +define(['jquery', 'knockout', 'text!./cohort-comparison-print-friendly.html', 'appConfig', 'cohortcomparison/ComparativeCohortAnalysis'], function ($, ko, view, config, cohortComparison, options) { function cohortComparisonPrintFriendly(params) { var self = this; self.config = config; self.loading = ko.observable(true); - self.cohortComparison = params.cohortComparison; + self.cohortComparison = params.cohortComparison; } var component = { @@ -14,4 +14,4 @@ define(['jquery', 'knockout', 'text!./cohort-comparison-print-friendly.html', 'a ko.components.register('cohort-comparison-print-friendly', component); return component; - }); \ No newline at end of file + }); diff --git a/js/components/cohort-comparison-r-code.js b/js/components/cohort-comparison-r-code.js index d50254e35..6f67d9b7f 100644 --- a/js/components/cohort-comparison-r-code.js +++ b/js/components/cohort-comparison-r-code.js @@ -1,9 +1,9 @@ -define(['jquery', 'knockout', 'text!./cohort-comparison-r-code.html', 'appConfig', 'cohortcomparison/ComparativeCohortAnalysis', 'vocabularyprovider', 'nvd3', 'css!./styles/nv.d3.min.css', 'prism', 'css!./styles/prism.css'], - function ($, ko, view, config, cohortComparison, vocabularyAPI, options) { +define(['jquery', 'knockout', 'text!./cohort-comparison-r-code.html', 'appConfig', 'cohortcomparison/ComparativeCohortAnalysis', 'prism', 'css!./styles/prism.css'], + function ($, ko, view, config) { function cohortComparisonRCode(params) { var self = this; self.config = config; - self.cohortComparison = params.cohortComparison; + self.cohortComparison = params.cohortComparison; } var component = { @@ -13,4 +13,4 @@ define(['jquery', 'knockout', 'text!./cohort-comparison-r-code.html', 'appConfig ko.components.register('cohort-comparison-r-code', component); return component; - }); \ No newline at end of file + }); diff --git a/js/components/cohort-comparison-results.html b/js/components/cohort-comparison-results.html new file mode 100644 index 000000000..e5f76d382 --- /dev/null +++ b/js/components/cohort-comparison-results.html @@ -0,0 +1,150 @@ +

PLEASE NOTE: THIS COMPONENT IS NO LONGER WORKING.

+
+
Available Data Sources
+
+
+
+
+ + + + + + +
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+ +
+
loading results
+
+ +
+
+ +
+ +
+
+ +
+
+ +
+
+ + + +
+
+
+
+
+ The result of the analysis when comparing the rate of the outcome defined by the cohort () between the treatment cohort () and the comparator cohort () is . +
+
+
+
+
+ + +
+
+
+ + + + + + + + + + + + + + + + + +
DescriptionTreated PersonsComparator PersonsTreated ExposuresComparator Exposures
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+ + +
+
+
diff --git a/js/components/cohort-comparison-results.js b/js/components/cohort-comparison-results.js new file mode 100644 index 000000000..8886d5a69 --- /dev/null +++ b/js/components/cohort-comparison-results.js @@ -0,0 +1,763 @@ +/* + README: This component was originally written and working + with D3 v3 and utilized the nvd3 library: http://nvd3.org/ + which is incompatable with D3 V4. + + WARNING: The code in this component is here for reference and currently + in disrepair. +*/ +define(['jquery', + 'knockout', + 'text!./cohort-comparison-results.html', + 'lodash', + 'appConfig', + 'ohdsi.util', + //'nvd3', + //'databindings/d3ChartBinding', + //'css!./styles/nv.d3.min.css', + ], + function ($, ko, view, _, config, ohdsiUtil) { + function cohortComparisonResults(params) { + var DEBUG = true; + var self = this; + self.cohortComparisonId = params.cohortComparisonId; + self.cohortComparison = params.cohortComparison; + + self.config = config; + self.loading = ko.observable(true); + self.loadingExecution = ko.observable(false); + self.loadingExecutionFailure = ko.observable(false); + self.covariates = ko.observableArray(); + self.currentExecutionId = ko.observable(); + self.currentExecutionAuc = ko.observable(); + self.poppropdist = ko.observableArray(); + self.popprefdist = ko.observableArray(); + self.psmodeldist = ko.observableArray(); + self.attrition = ko.observableArray(); + self.sources = ko.observableArray(); + self.currentExecutionSourceName = ko.observable(); + self.sourceHistoryDisplay = {}; + self.sourceProcessingStatus = {}; + self.sourceExecutions = {}; + self.expressionMode = ko.observable('print'); + self.om = ko.observable(); + self.modifiedJSON = ""; + self.importJSON = ko.observable(); + self.expressionJSON = ko.pureComputed({ + read: function () { + return ko.toJSON(self.cohortComparison(), function (key, value) { + if (value === 0 || value) { + delete value.analysisId; + delete value.name; + return value; + } else { + return + } + }, 2); + }, + write: function (value) { + self.modifiedJSON = value; + } + }); + + // for new scatter balance chart + /* + self.chartObj = ko.observable(); + self.domElement = ko.observable(); + self.chartData = ko.observableArray(self.chartData && self.chartData() || []); + self.sharedCrossfilter = ko.observable(new ohdsiUtil.SharedCrossfilter([])); + window.scf = self.sharedCrossfilter(); + self.chartData.subscribe(function(recs) { + self.sharedCrossfilter().replaceData(recs); + }); + $(self.sharedCrossfilter()).on('filterEvt', function(evt, stuff) { + console.log("filter in sharedCrossfilter", stuff); + }); + $(self.sharedCrossfilter()).on('newData', function(evt, stuff) { + console.log("new data in sharedCrossfilter; shouldn't happen much", stuff); + }); + self.chartResolution = ko.observable(); // junk + self.jqEventSpace = params.jqEventSpace || {}; + self.fields = ko.observable([]); + self.chartObj.subscribe(function(chart) { + }); + self.ready = ko.computed(function() { + return self.chartObj() && + self.chartData().length && + self.domElement() && + self.pillMode() === 'balance'; + }); + self.chartOptions = chartOptions(); + self.ready.subscribe(function(ready) { + if (ready) { + initializeBalanceComponents(); + } + }); + var initializeBalanceComponents = _.once(function() { + var opts = _.merge(self.chartObj().defaultOptions, self.chartOptions); + var fields = _.map(opts, + (opt, name) => { + if (opt.isField) { + if (!(opt instanceof ohdsiUtil.Field)) { + opt = new ohdsiUtil.Field(name, opt, opts); + } + opt.bindParams({data:self.chartData()}, false); + } + return opts[name] = opt; + }); + self.fields(fields); + self.chartObj().chartSetup(self.domElement(), 460, 150, opts); + self.chartObj().render(self.chartData(), self.domElement(), 460, 150, opts); + self.sharedCrossfilter().dimField('xy', opts.xy); + self.pillMode.subscribe(function(pillMode) { + if (pillMode === 'balance') + self.chartObj().render(self.chartData(), self.domElement(), 460, 150, opts); + }); + //self.chartOptions.xy.accessor = self.chartOptions.xy.accessors.value; + }); + $(self.jqEventSpace).on('brush', function(evt, {empty, x1,x2,y1,y2} = {}) { + //console.log('brush event', arguments); + var xyFilt; + if (empty) { + xyFilt = null; + util.deleteState('filters.brush'); + } else { + xyFilt = ([x,y] = [], i) => { + return x >= x1 && + x <= x2 && + y >= y1 && + y <= y2; + }; + util.setState('filters.brush', {x1,x2,y1,y2}); + } + self.sharedCrossfilter().filter('xy', xyFilt, + {source:'brush', x1, x2, y1, y2, empty}); + }); + $(self.sharedCrossfilter()).on('filterEvt', + function(evt, {dimField, source, x1, x2, y1, y2, empty, waitForMore} = {}) { + if (source === 'brush') { + // scatter has already zoomed. + if (empty) { + self.chartObj().cp.x.setZoomScale(); + self.chartObj().cp.y.setZoomScale(); + } else { + self.chartObj().cp.x.setZoomScale([x1,x2]); + self.chartObj().cp.y.setZoomScale([y1,y2]); + } + self.chartObj().updateData(self.sharedCrossfilter().dimRecs('xy')); + } else { + if (!waitForMore || waitForMore === 'done') { + self.chartObj().updateData(self.sharedCrossfilter().filteredRecs()); + } + } + }); + */ + /* + function dataSetup(raw) { + var points = raw.map(d => ({ + x: Math.abs(d.beforeMatchingStdDiff), + y: Math.abs(d.afterMatchingStdDiff), + tooltip: d.covariateName + })); + return points; + } + */ + + // end for balance chart + + var initSources = config.api.sources.filter(s => s.hasCDM); + for (var i = 0; i < initSources.length; i++) { + self.sourceHistoryDisplay[initSources[i].sourceKey] = ko.observable(false); + self.sourceProcessingStatus[initSources[i].sourceKey] = ko.observable(false); + } + + self.sources(initSources); + + self.loadExecutions = function () { + // reset before load + $.each(self.sources(), function (i, s) { + if (!self.sourceExecutions[s.sourceKey]) { + self.sourceExecutions[s.sourceKey] = ko.observableArray(); + } else { + self.sourceExecutions[s.sourceKey].removeAll(); + } + }); + + ohdsiUtil.cachedAjax({ + url: config.api.url + 'comparativecohortanalysis/' + self.cohortComparisonId() + '/executions', + method: 'GET', + contentType: 'application/json', + success: function (response) { + + response = response.sort(function (a, b) { + return a.executed - b.executed; + }); + + $.each(response, function (i, d) { + var sourceKey = self.sources().filter(s => s.sourceId == d.sourceId)[0].sourceKey; + var executedTimestamp = new Date(d.executed); + d.executedCaption = executedTimestamp.toLocaleDateString() + ' ' + executedTimestamp.toLocaleTimeString(); + + var h = Math.floor(((d.duration % 31536000) % 86400) / 3600); + var m = Math.floor((((d.duration % 31536000) % 86400) % 3600) / 60); + var s = (((d.duration % 31536000) % 86400) % 3600) % 60; + + d.durationCaption = '' + if (h > 0) d.durationCaption += h + 'h '; + if (m > 0) d.durationCaption += m + 'm '; + if (s > 0) d.durationCaption += s + 's '; + + if (h == 0 && m == 0 && s == 0) { + d.durationCaption = 'n/a'; + } + + // this will ensure that if the last execution is still running that we don't allow additional executions to begin + if (d.executionStatus != 'COMPLETED') { + self.sourceProcessingStatus[sourceKey](true); + } else { + self.sourceProcessingStatus[sourceKey](false); + } + + self.sourceExecutions[sourceKey].push(d); + }); + } + }); + } + + self.loadExecutions(); + + self.toggleHistoryDisplay = function (sourceKey) { + self.sourceHistoryDisplay[sourceKey](!self.sourceHistoryDisplay[sourceKey]()); + } + + self.covariateColumns = [ + { + title: 'Id', + data: 'id' + }, + { + title: 'Name', + data: 'name' + }, + { + title: 'Coefficient', + data: function (d) { + return d3.round(d.value, 2); + } + }, + { + title: '|Coefficient|', + data: function (d) { + return d3.round(Math.abs(d.value), 2); + } + } + ]; + + self.covariateOptions = { + lengthMenu: [[10, -1], ['10', 'All']], + Facets: [ + { + 'caption': 'Value', + 'binding': function (o) { + if (o.value > 2) { + return '> 2'; + } else if (o.value < -2) { + return '< -2'; + } else { + return 'Other'; + } + } + }, + { + 'caption': 'Analysis', + 'binding': function (o) { + return o.name.split(/[:,=]/)[0]; + } + } + ] + }; + + self.resultsMode = ko.observable('sources'); + self.pillMode = ko.observable('covariates'); + + self.pillMode.subscribe(function (d) { + window.setTimeout(function (d) { + window.dispatchEvent(new Event('resize')); + }, 1); + }) + + self.covariateSelected = function (d) { + console.log(d); + } + + self.closeExecution = function () { + self.resultsMode('sources'); + } + + self.viewLastExecution = function (source) { + var executionCount = self.sourceExecutions[source.sourceKey]().length - 1; + for (var e = executionCount; e >= 0; e--) { + var execution = self.sourceExecutions[source.sourceKey]()[e]; + if (execution.executionStatus == 'COMPLETED') { + self.executionSelected(execution); + break; + } + } + } + + self.executionSelected = function (d) { + self.loadingExecutionFailure(false); + self.resultsMode('execution'); + self.loadingExecution(true); + + var sourceName = self.sources().filter(s => s.sourceId == d.sourceId)[0].sourceName; + self.currentExecutionSourceName(sourceName); + + var p1 = ohdsiUtil.cachedAjax({ + url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/psmodel', + method: 'GET', + contentType: 'application/json', + success: function (response) { + self.currentExecutionId(d.executionId); + self.currentExecutionAuc(response.auc); + self.covariates(response.covariates); + } + }); + + var p2 = ohdsiUtil.cachedAjax({ + url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/attrition', + method: 'GET', + contentType: 'application/json', + success: function (response) { + self.attrition(response); + } + }); + + var p3 = ohdsiUtil.cachedAjax({ + url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/balance', + method: 'GET', + contentType: 'application/json', + success: function (response) { + + // self.chartData(response); + + nv.addGraph(function () { + var points = response.map(d => ({ + x: Math.abs(d.beforeMatchingStdDiff), + y: Math.abs(d.afterMatchingStdDiff), + tooltip: d.covariateName + })); + var data = [{ + key: 'Covariates', + values: points + }]; + + var balanceChart = nv.models.scatterChart() + .showDistX(true) + .showDistY(true) + .color( + d3.scaleOrdinal() + .range(d3.schemeCategory10) + ); + + balanceChart.tooltip.contentGenerator(function (d) { + return '
' + d.point.tooltip + '
Before Matching: ' + d.point.x + '
After Matching: ' + d.point.y + '
'; + }); + + //Axis settings + balanceChart.xAxis.tickFormat(d3.format('.02f')); + balanceChart.yAxis.tickFormat(d3.format('.02f')); + + d3.select('#balanceChart svg') + .datum(data) + .call(balanceChart); + + nv.utils.windowResize(balanceChart.update); + + return balanceChart; + }); + } + }); + + var p4 = ohdsiUtil.cachedAjax({ + url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/poppropdist', + method: 'GET', + contentType: 'application/json', + success: function (response) { + self.poppropdist(response); + + var data = [ + { + values: response.map(d => ({ + 'x': d.score, + 'y': d.comparator + })).sort(function (a, b) { + return a.x - b.x + }), + key: 'Comparator', + color: '#000088', + area: true + }, + { + values: response.map(d => ({ + 'x': d.score, + 'y': d.treatment + })).sort(function (a, b) { + return a.x - b.x + }), + key: 'Treatment', + color: '#880000', + area: true + } + ]; + + nv.addGraph(function () { + var matchedChart = nv.models.lineChart() + .useInteractiveGuideline(true) + .interpolate("basis"); + + matchedChart.duration(0); + + d3.select("#popdistChart svg") + .datum(data) + .call(matchedChart); + + nv.utils.windowResize(matchedChart.update); + return matchedChart; + }); + } + }); + + var p5 = ohdsiUtil.cachedAjax({ + url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/psmodelpropscore', + method: 'GET', + contentType: 'application/json', + success: function (response) { + var data = [ + { + values: response.map(d => ({ + 'x': d.score, + 'y': d.comparator + })).sort(function (a, b) { + return a.x - b.x + }), + key: 'Comparator', + color: '#000088', + area: true + }, + { + values: response.map(d => ({ + 'x': d.score, + 'y': d.treatment + })).sort(function (a, b) { + return a.x - b.x + }), + key: 'Treatment', + color: '#880000', + area: true + } + ]; + + + nv.addGraph(function () { + var modelChart = nv.models.lineChart() + .useInteractiveGuideline(true) + .interpolate("basis") + .showYAxis(false); + + modelChart.tooltip.enabled(false); + + modelChart.duration(0); + + d3.select("#psmodeldistChart svg") + .datum(data) + .call(modelChart); + + nv.utils.windowResize(modelChart.update); + return modelChart; + }); + } + }); + + var p6 = ohdsiUtil.cachedAjax({ + url: config.api.url + 'comparativecohortanalysis/execution/' + d.executionId + '/om', + method: 'GET', + contentType: 'application/json', + success: function (response) { + response.forEach(function (r) { + r.caption = r.estimate + ' (' + d3.round(r.lower95, 2) + '-' + d3.round(r.upper95, 2) + ')'; + }); + self.om(response); + } + }); + + + Promise.all([p1, p2, p3, p4, p5]) + .then(results => { + self.loadingExecution(false); + }) + .catch(error => { + self.loadingExecution(false); + self.resultsMode('sources'); + self.loadingExecutionFailure(true); + }); + } + + self.isHistoryVisible = function (d) { + return "fa fa-angle-double-down"; + } + + self.monitorJobExecution = function (jobExecutionId, sourceKey) { + setTimeout(function () { + ohdsiUtil.cachedAjax({ + url: config.api.url + 'job/execution/' + jobExecutionId, + method: 'GET', + contentType: 'application/json', + success: function (d) { + if (d.status == 'COMPLETED') { + completed = true; + self.sourceProcessingStatus[sourceKey](false); + self.loadExecutions(); + } else { + self.monitorJobExecution(jobExecutionId, sourceKey); + } + } + }); + }, 60000); + } + + self.executeCohortComparison = function (sourceKey) { + self.sourceProcessingStatus[sourceKey](true); + + var generatePromise = ohdsiUtil.cachedAjax({ + url: config.api.url + 'comparativecohortanalysis/' + self.cohortComparisonId() + '/execute/' + sourceKey, + method: 'GET', + contentType: 'application/json', + success: function (c, status, xhr) { + self.monitorJobExecution(c.executionId, sourceKey); + } + }); + }; + } + + var component = { + viewModel: cohortComparisonResults, + template: view + }; + + ko.components.register('cohort-comparison-results', component); + return component; + + function chartOptions() { + var junk = 1; + return { + x: { + value: d => { + return Math.abs(d.beforeMatchingStdDiff); + }, + label: 'Before matching StdDiff', + tooltipOrder: 1, + propName: 'beforeMatchingStdDiff', + isColumn: true, + colIdx: 1, + isField: true, + }, + beforeMatchingDirection: { + value: d => d.beforeMatchingStdDiff > 0 ? "positive" : d.beforeMatchingStdDiff < 0 ? "negative" : "0", + tooltipOrder: 1.5, + isField: true, + isFacet: true, + label: "Direction before matching", + }, + y: { + value: d => Math.abs(d.afterMatchingStdDiff), + label: "After matching StdDiff", + /* + format: d => { + var str = d.toString(); + var idx = str.indexOf('.'); + if (idx == -1) { + return d3.format('0%')(d); + } + + var precision = (str.length - (idx+1) - 2).toString(); + return d3.format('0.' + precision + '%')(d); + }, + */ + tooltipOrder: 2, + propName: 'afterMatchingStdDiff', + isColumn: true, + colIdx: 1, + isField: true, + }, + afterMatchingDirection: { + value: d => d.afterMatchingStdDiff > 0 ? "positive" : d.afterMatchingStdDiff < 0 ? "negative" : "0", + tooltipOrder: 2.5, + isField: true, + isFacet: true, + label: "Direction after matching", + }, + xy: { // for brushing + _accessors: { + value: { + func: function (d, allFields) { + return [ + allFields.x.accessors.value(d), + allFields.y.accessors.value(d)]; + }, + posParams: ['d', 'allFields'], + }, + }, + isField: true, + }, + /* + size: { + //value: d=>d.afterMatchingMeanTreated, + propName: 'afterMatchingMeanTreated', + //scale: d3.scale.log(), + label: "After matching mean treated", + tooltipOrder: 3, + isField: true, + _accessors: { + avg: { + posParams: ['data','allFields'], + func: (data, allFields) => { + return d3.mean(data.map(allFields.size.accessors.value)); + }, + }, + tooltip: { + posParams: ['d','allFields'], + func: (d, allFields) => { + return { + name: `After matching mean treated + (avg: ${round(allFields.size.accessors.avg(),4)})`, + value: round(allFields.size.accessors.value(d), 4), + } + }, + }, + range: { + func: () => [1,8], + }, + }, + }, + series: { + value: d => ['A','B','C','D'][Math.floor(Math.random() * 4)], + sortBy: d => d.afterMatchingStdDiff, + tooltipOrder: 5, + isField: true, + }, + color: { + _accessors: { + value: { + func: function(d,i,j,allFields,data,series) { + return allFields.series.value(d,i,j,data,series); + }, + posParams: ['d','i','j','allFields','data','series'], + }, + domain: { + func: function(data, series, allFields) { + return _.uniq(data.map(allFields.series.value)); + }, + posParams: ['data', 'series', 'allFields'], + }, + range: { + func: () => ['red', 'green', 'pink', 'blue'], + }, + }, + //value: d=>nthroot(d.coefficient, 7), + //value: d=>d.coefficient, + /* + ['NA','N/A','null','.'] + .indexOf(d.coefficient && + d.coefficient.toLowerCase() + .trim()) > -1 + ? 0 : d.coefficient || 0, // (set NA = 0) + * / + //label: "Coefficient", + label: "Nonsense series", + scale: d3.scale.ordinal(), + //range: ['#ef8a62','#ddd','#67a9cf'], + //range: ['red', 'green', 'pink', 'blue'], + isField: true, + //domainFuncNeedsExtent: true, + //domainFunc: (data, ext) => [ext[0], 0, ext[1]], + /* + rangeFunc: (layout, prop) => { + prop.scale.rangePoints( + ['#ef8a62','#ddd','#67a9cf']); + }, + domainFunc: (data, prop) => { + var vals = data.map(prop.value).sort(d3.ascending); + return vals; + var preScale = d3.scale.ordinal() + .domain(vals) + .rangePoints([-1, 0, 1]); + + + }, + * / + //range: ['red', 'yellow', 'blue'], + }, + shape: { + value: () => junk++ % 3, + label: "Random", + tooltipOrder: 4, + isField: true, + }, + CIup: { // support CI in both directions + value: d => d.upperBound, + value: d => y(d) - d.upperBoundDiff, + }, + */ + covariateName: { + propName: 'covariateName', + value: d => { + return d.covariateName.split(/:/).shift(); + }, + isColumn: true, + isFacet: true, + colIdx: 0, + tooltipOrder: 7, + label: 'Covariate Name', + isField: true, + }, + covariateValue: { + propName: 'covariateName', + value: d => d.covariateName.split(/:/).pop(), + isColumn: true, + colIdx: 0, + tooltipOrder: 8, + label: 'Covariate Value', + isField: true, + /* + _accessors: { + tooltip: { + posParams: ['d','allFields'], + func: (d, allFields) => { + return { + name: `Covariate value`, + value: allFields.covariateName.accessors.value(d).split(/:/).pop(), + } + }, + }, + }, + */ + }, + /* + conceptId: { + propName: 'conceptId', + isColumn: true, + isFacet: true, + colIdx: 3, + tooltipOrder: 5, + label: 'Concept ID', + needsValueFunc: true, // so ChartProps will make one + // even though this isn't a normal + // zoomScatter field + isField: true, + }, + */ + }; + } + + }); diff --git a/js/components/cohort-comparison-spec-editor.html b/js/components/cohort-comparison-spec-editor.html new file mode 100644 index 000000000..c14faf565 --- /dev/null +++ b/js/components/cohort-comparison-spec-editor.html @@ -0,0 +1,450 @@ +
+
+
+ +
+ + + + + +
+
The target cohort specified (#) was deleted. Please select a different cohort.
+
+
+ +
+ + + + + +
+
The comparator cohort specified (#) was deleted. Please select a different cohort.
+
+ +
+ +
+ + + +
+
+
+
Outcomes
+
+
    + +
  • + + +
  • + +
+
+
+
+ +
+ +
+
+
+ +
+ days from cohort start date +
+
+
+ +
+ days from + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
    +
  • Demographics
  • +
      +
    • + Gender
    • +
    • + Age group (5-year bands)
    • +
    • + Index year
    • +
    • + Index month
    • +
    • + Race
    • +
    • + Ethnicity
    • +
    +
+
    +
  • Conditions
  • +
      +
    • + In prior 30d
    • +
    • + In prior 365d
    • +
    • + In prior 180d within inpatient setting
    • +
    • + All time prior
    • +
    • + Overlapping index date
    • +
    +
+
    +
  • Condition aggregation
  • +
      +
    • + SNOMED
    • +
    • + MedDRA
    • +
    +
+
    +
  • Drugs
  • +
      +
    • + In prior 30d
    • +
    • + In prior 365d
    • +
    • + All time prior
    • +
    • + Overlapping index date
    • +
    +
+
    +
  • Drug aggregation
  • +
      +
    • + Clinical Drug
    • +
    • + Ingredient
    • +
    • + ATC Class
    • +
    +
+
    +
  • Procedures
  • +
      +
    • + In prior 30d
    • +
    • + In prior 365d
    • +
    +
+
    +
  • Measurement
  • +
      +
    • + Existence in prior 30d
    • +
    • + Existence in prior 365d
    • +
    • + Count in prior 365d
    • +
    • + Has latest prior numeric value below normal range
    • +
    • + Has latest prior numeric value above normal range
    • +
    +
+
    +
  • Risk scores
  • +
      +
    • + Charlson
    • +
    • + CHADS2
    • +
    • + DCSI
    • +
    +
+
    +
  • + Concept counts (count of distinct conditions/procedures/visits in history)
  • +
+
    +
  • Interaction terms
  • +
      +
    • + By index year
    • +
    • + By index month
    • +
    +
+
+
+ +
+ + + + + +
+
The concept set specified (#) was deleted. Please select a different concept set.
+
+
+ +
+ + + + + +
+
The concept set specified (#) was deleted. Please select a different concept set.
+
+
+ +
+ + Trim Fraction (1-100%): +
+
+
+ +
+ + How many comparator patients do you want to select for each target patient (within a defined caliper)? + How many strata do you want to use? +
+
+
+
+ +
+ +
+
+
+
+ +
    +
  • Demographics
  • +
      +
    • + Gender
    • +
    • + Age group (5-year bands)
    • +
    • + Index year
    • +
    • + Index month
    • +
    • + Race
    • +
    • + Ethnicity
    • +
    +
+
    +
  • Conditions
  • +
      +
    • + In prior 30d
    • +
    • + In prior 365d
    • +
    • + In prior 180d within inpatient setting
    • +
    • + All time prior
    • +
    • + Overlapping index date
    • +
    +
+
    +
  • Condition aggregation
  • +
      +
    • + SNOMED
    • +
    • + MedDRA
    • +
    +
+
    +
  • Drugs
  • +
      +
    • + In prior 30d
    • +
    • + In prior 365d
    • +
    • + All time prior
    • +
    • + Overlapping index date
    • +
    +
+
    +
  • Drug aggregation
  • +
      +
    • + Clinical Drug
    • +
    • + Ingredient
    • +
    • + ATC Class
    • +
    +
+
    +
  • Procedures
  • +
      +
    • + In prior 30d
    • +
    • + In prior 365d
    • +
    +
+
    +
  • Measurement
  • +
      +
    • + Existence in prior 30d
    • +
    • + Existence in prior 365d
    • +
    • + Count in prior 365d
    • +
    • + Has latest prior numeric value below normal range
    • +
    • + Has latest prior numeric value above normal range
    • +
    +
+
    +
  • Risk scores
  • +
      +
    • + Charlson
    • +
    • + CHADS2
    • +
    • + DCSI
    • +
    +
+
    +
  • + Concept counts (count of distinct conditions/procedures/visits in history)
  • +
+
    +
  • Interaction terms
  • +
      +
    • + By index year
    • +
    • + By index month
    • +
    +
+
+
+ +
+ + + + + +
+
The concept set specified (#) was deleted. Please select a different concept set.
+
+
+ +
+ + + + + +
+
The concept set specified (#) was deleted. Please select a different concept set.
+
+
+
+ +
+ + + + + +
+
The concept set specified (#) was deleted. Please select a different concept set.
+
+
+
+
+ + + + diff --git a/js/components/cohort-comparison-spec-editor.js b/js/components/cohort-comparison-spec-editor.js new file mode 100644 index 000000000..10d5e011f --- /dev/null +++ b/js/components/cohort-comparison-spec-editor.js @@ -0,0 +1,217 @@ +define(['jquery', 'knockout', 'text!./cohort-comparison-spec-editor.html', + 'webapi/CohortDefinitionAPI', 'appConfig', 'typeahead', 'bloodhound', + 'cohortcomparison/ComparativeCohortAnalysis', 'cohortbuilder/options', + 'cohortbuilder/CohortDefinition', 'vocabularyprovider', 'webapi/ComparativeCohortAnalysisAPI', + 'conceptsetbuilder/InputTypes/ConceptSet', 'cohortcomparison/OutcomeDefinition', 'ko.typeahead', ], + function ($, ko, view, cohortDefinitionAPI, config, typeahead, bloodhound, + ComparativeCohortAnalysis, options, CohortDefinition, vocabularyAPI, comparativeCohortAnalysisAPI, + ConceptSet, OutcomeDefinition) { + function cohortComparisonSpecificationEditor(params) { + var self = this; + self.cohortComparison = params.cohortComparison; + self.options = options; + + self.outcomeCohortLoading = ko.observable(false); + self.outcomeLoadingMsg = ko.pureComputed(function () { + if (self.outcomeCohortLoading()) { + return " Loading"; + } else { + return ""; + } + }) + self.outcomeListEmpty = ko.pureComputed(function () { + return (self.cohortComparison().outcomeList().length == 1 && self.cohortComparison().outcomeList()[0].outcomeId() == 0); + }); + + self.cohortDefinitionQueryURL = `${config.webAPIRoot}cohortdefinition/search?searchTerm=%QUERY`; + self.outcomeAutocompleteValue = ko.observable(); + self.outcomeAutocompleteValue.subscribe(function (d) { + if (d != null && d.id != null) { + // Check to see if the outcome is already present. + var match = ko.utils.arrayFirst(self.cohortComparison().outcomeList(), function (item) { + return d.id === item.outcomeId(); + }); + if (!match) { + self.outcomeCohortLoading(true); + // Get the outcome cohort definition information from the server. + cohortDefinitionAPI.getCohortDefinition(d.id).then(function (cohortDefinition) { + od = new OutcomeDefinition(); + od.outcomeId(d.id); + od.outcomeCaption(d.name); + cohortDefinition.expression = JSON.parse(cohortDefinition.expression); + od.outcomeCohortDefinition(new CohortDefinition(cohortDefinition)); + self.cohortComparison().addOutcome(od); + self.outcomeCohortLoading(false); + }); + } else { + console.log('It is already in the list!') + } + } + }); + self.removeOutcomeCohort = function (id) { + console.log('Remove ' + id); + self.cohortComparison().outcomeList.remove(function (item) { + return item.outcomeId() == id; + }); + } + + + self.cohortSelected = function (id) { + $('#modalCohortDefinition').modal('hide'); + cohortDefinitionAPI.getCohortDefinition(id).then(function (cohortDefinition) { + self.targetId(cohortDefinition.id); + self.targetCaption(cohortDefinition.name); + cohortDefinition.expression = JSON.parse(cohortDefinition.expression); + self.targetCohortDefinition(new CohortDefinition(cohortDefinition)); + }); + } + + self.conceptsetSelected = function (d) { + $('#modalConceptSet').modal('hide'); + vocabularyAPI.getConceptSetExpression(d.id).then(function (csExpression) { + self.targetId(d.id); + self.targetCaption(d.name); + var conceptSetData = new ConceptSet({ + id: d.id, + name: d.name, + expression: csExpression + }); + self.targetExpression.removeAll(); + self.targetExpression.push(conceptSetData); + + vocabularyAPI.getConceptSetExpressionSQL(csExpression).then( + function (data) { + self.targetConceptSetSQL(data); + }); + }); + } + + self.chooseTarget = function () { + $('#modalCohortDefinition').modal('show'); + self.targetId = self.cohortComparison().targetComparator().targetId; + self.targetCaption = self.cohortComparison().targetComparator().targetCaption; + self.targetCohortDefinition = self.cohortComparison().targetComparator().targetCohortDefinition; + } + + self.chooseComparator = function () { + $('#modalCohortDefinition').modal('show'); + self.targetId = self.cohortComparison().targetComparator().comparatorId; + self.targetCaption = self.cohortComparison().targetComparator().comparatorCaption; + self.targetCohortDefinition = self.cohortComparison().targetComparator().comparatorCohortDefinition; + } + + self.chooseOutcome = function () { + $('#modalCohortDefinition').modal('show'); + self.targetId = self.cohortComparison().outcome().outcomeId; + self.targetCaption = self.cohortComparison().outcome().outcomeCaption; + self.targetCohortDefinition = self.cohortComparison().outcome().outcomeCohortDefinition; + } + + self.clearTarget = function () { + self.cohortComparison().targetComparator().targetId(0); + self.cohortComparison().targetComparator().targetCaption(null); + self.cohortComparison().targetComparator().targetCohortDefinition(null); + } + + self.clearComparator = function () { + self.cohortComparison().targetComparator().comparatorId(0); + self.cohortComparison().targetComparator().comparatorCaption(null); + self.cohortComparison().targetComparator().comparatorCohortDefinition(null); + } + + self.clearOutcome = function () { + self.cohortComparison().outcome().outcomeId(0); + self.cohortComparison().outcome().outcomeCaption(null); + self.cohortComparison().outcome().outcomeCohortDefinition(null); + } + + self.clearPsExclusion = function () { + self.cohortComparison().targetComparator().psExclusionId(0); + self.cohortComparison().targetComparator().psExclusionCaption(null); + self.cohortComparison().targetComparator().psExclusionConceptSet.removeAll(); + self.cohortComparison().targetComparator().psExclusionConceptSetSQL(null); + } + + self.clearPsInclusion = function () { + self.cohortComparison().targetComparator().psInclusionId(0); + self.cohortComparison().targetComparator().psInclusionCaption(null); + self.cohortComparison().targetComparator().psInclusionConceptSet.removeAll(); + self.cohortComparison().targetComparator().psInclusionConceptSetSQL(null); + } + + self.clearOmExclusion = function () { + self.cohortComparison().analysis().omExclusionId(0); + self.cohortComparison().analysis().omExclusionCaption(null); + self.cohortComparison().analysis().omExclusionConceptSet.removeAll(); + self.cohortComparison().analysis().omExclusionConceptSetSQL(null); + } + + self.clearOmInclusion = function () { + self.cohortComparison().analysis().omInclusionId(0); + self.cohortComparison().analysis().omInclusionCaption(null); + self.cohortComparison().analysis().omInclusionConceptSet.removeAll(); + self.cohortComparison().analysis().omInclusionConceptSetSQL(null); + } + + self.clearNegativeControl = function () { + self.cohortComparison().analysis().negativeControlId(0); + self.cohortComparison().analysis().negativeControlCaption(null); + self.cohortComparison().analysis().negativeControlConceptSet.removeAll(); + self.cohortComparison().analysis().negativeControlConceptSetSQL(null); + } + + self.choosePsExclusion = function () { + $('#modalConceptSet').modal('show'); + self.targetId = self.cohortComparison().targetComparator().psExclusionId; + self.targetCaption = self.cohortComparison().targetComparator().psExclusionCaption; + self.targetExpression = self.cohortComparison().targetComparator().psExclusionConceptSet; + self.targetConceptSetSQL = self.cohortComparison().targetComparator().psExclusionConceptSetSQL; + } + + self.choosePsInclusion = function () { + $('#modalConceptSet').modal('show'); + self.targetId = self.cohortComparison().targetComparator().psInclusionId; + self.targetCaption = self.cohortComparison().targetComparator().psInclusionCaption; + self.targetExpression = self.cohortComparison().targetComparator().psInclusionConceptSet; + self.targetConceptSetSQL = self.cohortComparison().targetComparator().psInclusionConceptSetSQL; + } + + self.chooseOmExclusion = function () { + $('#modalConceptSet').modal('show'); + self.targetId = self.cohortComparison().analysis().omExclusionId; + self.targetCaption = self.cohortComparison().analysis().omExclusionCaption; + self.targetExpression = self.cohortComparison().analysis().omExclusionConceptSet; + self.targetConceptSetSQL = self.cohortComparison().analysis().omExclusionConceptSetSQL; + } + + self.chooseOmInclusion = function () { + $('#modalConceptSet').modal('show'); + self.targetId = self.cohortComparison().analysis().omInclusionId; + self.targetCaption = self.cohortComparison().analysis().omInclusionCaption; + self.targetExpression = self.cohortComparison().analysis().omInclusionConceptSet; + self.targetConceptSetSQL = self.cohortComparison().analysis().omInclusionConceptSetSQL; + } + + self.chooseNegativeControl = function () { + $('#modalConceptSet').modal('show'); + self.targetId = self.cohortComparison().analysis().negativeControlId; + self.targetCaption = self.cohortComparison().analysis().negativeControlCaption; + self.targetExpression = self.cohortComparison().analysis().negativeControlConceptSet; + self.targetConceptSetSQL = self.cohortComparison().analysis().negativeControlConceptSetSQL; + } + + self.chooseConceptSet = function (conceptSetType, observable) { + self.targetObservable = observable; + $('#modalConceptSet').modal('show'); + } + } + + var component = { + viewModel: cohortComparisonSpecificationEditor, + template: view + }; + + ko.components.register('cohort-comparison-spec-editor', component); + return component; + } +); diff --git a/js/ko.typeahead.js b/js/ko.typeahead.js new file mode 100644 index 000000000..8c9c2c21d --- /dev/null +++ b/js/ko.typeahead.js @@ -0,0 +1,93 @@ +define(['jquery', 'knockout', 'bloodhound', 'typeahead'], function ($, ko, bloodhound) { + ko.bindingHandlers.typeahead = { + update: function (element, valueAccessor, allBindings) { + // http://stackoverflow.com/a/19366003/1247130 get value to update properly when typeahead choice is selected. + + var templateName = ko.unwrap(allBindings().templateName); + var mapping = ko.unwrap(allBindings().mappingFunction); + var displayedProperty = ko.unwrap(allBindings().displayKey); + var value = allBindings.get("value"); + + var val = ko.unwrap(valueAccessor()); + var remoteFilter = ko.unwrap(allBindings.get("remoteFilter")); + var wildcard = ko.unwrap(allBindings.get("wildcard")) || '%QUERY'; + var auth = (allBindings.has("authToken")) ? { + "Authorization": "Bearer " + ko.unwrap(allBindings().authToken) + } : {}; + var resultsLimit = allBindings.get("limit") || 10; + var datumTokenizer = ko.unwrap(allBindings.get("datumTokenizer")) || bloodhound.tokenizers.whitespace; + + var localData = null; + var url = null; + var isLocal; + if (Array.isArray(val)) { + localData = val; + isLocal = true; + } else { + url = val; + isLocal = false; + } + + // If the URL is specified, set up the remote filter for suggestions + var suggestions = null; + if (isLocal) { + suggestions = new bloodhound({ + datumTokenizer: datumTokenizer, + queryTokenizer: bloodhound.tokenizers.whitespace, + local: localData + }); + } else { + var remoteData = { + url: url, + wildcard: wildcard, + ajax: { + headers: auth + } + }; + if (remoteFilter) { + remoteData.filter = remoteFilter; + }; + suggestions = new bloodhound({ + datumTokenizer: datumTokenizer, + queryTokenizer: bloodhound.tokenizers.whitespace, + remote: remoteData + }); + } + + suggestions.initialize(); + + $(element).typeahead("destroy"); + + var typeaheadOpts = { + source: suggestions.ttAdapter(), + displayKey: displayedProperty || function (item) { + return item; + }, + limit: resultsLimit + }; + + if (templateName) { + typeaheadOpts.templates = { + suggestion: function (item) { + var temp = document.createElement("div"); + var model = mapping ? mapping(item) : item; + ko.renderTemplate(templateName, model, null, temp, "replaceChildren"); + + return temp; + } + }; + } + + $(element) + .typeahead({ + hint: true, + highlight: true + }, typeaheadOpts) + .on("typeahead:selected typeahead:autocompleted", function (e, suggestion) { + if (value && ko.isObservable(value)) { + value(suggestion); + } + }); + } + }; +}); diff --git a/js/main.js b/js/main.js index 9f1c0dce3..46a700972 100644 --- a/js/main.js +++ b/js/main.js @@ -69,6 +69,8 @@ requirejs.config({ "optional": "plugins/optional", "clipboard": "clipboard.min", "knockout": "knockout.min", + "typeahead": "plugins/typeahead.jquery.min", + "bloodhound": "plugins/bloodhound.min", "ko.sortable": "https://cdnjs.cloudflare.com/ajax/libs/knockout-sortable/1.1.0/knockout-sortable.min", "knockout-mapping": "knockout.mapping", "datatables.net": "jquery.dataTables.min", @@ -93,6 +95,8 @@ requirejs.config({ "cohort-comparison-browser": "components/cohort-comparison-browser", "cohort-comparison-print-friendly": "components/cohort-comparison-print-friendly", "cohort-comparison-r-code": "components/cohort-comparison-r-code", + "cohort-comparison-results": "components/cohort-comparison-results", + "cohort-comparison-spec-editor": "components/cohort-comparison-spec-editor", "cohort-comparison-multi-r-code": "components/cohort-comparison-multi-r-code", "user-bar": "components/user-bar", "report-manager": "components/report-manager", @@ -104,7 +108,7 @@ requirejs.config({ "cohortcomparison": "modules/cohortcomparison", "r-manager": "components/r-manager", "negative-controls": "components/negative-controls", - "nvd3": "nv.d3", + //"nvd3": "nv.d3", "atlascharts": "https://unpkg.com/@ohdsi/atlascharts@1.1.0/dist/atlascharts.min", "jnj_chart": "jnj.chart", // scatterplot is not ported to separate library "lodash": "lodash.4.15.0.full", diff --git a/js/modules/WebAPIProvider/ComparativeCohortAnalysisAPI.js b/js/modules/WebAPIProvider/ComparativeCohortAnalysisAPI.js new file mode 100644 index 000000000..c78809ba7 --- /dev/null +++ b/js/modules/WebAPIProvider/ComparativeCohortAnalysisAPI.js @@ -0,0 +1,68 @@ +define(function (require, exports) { + + var $ = require('jquery'); + var config = require('appConfig'); + + function getComparativeCohortAnalysisList() { + var promise = $.ajax({ + url: config.api.url + 'comparativecohortanalysis', + method: 'GET', + contentType: 'application/json', + error: function (error) { + console.log("Error: " + error); + } + }); + return promise; + } + + function getComparativeCohortAnalysis(id) { + var promise = $.ajax({ + url: config.api.url + 'comparativecohortanalysis/' + id, + method: 'GET', + contentType: 'application/json', + error: function (error) { + console.log("Error: " + error); + } + }); + return promise; + } + + function deleteAnalysis(analysisId) { + var promise = $.ajax({ + url: config.api.url + 'comparativecohortanalysis/' + analysisId, + method: 'DELETE', + contentType: 'application/json', + error: function (error) { + console.log("Error: " + error); + } + }); + return promise; + } + + function saveAnalysis(analysis) { + var cca = analysis.jsonify(); + var json = JSON.stringify(cca); + + var promise = $.ajax({ + url: config.api.url + 'comparativecohortanalysis/' + (analysis.analysisId || ''), + method: analysis.analysisId ? 'PUT' : 'POST', + contentType: 'application/json', + data: json, + dataType: 'json', + error: function (error) { + console.log("Error: " + error); + } + }); + + return promise; + } + + var api = { + deleteAnalysis: deleteAnalysis, + getComparativeCohortAnalysis: getComparativeCohortAnalysis, + getComparativeCohortAnalysisList: getComparativeCohortAnalysisList, + saveAnalysis: saveAnalysis, + } + + return api; +}); diff --git a/js/modules/cohortcomparison/AnalysisDefinition.js b/js/modules/cohortcomparison/AnalysisDefinition.js new file mode 100644 index 000000000..0144fc7be --- /dev/null +++ b/js/modules/cohortcomparison/AnalysisDefinition.js @@ -0,0 +1,570 @@ +define(function (require, exports) { + + var $ = require('jquery'); + var ko = require('knockout'); + var ConceptSet = require('conceptsetbuilder/InputTypes/ConceptSet') + + function AnalysisDefinition(data, conceptSetList) { + var self = this; + var data = data || {}; + var conceptSetData; + var selectedConceptSet; + + // Options + self.modelTypeOptions = [ + { + name: 'Logistic regression', + cmArgValue: '"logistic"', + rate: 'odds', + id: 1 + }, { + name: 'Poisson regression', + cmArgValue: '"poisson"', + rate: 'rate', + id: 2 + }, { + name: 'Cox proportional hazards', + cmArgValue: '"cox"', + rate: 'hazards', + id: 3 + }]; + self.timeAtRiskEndOptions = [ + { + name: 'cohort end date', + id: 1 + }, { + name: 'cohort start date', + id: 0 + }]; + self.trimOptions = [ + { + name: 'None', + id: 0 + }, { + name: 'by Percentile', + id: 1 + }, { + name: 'by Equipoise', + id: 2 + }]; + self.matchingOptions = [ + { + name: 'No matching/stratification', + id: 0 + }, { + name: 'Matching', + id: 1 + }, { + name: 'Stratification', + id: 2 + }]; + + self.id = data.id; + self.description = ko.observable(data.description || null); + self.timeAtRiskStart = ko.observable(data.timeAtRiskStart != null ? data.timeAtRiskStart : 0); + self.timeAtRiskEnd = ko.observable(data.timeAtRiskEnd != null ? data.timeAtRiskEnd : 0); + self.addExposureDaysToEnd = ko.observable(data.addExposureDaysToEnd != null ? data.addExposureDaysToEnd : 1); + self.addExposureDaysToEndFormatted = ko.pureComputed(function () { + return self.addExposureDaysToEnd() == 1; + }) + self.minimumWashoutPeriod = ko.observable(data.minimumWashoutPeriod != null ? data.minimumWashoutPeriod : 0); + self.minimumDaysAtRisk = ko.observable(data.minimumDaysAtRisk != null ? data.minimumDaysAtRisk : 0); + self.rmSubjectsInBothCohorts = ko.observable(data.rmSubjectsInBothCohorts != null ? data.rmSubjectsInBothCohorts : 1); + self.rmSubjectsInBothCohortsFormatted = ko.pureComputed(function () { + return self.rmSubjectsInBothCohorts() == 1; + }); + self.rmPriorOutcomes = ko.observable(data.rmPriorOutcomes != null ? data.rmPriorOutcomes : 1); + self.rmPriorOutcomesFormatted = ko.pureComputed(function () { + return self.rmPriorOutcomes() == 1; + }); + + self.omExclusionId = ko.observable(data.omExclusionId != null ? data.omExclusionId : 0); + self.omExclusionCaption = ko.observable(null); + self.omExclusionConceptSet = ko.observableArray(null); + self.omExclusionConceptSetSQL = ko.observable(null); + if (conceptSetList && conceptSetList.length > 0) { + selectedConceptSet = $.grep(conceptSetList, function (f) { + return f.id == data.omExclusionId; + }); + if (selectedConceptSet.length > 0) { + self.omExclusionCaption(selectedConceptSet[0].name); + conceptSetData = { + id: self.omExclusionId(), + name: self.omExclusionCaption(), + expression: selectedConceptSet[0].expression, + }; + self.omExclusionConceptSet.push(new ConceptSet(conceptSetData)); + self.omExclusionConceptSetSQL(selectedConceptSet[0].sql); + } + } + + self.omInclusionId = ko.observable(data.omInclusionId != null ? data.omInclusionId : 0); + self.omInclusionCaption = ko.observable(null); + self.omInclusionConceptSet = ko.observableArray(null); + self.omInclusionConceptSetSQL = ko.observable(null); + if (conceptSetList && conceptSetList.length > 0) { + selectedConceptSet = $.grep(conceptSetList, function (f) { + return f.id == data.omInclusionId; + }); + if (selectedConceptSet.length > 0) { + self.omInclusionCaption(selectedConceptSet[0].name); + conceptSetData = { + id: self.omInclusionId(), + name: self.omInclusionCaption(), + expression: selectedConceptSet[0].expression, + }; + self.omInclusionConceptSet.push(new ConceptSet(conceptSetData)); + self.omInclusionConceptSetSQL(selectedConceptSet[0].sql); + } + } + + self.negativeControlId = ko.observable(data.negativeControlId != null ? data.negativeControlId : 0); + self.negativeControlCaption = ko.observable(null); + self.negativeControlConceptSet = ko.observableArray(null); + self.negativeControlConceptSetSQL = ko.observable(null); + if (conceptSetList && conceptSetList.length > 0) { + selectedConceptSet = $.grep(conceptSetList, function (f) { + return f.id == data.negativeControlId; + }); + if (selectedConceptSet.length > 0) { + self.negativeControlCaption(selectedConceptSet[0].name); + conceptSetData = { + id: self.negativeControlId(), + name: self.negativeControlCaption(), + expression: selectedConceptSet[0].expression, + }; + self.negativeControlConceptSet.push(new ConceptSet(conceptSetData)); + self.negativeControlConceptSetSQL(selectedConceptSet[0].sql); + } + } + + self.modelType = ko.observable(data.modelType != null ? data.modelType : null); + self.delCovariatesSmallCount = ko.observable(data.delCovariatesSmallCount != null ? data.delCovariatesSmallCount : 100); + + // Derived fields + self.modelTypeRate = function () { + var returnVal = ''; + if (self.modelType() != null) { + returnVal = self.modelTypeOptions.filter(function (item) { + return item.id == self.modelType(); + })[0].rate + } + return returnVal; + } + + self.modelTypeName = function () { + var returnVal = ''; + if (self.modelType() != null) { + returnVal = self.modelTypeOptions.filter(function (item) { + return item.id == self.modelType(); + })[0].name + } + return returnVal; + } + + self.modelTypeCmArgValue = function () { + var returnVal = ''; + if (self.modelType() != null) { + returnVal = self.modelTypeOptions.filter(function (item) { + return item.id == self.modelType(); + })[0].cmArgValue + } + return returnVal; + } + + self.addExposureDaysToEndDescription = function () { + var returnVal = ''; + if (self.addExposureDaysToEnd() != null) { + returnVal = self.timeAtRiskEndOptions.filter(function (item) { + return item.id == self.addExposureDaysToEnd(); + })[0].name + } + return returnVal; + } + + // Propensity Score Settings + self.psAdjustment = ko.observable(data.psAdjustment != null ? data.psAdjustment : 1); + + self.psTrim = ko.observable(data.psTrim != null ? data.psTrim : 0); + self.psTrimFraction = ko.observable(data.psTrimFraction != null ? data.psTrimFraction : 5); + self.psTrimFractionFormatted = ko.pureComputed(function () { + var trimFraction = self.psTrimFraction(); + if (trimFraction > 0) { + trimFraction = trimFraction / 100; + } + if (self.psTrim() == 1) { + return trimFraction; + } + if (self.psTrim() == 2) { + return trimFraction + ", " + (1 - trimFraction); + } + }); + self.psMatch = ko.observable(data.psMatch != null ? data.psMatch : 1); + self.psMatchMaxRatio = ko.observable(data.psMatchMaxRatio != null ? data.psMatchMaxRatio : 1); + self.psStratNumStrata = ko.observable(data.psStratNumStrata != null ? data.psStratNumStrata : 5); + + self.psDemographicsGender = ko.observable((data.psDemographicsGender == 1) || false); + self.psDemographicsRace = ko.observable((data.psDemographicsRace == 1) || false); + self.psDemographicsEthnicity = ko.observable((data.psDemographicsEthnicity == 1) || false); + self.psDemographicsAge = ko.observable((data.psDemographicsAge == 1) || false); + self.psDemographicsYear = ko.observable((data.psDemographicsYear == 1) || false); + self.psDemographicsMonth = ko.observable((data.psDemographicsMonth == 1) || false); + + self.psConditionOcc365d = ko.observable((data.psConditionOcc365d == 1) || false); + self.psConditionOcc30d = ko.observable((data.psConditionOcc30d == 1) || false); + self.psConditionOccInpt180d = ko.observable((data.psConditionOccInpt180d == 1) || false); + + self.psConditionEraEver = ko.observable((data.psConditionEraEver == 1) || false); + self.psConditionEraOverlap = ko.observable((data.psConditionEraOverlap == 1) || false); + self.psConditionGroupMeddra = ko.observable((data.psConditionGroupMeddra == 1) || false); + self.psConditionGroupSnomed = ko.observable((data.psConditionGroupSnomed == 1) || false); + + self.psDrugExposure = ko.observable((data.psDrugExposure == 1) || false); + self.psDrugInPrior30d = ko.observable((data.psDrugExposure30d == 1 || data.psDrugEra30d == 1) || false); + self.psDrugInPrior365d = ko.observable((data.psDrugExposure365d == 1 || data.psDrugEra365d == 1) || false); + + self.psDrugEra = ko.observable((data.psDrugEra == 1) || false); + self.psDrugEraOverlap = ko.observable((data.psDrugEraOverlap == 1) || false); + self.psDrugEraEver = ko.observable((data.psDrugEraEver == 1) || false); + self.psDrugGroup = ko.observable((data.psDrugGroup == 1) || false); + + self.psProcedureOcc365d = ko.observable((data.psProcedureOcc365d == 1) || false); + self.psProcedureOcc30d = ko.observable((data.psProcedureOcc30d == 1) || false); + self.psProcedureGroup = ko.observable((data.psProcedureGroup == 1) || false); + + self.psObservation = ko.observable((data.psObservation == 1) || false); + self.psObservation365d = ko.observable((data.psObservation365d == 1) || false); + self.psObservation30d = ko.observable((data.psObservation30d == 1) || false); + self.psObservationCount365d = ko.observable((data.psObservationCount365d == 1) || false); + + self.psMeasurement365d = ko.observable((data.psMeasurement365d == 1) || false); + self.psMeasurement30d = ko.observable((data.psMeasurement30d == 1) || false); + self.psMeasurementCount365d = ko.observable((data.psMeasurementCount365d == 1) || false); + self.psMeasurementBelow = ko.observable((data.psMeasurementBelow == 1) || false); + self.psMeasurementAbove = ko.observable((data.psMeasurementAbove == 1) || false); + + self.psConceptCounts = ko.observable((data.psConceptCounts == 1) || false); + + self.psRiskScoresCharlson = ko.observable((data.psRiskScoresCharlson == 1) || false); + self.psRiskScoresDcsi = ko.observable((data.psRiskScoresDcsi == 1) || false); + self.psRiskScoresChads2 = ko.observable((data.psRiskScoresChads2 == 1) || false); + self.psRiskScoresChads2vasc = ko.observable((data.psRiskScoresChads2vasc == 1) || false); + + self.psInteractionYear = ko.observable((data.psInteractionYear == 1) || false); + self.psInteractionMonth = ko.observable((data.psInteractionMonth == 1) || false); + + // Propensity Score Settings - Derived + self.psDemographics = ko.pureComputed(function () { + return (self.psDemographicsGender() || self.psDemographicsRace() || self.psDemographicsEthnicity() || self.psDemographicsAge() || self.psDemographicsYear() || self.psDemographicsMonth()); + }); + self.psDemographicsIndeterminate = function () { + var propCount = 0; + propCount += self.psDemographicsGender() | 0; + propCount += self.psDemographicsRace() | 0; + propCount += self.psDemographicsEthnicity() | 0; + propCount += self.psDemographicsAge() | 0; + propCount += self.psDemographicsYear() | 0; + propCount += self.psDemographicsMonth() | 0; + return (propCount > 0 && propCount < 6); + } + self.psStrat = ko.pureComputed(function () { + return self.psMatch() == 2; + }); + self.psStratOrMatch = ko.pureComputed(function () { + return self.psMatch() > 0; + }) + self.psConditionOcc = ko.pureComputed(function () { + return (self.psConditionOcc365d() || self.psConditionOcc30d() || self.psConditionOccInpt180d()) + }); + self.psConditionEra = ko.pureComputed(function () { + return (self.psConditionEraEver() || self.psConditionEraOverlap()) + }); + self.psConditionGroup = ko.pureComputed(function () { + return (self.psConditionGroupMeddra() || self.psConditionGroupSnomed()) + }); + self.psCondition = ko.pureComputed(function () { + return (self.psConditionOcc() || self.psConditionEra()) + }); + self.psDrug = ko.pureComputed(function () { + return (self.psDrugInPrior30d() || self.psDrugInPrior365d() || self.psDrugEraOverlap() || self.psDrugEraEver()) + }); + self.psDrugAggregation = ko.pureComputed(function () { + return (self.psDrugExposure() || self.psDrugEra() || self.psDrugGroup()) + }); + self.psDrugExposure365d = ko.pureComputed(function () { + return (self.psDrugExposure() && self.psDrugInPrior365d()) + }); + self.psDrugExposure30d = ko.pureComputed(function () { + return (self.psDrugExposure() && self.psDrugInPrior30d()) + }); + self.psDrugEra365d = ko.pureComputed(function () { + return (self.psDrugEra() && self.psDrugInPrior365d()) + }); + self.psDrugEra30d = ko.pureComputed(function () { + return (self.psDrugEra() && self.psDrugInPrior30d()) + }); + self.psProcedureOcc = ko.pureComputed(function () { + return (self.psProcedureOcc365d() || self.psProcedureOcc30d() || self.psProcedureGroup()) + }) + self.psMeasurement = ko.pureComputed(function () { + return (self.psMeasurement365d() || self.psMeasurement30d() || self.psMeasurementCount365d() || self.psMeasurementBelow() || self.psMeasurementAbove()) + }) + self.psRiskScores = ko.pureComputed(function () { + return (self.psRiskScoresCharlson() || self.psRiskScoresDcsi() || self.psRiskScoresChads2() || self.psRiskScoresChads2vasc()) + }) + self.psInteraction = ko.pureComputed(function () { + return (self.psInteractionYear() || self.psInteractionMonth()) + }) + + // Outcome model settings + self.omCovariates = ko.observable(data.omCovariates != null ? data.omCovariates : 0); + + self.omTrim = ko.observable(data.omTrim != null ? data.omTrim : 0); + self.omTrimFraction = ko.observable(data.omTrimFraction != null ? data.omTrimFraction : 5); + self.omMatch = ko.observable(data.omMatch != null ? data.omMatch : 1); + self.omMatchMaxRatio = ko.observable(data.omMatchMaxRatio != null ? data.omMatchMaxRatio : 1); + self.omStratNumStrata = ko.observable(data.omStratNumStrata != null ? data.omStratNumStrata : 5); + + self.omDemographicsGender = ko.observable((data.omDemographicsGender == 1) || false); + self.omDemographicsRace = ko.observable((data.omDemographicsRace == 1) || false); + self.omDemographicsEthnicity = ko.observable((data.omDemographicsEthnicity == 1) || false); + self.omDemographicsAge = ko.observable((data.omDemographicsAge == 1) || false); + self.omDemographicsYear = ko.observable((data.omDemographicsYear == 1) || false); + self.omDemographicsMonth = ko.observable((data.omDemographicsMonth == 1) || false); + + self.omConditionOcc365d = ko.observable((data.omConditionOcc365d == 1) || false); + self.omConditionOcc30d = ko.observable((data.omConditionOcc30d == 1) || false); + self.omConditionOccInpt180d = ko.observable((data.omConditionOccInpt180d == 1) || false); + + self.omConditionEraEver = ko.observable((data.omConditionEraEver == 1) || false); + self.omConditionEraOverlap = ko.observable((data.omConditionEraOverlap == 1) || false); + self.omConditionGroupMeddra = ko.observable((data.omConditionGroupMeddra == 1) || false); + self.omConditionGroupSnomed = ko.observable((data.omConditionGroupSnomed == 1) || false); + + self.omDrugExposure = ko.observable((data.omDrugExposure == 1) || false); + self.omDrugInPrior30d = ko.observable((data.omDrugExposure30d == 1 || data.omDrugEraExposure30d == 1) || false); + self.omDrugInPrior365d = ko.observable((data.omDrugExposure365d == 1 || data.omDrugEraExposure365d == 1) || false); + + self.omDrugEra = ko.observable((data.omDrugEra == 1) || false); + self.omDrugEraOverlap = ko.observable((data.omDrugEraOverlap == 1) || false); + self.omDrugEraEver = ko.observable((data.omDrugEraEver == 1) || false); + self.omDrugGroup = ko.observable((data.omDrugGroup == 1) || false); + + self.omProcedureOcc365d = ko.observable((data.omProcedureOcc365d == 1) || false); + self.omProcedureOcc30d = ko.observable((data.omProcedureOcc30d == 1) || false); + self.omProcedureGroup = ko.observable((data.omProcedureGroup == 1) || false); + + self.omObservation = ko.observable((data.omObservation == 1) || false); + self.omObservation365d = ko.observable((data.omObservation365d == 1) || false); + self.omObservation30d = ko.observable((data.omObservation30d == 1) || false); + self.omObservationCount365d = ko.observable((data.omObservationCount365d == 1) || false); + + self.omMeasurement365d = ko.observable((data.omMeasurement365d == 1) || false); + self.omMeasurement30d = ko.observable((data.omMeasurement30d == 1) || false); + self.omMeasurementCount365d = ko.observable((data.omMeasurementCount365d == 1) || false); + self.omMeasurementBelow = ko.observable((data.omMeasurementBelow == 1) || false); + self.omMeasurementAbove = ko.observable((data.omMeasurementAbove == 1) || false); + + self.omConceptCounts = ko.observable((data.omConceptCounts == 1) || false); + + self.omRiskScoresCharlson = ko.observable((data.omRiskScoresCharlson == 1) || false); + self.omRiskScoresDcsi = ko.observable((data.omRiskScoresDcsi == 1) || false); + self.omRiskScoresChads2 = ko.observable((data.omRiskScoresChads2 == 1) || false); + self.omRiskScoresChads2vasc = ko.observable((data.omRiskScoresChads2vasc == 1) || false); + + self.omInteractionYear = ko.observable((data.omInteractionYear == 1) || false); + self.omInteractionMonth = ko.observable((data.omInteractionMonth == 1) || false); + + // Outcome Model Settings - Derived + self.omDemographics = ko.pureComputed(function () { + return (self.omDemographicsGender() || self.omDemographicsRace() || self.omDemographicsEthnicity() || self.omDemographicsAge() || self.omDemographicsYear() || self.omDemographicsMonth()); + }); + self.omDemographicsIndeterminate = function () { + var propCount = 0; + propCount += self.omDemographicsGender() | 0; + propCount += self.omDemographicsRace() | 0; + propCount += self.omDemographicsEthnicity() | 0; + propCount += self.omDemographicsAge() | 0; + propCount += self.omDemographicsYear() | 0; + propCount += self.omDemographicsMonth() | 0; + return (propCount > 0 && propCount < 6); + } + self.omStrat = ko.pureComputed(function () { + return !self.omMatch(); + }); + self.omConditionOcc = ko.pureComputed(function () { + return (self.omConditionOcc365d() || self.omConditionOcc30d() || self.omConditionOccInpt180d()) + }); + self.omConditionEra = ko.pureComputed(function () { + return (self.omConditionEraEver() || self.omConditionEraOverlap()) + }); + self.omConditionGroup = ko.pureComputed(function () { + return (self.omConditionGroupMeddra() || self.omConditionGroupSnomed()) + }); + self.omCondition = ko.pureComputed(function () { + return (self.omConditionOcc() || self.omConditionEra()) + }); + self.omDrug = ko.pureComputed(function () { + return (self.omDrugInPrior30d() || self.omDrugInPrior365d() || self.omDrugEraOverlap() || self.omDrugEraEver()) + }); + self.omDrugAggregation = ko.pureComputed(function () { + return (self.omDrugExposure() || self.omDrugEra() || self.omDrugGroup()) + }); + self.omDrugExposure365d = ko.pureComputed(function () { + return (self.omDrugExposure() && self.omDrugInPrior365d()) + }); + self.omDrugExposure30d = ko.pureComputed(function () { + return (self.omDrugExposure() && self.omDrugInPrior30d()) + }); + self.omDrugEra365d = ko.pureComputed(function () { + return (self.omDrugEra() && self.omDrugInPrior365d()) + }); + self.omDrugEra30d = ko.pureComputed(function () { + return (self.omDrugEra() && self.omDrugInPrior30d()) + }); + self.omProcedureOcc = ko.pureComputed(function () { + return (self.omProcedureOcc365d() || self.omProcedureOcc30d() || self.omProcedureGroup()) + }); + self.omMeasurement = ko.pureComputed(function () { + return (self.omMeasurement365d() || self.omMeasurement30d() || self.omMeasurementCount365d() || self.omMeasurementBelow() || self.omMeasurementAbove()) + }); + self.omRiskScores = ko.pureComputed(function () { + return (self.omRiskScoresCharlson() || self.omRiskScoresDcsi() || self.omRiskScoresChads2() || self.omRiskScoresChads2vasc()) + }); + self.omInteraction = ko.pureComputed(function () { + return (self.omInteractionYear() || self.omInteractionMonth()) + }); + + self.jsonify = function () { + return { + id: self.id || null, + description: self.description(), + modelType: self.modelType(), + timeAtRiskStart: self.timeAtRiskStart(), + timeAtRiskEnd: self.timeAtRiskEnd(), + addExposureDaysToEnd: self.addExposureDaysToEnd(), + minimumWashoutPeriod: self.minimumWashoutPeriod(), + minimumDaysAtRisk: self.minimumDaysAtRisk(), + rmSubjectsInBothCohorts: self.rmSubjectsInBothCohorts(), + rmPriorOutcomes: self.rmPriorOutcomes(), + psAdjustment: self.psAdjustment(), + psDemographics: self.psDemographics() | 0, + psDemographicsGender: self.psDemographicsGender() | 0, + psDemographicsRace: self.psDemographicsRace() | 0, + psDemographicsEthnicity: self.psDemographicsEthnicity() | 0, + psDemographicsAge: self.psDemographicsAge() | 0, + psDemographicsYear: self.psDemographicsYear() | 0, + psDemographicsMonth: self.psDemographicsMonth() | 0, + psTrim: self.psTrim(), + psTrimFraction: self.psTrimFraction(), + psMatch: self.psMatch(), + psMatchMaxRatio: self.psMatchMaxRatio(), + psStrat: self.psStrat() | 0, + psStratNumStrata: self.psStratNumStrata(), + psConditionOcc: self.psConditionOcc() | 0, + psConditionOcc365d: self.psConditionOcc365d() | 0, + psConditionOcc30d: self.psConditionOcc30d() | 0, + psConditionOccInpt180d: self.psConditionOccInpt180d() | 0, + psConditionEra: self.psConditionEra() | 0, + psConditionEraEver: self.psConditionEraEver() | 0, + psConditionEraOverlap: self.psConditionEraOverlap() | 0, + psConditionGroup: self.psConditionGroup() | 0, + psConditionGroupMeddra: self.psConditionGroupMeddra() | 0, + psConditionGroupSnomed: self.psConditionGroupSnomed() | 0, + psDrugExposure: self.psDrugExposure() | 0, + psDrugExposure365d: self.psDrugExposure365d() | 0, + psDrugExposure30d: self.psDrugExposure30d() | 0, + psDrugEra: self.psDrugEra() | 0, + psDrugEra365d: self.psDrugEra365d() | 0, + psDrugEra30d: self.psDrugEra30d() | 0, + psDrugEraOverlap: self.psDrugEraOverlap() | 0, + psDrugEraEver: self.psDrugEraEver() | 0, + psDrugGroup: self.psDrugGroup() | 0, + psProcedureOcc: self.psProcedureOcc() | 0, + psProcedureOcc365d: self.psProcedureOcc365d() | 0, + psProcedureOcc30d: self.psProcedureOcc30d() | 0, + psProcedureGroup: self.psProcedureGroup() | 0, + psObservation: self.psObservation() | 0, + psObservation365d: self.psObservation365d() | 0, + psObservation30d: self.psObservation30d() | 0, + psObservationCount365d: self.psObservationCount365d() | 0, + psMeasurement: self.psMeasurement() | 0, + psMeasurement365d: self.psMeasurement365d() | 0, + psMeasurement30d: self.psMeasurement30d() | 0, + psMeasurementCount365d: self.psMeasurementCount365d() | 0, + psMeasurementBelow: self.psMeasurementBelow() | 0, + psMeasurementAbove: self.psMeasurementAbove() | 0, + psConceptCounts: self.psConceptCounts() | 0, + psRiskScores: self.psRiskScores() | 0, + psRiskScoresCharlson: self.psRiskScoresCharlson() | 0, + psRiskScoresDcsi: self.psRiskScoresDcsi() | 0, + psRiskScoresChads2: self.psRiskScoresChads2() | 0, + psRiskScoresChads2vasc: self.psRiskScoresChads2vasc() | 0, + psInteractionYear: self.psInteractionYear() | 0, + psInteractionMonth: self.psInteractionMonth() | 0, + omCovariates: self.omCovariates(), + omExclusionId: self.omExclusionId(), + omInclusionId: self.omInclusionId(), + omDemographics: self.omDemographics() | 0, + omDemographicsGender: self.omDemographicsGender() | 0, + omDemographicsRace: self.omDemographicsRace() | 0, + omDemographicsEthnicity: self.omDemographicsEthnicity() | 0, + omDemographicsAge: self.omDemographicsAge() | 0, + omDemographicsYear: self.omDemographicsYear() | 0, + omDemographicsMonth: self.omDemographicsMonth() | 0, + omTrim: self.omTrim(), + omTrimFraction: self.omTrimFraction(), + omMatch: self.omMatch(), + omMatchMaxRatio: self.omMatchMaxRatio(), + omStrat: self.omStrat() | 0, + omStratNumStrata: self.omStratNumStrata(), + omConditionOcc: self.omConditionOcc() | 0, + omConditionOcc365d: self.omConditionOcc365d() | 0, + omConditionOcc30d: self.omConditionOcc30d() | 0, + omConditionOccInpt180d: self.omConditionOccInpt180d() | 0, + omConditionEra: self.omConditionEra() | 0, + omConditionEraEver: self.omConditionEraEver() | 0, + omConditionEraOverlap: self.omConditionEraOverlap() | 0, + omConditionGroup: self.omConditionGroup() | 0, + omConditionGroupMeddra: self.omConditionGroupMeddra() | 0, + omConditionGroupSnomed: self.omConditionGroupSnomed() | 0, + omDrugExposure: self.omDrugExposure() | 0, + omDrugExposure365d: self.omDrugExposure365d() | 0, + omDrugExposure30d: self.omDrugExposure30d() | 0, + omDrugEra: self.omDrugEra() | 0, + omDrugEra365d: self.omDrugEra365d() | 0, + omDrugEra30d: self.omDrugEra30d() | 0, + omDrugEraOverlap: self.omDrugEraOverlap() | 0, + omDrugEraEver: self.omDrugEraEver() | 0, + omDrugGroup: self.omDrugGroup() | 0, + omProcedureOcc: self.omProcedureOcc() | 0, + omProcedureOcc365d: self.omProcedureOcc365d() | 0, + omProcedureOcc30d: self.omProcedureOcc30d() | 0, + omProcedureGroup: self.omProcedureGroup() | 0, + omObservation: self.omObservation() | 0, + omObservation365d: self.omObservation365d() | 0, + omObservation30d: self.omObservation30d() | 0, + omObservationCount365d: self.omObservationCount365d() | 0, + omMeasurement: self.omMeasurement() | 0, + omMeasurement365d: self.omMeasurement365d() | 0, + omMeasurement30d: self.omMeasurement30d() | 0, + omMeasurementCount365d: self.omMeasurementCount365d() | 0, + omMeasurementBelow: self.omMeasurementBelow() | 0, + omMeasurementAbove: self.omMeasurementAbove() | 0, + omConceptCounts: self.omConceptCounts() | 0, + omRiskScores: self.omRiskScores() | 0, + omRiskScoresCharlson: self.omRiskScoresCharlson() | 0, + omRiskScoresDcsi: self.omRiskScoresDcsi() | 0, + omRiskScoresChads2: self.omRiskScoresChads2() | 0, + omRiskScoresChads2vasc: self.omRiskScoresChads2vasc() | 0, + omInteractionYear: self.omInteractionYear() | 0, + omInteractionMonth: self.omInteractionMonth() | 0, + delCovariatesSmallCount: self.delCovariatesSmallCount(), + negativeControlId: self.negativeControlId() + }; + } + + } + + return AnalysisDefinition; +}); diff --git a/js/modules/cohortcomparison/ComparativeCohortAnalysis.js b/js/modules/cohortcomparison/ComparativeCohortAnalysis.js index 0811c07a7..d18a4e673 100644 --- a/js/modules/cohortcomparison/ComparativeCohortAnalysis.js +++ b/js/modules/cohortcomparison/ComparativeCohortAnalysis.js @@ -1,462 +1,164 @@ define(function (require, exports) { var ko = require('knockout'); - var CohortDefinition = require('cohortbuilder/CohortDefinition') - var ConceptSet = require('conceptsetbuilder/InputTypes/ConceptSet') + var TargetComparatorDefinition = require('cohortcomparison/TargetComparatorDefinition') + var OutcomeDefinition = require('cohortcomparison/OutcomeDefinition') + var AnalysisDefinition = require('cohortcomparison/AnalysisDefinition') function ComparativeCohortAnalysis(data) { var self = this; var data = data || {}; - // Options - self.modelTypeOptions = [{name: 'Logistic regression', cmArgValue: '"logistic"', rate: 'odds', id: 1}, {name: 'Poisson regression', cmArgValue: '"poisson"', rate: 'rate', id: 2}, {name: 'Cox proportional hazards', cmArgValue: '"cox"', rate: 'hazards', id: 3}]; - self.timeAtRiskEndOptions = [{name: 'cohort end date', id: 1}, {name: 'cohort start date', id: 0}]; - self.trimOptions = [{name: 'None', id: 0}, {name: 'by Percentile', id: 1}, {name: 'by Equipoise', id: 2}]; - self.matchingOptions = [{name: 'No matching/stratification', id: 0}, {name: 'Matching', id: 1}, {name: 'Stratification', id: 2}]; - - // Properties + // Properties self.analysisId = data.analysisId || null; self.name = ko.observable(data.name || null); - self.nameMultiLine = ko.pureComputed(function() { - var maxLength = 45; - var nameFormatted = []; - if (self.name() && self.name().length > 0) { + self.nameMultiLine = ko.pureComputed(function () { + var maxLength = 45; + var nameFormatted = []; + if (self.name() && self.name().length > 0) { var nameSplit = self.name().split(" "); var curName = ""; - for(i=0; i < nameSplit.length; i++) { + for (i = 0; i < nameSplit.length; i++) { if (curName.length > maxLength) { nameFormatted.push(curName); curName = ""; } curName += nameSplit[i] + " "; } - nameFormatted.push(curName); - } - return nameFormatted; - }) - self.timeAtRiskStart = ko.observable(data.timeAtRiskStart != null ? data.timeAtRiskStart : 0); - self.timeAtRiskEnd = ko.observable(data.timeAtRiskEnd != null ? data.timeAtRiskEnd : 0); - self.addExposureDaysToEnd = ko.observable(data.addExposureDaysToEnd != null ? data.addExposureDaysToEnd : 1); - self.addExposureDaysToEndFormatted = ko.pureComputed(function() { - return self.addExposureDaysToEnd() == 1; - }) - self.minimumWashoutPeriod = ko.observable(data.minimumWashoutPeriod != null ? data.minimumWashoutPeriod : 0); - self.minimumDaysAtRisk = ko.observable(data.minimumDaysAtRisk != null ? data.minimumDaysAtRisk : 0); - self.rmSubjectsInBothCohorts = ko.observable(data.rmSubjectsInBothCohorts != null ? data.rmSubjectsInBothCohorts : 1); - self.rmSubjectsInBothCohortsFormatted = ko.pureComputed(function() { - return self.rmSubjectsInBothCohorts() == 1; - }); - self.rmPriorOutcomes = ko.observable(data.rmPriorOutcomes != null ? data.rmPriorOutcomes : 1); - self.rmPriorOutcomesFormatted = ko.pureComputed(function () { - return self.rmPriorOutcomes() == 1; - }); - - self.treatmentId = ko.observable(data.treatmentId != null ? data.treatmentId : 0); - self.treatmentCaption = ko.observable(data.treatmentCaption != null ? data.treatmentCaption : null); - if (data.treatmentCohortDefinition != null) { - jsonCohortDefinition = JSON.parse(data.treatmentCohortDefinition); - self.treatmentCohortDefinition = ko.observable(new CohortDefinition(jsonCohortDefinition)); - } else { - self.treatmentCohortDefinition = ko.observable(null); - } - - self.comparatorId = ko.observable(data.comparatorId != null ? data.comparatorId : 0); - self.comparatorCaption = ko.observable(data.comparatorCaption != null ? data.comparatorCaption : null); - if (data.comparatorCohortDefinition != null) { - jsonCohortDefinition = JSON.parse(data.comparatorCohortDefinition); - self.comparatorCohortDefinition = ko.observable(new CohortDefinition(jsonCohortDefinition)); - } else { - self.comparatorCohortDefinition = ko.observable(null); - } - - self.outcomeId = ko.observable(data.outcomeId != null ? data.outcomeId : 0); - self.outcomeCaption = ko.observable(data.outcomeCaption != null ? data.outcomeCaption : null); - if (data.outcomeCohortDefinition != null) { - jsonCohortDefinition = JSON.parse(data.outcomeCohortDefinition); - self.outcomeCohortDefinition = ko.observable(new CohortDefinition(jsonCohortDefinition)); - } else { - self.outcomeCohortDefinition = ko.observable(null); - } - - self.psExclusionId = ko.observable(data.psExclusionId != null ? data.psExclusionId : 0); - self.psExclusionCaption = ko.observable(data.psExclusionCaption != null ? data.psExclusionCaption : null); - self.psExclusionConceptSet = ko.observableArray(null); - if (self.psExclusionId() > 0) { - var conceptSetData = { - id: self.psExclusionId(), - name: self.psExclusionCaption(), - expression: data.psExclusionConceptSet - }; - self.psExclusionConceptSet.push(new ConceptSet(conceptSetData)); - self.psExclusionConceptSetSQL = ko.observable(data.psExclusionConceptSetSql) - } else { - self.psExclusionConceptSetSQL = ko.observable(null); - } - - self.psInclusionId = ko.observable(data.psInclusionId != null ? data.psInclusionId : 0); - self.psInclusionCaption = ko.observable(data.psInclusionCaption != null ? data.psInclusionCaption : null); - self.psInclusionConceptSet = ko.observableArray(null); - if (self.psInclusionId() > 0) { - var conceptSetData = { - id: self.psInclusionId(), - name: self.psInclusionCaption(), - expression: data.psInclusionConceptSet - }; - self.psInclusionConceptSet.push(new ConceptSet(conceptSetData)); - self.psInclusionConceptSetSQL = ko.observable(data.psInclusionConceptSetSql) - } else { - self.psInclusionConceptSetSQL = ko.observable(null); - } - - self.omExclusionId = ko.observable(data.omExclusionId != null ? data.omExclusionId : 0); - self.omExclusionCaption = ko.observable(data.omExclusionCaption != null ? data.omExclusionCaption : null); - self.omExclusionConceptSet = ko.observableArray(null); - if (self.omExclusionId() > 0) { - var conceptSetData = { - id: self.omExclusionId(), - name: self.omExclusionCaption(), - expression: data.omExclusionConceptSet - }; - self.omExclusionConceptSet.push(new ConceptSet(conceptSetData)); - self.omExclusionConceptSetSQL = ko.observable(data.omExclusionConceptSetSql) - } else { - self.omExclusionConceptSetSQL = ko.observable(null); - } - - self.omInclusionId = ko.observable(data.omInclusionId != null ? data.omInclusionId : 0); - self.omInclusionCaption = ko.observable(data.omExclusionCaption != null ? data.omExclusionCaption : null); - self.omInclusionConceptSet = ko.observableArray(null); - if (self.omInclusionId() > 0) { - var conceptSetData = { - id: self.omInclusionId(), - name: self.omInclusionCaption(), - expression: data.omInclusionConceptSet - }; - self.omInclusionConceptSet.push(new ConceptSet(conceptSetData)); - self.omInclusionConceptSetSQL = ko.observable(data.omInclusionConceptSetSql) - } else { - self.omInclusionConceptSetSQL = ko.observable(null); - } - - self.negativeControlId = ko.observable(data.negativeControlId != null ? data.negativeControlId : 0); - self.negativeControlCaption = ko.observable(data.negativeControlCaption != null ? data.negativeControlCaption : null); - self.negativeControlConceptSet = ko.observableArray(null); - if (self.negativeControlId() > 0) { - var conceptSetData = { - id: self.negativeControlId(), - name: self.negativeControlCaption(), - expression: data.negativeControlConceptSet - }; - self.negativeControlConceptSet.push(new ConceptSet(conceptSetData)); - self.negativeControlConceptSetSQL = ko.observable(data.negativeControlConceptSetSql) - } else { - self.negativeControlConceptSetSQL = ko.observable(null); - } - - self.modelType = ko.observable(data.modelType != null ? data.modelType : null); - self.delCovariatesSmallCount = ko.observable(data.delCovariatesSmallCount != null ? data.delCovariatesSmallCount : 100); - - // Derived fields - self.modelTypeRate = function() { - returnVal = ''; - if(self.modelType() != null) { - returnVal = self.modelTypeOptions.filter(function(item) { - return item.id == self.modelType(); - })[0].rate - } - return returnVal; - } - - self.modelTypeName = function() { - returnVal = ''; - if(self.modelType() != null) { - returnVal = self.modelTypeOptions.filter(function(item) { - return item.id == self.modelType(); - })[0].name - } - return returnVal; - } - - self.modelTypeCmArgValue = function() { - returnVal = ''; - if(self.modelType() != null) { - returnVal = self.modelTypeOptions.filter(function(item) { - return item.id == self.modelType(); - })[0].cmArgValue - } - return returnVal; - } - - self.addExposureDaysToEndDescription = function() { - returnVal = ''; - if(self.addExposureDaysToEnd() != null) { - returnVal = self.timeAtRiskEndOptions.filter(function(item) { - return item.id == self.addExposureDaysToEnd(); - })[0].name - } - return returnVal; - } - - self.readyForDisplay = function() { - return (self.comparatorId() != null && self.comparatorId() > 0 && - self.treatmentId() != null && self.treatmentId() > 0 && - self.outcomeId() != null && self.outcomeId() > 0 && - self.modelType() != null) - } - - - // Propensity Score Settings - self.psAdjustment = ko.observable(data.psAdjustment != null ? data.psAdjustment : 1); - - self.psTrim = ko.observable(data.psTrim != null ? data.psTrim : 0); - self.psTrimFraction = ko.observable(data.psTrimFraction != null ? data.psTrimFraction : 5); - self.psTrimFractionFormatted = ko.pureComputed(function() { - var trimFraction = self.psTrimFraction(); - if (trimFraction > 0) { - trimFraction = trimFraction / 100; - } - if (self.psTrim() == 1) { - return trimFraction; - } - if (self.psTrim() == 2) { - return trimFraction + ", " + (1 - trimFraction); - } - }); - self.psMatch = ko.observable(data.psMatch != null ? data.psMatch : 1); - self.psMatchMaxRatio = ko.observable(data.psMatchMaxRatio != null ? data.psMatchMaxRatio : 1); - self.psStratNumStrata = ko.observable(data.psStratNumStrata != null ? data.psStratNumStrata : 5); - - self.psDemographicsGender = ko.observable((data.psDemographicsGender == 1)|| false); - self.psDemographicsRace = ko.observable((data.psDemographicsRace == 1)||false); - self.psDemographicsEthnicity = ko.observable((data.psDemographicsEthnicity == 1)||false); - self.psDemographicsAge = ko.observable((data.psDemographicsAge == 1)||false); - self.psDemographicsYear = ko.observable((data.psDemographicsYear == 1)||false); - self.psDemographicsMonth = ko.observable((data.psDemographicsMonth == 1)||false); - - self.psConditionOcc365d = ko.observable((data.psConditionOcc365d == 1)||false); - self.psConditionOcc30d = ko.observable((data.psConditionOcc30d == 1)||false); - self.psConditionOccInpt180d = ko.observable((data.psConditionOccInpt180d == 1)||false); - - self.psConditionEraEver = ko.observable((data.psConditionEraEver == 1)||false); - self.psConditionEraOverlap = ko.observable((data.psConditionEraOverlap == 1)||false); - self.psConditionGroupMeddra = ko.observable((data.psConditionGroupMeddra == 1)||false); - self.psConditionGroupSnomed = ko.observable((data.psConditionGroupSnomed == 1)||false); - - self.psDrugExposure = ko.observable((data.psDrugExposure == 1)||false); - self.psDrugInPrior30d = ko.observable((data.psDrugExposure30d == 1 || data.psDrugEra30d == 1) || false); - self.psDrugInPrior365d = ko.observable((data.psDrugExposure365d == 1 || data.psDrugEra365d == 1) || false); - - self.psDrugEra = ko.observable((data.psDrugEra == 1)||false); - self.psDrugEraOverlap = ko.observable((data.psDrugEraOverlap == 1)||false); - self.psDrugEraEver = ko.observable((data.psDrugEraEver == 1)||false); - self.psDrugGroup = ko.observable((data.psDrugGroup == 1)||false); - - self.psProcedureOcc365d = ko.observable((data.psProcedureOcc365d == 1)||false); - self.psProcedureOcc30d = ko.observable((data.psProcedureOcc30d == 1)||false); - self.psProcedureGroup = ko.observable((data.psProcedureGroup == 1)||false); - - self.psObservation = ko.observable((data.psObservation == 1)||false); - self.psObservation365d = ko.observable((data.psObservation365d == 1)||false); - self.psObservation30d = ko.observable((data.psObservation30d == 1)||false); - self.psObservationCount365d = ko.observable((data.psObservationCount365d == 1)||false); - - self.psMeasurement365d = ko.observable((data.psMeasurement365d == 1)||false); - self.psMeasurement30d = ko.observable((data.psMeasurement30d == 1)||false); - self.psMeasurementCount365d = ko.observable((data.psMeasurementCount365d == 1)||false); - self.psMeasurementBelow = ko.observable((data.psMeasurementBelow == 1)||false); - self.psMeasurementAbove = ko.observable((data.psMeasurementAbove == 1)||false); - - self.psConceptCounts = ko.observable((data.psConceptCounts == 1)||false); - - self.psRiskScoresCharlson = ko.observable((data.psRiskScoresCharlson == 1)||false); - self.psRiskScoresDcsi = ko.observable((data.psRiskScoresDcsi == 1)||false); - self.psRiskScoresChads2 = ko.observable((data.psRiskScoresChads2 == 1)||false); - self.psRiskScoresChads2vasc = ko.observable((data.psRiskScoresChads2vasc == 1)||false); - - self.psInteractionYear = ko.observable((data.psInteractionYear == 1)||false); - self.psInteractionMonth = ko.observable((data.psInteractionMonth == 1)||false); - - // Propensity Score Settings - Derived - self.psDemographics = ko.pureComputed(function () { - return (self.psDemographicsGender() || self.psDemographicsRace() || self.psDemographicsEthnicity() || self.psDemographicsAge() || self.psDemographicsYear() || self.psDemographicsMonth()); + nameFormatted.push(curName); + } + return nameFormatted; + }) + + self.targetComparatorList = ko.observableArray(); + if (data.targetComparatorList && data.targetComparatorList.length > 0) { + for (var i = 0; i < data.targetComparatorList.length; i++) { + var tc = new TargetComparatorDefinition(data.targetComparatorList[i], data.cohortInfo, data.conceptSetInfo); + self.targetComparatorList.push(tc); + } + } + self.targetComparator = ko.pureComputed(function() { + if (self.targetComparatorList().length <= 0) { + self.targetComparatorList.push(new TargetComparatorDefinition()); + } + return self.targetComparatorList()[0]; }); - self.psDemographicsIndeterminate = function() { - var propCount = 0; - propCount += self.psDemographicsGender() | 0; - propCount += self.psDemographicsRace() | 0; - propCount += self.psDemographicsEthnicity() | 0; - propCount += self.psDemographicsAge() | 0; - propCount += self.psDemographicsYear() | 0; - propCount += self.psDemographicsMonth() | 0; - return (propCount > 0 && propCount < 6); - } - self.psStrat = ko.pureComputed(function () { - return self.psMatch() == 2; + + self.outcomeList = ko.observableArray(); + self.originalOutcomeList = []; + if (data.outcomeList && data.outcomeList.length > 0) { + for (i = 0; i < data.outcomeList.length; i++) { + var o = new OutcomeDefinition(data.outcomeList[i], data.cohortInfo); + self.outcomeList.push(o); + self.originalOutcomeList.push(o); + } + } + self.outcome = ko.pureComputed(function() { + if (self.outcomeList().length <= 0) { + self.outcomeList.push(new OutcomeDefinition()); + } + return self.outcomeList()[0]; }); - self.psStratOrMatch = ko.pureComputed(function() { - return self.psMatch() > 0; - }) - self.psConditionOcc = ko.pureComputed(function() { - return (self.psConditionOcc365d() || self.psConditionOcc30d() || self.psConditionOccInpt180d()) - }); - self.psConditionEra = ko.pureComputed(function() { - return (self.psConditionEraEver() || self.psConditionEraOverlap()) - }); - self.psConditionGroup = ko.pureComputed(function() { - return (self.psConditionGroupMeddra() || self.psConditionGroupSnomed()) - }); - self.psCondition = ko.pureComputed(function() { - return (self.psConditionOcc() || self.psConditionEra()) - }); - self.psDrug = ko.pureComputed(function() { - return (self.psDrugInPrior30d() || self.psDrugInPrior365d() || self.psDrugEraOverlap() || self.psDrugEraEver()) - }); - self.psDrugAggregation = ko.pureComputed(function() { - return (self.psDrugExposure() || self.psDrugEra() || self.psDrugGroup()) - }); - self.psDrugExposure365d = ko.pureComputed(function() { - return (self.psDrugExposure() && self.psDrugInPrior365d()) - }); - self.psDrugExposure30d = ko.pureComputed(function() { - return (self.psDrugExposure() && self.psDrugInPrior30d()) - }); - self.psDrugEra365d = ko.pureComputed(function() { - return (self.psDrugEra() && self.psDrugInPrior365d()) - }); - self.psDrugEra30d = ko.pureComputed(function() { - return (self.psDrugEra() && self.psDrugInPrior30d()) - }); - self.psProcedureOcc = ko.pureComputed(function() { - return (self.psProcedureOcc365d() || self.psProcedureOcc30d() || self.psProcedureGroup()) - }) - self.psMeasurement = ko.pureComputed(function() { - return (self.psMeasurement365d() || self.psMeasurement30d() || self.psMeasurementCount365d() || self.psMeasurementBelow() || self.psMeasurementAbove()) - }) - self.psRiskScores = ko.pureComputed(function() { - return (self.psRiskScoresCharlson() || self.psRiskScoresDcsi() || self.psRiskScoresChads2() || self.psRiskScoresChads2vasc()) - }) - self.psInteraction = ko.pureComputed(function() { - return (self.psInteractionYear() || self.psInteractionMonth()) - }) - - // Outcome model settings - self.omCovariates = ko.observable(data.omCovariates != null ? data.omCovariates : 0); - - self.omTrim = ko.observable(data.omTrim != null ? data.omTrim : 0); - self.omTrimFraction = ko.observable(data.omTrimFraction != null ? data.omTrimFraction : 5); - self.omMatch = ko.observable(data.omMatch != null ? data.omMatch : 1); - self.omMatchMaxRatio = ko.observable(data.omMatchMaxRatio != null ? data.omMatchMaxRatio : 1); - self.omStratNumStrata = ko.observable(data.omStratNumStrata != null ? data.omStratNumStrata : 5); - - self.omDemographicsGender = ko.observable((data.omDemographicsGender == 1)|| false); - self.omDemographicsRace = ko.observable((data.omDemographicsRace == 1)||false); - self.omDemographicsEthnicity = ko.observable((data.omDemographicsEthnicity == 1)||false); - self.omDemographicsAge = ko.observable((data.omDemographicsAge == 1)||false); - self.omDemographicsYear = ko.observable((data.omDemographicsYear == 1)||false); - self.omDemographicsMonth = ko.observable((data.omDemographicsMonth == 1)||false); - - self.omConditionOcc365d = ko.observable((data.omConditionOcc365d == 1)||false); - self.omConditionOcc30d = ko.observable((data.omConditionOcc30d == 1)||false); - self.omConditionOccInpt180d = ko.observable((data.omConditionOccInpt180d == 1)||false); - - self.omConditionEraEver = ko.observable((data.omConditionEraEver == 1)||false); - self.omConditionEraOverlap = ko.observable((data.omConditionEraOverlap == 1)||false); - self.omConditionGroupMeddra = ko.observable((data.omConditionGroupMeddra == 1)||false); - self.omConditionGroupSnomed = ko.observable((data.omConditionGroupSnomed == 1)||false); - - self.omDrugExposure = ko.observable((data.omDrugExposure == 1)||false); - self.omDrugInPrior30d = ko.observable((data.omDrugExposure30d == 1 || data.omDrugEraExposure30d == 1) || false); - self.omDrugInPrior365d = ko.observable((data.omDrugExposure365d == 1 || data.omDrugEraExposure365d == 1) || false); - - self.omDrugEra = ko.observable((data.omDrugEra == 1)||false); - self.omDrugEraOverlap = ko.observable((data.omDrugEraOverlap == 1)||false); - self.omDrugEraEver = ko.observable((data.omDrugEraEver == 1)||false); - self.omDrugGroup = ko.observable((data.omDrugGroup == 1)||false); - - self.omProcedureOcc365d = ko.observable((data.omProcedureOcc365d == 1)||false); - self.omProcedureOcc30d = ko.observable((data.omProcedureOcc30d == 1)||false); - self.omProcedureGroup = ko.observable((data.omProcedureGroup == 1)||false); - - self.omObservation = ko.observable((data.omObservation == 1)||false); - self.omObservation365d = ko.observable((data.omObservation365d == 1)||false); - self.omObservation30d = ko.observable((data.omObservation30d == 1)||false); - self.omObservationCount365d = ko.observable((data.omObservationCount365d == 1)||false); - - self.omMeasurement365d = ko.observable((data.omMeasurement365d == 1)||false); - self.omMeasurement30d = ko.observable((data.omMeasurement30d == 1)||false); - self.omMeasurementCount365d = ko.observable((data.omMeasurementCount365d == 1)||false); - self.omMeasurementBelow = ko.observable((data.omMeasurementBelow == 1)||false); - self.omMeasurementAbove = ko.observable((data.omMeasurementAbove == 1)||false); - - self.omConceptCounts = ko.observable((data.omConceptCounts == 1)||false); - - self.omRiskScoresCharlson = ko.observable((data.omRiskScoresCharlson == 1)||false); - self.omRiskScoresDcsi = ko.observable((data.omRiskScoresDcsi == 1)||false); - self.omRiskScoresChads2 = ko.observable((data.omRiskScoresChads2 == 1)||false); - self.omRiskScoresChads2vasc = ko.observable((data.omRiskScoresChads2vasc == 1)||false); - - self.omInteractionYear = ko.observable((data.omInteractionYear == 1)||false); - self.omInteractionMonth = ko.observable((data.omInteractionMonth == 1)||false); - - // Outcome Model Settings - Derived - self.omDemographics = ko.pureComputed(function () { - return (self.omDemographicsGender() || self.omDemographicsRace() || self.omDemographicsEthnicity() || self.omDemographicsAge() || self.omDemographicsYear() || self.omDemographicsMonth()); + self.addOutcome = function(od) { + // If the outcome was already part of the original definition. + // If yes, then preserve the internal id + var match = self.originalOutcomeList.find(function(item) { + return item.outcomeId() == od.outcomeId(); + }); + if (match) { + od.id = match.id; + } + self.outcomeList.push(od); + + // Remove any default outcome definitions that might be sitting there + self.outcomeList.remove(function (item) { + return item.outcomeId() == 0; + }); + } + self.outcomeCaption = ko.pureComputed(function() { + if (self.outcomeList().length > 0) { + var outcomeCaption = ""; + for (var i = 0; i < self.outcomeList().length; i++) { + outcomeCaption += self.outcomeList()[i].outcomeCaption(); + if (i >= 0 && i < self.outcomeList().length - 2) { + outcomeCaption += ", "; + } else if (i >= 0 && self.outcomeList().length >= 2 && i == self.outcomeList().length - 2) { + outcomeCaption += " and "; + } + } + return outcomeCaption; + } else { + self.outcome().outcomeCaption(); + } }); - self.omDemographicsIndeterminate = function() { - var propCount = 0; - propCount += self.omDemographicsGender() | 0; - propCount += self.omDemographicsRace() | 0; - propCount += self.omDemographicsEthnicity() | 0; - propCount += self.omDemographicsAge() | 0; - propCount += self.omDemographicsYear() | 0; - propCount += self.omDemographicsMonth() | 0; - return (propCount > 0 && propCount < 6); - } - self.omStrat = ko.pureComputed(function () { - return !self.omMatch(); + self.outcomeIdList = ko.pureComputed(function() { + if (self.outcomeList().length > 0) { + var outcomeIds = ""; + for (var i = 0; i < self.outcomeList().length; i++) { + outcomeIds += self.outcomeList()[i].outcomeId(); + if (i >= 0 && i < self.outcomeList().length - 1) { + outcomeIds += ", "; + } + } + return outcomeIds; + } else { + self.outcome().outcomeId(); + } + }); + + self.analysisList = ko.observableArray(); + if (data.analysisList && data.analysisList.length > 0) { + for (i = 0; i < data.analysisList.length; i++) { + var a = new AnalysisDefinition(data.analysisList[i], data.conceptSetInfo); + self.analysisList.push(a); + } + } + self.analysis = ko.pureComputed(function() { + if (self.analysisList().length <= 0) { + self.analysisList.push(new AnalysisDefinition()); + } + return self.analysisList()[0]; + }); + + self.readyForDisplay = ko.pureComputed(function () { + return (self.targetComparator().comparatorId() != null && self.targetComparator().comparatorId() > 0 && + self.targetComparator().targetId() != null && self.targetComparator().targetId() > 0 && + self.outcome().outcomeId() != null && self.outcome().outcomeId() > 0 && + self.analysis().modelType() != null) }); - self.omConditionOcc = ko.pureComputed(function() { - return (self.omConditionOcc365d() || self.omConditionOcc30d() || self.omConditionOccInpt180d()) - }); - self.omConditionEra = ko.pureComputed(function() { - return (self.omConditionEraEver() || self.omConditionEraOverlap()) - }); - self.omConditionGroup = ko.pureComputed(function() { - return (self.omConditionGroupMeddra() || self.omConditionGroupSnomed()) - }); - self.omCondition = ko.pureComputed(function() { - return (self.omConditionOcc() || self.omConditionEra()) - }); - self.omDrug = ko.pureComputed(function() { - return (self.omDrugInPrior30d() || self.omDrugInPrior365d() || self.omDrugEraOverlap() || self.omDrugEraEver()) - }); - self.omDrugAggregation = ko.pureComputed(function() { - return (self.omDrugExposure() || self.omDrugEra() || self.omDrugGroup()) - }); - self.omDrugExposure365d = ko.pureComputed(function() { - return (self.omDrugExposure() && self.omDrugInPrior365d()) - }); - self.omDrugExposure30d = ko.pureComputed(function() { - return (self.omDrugExposure() && self.omDrugInPrior30d()) - }); - self.omDrugEra365d = ko.pureComputed(function() { - return (self.omDrugEra() && self.omDrugInPrior365d()) - }); - self.omDrugEra30d = ko.pureComputed(function() { - return (self.omDrugEra() && self.omDrugInPrior30d()) - }); - self.omProcedureOcc = ko.pureComputed(function() { - return (self.omProcedureOcc365d() || self.omProcedureOcc30d() || self.omProcedureGroup()) - }); - self.omMeasurement = ko.pureComputed(function() { - return (self.omMeasurement365d() || self.omMeasurement30d() || self.omMeasurementCount365d() || self.omMeasurementBelow() || self.omMeasurementAbove()) - }); - self.omRiskScores = ko.pureComputed(function() { - return (self.omRiskScoresCharlson() || self.omRiskScoresDcsi() || self.omRiskScoresChads2() || self.omRiskScoresChads2vasc()) - }); - self.omInteraction = ko.pureComputed(function() { - return (self.omInteractionYear() || self.omInteractionMonth()) - }); + + self.jsonify = function() { + var cca = { + analysisId: self.analysisId || null, + name: self.name(), + targetComparatorList: [], + outcomeList: [], + analysisList: [] + }; + + if (self.targetComparatorList() && self.targetComparatorList().length > 0) { + for (i = 0; i < self.targetComparatorList().length; i++) { + var tc = self.targetComparatorList()[i].jsonify(); + cca.targetComparatorList.push(tc); + } + } + + if (self.outcomeList() && self.outcomeList().length > 0) { + for (i = 0; i < self.outcomeList().length; i++) { + var o = self.outcomeList()[i].jsonify(); + cca.outcomeList.push(o); + } + } + + if (self.analysisList() && self.analysisList().length > 0) { + for (i = 0; i < self.analysisList().length; i++) { + var a = self.analysisList()[i].jsonify(); + cca.analysisList.push(a); + } + } + + return cca; + } } return ComparativeCohortAnalysis; -}); \ No newline at end of file +}); diff --git a/js/modules/cohortcomparison/OutcomeDefinition.js b/js/modules/cohortcomparison/OutcomeDefinition.js new file mode 100644 index 000000000..6267d71a3 --- /dev/null +++ b/js/modules/cohortcomparison/OutcomeDefinition.js @@ -0,0 +1,40 @@ +define(function (require, exports) { + + var $ = require('jquery'); + var ko = require('knockout'); + var CohortDefinition = require('cohortbuilder/CohortDefinition') + + function OutcomeDefinition(data, cohortList) { + var self = this; + var data = data || {}; + var cohortData; + + self.id = data.id; + self.outcomeId = ko.observable(data.outcomeId != null ? data.outcomeId : 0); + self.outcomeCaption = ko.observable(null); + self.outcomeCohortDefinition = ko.observable(null); + if (cohortList && cohortList.length > 0) { + var selectedCohort = $.grep(cohortList, function (f) { + return f.id == data.outcomeId; + }); + if (selectedCohort.length > 0) { + self.outcomeCaption(selectedCohort[0].name); + cohortData = { + id: selectedCohort[0].id, + name: selectedCohort[0].name, + expression: JSON.parse(selectedCohort[0].expression), + } + self.outcomeCohortDefinition(new CohortDefinition(cohortData)); + } + } + + self.jsonify = function() { + return { + id: self.id, + outcomeId: self.outcomeId(), + } + } + } + + return OutcomeDefinition; +}); diff --git a/js/modules/cohortcomparison/TargetComparatorDefinition.js b/js/modules/cohortcomparison/TargetComparatorDefinition.js new file mode 100644 index 000000000..6b65eba30 --- /dev/null +++ b/js/modules/cohortcomparison/TargetComparatorDefinition.js @@ -0,0 +1,105 @@ +define(function (require, exports) { + + var $ = require('jquery'); + var ko = require('knockout'); + var CohortDefinition = require('cohortbuilder/CohortDefinition') + var ConceptSet = require('conceptsetbuilder/InputTypes/ConceptSet') + + function TargetComparatorDefinition(data, cohortList, conceptSetList) { + var self = this; + var data = data || {}; + var conceptSetData; + var cohortData; + var selectedCohort; + var selectedConceptSet; + + self.id = data.id; + self.targetId = ko.observable(data.targetId != null ? data.targetId : 0); + self.targetCaption = ko.observable(null); + self.targetCohortDefinition = ko.observable(null); + if (cohortList && cohortList.length > 0) { + selectedCohort = $.grep(cohortList, function (f) { + return f.id == data.targetId; + }); + if (selectedCohort.length > 0) { + self.targetCaption(selectedCohort[0].name); + cohortData = { + id: selectedCohort[0].id, + name: selectedCohort[0].name, + expression: JSON.parse(selectedCohort[0].expression), + } + self.targetCohortDefinition(new CohortDefinition(cohortData)); + } + } + + self.comparatorId = ko.observable(data.comparatorId != null ? data.comparatorId : 0); + self.comparatorCaption = ko.observable(null); + self.comparatorCohortDefinition = ko.observable(null); + if (cohortList && cohortList.length > 0) { + selectedCohort = $.grep(cohortList, function (f) { + return f.id == data.comparatorId; + }); + if (selectedCohort.length > 0) { + self.comparatorCaption(selectedCohort[0].name); + cohortData = { + id: selectedCohort[0].id, + name: selectedCohort[0].name, + expression: JSON.parse(selectedCohort[0].expression), + } + self.comparatorCohortDefinition(new CohortDefinition(cohortData)); + } + } + + self.psExclusionId = ko.observable(data.psExclusionId != null ? data.psExclusionId : 0); + self.psExclusionCaption = ko.observable(null); + self.psExclusionConceptSet = ko.observableArray(null); + self.psExclusionConceptSetSQL = ko.observable(null); + if (conceptSetList && conceptSetList.length > 0) { + selectedConceptSet = $.grep(conceptSetList, function (f) { + return f.id == data.psExclusionId; + }); + if (selectedConceptSet.length > 0) { + self.psExclusionCaption(selectedConceptSet[0].name); + conceptSetData = { + id: self.psExclusionId(), + name: self.psExclusionCaption(), + expression: selectedConceptSet[0].expression, + }; + self.psExclusionConceptSet.push(new ConceptSet(conceptSetData)); + self.psExclusionConceptSetSQL(selectedConceptSet[0].sql); + } + } + + self.psInclusionId = ko.observable(data.psInclusionId != null ? data.psInclusionId : 0); + self.psInclusionCaption = ko.observable(null); + self.psInclusionConceptSet = ko.observableArray(null); + self.psInclusionConceptSetSQL = ko.observable(null); + if (conceptSetList && conceptSetList.length > 0) { + selectedConceptSet = $.grep(conceptSetList, function (f) { + return f.id == data.psInclusionId; + }); + if (selectedConceptSet.length > 0) { + self.psInclusionCaption(selectedConceptSet[0].name); + conceptSetData = { + id: self.psInclusionId(), + name: self.psInclusionCaption(), + expression: selectedConceptSet[0].expression, + }; + self.psInclusionConceptSet.push(new ConceptSet(conceptSetData)); + self.psInclusionConceptSetSQL(selectedConceptSet[0].sql); + } + } + + self.jsonify = function() { + return { + id: self.id, + targetId: self.targetId(), + comparatorId: self.comparatorId(), + psExclusionId: self.psExclusionId(), + psInclusionId: self.psInclusionId(), + }; + } + } + + return TargetComparatorDefinition; +}); diff --git a/js/plugins/bloodhound.min.js b/js/plugins/bloodhound.min.js new file mode 100644 index 000000000..ee0891454 --- /dev/null +++ b/js/plugins/bloodhound.min.js @@ -0,0 +1,7 @@ +/*! + * typeahead.js 0.11.1 + * https://github.com/twitter/typeahead.js + * Copyright 2013-2016 Twitter, Inc. and other contributors; Licensed MIT + */ + +!function(a,b){"function"==typeof define&&define.amd?define("bloodhound",["jquery"],function(c){return a.Bloodhound=b(c)}):"object"==typeof exports?module.exports=b(require("jquery")):a.Bloodhound=b(jQuery)}(this,function(a){var b=function(){"use strict";return{isMsie:function(){return/(msie|trident)/i.test(navigator.userAgent)?navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2]:!1},isBlankString:function(a){return!a||/^\s*$/.test(a)},escapeRegExChars:function(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isString:function(a){return"string"==typeof a},isNumber:function(a){return"number"==typeof a},isArray:a.isArray,isFunction:a.isFunction,isObject:a.isPlainObject,isUndefined:function(a){return"undefined"==typeof a},isElement:function(a){return!(!a||1!==a.nodeType)},isJQuery:function(b){return b instanceof a},toStr:function(a){return b.isUndefined(a)||null===a?"":a+""},bind:a.proxy,each:function(b,c){function d(a,b){return c(b,a)}a.each(b,d)},map:a.map,filter:a.grep,every:function(b,c){var d=!0;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?void 0:!1}),!!d):d},some:function(b,c){var d=!1;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?!1:void 0}),!!d):d},mixin:a.extend,identity:function(a){return a},clone:function(b){return a.extend(!0,{},b)},getIdGenerator:function(){var a=0;return function(){return a++}},templatify:function(b){function c(){return String(b)}return a.isFunction(b)?b:c},defer:function(a){setTimeout(a,0)},debounce:function(a,b,c){var d,e;return function(){var f,g,h=this,i=arguments;return f=function(){d=null,c||(e=a.apply(h,i))},g=c&&!d,clearTimeout(d),d=setTimeout(f,b),g&&(e=a.apply(h,i)),e}},throttle:function(a,b){var c,d,e,f,g,h;return g=0,h=function(){g=new Date,e=null,f=a.apply(c,d)},function(){var i=new Date,j=b-(i-g);return c=this,d=arguments,0>=j?(clearTimeout(e),e=null,g=i,f=a.apply(c,d)):e||(e=setTimeout(h,j)),f}},stringify:function(a){return b.isString(a)?a:JSON.stringify(a)},noop:function(){}}}(),c="0.11.1",d=function(){"use strict";function a(a){return a=b.toStr(a),a?a.split(/\s+/):[]}function c(a){return a=b.toStr(a),a?a.split(/\W+/):[]}function d(a){return function(c){return c=b.isArray(c)?c:[].slice.call(arguments,0),function(d){var e=[];return b.each(c,function(c){e=e.concat(a(b.toStr(d[c])))}),e}}}return{nonword:c,whitespace:a,obj:{nonword:d(c),whitespace:d(a)}}}(),e=function(){"use strict";function c(c){this.maxSize=b.isNumber(c)?c:100,this.reset(),this.maxSize<=0&&(this.set=this.get=a.noop)}function d(){this.head=this.tail=null}function e(a,b){this.key=a,this.val=b,this.prev=this.next=null}return b.mixin(c.prototype,{set:function(a,b){var c,d=this.list.tail;this.size>=this.maxSize&&(this.list.remove(d),delete this.hash[d.key],this.size--),(c=this.hash[a])?(c.val=b,this.list.moveToFront(c)):(c=new e(a,b),this.list.add(c),this.hash[a]=c,this.size++)},get:function(a){var b=this.hash[a];return b?(this.list.moveToFront(b),b.val):void 0},reset:function(){this.size=0,this.hash={},this.list=new d}}),b.mixin(d.prototype,{add:function(a){this.head&&(a.next=this.head,this.head.prev=a),this.head=a,this.tail=this.tail||a},remove:function(a){a.prev?a.prev.next=a.next:this.head=a.next,a.next?a.next.prev=a.prev:this.tail=a.prev},moveToFront:function(a){this.remove(a),this.add(a)}}),c}(),f=function(){"use strict";function c(a,c){this.prefix=["__",a,"__"].join(""),this.ttlKey="__ttl__",this.keyMatcher=new RegExp("^"+b.escapeRegExChars(this.prefix)),this.ls=c||h,!this.ls&&this._noop()}function d(){return(new Date).getTime()}function e(a){return JSON.stringify(b.isUndefined(a)?null:a)}function f(b){return a.parseJSON(b)}function g(a){var b,c,d=[],e=h.length;for(b=0;e>b;b++)(c=h.key(b)).match(a)&&d.push(c.replace(a,""));return d}var h;try{h=window.localStorage,h.setItem("~~~","!"),h.removeItem("~~~")}catch(i){h=null}return b.mixin(c.prototype,{_prefix:function(a){return this.prefix+a},_ttlKey:function(a){return this._prefix(a)+this.ttlKey},_noop:function(){this.get=this.set=this.remove=this.clear=this.isExpired=b.noop},_safeSet:function(a,b){try{this.ls.setItem(a,b)}catch(c){"QuotaExceededError"===c.name&&(this.clear(),this._noop())}},get:function(a){return this.isExpired(a)&&this.remove(a),f(this.ls.getItem(this._prefix(a)))},set:function(a,c,f){return b.isNumber(f)?this._safeSet(this._ttlKey(a),e(d()+f)):this.ls.removeItem(this._ttlKey(a)),this._safeSet(this._prefix(a),e(c))},remove:function(a){return this.ls.removeItem(this._ttlKey(a)),this.ls.removeItem(this._prefix(a)),this},clear:function(){var a,b=g(this.keyMatcher);for(a=b.length;a--;)this.remove(b[a]);return this},isExpired:function(a){var c=f(this.ls.getItem(this._ttlKey(a)));return b.isNumber(c)&&d()>c?!0:!1}}),c}(),g=function(){"use strict";function c(a){a=a||{},this.cancelled=!1,this.lastReq=null,this._send=a.transport,this._get=a.limiter?a.limiter(this._get):this._get,this._cache=a.cache===!1?new e(0):h}var d=0,f={},g=6,h=new e(10);return c.setMaxPendingRequests=function(a){g=a},c.resetCache=function(){h.reset()},b.mixin(c.prototype,{_fingerprint:function(b){return b=b||{},b.url+b.type+a.param(b.data||{})},_get:function(a,b){function c(a){b(null,a),k._cache.set(i,a)}function e(){b(!0)}function h(){d--,delete f[i],k.onDeckRequestArgs&&(k._get.apply(k,k.onDeckRequestArgs),k.onDeckRequestArgs=null)}var i,j,k=this;i=this._fingerprint(a),this.cancelled||i!==this.lastReq||((j=f[i])?j.done(c).fail(e):g>d?(d++,f[i]=this._send(a).done(c).fail(e).always(h)):this.onDeckRequestArgs=[].slice.call(arguments,0))},get:function(c,d){var e,f;d=d||a.noop,c=b.isString(c)?{url:c}:c||{},f=this._fingerprint(c),this.cancelled=!1,this.lastReq=f,(e=this._cache.get(f))?d(null,e):this._get(c,d)},cancel:function(){this.cancelled=!0}}),c}(),h=window.SearchIndex=function(){"use strict";function c(c){c=c||{},c.datumTokenizer&&c.queryTokenizer||a.error("datumTokenizer and queryTokenizer are both required"),this.identify=c.identify||b.stringify,this.datumTokenizer=c.datumTokenizer,this.queryTokenizer=c.queryTokenizer,this.reset()}function d(a){return a=b.filter(a,function(a){return!!a}),a=b.map(a,function(a){return a.toLowerCase()})}function e(){var a={};return a[i]=[],a[h]={},a}function f(a){for(var b={},c=[],d=0,e=a.length;e>d;d++)b[a[d]]||(b[a[d]]=!0,c.push(a[d]));return c}function g(a,b){var c=0,d=0,e=[];a=a.sort(),b=b.sort();for(var f=a.length,g=b.length;f>c&&g>d;)a[c]b[d]?d++:(e.push(a[c]),c++,d++);return e}var h="c",i="i";return b.mixin(c.prototype,{bootstrap:function(a){this.datums=a.datums,this.trie=a.trie},add:function(a){var c=this;a=b.isArray(a)?a:[a],b.each(a,function(a){var f,g;c.datums[f=c.identify(a)]=a,g=d(c.datumTokenizer(a)),b.each(g,function(a){var b,d,g;for(b=c.trie,d=a.split("");g=d.shift();)b=b[h][g]||(b[h][g]=e()),b[i].push(f)})})},get:function(a){var c=this;return b.map(a,function(a){return c.datums[a]})},search:function(a){var c,e,j=this;return c=d(this.queryTokenizer(a)),b.each(c,function(a){var b,c,d,f;if(e&&0===e.length)return!1;for(b=j.trie,c=a.split("");b&&(d=c.shift());)b=b[h][d];return b&&0===c.length?(f=b[i].slice(0),void(e=e?g(e,f):f)):(e=[],!1)}),e?b.map(f(e),function(a){return j.datums[a]}):[]},all:function(){var a=[];for(var b in this.datums)a.push(this.datums[b]);return a},reset:function(){this.datums={},this.trie=e()},serialize:function(){return{datums:this.datums,trie:this.trie}}}),c}(),i=function(){"use strict";function a(a){this.url=a.url,this.ttl=a.ttl,this.cache=a.cache,this.prepare=a.prepare,this.transform=a.transform,this.transport=a.transport,this.thumbprint=a.thumbprint,this.storage=new f(a.cacheKey)}var c;return c={data:"data",protocol:"protocol",thumbprint:"thumbprint"},b.mixin(a.prototype,{_settings:function(){return{url:this.url,type:"GET",dataType:"json"}},store:function(a){this.cache&&(this.storage.set(c.data,a,this.ttl),this.storage.set(c.protocol,location.protocol,this.ttl),this.storage.set(c.thumbprint,this.thumbprint,this.ttl))},fromCache:function(){var a,b={};return this.cache?(b.data=this.storage.get(c.data),b.protocol=this.storage.get(c.protocol),b.thumbprint=this.storage.get(c.thumbprint),a=b.thumbprint!==this.thumbprint||b.protocol!==location.protocol,b.data&&!a?b.data:null):null},fromNetwork:function(a){function b(){a(!0)}function c(b){a(null,e.transform(b))}var d,e=this;a&&(d=this.prepare(this._settings()),this.transport(d).fail(b).done(c))},clear:function(){return this.storage.clear(),this}}),a}(),j=function(){"use strict";function a(a){this.url=a.url,this.prepare=a.prepare,this.transform=a.transform,this.transport=new g({cache:a.cache,limiter:a.limiter,transport:a.transport})}return b.mixin(a.prototype,{_settings:function(){return{url:this.url,type:"GET",dataType:"json"}},get:function(a,b){function c(a,c){b(a?[]:e.transform(c))}var d,e=this;if(b)return a=a||"",d=this.prepare(a,this._settings()),this.transport.get(d,c)},cancelLastRequest:function(){this.transport.cancel()}}),a}(),k=function(){"use strict";function d(d){var e;return d?(e={url:null,ttl:864e5,cache:!0,cacheKey:null,thumbprint:"",prepare:b.identity,transform:b.identity,transport:null},d=b.isString(d)?{url:d}:d,d=b.mixin(e,d),!d.url&&a.error("prefetch requires url to be set"),d.transform=d.filter||d.transform,d.cacheKey=d.cacheKey||d.url,d.thumbprint=c+d.thumbprint,d.transport=d.transport?h(d.transport):a.ajax,d):null}function e(c){var d;if(c)return d={url:null,cache:!0,prepare:null,replace:null,wildcard:null,limiter:null,rateLimitBy:"debounce",rateLimitWait:300,transform:b.identity,transport:null},c=b.isString(c)?{url:c}:c,c=b.mixin(d,c),!c.url&&a.error("remote requires url to be set"),c.transform=c.filter||c.transform,c.prepare=f(c),c.limiter=g(c),c.transport=c.transport?h(c.transport):a.ajax,delete c.replace,delete c.wildcard,delete c.rateLimitBy,delete c.rateLimitWait,c}function f(a){function b(a,b){return b.url=f(b.url,a),b}function c(a,b){return b.url=b.url.replace(g,encodeURIComponent(a)),b}function d(a,b){return b}var e,f,g;return e=a.prepare,f=a.replace,g=a.wildcard,e?e:e=f?b:a.wildcard?c:d}function g(a){function c(a){return function(c){return b.debounce(c,a)}}function d(a){return function(c){return b.throttle(c,a)}}var e,f,g;return e=a.limiter,f=a.rateLimitBy,g=a.rateLimitWait,e||(e=/^throttle$/i.test(f)?d(g):c(g)),e}function h(c){return function(d){function e(a){b.defer(function(){g.resolve(a)})}function f(a){b.defer(function(){g.reject(a)})}var g=a.Deferred();return c(d,e,f),g}}return function(c){var f,g;return f={initialize:!0,identify:b.stringify,datumTokenizer:null,queryTokenizer:null,sufficient:5,sorter:null,local:[],prefetch:null,remote:null},c=b.mixin(f,c||{}),!c.datumTokenizer&&a.error("datumTokenizer is required"),!c.queryTokenizer&&a.error("queryTokenizer is required"),g=c.sorter,c.sorter=g?function(a){return a.sort(g)}:b.identity,c.local=b.isFunction(c.local)?c.local():c.local,c.prefetch=d(c.prefetch),c.remote=e(c.remote),c}}(),l=function(){"use strict";function c(a){a=k(a),this.sorter=a.sorter,this.identify=a.identify,this.sufficient=a.sufficient,this.local=a.local,this.remote=a.remote?new j(a.remote):null,this.prefetch=a.prefetch?new i(a.prefetch):null,this.index=new h({identify:this.identify,datumTokenizer:a.datumTokenizer,queryTokenizer:a.queryTokenizer}),a.initialize!==!1&&this.initialize()}var e;return e=window&&window.Bloodhound,c.noConflict=function(){return window&&(window.Bloodhound=e),c},c.tokenizers=d,b.mixin(c.prototype,{__ttAdapter:function(){function a(a,b,d){return c.search(a,b,d)}function b(a,b){return c.search(a,b)}var c=this;return this.remote?a:b},_loadPrefetch:function(){function b(a,b){return a?c.reject():(e.add(b),e.prefetch.store(e.index.serialize()),void c.resolve())}var c,d,e=this;return c=a.Deferred(),this.prefetch?(d=this.prefetch.fromCache())?(this.index.bootstrap(d),c.resolve()):this.prefetch.fromNetwork(b):c.resolve(),c.promise()},_initialize:function(){function a(){b.add(b.local)}var b=this;return this.clear(),(this.initPromise=this._loadPrefetch()).done(a),this.initPromise},initialize:function(a){return!this.initPromise||a?this._initialize():this.initPromise},add:function(a){return this.index.add(a),this},get:function(a){return a=b.isArray(a)?a:[].slice.call(arguments),this.index.get(a)},search:function(a,c,d){function e(a){var c=[];b.each(a,function(a){!b.some(f,function(b){return g.identify(a)===g.identify(b)})&&c.push(a)}),d&&d(c)}var f,g=this;return f=this.sorter(this.index.search(a)),c(this.remote?f.slice():f),this.remote&&f.length=j?(clearTimeout(e),e=null,g=i,f=a.apply(c,d)):e||(e=setTimeout(h,j)),f}},stringify:function(a){return b.isString(a)?a:JSON.stringify(a)},noop:function(){}}}(),c=function(){"use strict";function a(a){var g,h;return h=b.mixin({},f,a),g={css:e(),classes:h,html:c(h),selectors:d(h)},{css:g.css,html:g.html,classes:g.classes,selectors:g.selectors,mixin:function(a){b.mixin(a,g)}}}function c(a){return{wrapper:'',menu:'
'}}function d(a){var c={};return b.each(a,function(a,b){c[b]="."+a}),c}function e(){var a={wrapper:{position:"relative",display:"inline-block"},hint:{position:"absolute",top:"0",left:"0",borderColor:"transparent",boxShadow:"none",opacity:"1"},input:{position:"relative",verticalAlign:"top",backgroundColor:"transparent"},inputWithNoHint:{position:"relative",verticalAlign:"top"},menu:{position:"absolute",top:"100%",left:"0",zIndex:"100",display:"none"},ltr:{left:"0",right:"auto"},rtl:{left:"auto",right:" 0"}};return b.isMsie()&&b.mixin(a.input,{backgroundImage:"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"}),a}var f={wrapper:"twitter-typeahead",input:"tt-input",hint:"tt-hint",menu:"tt-menu",dataset:"tt-dataset",suggestion:"tt-suggestion",selectable:"tt-selectable",empty:"tt-empty",open:"tt-open",cursor:"tt-cursor",highlight:"tt-highlight"};return a}(),d=function(){"use strict";function c(b){b&&b.el||a.error("EventBus initialized without el"),this.$el=a(b.el)}var d,e;return d="typeahead:",e={render:"rendered",cursorchange:"cursorchanged",select:"selected",autocomplete:"autocompleted"},b.mixin(c.prototype,{_trigger:function(b,c){var e;return e=a.Event(d+b),(c=c||[]).unshift(e),this.$el.trigger.apply(this.$el,c),e},before:function(a){var b,c;return b=[].slice.call(arguments,1),c=this._trigger("before"+a,b),c.isDefaultPrevented()},trigger:function(a){var b;this._trigger(a,[].slice.call(arguments,1)),(b=e[a])&&this._trigger(b,[].slice.call(arguments,1))}}),c}(),e=function(){"use strict";function a(a,b,c,d){var e;if(!c)return this;for(b=b.split(i),c=d?h(c,d):c,this._callbacks=this._callbacks||{};e=b.shift();)this._callbacks[e]=this._callbacks[e]||{sync:[],async:[]},this._callbacks[e][a].push(c);return this}function b(b,c,d){return a.call(this,"async",b,c,d)}function c(b,c,d){return a.call(this,"sync",b,c,d)}function d(a){var b;if(!this._callbacks)return this;for(a=a.split(i);b=a.shift();)delete this._callbacks[b];return this}function e(a){var b,c,d,e,g;if(!this._callbacks)return this;for(a=a.split(i),d=[].slice.call(arguments,1);(b=a.shift())&&(c=this._callbacks[b]);)e=f(c.sync,this,[b].concat(d)),g=f(c.async,this,[b].concat(d)),e()&&j(g);return this}function f(a,b,c){function d(){for(var d,e=0,f=a.length;!d&&f>e;e+=1)d=a[e].apply(b,c)===!1;return!d}return d}function g(){var a;return a=window.setImmediate?function(a){setImmediate(function(){a()})}:function(a){setTimeout(function(){a()},0)}}function h(a,b){return a.bind?a.bind(b):function(){a.apply(b,[].slice.call(arguments,0))}}var i=/\s+/,j=g();return{onSync:c,onAsync:b,off:d,trigger:e}}(),f=function(a){"use strict";function c(a,c,d){for(var e,f=[],g=0,h=a.length;h>g;g++)f.push(b.escapeRegExChars(a[g]));return e=d?"\\b("+f.join("|")+")\\b":"("+f.join("|")+")",c?new RegExp(e):new RegExp(e,"i")}var d={node:null,pattern:null,tagName:"strong",className:null,wordsOnly:!1,caseSensitive:!1};return function(e){function f(b){var c,d,f;return(c=h.exec(b.data))&&(f=a.createElement(e.tagName),e.className&&(f.className=e.className),d=b.splitText(c.index),d.splitText(c[0].length),f.appendChild(d.cloneNode(!0)),b.parentNode.replaceChild(f,d)),!!c}function g(a,b){for(var c,d=3,e=0;e