Skip to content

Commit

Permalink
Add demographic reports to cohort generation. (#2959)
Browse files Browse the repository at this point in the history
* Add demographics column, demographics tab

---------

Co-authored-by: hernaldo.urbina <[email protected]>
Co-authored-by: oleg-odysseus <[email protected]>
Co-authored-by: Chris Knoll <[email protected]>
  • Loading branch information
4 people authored Mar 4, 2025
1 parent 30acf8a commit 3561f67
Show file tree
Hide file tree
Showing 22 changed files with 1,864 additions and 18 deletions.
4 changes: 4 additions & 0 deletions js/pages/cohort-definitions/cohort-definition-manager.css
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ table.sources-table tr {
table.sources-table tr:hover {
background-color: #f2f2f2;
}
table.sources-table tr td {
border-top: solid 1px #FFF;
border-bottom: solid 1px #FFF;
}
table.sources-table tr:hover td {
border-top: solid 1px #ccc;
border-bottom: solid 1px #ccc;
Expand Down
11 changes: 10 additions & 1 deletion js/pages/cohort-definitions/cohort-definition-manager.html
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ <h3 data-bind="text: ko.i18n('cohortDefinitions.cohortDefinitionManager.panels.a
</tbody>
</table>
<!-- ko if: selectedReportSource() -->
<cohort-reports params="cohort: currentCohortDefinition, source: selectedReportSource"></cohort-reports>
<cohort-reports params="cohort: currentCohortDefinition, source: selectedReportSource, infoSelected: selectedReportSource"></cohort-reports>
<!-- /ko -->
</div>
<div role="tabpanel" data-bind="css: { active: $component.tabMode() == 'reporting' }" class="tab-pane">
Expand Down Expand Up @@ -793,4 +793,13 @@ <h3 data-bind="text: ko.i18n('cohortDefinitions.cohortDefinitionManager.panels.a
</div>
</script>

<script type="text/html" id="generation-checkbox-demographic">
<span>
<input class="hover-toolbox" type="checkbox" data-bind="attr:{ id: $data.sourceKey } , checked: viewDemographic, tooltip: 'Results with Demographics', eventListener: [
{event: 'mouseover', selector: 'input', callback: $component.addToolTipDemographic },
{event: 'mouseout', selector: 'input', callback: $component.removeToolTipDemographic },
{event: 'mouseup', selector: 'input', callback: $component.handleViewDemographic }]" data-placement="right"/>
</span>
</script>

<!-- /ko -->
95 changes: 93 additions & 2 deletions js/pages/cohort-definitions/cohort-definition-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html',
'utilities/sql',
'components/conceptset/conceptset-list',
'components/name-validation',
'components/versions/versions'
'components/versions/versions',
'databindings/tooltipBinding'
], function (
$,
ko,
Expand Down Expand Up @@ -604,6 +605,16 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html',
}, {
title: ko.i18n('cohortDefinitions.cohortDefinitionManager.panels.generationDuration', 'Generation Duration'),
data: 'executionDuration'
}, {
title: ko.i18n(
'cohortDefinitions.cohortDefinitionManager.panels.generationDuration3',
'Demographics'
),
data: 'viewDemographic',
sortable: false,
tooltip: 'Results with Demographics',
render: () =>
`<span data-bind="template: {name: 'generation-checkbox-demographic', data: $data }"></span>`,
}];

this.stopping = ko.pureComputed(() => this.cohortDefinitionSourceInfo().reduce((acc, target) => ({...acc, [target.sourceKey]: ko.observable(false)}), {}));
Expand Down Expand Up @@ -653,6 +664,8 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html',
source.personCount(commaFormatted(info.personCount));
source.recordCount(commaFormatted(info.recordCount));
source.failMessage(info.failMessage);
source.ccGenerateId(info.ccGenerateId);
source.viewDemographic(info.isDemographic);
}
}
});
Expand Down Expand Up @@ -1094,13 +1107,25 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html',
if (this.selectedSource() && this.selectedSource().sourceId === source.sourceId) {
this.toggleCohortReport(null);
}
cohortDefinitionService.generate(this.currentCohortDefinition().id(), source.sourceKey)
cohortDefinitionService.generate(this.currentCohortDefinition().id(), source.sourceKey, source.viewDemographic())
.catch(this.authApi.handleAccessDenied)
.then(({data}) => {
jobDetailsService.createJob(data);
});
}

