Skip to content

Commit

Permalink
Merge pull request #1200 from nextstrain/filters
Browse files Browse the repository at this point in the history
Sidebar filtering of data
  • Loading branch information
jameshadfield authored Nov 18, 2020
2 parents 43d9b05 + 5914192 commit f9be5b5
Show file tree
Hide file tree
Showing 27 changed files with 623 additions and 634 deletions.
2 changes: 1 addition & 1 deletion bundlesize.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
},
{
"path": "./dist/auspice.chunk.other-vendors.bundle.*.js",
"maxSize": "80 kB"
"maxSize": "85 kB"
},
{
"path": "./dist/auspice.chunk.locales.bundle.*.js",
Expand Down
23 changes: 17 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
"@babel/preset-react": "^7.0.0",
"@hot-loader/react-dom": "^16.13.0",
"argparse": "^1.0.10",
"awesomplete": "^1.1.2",
"babel-loader": "^8.0.4",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-strip-function-call": "^1.0.2",
Expand Down Expand Up @@ -112,6 +111,7 @@
"react-icons": "^3.9.0",
"react-redux": "^5.1.0",
"react-select": "^1.0.0-rc.5",
"react-tooltip": "^4.2.10",
"react-tweet-embed": "^1.1.0",
"redux": "^4.0.1",
"redux-devtools": "^3.5.0",
Expand Down
74 changes: 36 additions & 38 deletions src/actions/recomputeReduxState.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import queryString from "query-string";
import { cloneDeep } from 'lodash';
import { numericToCalendar, calendarToNumeric } from "../util/dateHelpers";
import { reallySmallNumber, twoColumnBreakpoint, defaultColorBy, defaultGeoResolution, defaultDateRange, nucleotide_gene } from "../util/globals";
import { reallySmallNumber, twoColumnBreakpoint, defaultColorBy, defaultGeoResolution, defaultDateRange, nucleotide_gene, strainSymbol } from "../util/globals";
import { calcBrowserDimensionsInitialState } from "../reducers/browserDimensions";
import { strainNameToIdx, getIdxMatchingLabel, calculateVisiblityAndBranchThickness } from "../util/treeVisibilityHelpers";
import { getIdxMatchingLabel, calculateVisiblityAndBranchThickness } from "../util/treeVisibilityHelpers";
import { constructVisibleTipLookupBetweenTrees } from "../util/treeTangleHelpers";
import { calcTipRadii } from "../util/tipRadiusHelpers";
import { getDefaultControlsState, shouldDisplayTemporalConfidence } from "../reducers/controls";
import { countTraitsAcrossTree, calcTotalTipsInTree } from "../util/treeCountingHelpers";
import { calcEntropyInView } from "../util/entropy";
Expand Down Expand Up @@ -88,7 +87,11 @@ const modifyStateViaURLQuery = (state, query) => {
state["dateMaxNumeric"] = calendarToNumeric(query.dmax);
}
for (const filterKey of Object.keys(query).filter((c) => c.startsWith('f_'))) {
state.filters[filterKey.replace('f_', '')] = query[filterKey].split(',');
state.filters[filterKey.replace('f_', '')] = query[filterKey].split(',')
.map((value) => ({value, active: true})); /* all filters in the URL are "active" */
}
if (query.s) { // selected strains are a filter too
state.filters[strainSymbol] = query.s.split(',').map((value) => ({value, active: true}));
}
if (query.animate) {
const params = query.animate.split(',');
Expand Down Expand Up @@ -136,7 +139,6 @@ const modifyStateViaURLQuery = (state, query) => {
state.showTransmissionLines = false;
}
}

return state;
};

Expand Down Expand Up @@ -193,13 +195,17 @@ const modifyStateViaMetadata = (state, metadata) => {
state["analysisSlider"] = {key: metadata.analysisSlider, valid: false};
}
if (metadata.filters) {
/* the `meta -> filters` JSON spec should define which filters are displayed in the footer.
Note that this UI may change, and if so then we can change this state name. */
state.filtersInFooter = [...metadata.filters];
/* TODO - these will be searchable => all available traits should be added and this block shifted up */
metadata.filters.forEach((v) => {
state.filters[v] = [];
state.defaults.filters[v] = [];
});
} else {
console.warn("JSON did not include any filters");
}
state.filters[strainSymbol] = [];
if (metadata.displayDefaults) {
const keysToCheckFor = ["geoResolution", "colorBy", "distanceMeasure", "layout", "mapTriplicate", "selectedBranchLabel", 'sidebar', "showTransmissionLines", "normalizeFrequencies"];
const expectedTypes = ["string", "string", "string", "string", "boolean", "string", 'string', "boolean" , "boolean"]; // eslint-disable-line
Expand Down Expand Up @@ -489,18 +495,25 @@ const checkAndCorrectErrorsInState = (state, metadata, query, tree, viewingNarra
}
}

