Skip to content

Commit

Permalink
Merge branch 'OHDSI:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
bdeboe authored Jan 8, 2025
2 parents 8f5418b + d85c48a commit 1a662b3
Show file tree
Hide file tree
Showing 30 changed files with 758 additions and 153 deletions.
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = crlf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false
quote_type = single
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ RUN find . -type f "(" \
| xargs -0 -n 1 gzip -kf

# Production Nginx image
FROM docker.io/nginxinc/nginx-unprivileged:1.23.3-alpine@sha256:c748ba587e7436aaa8729b64d4e0412410a486f0c592f0eec100fb3804ff9afd
FROM docker.io/nginxinc/nginx-unprivileged:1.27.2-alpine

LABEL org.opencontainers.image.title="OHDSI-Atlas"
LABEL org.opencontainers.image.authors="Joris Borgdorff <[email protected]>, Lee Evans - www.ltscomputingllc.com, Shaun Turner<[email protected]>"
Expand Down
5 changes: 5 additions & 0 deletions js/components/atlas-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,28 @@ define(['knockout', 'lscache', 'services/job/jobDetail', 'assets/ohdsi.util', 'c
state.vocabularyUrl = ko.observable(sessionStorage.vocabularyUrl);
state.evidenceUrl = ko.observable(sessionStorage.evidenceUrl);
state.resultsUrl = ko.observable(sessionStorage.resultsUrl);
state.currentVocabularyVersion = ko.observable(sessionStorage.currentVocabularyVersion);
state.vocabularyUrl.subscribe(value => updateKey('vocabularyUrl', value));
state.evidenceUrl.subscribe(value => updateKey('evidenceUrl', value));
state.resultsUrl.subscribe(value => updateKey('resultsUrl', value));
state.currentVocabularyVersion.subscribe(value => updateKey('currentVocabularyVersion', value));

// This default values are stored during initialization
// and used to reset after session finished
state.defaultVocabularyUrl = ko.observable();
state.defaultEvidenceUrl = ko.observable();
state.defaultResultsUrl = ko.observable();
state.defaultVocabularyVersion = ko.observable();
state.defaultVocabularyUrl.subscribe((value) => state.vocabularyUrl(value));
state.defaultEvidenceUrl.subscribe((value) => state.evidenceUrl(value));
state.defaultResultsUrl.subscribe((value) => state.resultsUrl(value));
state.defaultVocabularyVersion.subscribe((value) => state.currentVocabularyVersion(value));

state.resetCurrentDataSourceScope = function() {
state.vocabularyUrl(state.defaultVocabularyUrl());
state.evidenceUrl(state.defaultEvidenceUrl());
state.resultsUrl(state.defaultResultsUrl());
state.currentVocabularyVersion(state.defaultVocabularyVersion());
}

state.sourceKeyOfVocabUrl = ko.computed(() => {
Expand Down
14 changes: 14 additions & 0 deletions js/components/conceptAddBox/concept-add-box.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,20 @@ define([

sharedState.activeConceptSet(conceptSet);

const filterSource = localStorage?.getItem('filter-source') || null;
const filterData = JSON.parse(localStorage?.getItem('filter-data') || null);
const datasAdded = JSON.parse(localStorage?.getItem('data-add-selected-concept') || null) || [];
const dataSearch = { filterData, filterSource }
const payloadAdd = this.conceptsToAdd().map(item => {
return {
"searchData": dataSearch,
"vocabularyVersion": sharedState.currentVocabularyVersion(),
"conceptId": item.CONCEPT_ID
}
})

localStorage.setItem('data-add-selected-concept', JSON.stringify([...datasAdded, ...payloadAdd]))

// if concepts were previewed, then they already built and can have individual option flags!
if (this.previewConcepts().length > 0) {
if (!conceptSet.current()) {
Expand Down
1 change: 1 addition & 0 deletions js/components/conceptset/const.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ define([
RECOMMEND: 'recommend',
EXPORT: 'conceptset-export',
IMPORT: 'conceptset-import',
ANNOTATION: 'annotation'
};

const ConceptSetSources = {
Expand Down
45 changes: 45 additions & 0 deletions js/components/faceted-datatable.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,52 @@ define(['knockout', 'text!./faceted-datatable.html', 'crossfilter', 'utils/Commo

self.outsideFilters = (params.outsideFilters || ko.observable()).extend({notify: 'always'});

self.setDataLocalStorage = (data, nameItem) => {
const filterArrayString = localStorage.getItem(nameItem)
let filterArrayObj = filterArrayString? JSON.parse(filterArrayString): []

if(!data?.selected()){
filterArrayObj.push({title:data.facet.caption(), value:`${data.key} (${data.value})`,key:data.key})
}else{
filterArrayObj = filterArrayObj.filter((item)=> item.key !== data.key)
}
localStorage.setItem(nameItem, JSON.stringify(filterArrayObj))
}

self.setDataObjectLocalStorage = (data, nameItem) => {
const filterObjString = localStorage.getItem(nameItem)
let filterObj = filterObjString ? JSON.parse(filterObjString): {}
let newFilterObj = {}

if(!data?.selected()){
const dataPush = { title: data.facet.caption(), value: `${data.key} (${data.value})`, key: data.key };
newFilterObj.filterColumns = filterObj['filterColumns'] ? [...filterObj['filterColumns'], dataPush] : [dataPush]
newFilterObj = { ...filterObj, filterColumns : newFilterObj.filterColumns };
}else{
newFilterObj.filterColumns = filterObj['filterColumns'].filter((item)=> item.key !== data.key);
newFilterObj = { ...filterObj, filterColumns : newFilterObj.filterColumns };
}
localStorage.setItem(nameItem, JSON.stringify(newFilterObj))
}

self.updateFilters = function (data, event) {
const currentPath = window.location?.href;
if (currentPath?.includes('/conceptset/')) {
if (currentPath?.includes('/included-sourcecodes')) {
localStorage.setItem('filter-source', 'Included Source Codes');
} else if (currentPath?.includes('/included')) {
localStorage.setItem('filter-source', 'Included Concepts');
}
self.setDataLocalStorage(data, 'filter-data');
}
const isAddConcept = currentPath?.split('?').reduce((prev, curr) => prev || curr.includes('search'), false) &&
currentPath?.split('?').reduce((prev, curr) => prev || curr.includes('query'), false) ||
currentPath?.includes('/concept/')

if (isAddConcept) {
localStorage.setItem('filter-source', 'Search');
self.setDataObjectLocalStorage(data, 'filter-data')
}
var facet = data.facet;
data.selected(!data.selected());
if (data.selected()) {
Expand Down
4 changes: 2 additions & 2 deletions js/components/multi-select.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<div class="multi-select" data-bind="eventListener: [{event: 'hide.bs.dropdown', selector: 'div.dropdown.bootstrap-select', callback: onSelectionComplete}]">
<!-- ko if: multiple == true -->
<select data-bind="selectedOptions: selectedValues, options: optionVals, optionsText: optionsText, multiSelect: { liveSearch: true, selectedTextFormat: selectedTextFormat, noneSelectedText: ko.unwrap(noneSelectedText), noneResultsText: ko.unwrap(noneResultsText), countSelectedText: ko.unwrap(countSelectedText) }" multiple></select>
<select data-bind="attr: disabled, selectedOptions: selectedValues, options: optionVals, optionsText: optionsText, multiSelect: { liveSearch: true, selectedTextFormat: selectedTextFormat, noneSelectedText: ko.unwrap(noneSelectedText), noneResultsText: ko.unwrap(noneResultsText), countSelectedText: ko.unwrap(countSelectedText) }" multiple></select>
<!-- /ko -->
<!-- ko if: multiple == false -->
<select data-bind="options: options, value: selectedValue, optionsText: 'label', optionsValue: 'value', multiSelect: { liveSearch: true, selectedTextFormat: 'count > 2', noneSelectedText: ko.unwrap(noneSelectedText), noneResultsText: ko.unwrap(noneResultsText) }"></select>
<select data-bind="attr: disabled, options: options, value: selectedValue, optionsText: 'label', optionsValue: 'value', multiSelect: { liveSearch: true, selectedTextFormat: 'count > 2', noneSelectedText: ko.unwrap(noneSelectedText), noneResultsText: ko.unwrap(noneResultsText) }"></select>
<!-- /ko -->
</div>
1 change: 1 addition & 0 deletions js/components/multi-select.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ define(

self.multiple = params.multiple;
self.options = params.options;
self.disable = (params.disable || false);
self.selectedValues = ko.observableArray(params.selectedValues && params.selectedValues());
self.selectedValue = params.selectedValue;
self.selectedTextFormat = params.selectedTextFormat || 'count > 2';
Expand Down
11 changes: 5 additions & 6 deletions js/components/reports/classes/Treemap.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,10 @@ define([
data
}) {
const normalizedData = atlascharts.chart.normalizeDataframe(ChartUtils.normalizeArray(data, true));
let tableData = [];

if (!normalizedData.empty) {
let distinctConceptIds = new Set([]);
let tableData = [];
// Make the values unique per https://github.com/OHDSI/Atlas/issues/913
normalizedData.conceptPath.forEach((d, i) => {
if (!distinctConceptIds.has(normalizedData.conceptId[i])) {
Expand All @@ -165,14 +165,13 @@ define([
});
}
});
this.tableData(tableData);
this.treeData(normalizedData);

return {
data
};
}
this.tableData(tableData);

return {
data
};
}

getData() {
Expand Down
2 changes: 1 addition & 1 deletion js/components/reports/reportDrilldown.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ define([
this.chartFormats.frequencyDistribution.yMax = yScaleMax;
this.chartFormats.frequencyDistribution.xLabel = ko.pureComputed(function () {
return ko.i18n('dataSources.drilldown.chartFormat.frequencyDistribution.xLabel1', 'Count ("x" or more ')() +
report() +
report +
ko.i18n('dataSources.drilldown.chartFormat.frequencyDistribution.xLabel2', 's)')();
});
this.chartFormats.frequencyDistribution.ticks = Math.min(5, frequencyHistogram.INTERVALS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
<!-- /ko -->
<textarea data-bind="
css: classes({ element: 'descr', extra: 'form-control' }),
attr: { disabled: $component.canEdit() ? null : true },
attr: { disabled: $component.canEdit() ? null : true }
textInput: $component.data().descr
"></textarea>
</div>
</div>

<div data-bind="css: classes({ element: 'design-panel', extra: 'panel panel-primary' })">
<div data-bind="css: classes({ element: 'design-panel', extra: 'panel panel-primary'})">
<div class="panel-heading" data-bind="text: ko.i18n('cc.fa.design', 'Design')"></div>
<div data-bind="css: classes({ element: 'design-panel-body' })">
<div data-bind="css: classes('type-selector')">
Expand All @@ -32,6 +32,7 @@
data-bind="
if: $component.canEdit() || $component.data().type() === $component.featureTypes.PRESET,
css: classes({ element: 'nav-pill', extra: $component.data().type() === $component.featureTypes.PRESET ? 'active' : null }),
attr: { disabled: $component.canEdit() ? null : true },
click: setType.bind(null, $component.featureTypes.PRESET)
">
<a data-bind="text: ko.i18n('cc.fa.preset', 'Preset')"></a>
Expand All @@ -44,6 +45,7 @@
data-bind="
if: ($component.canEdit() && !$component.featureId()) || $component.data().type() === $component.featureTypes.CRITERIA_SET,
css: classes({ element: 'nav-pill', extra: $component.data().type() === $component.featureTypes.CRITERIA_SET ? 'active' : null }),
attr: { disabled: $component.canEdit() ? null : true },
click: setType.bind(null, $component.featureTypes.CRITERIA_SET)
">
<a data-bind="text: ko.i18n('cc.fa.criteria', 'Criteria')"></a>
Expand All @@ -52,6 +54,7 @@
data-bind="
if: ($component.canEdit() && !$component.featureId()) || $component.data().type() === $component.featureTypes.CUSTOM_FE,
css: classes({ element: 'nav-pill', extra: $component.data().type() === $component.featureTypes.CUSTOM_FE ? 'active' : null }),
attr: { disabled: $component.canEdit() ? null : true },
click: setType.bind(null, $component.featureTypes.CUSTOM_FE)
">
<a data-bind="text: ko.i18n('cc.fa.custom', 'Custom')"></a>
Expand All @@ -63,27 +66,27 @@
<span data-bind="css: classes('criteria-descr'), text: ko.i18n('cc.fa.featureExtractionPresetName', 'FeatureExtraction preset name:')"></span>
<input data-bind="
css: classes({ element: 'preset-input', extra: 'form-control' }),
attr: { disabled: $component.canEdit() ? null : true },
textInput: $component.data().design
" />
</div>
<!-- /ko -->
<div data-bind="
if: $component.data().type() === $component.featureTypes.CRITERIA_SET, css: classes('design')
if: $component.data().type() === $component.featureTypes.CRITERIA_SET,
css: classes('design')
">
<div data-bind="css: classes({extra: 'panel panel-primary'})">
<div data-bind="css: classes({ extra: 'panel-heading' }), text: ko.i18n('cc.fa.analysisType', 'Analysis type:')"></div>
<div data-bind="css: classes('stat-type-selector')">
<multi-select
data-bind="css: classes()"
params="options: $component.statTypeOptions,
params="options: $component.statTypeOptions, disable: !$component.canEdit(),
selectedValue: $component.data().statType, multiple: false"></multi-select>
</div>
</div>
<span data-bind="if: data().statType() === 'PREVALENCE'">
<button data-bind="css: classes({ element: 'add-criteria-group', extra: 'btn btn-success' }), click: addCriteria, text: ko.i18n('cc.fa.addCriteriaFeature', 'Add Criteria feature')"></button>
<button data-bind="css: classes({ element: 'add-criteria-group', extra: 'btn btn-success' }), attr: { disabled: $component.canEdit() ? null : true }, click: addCriteria, text: ko.i18n('cc.fa.addCriteriaFeature', 'Add Criteria feature')"></button>
</span>
<span data-bind="if: data().statType() === 'DISTRIBUTION', css: classes('add-criteria-windowed')">
<span data-bind="if: data().statType() === 'DISTRIBUTION', css: classes('add-criteria-windowed'), attr: { disabled: $component.canEdit() ? null : true }">
<div>
<div class="btn-group">
<button type="button" class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown"><i class="fa fa-plus"></i>
Expand All @@ -100,35 +103,37 @@
</div>
</span>
<!-- ko foreach: $component.data().design -->
<div data-bind="
css: $component.classes('criteria'),
eventListener: [
{ event: 'click', selector: '.conceptset_import', callback: $component.handleConceptSetImport},
{ event: 'click', selector: '.conceptset_edit', callback: $component.handleEditConceptSet }
]
">
<div data-bind="css: $component.classes('criteria-heading')">
<input
data-bind="css: $component.classes({ element: 'criteria-name', extra: 'form-control' }), textInput: $data.name, placeholder: ko.i18n('cc.fa.criteriaName', 'Criteria name')"
/>
<button
type="button"
data-bind="css: $component.classes({ element: 'criteria-delete', extra: 'btn btn-sm btn-danger' }), click: () => $component.removeCriteria($index())"
><i class="fa fa-trash-alt"></i></button>
</div>
<!-- Aggregate -->
<div data-bind="if: $parent.data().statType() === 'DISTRIBUTION' && $data.criteriaType !== 'DemographicCriteria'">
<aggregate-select params="aggregates: $component.aggregates, currentAggregate: $data.aggregate, domains: $component.domains, criteria: $data"></aggregate-select>
</div>
<div data-bind="if: $data.criteriaType === 'CriteriaGroup'">
<criteria-group params="{ expression: { ConceptSets: $component.data().conceptSets }, group: $data.expression }"></criteria-group>
</div>
<div data-bind="if: $data.criteriaType === 'DemographicCriteria'">
<demographic-criteria params="{ criteria: $data.expression }"></demographic-criteria>
</div>
<div data-bind="if: $data.criteriaType === 'WindowedCriteria'">
<windowed-criteria params="{ expression: { ConceptSets: $component.data().conceptSets }, criteria: $data.expression, disableObservationPeriod: true, defaultObservationPeriod: true }"></windowed-criteria>
</div>
<div data-bind="css: $component.canEdit() ? '' : 'feature-analysis-view-edit__content--disabled'">
<div data-bind="
css: $component.classes('criteria'),
eventListener: [
{ event: 'click', selector: '.conceptset_import', callback: $component.handleConceptSetImport},
{ event: 'click', selector: '.conceptset_edit', callback: $component.handleEditConceptSet }
]
">
<div data-bind="css: $component.classes('criteria-heading')">
<input
data-bind="css: $component.classes({ element: 'criteria-name', extra: 'form-control' }), textInput: $data.name, placeholder: ko.i18n('cc.fa.criteriaName', 'Criteria name')"
/>
<button
type="button"
data-bind="css: $component.classes({ element: 'criteria-delete', extra: 'btn btn-sm btn-danger' }), attr: { disabled: $component.canEdit() ? null : true }, click: () => $component.removeCriteria($index())"
><i class="fa fa-trash-alt"></i></button>
</div>
<!-- Aggregate -->
<div data-bind="if: $parent.data().statType() === 'DISTRIBUTION' && $data.criteriaType !== 'DemographicCriteria'">
<aggregate-select params="aggregates: $component.aggregates, currentAggregate: $data.aggregate, domains: $component.domains, criteria: $data"></aggregate-select>
</div>
<div data-bind="if: $data.criteriaType === 'CriteriaGroup'">
<criteria-group params="{ expression: { ConceptSets: $component.data().conceptSets }, group: $data.expression}"></criteria-group>
</div>
<div data-bind="if: $data.criteriaType === 'DemographicCriteria'">
<demographic-criteria params="{ criteria: $data.expression }"></demographic-criteria>
</div>
<div data-bind="if: $data.criteriaType === 'WindowedCriteria'">
<windowed-criteria params="{ expression: { ConceptSets: $component.data().conceptSets }, criteria: $data.expression, disableObservationPeriod: true, defaultObservationPeriod: true }"></windowed-criteria>
</div>
</div>
</div>
<!-- /ko -->
</div>
Expand All @@ -146,7 +151,7 @@
<span data-bind="css: classes('criteria-sql')">SELECT covariate_id, covariate_name, concept_id, sum_value, average_value FROM (</span>
<textarea data-bind="
css: classes({ element: 'raw-sql-box', extra: 'form-control' }),
attr: { disabled: $component.canEdit() ? null : true, placeholder: demoCustomSqlAnalysisDesign },
attr: { placeholder: demoCustomSqlAnalysisDesign },
textInput: $component.data().design
"></textarea>
<span data-bind="css: classes('criteria-sql')">)</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
</div>

<!-- ko if: selectedTabKey -->
<tabs data-bind="css: editorClasses(), visible: !$component.loading()" params="
<tabs data-bind="visible: !$component.loading()" params="
selectedTabKey: $component.selectedTabKey,
selectTab: $component.selectTab,
tabs: $component.tabs">
Expand Down
Loading

0 comments on commit 1a662b3

Please sign in to comment.