Skip to content

Commit

Permalink
Refactoring displayRows, etc
Browse files Browse the repository at this point in the history
- Docs: Added / updated code comments
- Misc: Minor refactoring
  • Loading branch information
joeflack4 committed Oct 23, 2024
1 parent cf33409 commit 894e8de
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 53 deletions.
4 changes: 2 additions & 2 deletions frontend/src/components/CsetComparisonPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -712,8 +712,8 @@ function getCollapseIconAndName(
let direction;
if (
(graphOptions.expandAll &&
graphOptions.specificPaths[row.rowPath] !== 'collapse'
) || graphOptions.specificPaths[row.rowPath] === 'expand'
graphOptions.expandStateByPath[row.rowPath] !== 'collapse'
) || graphOptions.expandStateByPath[row.rowPath] === 'expand'
) {
Component = RemoveCircleOutline;
direction = 'collapse';
Expand Down
32 changes: 20 additions & 12 deletions frontend/src/state/AppState.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,11 @@ export function graphOptionsReducer(state, action) {
if (!validValues.includes(action.direction)) {
console.error(`Invalid direction for TOGGLE_NODE_EXPANDED: ${action.direction}`);
}
let specificPaths = {...graphOptions.specificPaths};
let current = specificPaths[rowPath];
let expandStateByPath = {...graphOptions.expandStateByPath};
let current = expandStateByPath[rowPath];
if (typeof(current) === 'undefined') {
// if no current expand/collapse for path, set it
specificPaths[rowPath] = action.direction;
expandStateByPath[rowPath] = action.direction;
} else {
// path has current state -- should be opposite of action.direction
// so just delete it (to unexpand/uncollapse)
Expand All @@ -194,9 +194,9 @@ export function graphOptionsReducer(state, action) {
if (current === action.direction) {
console.error(`Trying to ${action.direction} ${rowPath} but is already`);
}
delete specificPaths[rowPath];
delete expandStateByPath[rowPath];
}
graphOptions = { ...graphOptions, specificPaths};
graphOptions = { ...graphOptions, expandStateByPath};
break;
}
case 'TOGGLE_OPTION':
Expand All @@ -206,7 +206,7 @@ export function graphOptionsReducer(state, action) {
break;
case 'TOGGLE_EXPAND_ALL':
graphOptions = {...graphOptions, expandAll:!graphOptions.expandAll};
graphOptions.specificPaths = {};
graphOptions.expandStateByPath = {};
// just start over when expandAll flips
// could have two sets of specificPaths, one for expandAll, one for not
break;
Expand All @@ -231,10 +231,19 @@ if (process.env.NODE_ENV !== 'production') {
// window.appStateW = {}; // playwright complaining that window isn't defined
let appStateW = {};

/* makeProvider()
Makes provider to manage both a regular reducer and a storage provider.
I think the idea was to put update logic into reducers and try to have storage providers.
just emulate localStorage (whether for localStorage, sessionStorage, or querystring).
Returns:
Provider, useReducerWithStorage
Side effects:
- Updates global `resetFuncs` obj w/ the `resetFunc` for the given `stateName`.
*/
function makeProvider({stateName, reducer, initialSettings, storageProviderGetter, jsonify=false, }) {
// makes provider to manage both a regular reducer and a storage provider
// I think the idea was to put update logic into reducers and try to have storage providers
// just emulate localStorage (whether for localStorage, sessionStorage, or querystring)
/*
const regularReducer = (state, action) => {
const newState = reducer(state, action);
Expand Down Expand Up @@ -273,9 +282,8 @@ function makeProvider({stateName, reducer, initialSettings, storageProviderGette
storageProvider.setItem(stateName, newState);
}, [stateName, state]);
*/

const resetFunc = () => dispatch({type: 'reset', resetValue: initialSettings});
resetFuncs[stateName] = resetFunc;

resetFuncs[stateName] = () => dispatch({type: 'reset', resetValue: initialSettings});

return (
// <Context.Provider value={[storageProvider.getItem(stateName) ?? initialSettings, dispatch]}>
Expand Down
111 changes: 72 additions & 39 deletions frontend/src/state/GraphState.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,21 @@ export class GraphContainer {
}

getDisplayedRows(graphOptions) {
/*
/* Terms
STC: showThoughCollapsed
HTE: hideThoughExpanded
SNC: specificPathsCollapsed (N=Nodes/Paths)
SNE: specificPathsExpanded (N=Nodes/Paths)
New algorithm
Special classes Action Default
Special classes Action Default
concepts expandAll false
TODO: make the expandAll default depend on number of concepts rather
than being always false
standard nothing
classification nothing
specificPaths (expanded/collapsed) // rename -- chosenPaths?
expandStateByPath (expanded/collapsed)
addedCids showThoughCollapsed true
definitionConcepts showThoughCollapsed false
Expand Down Expand Up @@ -201,7 +206,7 @@ export class GraphContainer {
if (graphOptions.expandAll) {
// 3.... no need to expand STC, because nothing collapsed except SNC
} else {
// 4. Hide non-root rows; just hide depth > 0;
// 4. Hide non-root rows (depth > 0)
for (let row of allRows) {
if (row.depth > 0) {
row.display.hideReasons.nonRoot = true;
Expand Down Expand Up @@ -231,32 +236,32 @@ export class GraphContainer {
this.insertShowThoughCollapsed([nodeIdToShow], shown, nodeRows);
});
*/
// 5a. Expand children of specificPaths: expand, but only for displayed rows
// 5a. Expand children of expandStateByPath: expand, but only for displayed rows


const hideThoughExpanded = new StringSet();
}

// Expand and collapse children based on user having clicked +/- on row
// expandStateByPath: Expand and collapse children based on user having clicked +/- on row
allRows.forEach((row, rowIdx) => {
if (row.display.result === 'hide') return;
if (graphOptions.specificPaths[row.rowPath]) {
this.rowDisplay(rowIdx, graphOptions.specificPaths[row.rowPath], 'specific', allRows)
let expandState = graphOptions.expandStateByPath[row.rowPath];
if (expandState) {
this.rowDisplay(rowIdx, expandState, 'specific', allRows);
}
});

// hide all HTE (non-standard, zero pt, expansion only)
for (let type in graphOptions.specialConceptTreatment) {
if (type === 'allButFirstOccurrence') continue; // handle this differently
if (get(this, ['graphDisplayConfig', type, 'specialTreatmentRule'])
=== 'hide though expanded' &&
graphOptions.specialConceptTreatment[type]) {
let optionIsHTE = get(this, ['graphDisplayConfig', type, 'specialTreatmentRule']) === 'hide though expanded';
if(optionIsHTE && graphOptions.specialConceptTreatment[type]) {
// gather all the hideThoughExpanded ids
this.gd.specialConcepts[type].forEach(id => {
const rowsToHide = allRowsById.get(id) || [];
for (const rowToHide of rowsToHide) {
const rowToHideIdx = rowToHide.allRowsIdx;
this.rowDisplay(rowToHideIdx, graphOptions.specificPaths[rowToHide.rowPath], type, allRows)
this.rowDisplay(rowToHideIdx, graphOptions.expandStateByPath[rowToHide.rowPath], type, allRows)
}
})
}
Expand Down Expand Up @@ -286,6 +291,7 @@ export class GraphContainer {
return displayedRows;
// return this.getDisplayedRowsOLD(graphOptions);
}

insertShowThoughCollapsed(path, shown, nodeRows) {
// moved out of getDisplayedRows where shown was a closure var
// path starts with the nodeIdToShow and recurses up, prepending parents
Expand Down Expand Up @@ -334,9 +340,23 @@ export class GraphContainer {
});
shown.add(nodeIdToShow);
};

/* rowDisplay()
Sets properties regarding whether or not a row should be displayed, and why.
Nomenclature:
specific: Means that a row is being shown or hidden not because of a general rules, but a special circumstance
particular to that and only that row, typically because of a user action.
* :param rowIdx (Int): index of row in allRows
* :param showHide (String): Factor of 'expand' or 'collapse'
* :param reason (String): Factor variable; reason for showing or hiding
* :param allRows (Array[Row]): list of all rows
* */
rowDisplay(rowIdx, showHide, reason, allRows) {
// this.rowDisplay(row, graphOptions.specificPaths[row.rowPath], 'specific')
// this.rowDisplay(rowToHide, graphOptions.specificPaths[rowToHide.rowPath], type)
// this.rowDisplay(row, graphOptions.expandStateByPath[row.rowPath], 'specific')
// this.rowDisplay(rowToHide, graphOptions.expandStateByPath[rowToHide.rowPath], type)
// TODO: don't hide if it has children that should be shown
if (reason === 'specific') {
if (showHide === 'expand') {
Expand All @@ -362,6 +382,7 @@ export class GraphContainer {
}
}
}

getDescendantRows(parentRowIdx, allRows, howDeep=Infinity) {
// sort of a fragile way to do it, but will get all rows deeper
// than current row until the next row of the same depth
Expand All @@ -379,30 +400,36 @@ export class GraphContainer {
}
return rows;
}

/* setupAllRows
* Nomenclature: Rows, nodes, &concepts are all the same thing; just using term that fits purpose at the moment. */
setupAllRows(rootNodes) {
let allRows = [];
let allRowsById = new StringKeyMap(); // start by getting lists of rowIdx by concept_id
// rows and nodes and concepts are all the same thing, I just use the term
// that fits the purpose at the moment
const addRows = (nodeIds, parentPath = '', depth = 0) => {
let nodes = nodeIds.map(id => this.nodes[id]);
nodes = sortBy(nodes, this.sortFunc);
for (let node of nodes) {
let nodeId = node.concept_id;
let row = {...node, depth, rowPath: `${parentPath}/${nodeId}` };
// Create `row`: `node` props, plus `depth`, `rowPath`, `display`, index
let row = {...node, depth, rowPath: `${parentPath}/${node.concept_id}` };
row.display = {
hideReasons: {},
showReasons: {},
result: '',
}
row.allRowsIdx = allRows.length;

// Add row
// - to rows array
allRows.push(row);
// - too lookup
if (allRowsById.has(row.concept_id)) {
allRowsById.get(row.concept_id).push(row);
} else {
allRowsById.set(row.concept_id, [row]);
}


// If children/descendants, add rows for them, too
if (node.childIds && node.childIds.length) {
addRows(node.childIds, row.rowPath, depth + 1);
}
Expand Down Expand Up @@ -489,9 +516,31 @@ export class GraphContainer {
return this.graph.copy();
}

/* `setGraphDisplayConfig()`: These are all options that appear in Show Stats/Options
*
* Returns:
* graphOptions: Object
*
* Side effects:
* Sets: this.graphDisplayConfig (Object)
* Sets: this.graphDisplayConfigList (Array)
* Sets: graphOptions.specialConceptTreatment[type]
*
* displayOptions logic
* See code for hidden-rows column in CsetComparisonPage StatsAndOptions table.
*
* If specialTreatmentRule is 'show though collapsed', then what we care
* about are how many currently hidden rows will be shown if option is
* turned on and how many currently shown rows will be hidden if option
* is turned off.
*
* If specialTreatmentRule is 'hide though expanded', then what we care
* about are how many currently visible rows will be hidden if option is
* turned on and how many currently hidden rows will be unhidden if option
* is turned off.
*
* */
setGraphDisplayConfig(graphOptions) {
// these are all options that appear in Show Stats/Options

const displayedConcepts = this.displayedRows || []; // first time through, don't have displayed rows yet
const displayedConceptIds = displayedConcepts.map(r => r.concept_id);
let displayOrder = 0;
Expand All @@ -503,22 +552,7 @@ export class GraphContainer {
nested: true,
};
}
let displayOptions = {
/*
displayOptions logic
See code for hidden-rows column in CsetComparisonPage StatsAndOptions
table.
If specialTreatmentRule is 'show though collapsed', then what we care
about are how many currently hidden rows will be shown if option is
turned on and how many currently shown rows will be hidden if option
is turned off.
If specialTreatmentRule is 'hide though expanded', then what we care
about are how many currently visible rows will be hidden if option is
turned on and how many currently hidden rows will be unhidden if option
is turned off.
*/
let displayOptions= {
displayedRows: {
name: "Visible rows", displayOrder: displayOrder++,
value: displayedConcepts.length,
Expand Down Expand Up @@ -606,8 +640,7 @@ export class GraphContainer {
: 0,
/* special_v_displayed: () => {
let special = this.gd.specialConcepts.allButFirstOccurrence.map(p => p.join('/'));
let displayed = flatten(Object.values(this.displayedNodePaths)
.map(paths => paths.map(path => path.join('/'))))
let displayed = flatten(Object.values(this.displayedNodePaths).map(paths => paths.map(path => path.join('/'))))
return [special, displayed];
}, */
specialTreatmentDefault: true,
Expand Down

0 comments on commit 894e8de

Please sign in to comment.