/* are filters valid? */
const activeFilters = Object.keys(state.filters).filter((f) => f.length);
const stateCounts = countTraitsAcrossTree(tree.nodes, activeFilters, false, true);
for (const filterType of activeFilters) {
const validValues = state.filters[filterType]
.filter((filterValue) => stateCounts[filterType].has(filterValue));
state.filters[filterType] = validValues;
if (!validValues.length) {
delete query[`f_${filterType}`];
/* ensure selected filters (via the URL query) are valid. If not, modify state + URL. */
const filterNames = Object.keys(state.filters).filter((filterName) => state.filters[filterName].length);
const stateCounts = countTraitsAcrossTree(tree.nodes, filterNames, false, true);
filterNames.forEach((filterName) => {
const validItems = state.filters[filterName]
.filter((item) => stateCounts[filterName].has(item.value));
state.filters[filterName] = validItems;
if (!validItems.length) {
delete query[`f_${filterName}`];
} else {
query[`f_${filterType}`] = validValues.join(",");
query[`f_${filterName}`] = validItems.map((x) => x.value).join(",");
}
});
if (state.filters[strainSymbol]) {
const validNames = tree.nodes.map((n) => n.name);
state.filters[strainSymbol] = state.filters[strainSymbol]
.filter((strainFilter) => validNames.includes(strainFilter.value));
query.s = state.filters[strainSymbol].map((f) => f.value).join(",");
if (!query.s) delete query.s;
}

/* can we display branch length by div or num_date? */
Expand All @@ -521,30 +534,23 @@ const checkAndCorrectErrorsInState = (state, metadata, query, tree, viewingNarra
return state;
};

const modifyTreeStateVisAndBranchThickness = (oldState, tipSelected, zoomSelected, controlsState, dispatch) => {
const modifyTreeStateVisAndBranchThickness = (oldState, zoomSelected, controlsState, dispatch) => {
/* calculate new branch thicknesses & visibility */
let tipSelectedIdx = 0;
/* check if the query defines a strain to be selected */
let newIdxRoot = oldState.idxOfInViewRootNode;
if (tipSelected) {
tipSelectedIdx = strainNameToIdx(oldState.nodes, tipSelected);
oldState.selectedStrain = tipSelected;
}
if (zoomSelected) {
// Check and fix old format 'clade=B' - in this case selectionClade is just 'B'
const [labelName, labelValue] = zoomSelected.split(":");
const cladeSelectedIdx = getIdxMatchingLabel(oldState.nodes, labelName, labelValue, dispatch);
oldState.selectedClade = zoomSelected;
newIdxRoot = applyInViewNodesToTree(cladeSelectedIdx, oldState); // tipSelectedIdx, oldState);
newIdxRoot = applyInViewNodesToTree(cladeSelectedIdx, oldState);
} else {
oldState.selectedClade = undefined;
newIdxRoot = applyInViewNodesToTree(0, oldState); // tipSelectedIdx, oldState);
newIdxRoot = applyInViewNodesToTree(0, oldState);
}
const visAndThicknessData = calculateVisiblityAndBranchThickness(
oldState,
controlsState,
{dateMinNumeric: controlsState.dateMinNumeric, dateMaxNumeric: controlsState.dateMaxNumeric},
{tipSelectedIdx}
{dateMinNumeric: controlsState.dateMinNumeric, dateMaxNumeric: controlsState.dateMaxNumeric}
);

const newState = Object.assign({}, oldState, visAndThicknessData);
Expand All @@ -553,10 +559,6 @@ const modifyTreeStateVisAndBranchThickness = (oldState, tipSelected, zoomSelecte
newState.visibleStateCounts = countTraitsAcrossTree(newState.nodes, newState.stateCountAttrs, newState.visibility, true);
newState.totalStateCounts = countTraitsAcrossTree(newState.nodes, newState.stateCountAttrs, false, true); // eslint-disable-line

if (tipSelectedIdx) { /* i.e. query.s was set */
newState.tipRadii = calcTipRadii({tipSelectedIdx, colorScale: controlsState.colorScale, tree: newState});
newState.tipRadiiVersion = 1;
}
return newState;
};

Expand Down Expand Up @@ -772,12 +774,12 @@ export const createStateFromQueryOrJSONs = ({
}

/* if query.label is undefined then we intend to zoom to the root */
tree = modifyTreeStateVisAndBranchThickness(tree, query.s, query.label, controls, dispatch);
tree = modifyTreeStateVisAndBranchThickness(tree, query.label, controls, dispatch);

if (treeToo && treeToo.loaded) {
treeToo.nodeColorsVersion = tree.nodeColorsVersion;
treeToo.nodeColors = calcNodeColor(treeToo, controls.colorScale);
treeToo = modifyTreeStateVisAndBranchThickness(treeToo, query.s, undefined, controls, dispatch);
treeToo = modifyTreeStateVisAndBranchThickness(treeToo, undefined, controls, dispatch);
controls = modifyControlsViaTreeToo(controls, treeToo.name);
treeToo.tangleTipLookup = constructVisibleTipLookupBetweenTrees(tree.nodes, treeToo.nodes, tree.visibility, treeToo.visibility);
}
Expand Down Expand Up @@ -829,7 +831,7 @@ export const createTreeTooState = ({
treeToo.debug = "RIGHT";
controls = modifyControlsStateViaTree(controls, tree, treeToo, oldState.metadata.colorings);
controls = modifyControlsViaTreeToo(controls, secondTreeUrl);
treeToo = modifyTreeStateVisAndBranchThickness(treeToo, tree.selectedStrain, undefined, controls, dispatch);
treeToo = modifyTreeStateVisAndBranchThickness(treeToo, undefined, controls, dispatch);

/* calculate colours if loading from JSONs or if the query demands change */
const colorScale = calcColorScale(controls.colorBy, controls, tree, treeToo, oldState.metadata);
Expand All @@ -846,9 +848,5 @@ export const createTreeTooState = ({
tree.nodes, treeToo.nodes, tree.visibility, treeToo.visibility
);

// if (tipSelectedIdx) { /* i.e. query.s was set */
// tree.tipRadii = calcTipRadii({tipSelectedIdx, colorScale: controls.colorScale, tree});
// tree.tipRadiiVersion = 1;
// }
return {tree, treeToo, controls};
};
Loading

0 comments on commit f9be5b5

Please sign in to comment.