handleCheckboxDemographic(source) {
const targetSource = this.getSourceKeyInfo(source.sourceKey);
targetSource.viewDemographic(targetSource.viewDemographic());
const restSourceInfos = this.cohortDefinitionSourceInfo().filter(
(d) => {
return d.sourceKey !== source.sourceKey;
}
);

this.cohortDefinitionSourceInfo([...restSourceInfos, targetSource])
}

cancelGenerate (source) {
this.stopping()[source.sourceKey](true);
cohortDefinitionService.cancelGenerate(this.currentCohortDefinition().id(), source.sourceKey);
Expand Down Expand Up @@ -1261,6 +1286,9 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html',
}
cdsi.failMessage = ko.observable(sourceInfo.failMessage);
cdsi.createdBy = ko.observable(sourceInfo.createdBy);
cdsi.viewDemographic = ko.observable(sourceInfo?.viewDemographic || sourceInfo.isDemographic || false);
cdsi.tooltipDemographic = ko.observable(sourceInfo?.tooltipDemographic || null);
cdsi.ccGenerateId = ko.observable(sourceInfo.ccGenerateId);
} else {
cdsi.isValid = ko.observable(false);
cdsi.isCanceled = ko.observable(false);
Expand All @@ -1271,6 +1299,9 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html',
cdsi.recordCount = ko.observable('n/a');
cdsi.failMessage = ko.observable(null);
cdsi.createdBy = ko.observable(null);
cdsi.viewDemographic = ko.observable(false);
cdsi.tooltipDemographic = ko.observable(null);
cdsi.ccGenerateId = ko.observable(null);
}
return cdsi;
}
Expand Down Expand Up @@ -1316,6 +1347,23 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html',

async prepareCohortDefinition(cohortDefinitionId, conceptSetId, selectedSourceId, sourceKey, versionNumber) {
this.isLoading(true);
ko.bindingHandlers.tooltip = {
init: function (element, valueAccessor) {
const value = ko.utils.unwrapObservable(valueAccessor());
$("[aria-label='Demographics']").attr('data-original-title', 'Results with Demographics').bstooltip({
html: true,
container:'body',
});
$(element).attr('data-original-title', value).bstooltip({
html: true,
container:'body'
});
},
update: function (element, valueAccessor) {
const value = ko.utils.unwrapObservable(valueAccessor());
$(element).attr('data-original-title', value);
}
}
if(parseInt(cohortDefinitionId) === 0) {
this.setNewCohortDefinition();
} else if (versionNumber) {
Expand Down Expand Up @@ -1348,6 +1396,49 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html',
}
}

addToolTipDemographic(source){
const targetSource = this.getSourceKeyInfo(source?.sourceKey);
targetSource?.tooltipDemographic('Results with Demographics');
const restSourceInfos = this.cohortDefinitionSourceInfo().filter(
(d) => {
return d.sourceKey !== source?.sourceKey;
}
);
this.cohortDefinitionSourceInfo([
...restSourceInfos,
targetSource
])
}

removeToolTipDemographic(source){
const targetSource = this.getSourceKeyInfo(source?.sourceKey);
targetSource?.tooltipDemographic(null);
const restSourceInfos = this.cohortDefinitionSourceInfo().filter(
(d) => {
return d?.sourceKey !== source?.sourceKey;
}
);
this.cohortDefinitionSourceInfo([
...restSourceInfos,
targetSource
])
}

handleViewDemographic(source) {
const targetSource = this.getSourceKeyInfo(source?.sourceKey);
targetSource.viewDemographic(!targetSource.viewDemographic());
targetSource?.tooltipDemographic(null);
const restSourceInfos = this.cohortDefinitionSourceInfo().filter(
(d) => {
return d?.sourceKey !== source?.sourceKey;
}
);
this.cohortDefinitionSourceInfo([
...restSourceInfos,
targetSource
])
}

