Skip to content

Commit 3d9555a

Browse files
authored
Merge pull request #113 from Caltech-IPAC/dm-6022_lazychart
DM-6022 Lazy load related chart data on table data update
2 parents 2e19f31 + fcae7a1 commit 3d9555a

File tree

11 files changed

+439
-227
lines changed

11 files changed

+439
-227
lines changed

src/firefly/js/api/ApiHighlevelBuild.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -357,12 +357,12 @@ function makePlotId() {
357357
//================================================================
358358

359359
function showXYPlot(llApi, targetDiv, params={}) {
360-
const {dispatchSetupTblTracking, dispatchTableFetch}= llApi.action;
360+
const {dispatchTableFetch}= llApi.action;
361361
const {TBL_RESULTS_ACTIVE} = llApi.action.type;
362362
const {renderDOM} = llApi.util;
363363
const {makeFileRequest, getActiveTableId, uniqueTblId} = llApi.util.table;
364364
const {uniqueChartId, loadPlotDataForTbl} = llApi.util.chart;
365-
const {ChartsContainer, ChartsTableViewPanel}= llApi.ui;
365+
const {ChartsTableViewPanel}= llApi.ui;
366366
const {addActionListener} = llApi.util;
367367

368368
if ((typeof targetDiv).match(/string|HTMLDivElement/) === null) {
@@ -397,7 +397,6 @@ function showXYPlot(llApi, targetDiv, params={}) {
397397
);
398398
tblId = searchRequest.tbl_id;
399399
dispatchTableFetch(searchRequest);
400-
dispatchSetupTblTracking(tblId);
401400
}
402401

403402
const chartId = uniqueChartId(tblId||tblGroup);

src/firefly/js/tables/TablesCntlr.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import shallowequal from 'shallowequal';
1010
import {dataReducer} from './reducer/TableDataReducer.js';
1111
import {uiReducer} from './reducer/TableUiReducer.js';
1212
import {resultsReducer} from './reducer/TableResultsReducer.js';
13-
import {dispatchSetupTblTracking} from '../visualize/TableStatsCntlr.js';
1413
import {dispatchAddSaga} from '../core/MasterSaga.js';
1514

1615
export const TABLE_SPACE_PATH = 'table_space';
@@ -48,8 +47,7 @@ export function tableSearch(action) {
4847
var {request, options, tbl_group} = action.payload;
4948
const {tbl_id} = request;
5049
const title = get(request, 'META_INFO.title');
51-
52-
dispatchSetupTblTracking(tbl_id);
50+
5351
dispatchTableFetch(request);
5452
if (!TblUtil.getTableInGroup(tbl_id, tbl_group)) {
5553
const {tbl_group, removable} = options || {};

src/firefly/js/visualize/ChartUtil.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {logError} from '../util/WebUtil.js';
1818
import {XYPLOT_DATA_KEY} from '../visualize/XYPlotCntlr.js';
1919
import {HISTOGRAM_DATA_KEY} from '../visualize/HistogramCntlr';
2020

21+
export const SCATTER = 'scatter';
22+
export const HISTOGRAM = 'histogram';
2123

2224
/**
2325
* This method returns an object with the keys x,y,highlightedRow
@@ -63,20 +65,33 @@ function getExpressionValue(strExpr, tableModel, rowIdx) {
6365
}
6466
}
6567

68+
69+
export function getChartSpace(chartType) {
70+
switch(chartType) {
71+
case SCATTER:
72+
return flux.getState()[XYPLOT_DATA_KEY];
73+
case HISTOGRAM:
74+
return flux.getState()[HISTOGRAM_DATA_KEY];
75+
default:
76+
logError(`Unknown chart type ${chartType}`);
77+
return undefined;
78+
}
79+
}
80+
6681
export function hasRelatedCharts(tblId, space) {
6782
if (space) {
6883
return Boolean(Object.keys(space).find((chartId) => {
6984
return space[chartId].tblId === tblId;
7085
}));
7186
} else {
72-
return hasRelatedCharts(tblId, flux.getState()[XYPLOT_DATA_KEY]) ||
73-
hasRelatedCharts(tblId, flux.getState()[HISTOGRAM_DATA_KEY]);
87+
return hasRelatedCharts(tblId, getChartSpace(SCATTER)) ||
88+
hasRelatedCharts(tblId, getChartSpace(HISTOGRAM));
7489
}
7590
}
7691

7792
export function getTblIdForChartId(chartId) {
78-
return get(flux.getState()[XYPLOT_DATA_KEY], [chartId, 'tblId']) ||
79-
get(flux.getState()[HISTOGRAM_DATA_KEY], [chartId, 'tblId']);
93+
return get(getChartSpace(SCATTER), [chartId, 'tblId']) ||
94+
get(getChartSpace(HISTOGRAM), [chartId, 'tblId']);
8095
}
8196

8297
export function numRelatedCharts(tblId) {
@@ -86,7 +101,7 @@ export function numRelatedCharts(tblId) {
86101
keys.forEach( (key) => {
87102
const space = flux.getState()[key];
88103
for (c in space) {
89-
if (space[c].tblId === tblId) {
104+
if (space.hasOwnProperty(c) && space[c].tblId === tblId) {
90105
numRelated++;
91106
}
92107
}

src/firefly/js/visualize/ChartsCntlr.js

Lines changed: 134 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,20 @@
22
* License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt
33
*/
44

5+
import {has, get, isUndefined, omit} from 'lodash';
6+
57
import {flux} from '../Firefly.js';
6-
import {updateSet} from '../util/WebUtil.js';
8+
import {updateSet, updateMerge} from '../util/WebUtil.js';
9+
10+
import * as TablesCntlr from '../tables/TablesCntlr.js';
711

812
export const CHART_SPACE_PATH = 'charts';
913
export const UI_PREFIX = `${CHART_SPACE_PATH}.ui`;
1014

1115
export const CHART_UI_EXPANDED = `${UI_PREFIX}.expanded`;
16+
export const CHART_MOUNTED = `${UI_PREFIX}/mounted`;
17+
export const CHART_UNMOUNTED = `${UI_PREFIX}/unmounted`;
18+
export const DELETE = `${UI_PREFIX}/delete`;
1219

1320
/**
1421
* request to put a chart into an expanded mode.
@@ -21,15 +28,132 @@ export function dispatchChartExpanded(chartId, tblId, chartType, optionsPopup) {
2128
flux.process( {type: CHART_UI_EXPANDED, payload: {chartId, tblId, chartType, optionsPopup}});
2229
}
2330

31+
/*
32+
* Dispatched when chart becomes visible (is rendered for the first time after being invisible)
33+
* When chart is mounted, its data need to be in sync with the related table
34+
* @param {string} tblId - table id (table data for this chart)
35+
* @param {string} chartId - chart id
36+
* @param {string} chartType - chart type, ex. scatter, histogram
37+
*/
38+
export function dispatchChartMounted(tblId, chartId, chartType, dispatcher= flux.process) {
39+
dispatcher({type: CHART_MOUNTED, payload: {tblId, chartId, chartType}});
40+
}
41+
42+
/*
43+
* Dispatched when chart becomes invisible
44+
* When chart is unmounted, its data synced with the related table only when it becomes mounted again
45+
* @param {string} tblId - table id (table data for this chart)
46+
* @param {string} chartId - chart id
47+
* @param {string} chartType - chart type, ex. scatter, histogram
48+
*/
49+
export function dispatchChartUnmounted(tblId, chartId, chartType, dispatcher= flux.process) {
50+
dispatcher({type: CHART_UNMOUNTED, payload: {tblId, chartId, chartType}});
51+
}
52+
53+
/*
54+
* Delete chart and related data
55+
* @param {string} tblId - table id (table data for this chart)
56+
* @param {string} chartId - chart id
57+
* @param {string} chartType - chart type, ex. scatter, histogram
58+
*/
59+
export function dispatchDelete(tblId, chartId, chartType, dispatcher= flux.process) {
60+
dispatcher({type: DELETE, payload: {tblId, chartId, chartType}});
61+
}
62+
63+
const uniqueId = (chartId, chartType) => { return `${chartType}|${chartId}`; };
64+
const isChartType = (uChartId, chartType) => { return uChartId.startsWith(chartType); };
65+
66+
const chartActions = [CHART_UI_EXPANDED,CHART_MOUNTED,CHART_UNMOUNTED,DELETE];
67+
2468
export function reducer(state={ui:{}}, action={}) {
25-
//var root = state.ui;
26-
switch (action.type) {
27-
case (CHART_UI_EXPANDED) :
28-
const {chartId, tblId, chartType, optionsPopup} = action.payload;
29-
//return updateSet(root, 'expanded', {chartId, tblId});
30-
return updateSet(state, 'ui.expanded', {chartId, tblId, chartType, optionsPopup});
31-
default:
32-
//return root;
33-
return state;
69+
if (action.type === TablesCntlr.TABLE_REMOVE) {
70+
const {tbl_id} = action.payload;
71+
if (has(state, tbl_id)) {
72+
return Object.assign({}, omit(state, [tbl_id]));
73+
}
74+
return state;
75+
} else if (chartActions.indexOf(action.type) > -1) {
76+
const {chartId, tblId, chartType} = action.payload;
77+
const uChartId = uniqueId(chartId, chartType);
78+
switch (action.type) {
79+
80+
case (CHART_UI_EXPANDED) :
81+
const {optionsPopup} = action.payload;
82+
//return updateSet(root, 'expanded', {chartId, tblId});
83+
return updateSet(state, 'ui.expanded', {chartId, tblId, chartType, optionsPopup});
84+
case (CHART_MOUNTED) :
85+
86+
if (!has(state, ['tbl', tblId])) {
87+
return updateSet(state, ['tbl',tblId], {[uChartId]: {mounted: true}});
88+
} else {
89+
if (get(state, ['tbl', tblId, uChartId, 'mounted'])) {
90+
//other version of the same chart is mounted
91+
let n = get(state, ['tbl', tblId, uChartId, 'n']);
92+
n = n ? Number(n) : 1;
93+
return updateMerge(state, ['tbl', tblId, uChartId], {upToDate: true, n: n + 1});
94+
} else {
95+
return updateSet(state, ['tbl', tblId, uChartId], {mounted: true});
96+
}
97+
}
98+
case (CHART_UNMOUNTED) :
99+
if (has(state, ['tbl', tblId])) {
100+
if (get(state, ['tbl', tblId, uChartId, 'mounted'])) {
101+
let n = get(state, ['tbl', tblId, uChartId, 'n']);
102+
n = n ? Number(n) : 1;
103+
if (n > 1) {
104+
//multiple versions of the same chart are mounted
105+
return updateMerge(state, ['tbl', tblId, uChartId], {n: n - 1});
106+
}
107+
} else {
108+
return updateSet(state, ['tbl', tblId, uChartId], {mounted: false});
109+
}
110+
}
111+
return state;
112+
case (DELETE) :
113+
if (has(state, ['tbl', tblId, uChartId])) {
114+
if (Object.keys(state['tbl'][tblId]).length > 1) {
115+
return updateSet(state, ['tbl', tblId], omit(state['tbl'][tblId], [uChartId]));
116+
} else {
117+
return updateSet(state, 'tbl', omit(state['tbl'], [tblId]));
118+
}
119+
}
120+
return state;
121+
default:
122+
return state;
123+
}
124+
} else {
125+
return state;
34126
}
127+
}
128+
129+
130+
export function getExpandedChartProps() {
131+
return get(flux.getState(), [CHART_SPACE_PATH, 'ui', 'expanded']);
132+
}
133+
134+
135+
export function getNumRelatedCharts(tblId, mounted, chartType) {
136+
let numRelated = 0;
137+
const byTblSpace = get(flux.getState(), [CHART_SPACE_PATH, 'tbl']);
138+
if (byTblSpace) {
139+
if (isUndefined(tblId)) {
140+
Object.keys(byTblSpace).forEach((tblId)=>{
141+
numRelated += getNumRelatedCharts(tblId, mounted);
142+
});
143+
} else if (byTblSpace[tblId]) {
144+
Object.keys(byTblSpace[tblId]).forEach((uChartId) => {
145+
if (isUndefined(mounted)||byTblSpace[tblId][uChartId].mounted===mounted) {
146+
if (isUndefined(chartType) || isChartType(uChartId, chartType)) {
147+
numRelated++;
148+
}
149+
}
150+
});
151+
}
152+
}
153+
return numRelated;
154+
}
155+
156+
export function isChartMounted(tblId, chartId, chartType) {
157+
const uChartId = uniqueId(chartId, chartType);
158+
return Boolean(get(flux.getState(), [CHART_SPACE_PATH, 'tbl', tblId, uChartId, 'mounted']));
35159
}

src/firefly/js/visualize/ChartsContainer.jsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,13 @@ import shallowequal from 'shallowequal';
88

99

1010
import {flux} from '../Firefly.js';
11-
import {get} from 'lodash';
1211

1312
import {LO_VIEW, LO_MODE, dispatchSetLayoutMode, getExpandedMode} from '../core/LayoutCntlr.js';
1413
import {CloseButton} from '../ui/CloseButton.jsx';
1514

1615
import {ChartsTableViewPanel} from '../visualize/ChartsTableViewPanel.jsx';
17-
import {CHART_SPACE_PATH} from '../visualize/ChartsCntlr.js';
16+
import {getExpandedChartProps} from '../visualize/ChartsCntlr.js';
1817

19-
export function getExpandedChartProps() {
20-
return get(flux.getState(),[CHART_SPACE_PATH, 'ui', 'expanded']);
21-
}
2218

2319
function nextState(props) {
2420
const {closeable, chartId, tblId, chartType, optionsPopup} = props;
@@ -34,16 +30,18 @@ export class ChartsContainer extends Component {
3430
}
3531

3632
componentWillReceiveProps(np) {
37-
if (!this.isUnmounted && !shallowequal(this.props, np)) {
33+
if (this.iAmMounted && !shallowequal(this.props, np)) {
3834
this.setState(nextState(np));
3935
}
4036
}
4137

4238
componentDidMount() {
4339
this.removeListener= flux.addListener(() => this.storeUpdate());
40+
this.iAmMounted = true;
4441
}
4542

4643
componentWillUnmount() {
44+
this.iAmMounted = false;
4745
this.removeListener && this.removeListener();
4846
}
4947

@@ -53,7 +51,7 @@ export class ChartsContainer extends Component {
5351

5452

5553
storeUpdate() {
56-
if (!this.isUnmounted) {
54+
if (this.iAmMounted) {
5755
const expandedMode = this.props.expandedMode && getExpandedMode() === LO_VIEW.xyPlots;
5856
if (expandedMode !== this.state.expandedMode) {
5957
this.setState(nextState(this.props));

0 commit comments

Comments
 (0)