Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
7aed8d1
feat: query state url decoding
ZakirG Mar 4, 2021
e317373
-
ZakirG Mar 11, 2021
55ded24
ddebugging
ZakirG Mar 12, 2021
fbe9925
debugging
ZakirG Mar 13, 2021
2485501
debugging
ZakirG Mar 13, 2021
5ada8ab
debugging
ZakirG Mar 13, 2021
1da2a02
debugging
ZakirG Mar 13, 2021
af81389
debugging
ZakirG Mar 13, 2021
c3f71dc
debugging
ZakirG Mar 13, 2021
5c83261
debugging
ZakirG Mar 13, 2021
9cc2bb9
debugging
ZakirG Mar 13, 2021
6067bf5
debugging
ZakirG Mar 13, 2021
ad07137
debugging
ZakirG Mar 13, 2021
72f824b
debugging
ZakirG Mar 13, 2021
e6ce1b9
debugging
ZakirG Mar 13, 2021
c7f358f
debugging
ZakirG Mar 13, 2021
9899588
debugging
ZakirG Mar 13, 2021
319e20c
debugging
ZakirG Mar 13, 2021
5ae5f31
debugging
ZakirG Mar 13, 2021
9bb3322
debugging
ZakirG Mar 15, 2021
93349ab
debugging
ZakirG Mar 15, 2021
cac9520
debugging
ZakirG Mar 15, 2021
cbc9ebd
debugging
ZakirG Mar 15, 2021
27b0007
debugging
ZakirG Mar 15, 2021
384f4bb
loading url filter into UI is working
ZakirG Mar 15, 2021
2926308
debugging
ZakirG Mar 15, 2021
118d8fb
fix eslint
ZakirG Mar 15, 2021
9ccd3bd
fix eslint
ZakirG Mar 15, 2021
526e4f4
debugging
ZakirG Mar 15, 2021
4cc039e
cleaning up
ZakirG Mar 16, 2021
dcd919b
cleaning up
ZakirG Mar 16, 2021
48d973b
cleaning up
ZakirG Mar 16, 2021
4038de4
cleaning up
ZakirG Mar 16, 2021
0118ff2
cleaning up
ZakirG Mar 16, 2021
fc4166b
debugging
ZakirG Mar 16, 2021
86a6c58
debugging
ZakirG Mar 16, 2021
3a1cb56
debugging
ZakirG Mar 17, 2021
9c22252
debugging
ZakirG Mar 17, 2021
b787fd7
debugging
ZakirG Mar 19, 2021
e342f44
debugging
ZakirG Mar 23, 2021
d5e9557
debugging
ZakirG Mar 24, 2021
8c7c245
debugging
ZakirG Mar 24, 2021
d22ae89
debugging
ZakirG Mar 24, 2021
6ac40d5
debugging
ZakirG Mar 24, 2021
37b7c33
debugging
ZakirG Mar 24, 2021
9a730de
debugging
ZakirG Mar 25, 2021
85dcdd8
debugging
ZakirG Mar 26, 2021
df44206
debugging
ZakirG Mar 26, 2021
db72e1a
debugging
ZakirG Mar 26, 2021
bd9a3b5
cleaning up
ZakirG Mar 26, 2021
bc0163b
cleaning up
ZakirG Mar 26, 2021
e4ca414
Merge branch 'master' of https://github.com/uc-cdis/guppy into feat/q…
ZakirG Mar 26, 2021
3e522be
sketching out unit test
ZakirG Mar 29, 2021
c7f950a
sketching out unit test
ZakirG Mar 29, 2021
7a64324
sketching out unit test
ZakirG Mar 29, 2021
8e2c895
cleaning up logics
ZakirG Mar 29, 2021
f7e2e8b
simplifying logic
ZakirG Mar 29, 2021
9535761
simplifying logic
ZakirG Mar 29, 2021
f07dd4b
eslint
ZakirG Mar 29, 2021
c808999
simplify logic
ZakirG Mar 29, 2021
eb4ab2c
cleaning up
ZakirG Mar 29, 2021
91b8728
cleaning up
ZakirG Mar 29, 2021
76432c2
package
ZakirG Mar 30, 2021
ac0d581
package
ZakirG Mar 30, 2021
9963709
fix Dockerfile
ZakirG Mar 30, 2021
6894ae0
debugging issue
ZakirG Mar 30, 2021
4a6914f
debugging issue
ZakirG Mar 30, 2021
bd27d03
debugging
ZakirG Apr 1, 2021
7175a57
debugging
ZakirG Apr 1, 2021
2af89ba
debugging
ZakirG Apr 1, 2021
6c1daec
ddedbugging
ZakirG Apr 1, 2021
dbdb1d1
ddedbugging
ZakirG Apr 1, 2021
0312c12
ddedbugging
ZakirG Apr 1, 2021
6ed0a53
ddedbugging
ZakirG Apr 2, 2021
083d441
ddedbugging
ZakirG Apr 2, 2021
da2d91c
ddedbugging
ZakirG Apr 2, 2021
afdb027
ok
ZakirG Apr 2, 2021
c17a9a6
ok
ZakirG Apr 2, 2021
cf2314b
ok
ZakirG Apr 2, 2021
bbb9082
ok
ZakirG Apr 2, 2021
cef3c53
cleaning up
ZakirG Apr 2, 2021
b5865dd
cleaning up
ZakirG Apr 2, 2021
d17c085
cleaning up
ZakirG Apr 2, 2021
87f8821
eslint
ZakirG Apr 2, 2021
b172d69
PR feedback
ZakirG Apr 8, 2021
5c9bd3c
debugging newly found chart issue
ZakirG Apr 8, 2021
1eab921
PR feedback
ZakirG Apr 8, 2021
006871e
debugging chart issue
ZakirG Apr 8, 2021
2b8f9b7
debugging chart issue
ZakirG Apr 8, 2021
f28aec5
debugging chart issue
ZakirG Apr 8, 2021
a526108
cleaning up
ZakirG Apr 8, 2021
2c3d566
testing new PR feedback
ZakirG Apr 12, 2021
e85ab0c
update ui-component package version
ZakirG Apr 12, 2021
8c4ecf2
add comment
ZakirG Apr 13, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ RUN apt-get update \
vim \
&& curl -sL https://deb.nodesource.com/setup_10.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*
&& rm -rf /var/lib/apt/lists/* \
&& npm install -g [email protected] \
&& npm config set maxsockets 5

COPY . /guppy/
WORKDIR /guppy
Expand Down
16,167 changes: 4,913 additions & 11,254 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"homepage": "https://github.com/uc-cdis/guppy#readme",
"dependencies": {
"@elastic/elasticsearch": "^7.0.0-rc.1",
"@gen3/ui-component": "^0.7.0",
"@gen3/ui-component": "^0.9.0",
"apollo-server": "^2.4.8",
"apollo-server-express": "^2.4.8",
"array.prototype.flat": "^1.2.2",
Expand Down
95 changes: 62 additions & 33 deletions src/components/ConnectedFilter/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
updateCountsInInitialTabsOptions,
sortTabsOptions,
mergeTabOptions,
buildFilterStatusForURLFilter,
} from '../Utils/filters';

class ConnectedFilter extends React.Component {
Expand All @@ -32,14 +33,25 @@ class ConnectedFilter extends React.Component {
: filterConfigsFields;

this.initialTabsOptions = {};
let initialFilter = this.props.adminAppliedPreFilters;
let filterStatusArray = [];
let filtersApplied = {};
if (this.props.userFilterFromURL && Object.keys(this.props.userFilterFromURL).length > 0) {
filterStatusArray = buildFilterStatusForURLFilter(this.props.userFilterFromURL,
this.getTabsWithSearchFields());
filtersApplied = this.props.userFilterFromURL;
initialFilter = mergeFilters(this.props.userFilterFromURL, this.props.adminAppliedPreFilters);
}

this.state = {
allFields,
initialAggsData: {},
receivedAggsData: {},
accessibility: ENUM_ACCESSIBILITY.ALL,
adminAppliedPreFilters: { ...this.props.adminAppliedPreFilters },
filter: { ...this.props.adminAppliedPreFilters },
filtersApplied: {},
filter: { ...initialFilter },
filtersApplied,
filterStatusArray,
};
this.filterGroupRef = React.createRef();
this.adminPreFiltersFrozen = JSON.stringify(this.props.adminAppliedPreFilters).slice();
Expand Down Expand Up @@ -104,7 +116,11 @@ class ConnectedFilter extends React.Component {
handleFilterChange(filterResults) {
this.setState({ adminAppliedPreFilters: JSON.parse(this.adminPreFiltersFrozen) });
const mergedFilterResults = mergeFilters(filterResults, JSON.parse(this.adminPreFiltersFrozen));
this.setState({ filtersApplied: mergedFilterResults });

const newFilterStatusArray = buildFilterStatusForURLFilter(mergedFilterResults,
this.getTabsWithSearchFields());

this.setState({ filtersApplied: mergedFilterResults, filterStatusArray: newFilterStatusArray });
askGuppyForAggregationData(
this.props.guppyConfig.path,
this.props.guppyConfig.type,
Expand All @@ -124,6 +140,16 @@ class ConnectedFilter extends React.Component {
}
}

getTabsWithSearchFields() {
const newTabs = this.props.filterConfig.tabs.map(({ title, fields, searchFields }) => {
if (searchFields) {
return { title, fields: searchFields.concat(fields) };
}
return { title, fields };
});
return newTabs;
}

setFilter(filter) {
if (this.filterGroupRef.current) {
this.filterGroupRef.current.resetFilter();
Expand All @@ -139,6 +165,7 @@ class ConnectedFilter extends React.Component {
* component could do some pre-processing modification about filter.
*/
getFilterTabs() {
const filtersToDisplay = this.state.filtersApplied;
if (this.props.hidden) return null;
let processedTabsOptions = this.props.onProcessFilterAggsData(this.state.receivedAggsData);
if (Object.keys(this.initialTabsOptions).length === 0) {
Expand All @@ -148,12 +175,12 @@ class ConnectedFilter extends React.Component {
processedTabsOptions = updateCountsInInitialTabsOptions(
this.initialTabsOptions,
processedTabsOptions,
this.state.filtersApplied,
filtersToDisplay,
// for tiered access filters
this.props.tierAccessLimit ? this.props.accessibleFieldCheckList : [],
);

if (Object.keys(this.state.filtersApplied).length) {
if (Object.keys(filtersToDisplay).length) {
// if has applied filters, sort tab options as selected/unselected separately
const selectedTabsOptions = {};
const unselectedTabsOptions = {};
Expand All @@ -166,9 +193,9 @@ class ConnectedFilter extends React.Component {
return;
}
processedTabsOptions[`${opt}`].histogram.forEach((entry) => {
if (this.state.filtersApplied[`${opt}`]
&& this.state.filtersApplied[`${opt}`].selectedValues
&& this.state.filtersApplied[`${opt}`].selectedValues.includes(entry.key)) {
if (filtersToDisplay[`${opt}`]
&& filtersToDisplay[`${opt}`].selectedValues
&& filtersToDisplay[`${opt}`].selectedValues.includes(entry.key)) {
if (!selectedTabsOptions[`${opt}`]) {
selectedTabsOptions[`${opt}`] = {};
}
Expand Down Expand Up @@ -200,10 +227,10 @@ class ConnectedFilter extends React.Component {
allSearchFields = allSearchFields.concat(tab.searchFields);
});
allSearchFields.forEach((field) => {
if (this.state.filtersApplied[`${field}`]) {
const { selectedValues } = this.state.filtersApplied[`${field}`];
if (filtersToDisplay[`${field}`]) {
const { selectedValues } = filtersToDisplay[`${field}`];
if (selectedValues) {
this.state.filtersApplied[`${field}`].selectedValues.forEach((val) => {
filtersToDisplay[`${field}`].selectedValues.forEach((val) => {
if (!selectedTabsOptions[`${field}`]) {
selectedTabsOptions[`${field}`] = {};
}
Expand All @@ -221,24 +248,27 @@ class ConnectedFilter extends React.Component {
} else {
processedTabsOptions = sortTabsOptions(processedTabsOptions);
}

if (!processedTabsOptions || Object.keys(processedTabsOptions).length === 0) return null;
const { fieldMapping } = this.props;
const tabs = this.props.filterConfig.tabs.map(({ fields, searchFields }, index) => (
<FilterList
key={index}
sections={
getFilterSections(fields, searchFields, fieldMapping, processedTabsOptions,
this.state.initialAggsData, this.state.adminAppliedPreFilters,
this.props.guppyConfig, this.arrayFields)
}
hideEmptyFilterSection={this.props.hideEmptyFilterSection}
tierAccessLimit={this.props.tierAccessLimit}
lockedTooltipMessage={this.props.lockedTooltipMessage}
disabledTooltipMessage={this.props.disabledTooltipMessage}
arrayFields={this.arrayFields}
/>
));
const tabs = this.props.filterConfig.tabs.map(({ fields, searchFields }, index) => {
const sections = getFilterSections(fields, searchFields, fieldMapping, processedTabsOptions,
this.state.initialAggsData, this.state.adminAppliedPreFilters,
this.props.guppyConfig, this.arrayFields);
const filterStatus = this.state.filterStatusArray
? this.state.filterStatusArray[index] : null;
return (
<FilterList
key={index}
sections={sections}
hideEmptyFilterSection={this.props.hideEmptyFilterSection}
tierAccessLimit={this.props.tierAccessLimit}
lockedTooltipMessage={this.props.lockedTooltipMessage}
disabledTooltipMessage={this.props.disabledTooltipMessage}
arrayFields={this.arrayFields}
filterStatusFromParent={filterStatus}
/>
);
});
return tabs;
}

Expand All @@ -259,12 +289,7 @@ class ConnectedFilter extends React.Component {
}
// If there are any search fields, insert them at the top of each tab's fields.
const filterConfig = {
tabs: this.props.filterConfig.tabs.map(({ title, fields, searchFields }) => {
if (searchFields) {
return { title, fields: searchFields.concat(fields) };
}
return { title, fields };
}),
tabs: this.getTabsWithSearchFields(),
};
return (
<FilterGroup
Expand All @@ -274,6 +299,8 @@ class ConnectedFilter extends React.Component {
filterConfig={filterConfig}
onFilterChange={(e) => this.handleFilterChange(e)}
hideZero={this.props.hideZero}
filterStatusFromParent={this.state.filterStatusArray}
filterResultsFromParent={this.state.filtersApplied}
/>
);
}
Expand Down Expand Up @@ -307,6 +334,7 @@ ConnectedFilter.propTypes = {
accessibleFieldCheckList: PropTypes.arrayOf(PropTypes.string),
hideZero: PropTypes.bool,
hidden: PropTypes.bool,
userFilterFromURL: PropTypes.object,
hideEmptyFilterSection: PropTypes.bool,
};

Expand All @@ -324,6 +352,7 @@ ConnectedFilter.defaultProps = {
accessibleFieldCheckList: undefined,
hideZero: false,
hidden: false,
userFilterFromURL: {},
hideEmptyFilterSection: false,
};

Expand Down
24 changes: 21 additions & 3 deletions src/components/GuppyWrapper/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,19 @@ import { mergeFilters } from '../Utils/filters';
class GuppyWrapper extends React.Component {
constructor(props) {
super(props);
let initialFilter = this.props.adminAppliedPreFilters;
if (Object.keys(this.props.initialFilterFromURL).length > 0) {
initialFilter = mergeFilters(this.props.initialFilterFromURL,
this.props.adminAppliedPreFilters);
}

// to avoid asynchronizations, we store another filter as private var
this.filter = { ...this.props.adminAppliedPreFilters };
this.filter = { ...initialFilter };
this.adminPreFiltersFrozen = JSON.stringify(this.props.adminAppliedPreFilters).slice();
this.state = {
gettingDataFromGuppy: false,
aggsData: {},
filter: { ...this.props.adminAppliedPreFilters },
filter: { ...initialFilter },
rawData: [],
totalCount: 0,
allFields: [],
Expand All @@ -62,6 +68,7 @@ class GuppyWrapper extends React.Component {
unaccessibleFieldObject: undefined,
accessibility: ENUM_ACCESSIBILITY.ALL,
adminAppliedPreFilters: { ...this.props.adminAppliedPreFilters },
userFilterFromURL: { ...this.props.initialFilterFromURL },
};
}

Expand Down Expand Up @@ -100,7 +107,16 @@ class GuppyWrapper extends React.Component {
this.setState({ aggsData });
}

handleFilterChange(userFilter, accessibility) {
handleFilterChange(userFilterFromUserInput, accessibility) {
let userFilter = userFilterFromUserInput;

// Apply user filters from URL on page load. Empty out state to avoid reapplying used filters.
if (Object.keys(userFilter).length === 0
&& Object.keys(this.state.userFilterFromURL).length > 0) {
userFilter = JSON.parse(JSON.stringify(this.state.userFilterFromURL));
this.setState({ userFilterFromURL: {} });
}

this.setState({ adminAppliedPreFilters: JSON.parse(this.adminPreFiltersFrozen) });
let filter = { ...userFilter };
if (Object.keys(this.state.adminAppliedPreFilters).length > 0) {
Expand Down Expand Up @@ -352,6 +368,7 @@ GuppyWrapper.propTypes = {
onFilterChange: PropTypes.func,
accessibleFieldCheckList: PropTypes.arrayOf(PropTypes.string),
adminAppliedPreFilters: PropTypes.object,
initialFilterFromURL: PropTypes.object,
};

GuppyWrapper.defaultProps = {
Expand All @@ -360,6 +377,7 @@ GuppyWrapper.defaultProps = {
rawDataFields: [],
accessibleFieldCheckList: undefined,
adminAppliedPreFilters: {},
initialFilterFromURL: {},
};

export default GuppyWrapper;
32 changes: 32 additions & 0 deletions src/components/Utils/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,35 @@ export const mergeTabOptions = (firstTabsOptions, secondTabsOptions) => {
});
return mergedTabOptions;
};

export const buildFilterStatusForURLFilter = (userFilter, tabs) => {
// Converts filter-applied form to filter-displayed form
// TODO: add support for search filters
const filteringFields = Object.keys(userFilter);
const filterStatusArray = tabs.map(() => ([]));

for (let tabIndex = 0; tabIndex < tabs.length; tabIndex += 1) {
const allFieldsForThisTab = tabs[tabIndex].fields;
filterStatusArray[tabIndex] = allFieldsForThisTab.map(() => ({}));
for (let i = 0; i < filteringFields.length; i += 1) {
const sectionIndex = allFieldsForThisTab.indexOf(filteringFields[i]);
if (sectionIndex !== -1) {
let userFilterSmallForm = {};
const filterVar = userFilter[filteringFields[i]];
if (typeof filterVar === 'object' && filterVar.selectedValues) {
// Single select values:
for (let j = 0; j < filterVar.selectedValues.length; j += 1) {
userFilterSmallForm[filterVar.selectedValues[j]] = true;
}
} else if (typeof filterVar === 'object'
&& (filterVar.lowerBound || filterVar.upperBound)) {
// Range values:
userFilterSmallForm = [filterVar.lowerBound, filterVar.upperBound];
}
filterStatusArray[tabIndex][sectionIndex] = userFilterSmallForm;
}
}
}

return filterStatusArray;
};
53 changes: 52 additions & 1 deletion src/components/__tests__/filters.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint-disable global-require,import/no-dynamic-require */
// Tests for Utils/filters.js
import { mergeFilters, updateCountsInInitialTabsOptions, sortTabsOptions } from '../Utils/filters';
import {
mergeFilters, updateCountsInInitialTabsOptions, sortTabsOptions, buildFilterStatusForURLFilter,
} from '../Utils/filters';

describe('can merge simple selectedValue filters', () => {
const userFilter = { data_format: { selectedValues: ['VCF'] } };
Expand Down Expand Up @@ -232,3 +234,52 @@ describe('can sort tabs options', () => {
.toEqual(expectedSort);
});
});

describe('can convert between filter applied and filter displayed forms', () => {
// Unit test for buildFilterStatusForURLFilter()
const inputFilterFromURL = {
carotid_plaque: { selectedValues: ['Plaque present', 'Plaque not present'] },
cac_score: { lowerBound: 33, upperBound: 97 },
project_id: { selectedValues: ['DEV-test'] },
};
const tabs = [
{
title: 'Medical History',
fields: ['cac_score', 'cac_volume', 'carotid_plaque',
'carotid_stenosis', 'cimt_1', 'cimt_2', 'vte_case_status',
'vte_followup_start_age', 'vte_prior_history', 'antihypertensive_meds',
'fasting_lipids', 'lipid_lowering_medication',
],
},
{
title: 'Diagnosis',
fields: [
'bp_diastolic', 'basophil_ncnc_bld', 'eosinophil_ncnc_bld', 'hdl',
'hematocrit_vfr_bld', 'hemoglobin_mcnc_bld', 'ldl', 'lymphocyte_ncnc_bld',
'mch_entmass_rbc', 'mchc_mcnc_rbc', 'mcv_entvol_rbc', 'monocyte_ncnc_bld',
'neutrophil_ncnc_bld', 'platelet_ncnc_bld', 'pmv_entvol_bld', 'rbc_ncnc_bld',
'rdw_ratio_rbc', 'wbc_ncnc_bld',
],
},
{
title: 'Subject',
fields: [
'project_id', 'consent_codes', 'data_type', 'data_format',
'race', 'annotated_sex', 'hispanic_subgroup', 'ethnicity', 'subcohort', 'weight_baseline',
],
},
];

const displayFilterExpected = [
[[33, 97], {}, { 'Plaque present': true, 'Plaque not present': true }, {}, {}, {},
{}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{ 'DEV-test': true }, {}, {}, {}, {}, {}, {}, {}, {}, {}],
];

test('build filter display from url', async () => {
const displayFilter = buildFilterStatusForURLFilter(inputFilterFromURL, tabs);
expect(displayFilter)
.toEqual(displayFilterExpected);
});
});