checkifDataLoaded(cohortDefinitionId, conceptSetId, sourceKey) {
if (this.currentCohortDefinition() && this.currentCohortDefinition().id() == cohortDefinitionId) {
if (this.currentConceptSet() && this.currentConceptSet().id == conceptSetId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ define([
PluginRegistry.add(globalConstants.pluginTypes.COHORT_REPORT, {
title: ko.i18n('cohortDefinitions.cohortreports.inclusionReport', 'Inclusion Report'),
priority: 1,
html: `<cohort-report-inclusion params="{ sourceKey: sourceKey, cohortId: cohortId }"></cohort-report-inclusion>`
html: `<cohort-report-inclusion params="{ sourceKey: sourceKey, cohortId: cohortId, isViewDemographic: isViewDemographic, ccGenerateId: ccGenerateId }"></cohort-report-inclusion>`
});

class CohortReports extends Component {
Expand All @@ -30,18 +30,37 @@ define([

this.sourceKey = ko.computed(() => params.source() && params.source().sourceKey);
this.cohortId = ko.computed(() => params.cohort().id());

this.isViewDemographic = ko.computed(() => params.source() && params.source().viewDemographic());
this.ccGenerateId = ko.computed(() => params.infoSelected() && params.infoSelected().ccGenerateId());

const componentParams = {
sourceKey: this.sourceKey,
cohortId: this.cohortId
cohortId: this.cohortId,
isViewDemographic: this.isViewDemographic,
ccGenerateId: this.ccGenerateId,
};

this.tabs = PluginRegistry.findByType(globalConstants.pluginTypes.COHORT_REPORT).map(t => ({ ...t, componentParams }));

if (this.isViewDemographic()) {
this.tabs.push({
title: ko.i18n('cohortDefinitions.cohortreports.tabs.byPerson3', 'Demographics'),
componentName: 'demographic-report',
componentParams: {
...componentParams,
reportType: constants.INCLUSION_REPORT.BY_DEMOGRAPHIC,
buttons: null,
tableDom: "Blfiprt"
}
});
}
}

dispose() {
this.sourceKey.dispose();
this.cohortId.dispose();
this.isViewDemographic.dispose();
this.ccGenerateId.dispose();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@ define([], () => {
const INCLUSION_REPORT = {
BY_EVENT: 0,
BY_PERSON: 1,
BY_DEMOGRAPHIC: 2
};

const feAnalysisTypes = {
PRESET: 'PRESET',
CRITERIA_SET: 'CRITERIA_SET',
CUSTOM_FE: 'CUSTOM_FE'
};

return {
INCLUSION_REPORT
INCLUSION_REPORT,
feAnalysisTypes
};
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
define([
'knockout',
'./BaseStatConverter',
'./DistributionStat',
], function (
ko,
BaseStatConverter,
DistributionStat,
) {

class BaseDistributionStatConverter extends BaseStatConverter {

constructor(classes) {
super(classes);
}

convertAnalysisToTabularData(analysis, stratas = null) {

const result = super.convertAnalysisToTabularData(analysis, stratas);
stratas && stratas.filter(s => result.data.findIndex(row => row.strataId === s.strataId) < 0)
.forEach(s => result.data.push(this.getResultObject(
{
analysisId: analysis.analysisId,
analysisName: analysis.analysisName,
domainId: analysis.domainId,
cohorts: [],
strataId: s.strataId,
strataName: s.strataName,
}
)));
return result;
}

getRowId(stat) {
return stat.strataId * 100000 + stat.covariateId;
}

getResultObject(stat) {
return new DistributionStat(stat);
}

extractStrata(stat) {
return { strataId: 0, strataName: '' };
}

getDefaultColumns(analysis) {
return [{
title: ko.i18n('columns.strata', 'Strata'),
data: 'strataName',
className: this.classes('col-distr-title'),
xssSafe:false,
},
{
title: 'Covariate',
data: 'covariateName',
className: this.classes('col-distr-cov'),
xssSafe: false,
},
{
title: 'Value field',
data: (row, type) => {
let data = (row.faType === 'CRITERIA_SET' && row.aggregateName) || "Events count" ;
if (row.missingMeansZero) {
data = data + "*";
}
return data;
}
}];
}

convertFields(result, strataId, cohortId, stat, prefix) {
result.strataName = stat.strataName;
['count', 'avg', 'pct', 'stdDev', 'median', 'max', 'min', 'p10', 'p25', 'p75', 'p90'].forEach(field => {
const statName = prefix ? prefix + field.charAt(0).toUpperCase() + field.slice(1) : field;
this.setNestedValue(result, field, strataId, cohortId, stat[statName]);
});
}

convertCompareFields(result, strataId, stat) {
this.convertFields(result, strataId, stat.targetCohortId, stat, "target");
this.convertFields(result, strataId, stat.comparatorCohortId, stat, "comparator");
}
}

return BaseDistributionStatConverter;
});
Loading

0 comments on commit 3561f67

Please sign in to comment.