Skip to content

Commit 5b86bdb

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents f817b66 + 3ced9e6 commit 5b86bdb

26 files changed

+832
-245
lines changed

.eslintrc.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@
4747
"react/jsx-props-no-spreading": 0,
4848
"react/self-closing-comp": "off",
4949
"no-restricted-exports": 0,
50-
"no-shadow": "off",
51-
"no-use-before-define": "off",
52-
"max-len": "off"
50+
"no-shadow": "off"
5351
},
5452
"overrides": [
5553
{

lib/components/TimeSeriesViewer/TimeSeriesViewerAxes.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,11 +512,13 @@ const TimeStepOption = () => {
512512
}
513513
}, Array.from(availableTimeSteps).map(timeStep => {
514514
const className = timeStep === selectedTimeStep ? "".concat(classes.optionButton, " ").concat(classes.optionButtonSelected) : classes.optionButton;
515+
const isDisabled = _TimeSeriesViewerContext.default.calcPredictedPointsByTimeStep(state, timeStep) > _TimeSeriesViewerContext.POINTS_PERFORMANCE_LIMIT;
515516
return /*#__PURE__*/_react.default.createElement(_ToggleButton.default, {
516517
key: timeStep,
517518
value: timeStep,
518519
size: "small",
519-
className: className
520+
className: className,
521+
disabled: isDisabled
520522
}, timeStep);
521523
}));
522524
};

lib/components/TimeSeriesViewer/TimeSeriesViewerContainer.js

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ var _Place = _interopRequireDefault(require("@material-ui/icons/Place"));
2626
var _DateRange = _interopRequireDefault(require("@material-ui/icons/DateRange"));
2727
var _Timeline = _interopRequireDefault(require("@material-ui/icons/Timeline"));
2828
var _BorderInner = _interopRequireDefault(require("@material-ui/icons/BorderInner"));
29+
var _Warning = _interopRequireDefault(require("@material-ui/icons/Warning"));
30+
var _Computer = _interopRequireDefault(require("@material-ui/icons/Computer"));
2931
var _ReleaseChip = _interopRequireDefault(require("../Chip/ReleaseChip"));
3032
var _Theme = _interopRequireWildcard(require("../Theme/Theme"));
3133
var _RouteService = _interopRequireDefault(require("../../service/RouteService"));
@@ -133,6 +135,22 @@ const useStyles = (0, _styles.makeStyles)(theme => ({
133135
display: 'flex',
134136
justifyContent: 'flex-start',
135137
alignItems: 'center'
138+
},
139+
statusBar: {
140+
marginLeft: '-4px',
141+
marginRight: '-4px',
142+
backgroundColor: _Theme.default.palette.grey[200],
143+
padding: '4px 4px 4px 12px',
144+
'& svg': {
145+
verticalAlign: 'bottom',
146+
marginRight: '4px'
147+
},
148+
'& .warningMessage': {
149+
marginLeft: '20px',
150+
'& svg': {
151+
color: _Theme.default.colors.BROWN[300]
152+
}
153+
}
136154
}
137155
}));
138156
const useTabsStyles = (0, _styles.makeStyles)(theme => ({
@@ -579,6 +597,51 @@ function TimeSeriesViewerContainer() {
579597
}
580598
return null;
581599
};
600+
601+
// function complements of Battelle ANNI
602+
// move to 'helper' file if one exists
603+
const addThousandsSeparator = numStr => {
604+
/* eslint-disable */
605+
if (!numStr) {
606+
return '0';
607+
}
608+
const numStrString = numStr.toString();
609+
let [integer, decimal] = numStrString.split('.');
610+
let result = '';
611+
let count = 0;
612+
613+
// Process integer part from right to left
614+
for (let i = integer.length - 1; i >= 0; i--) {
615+
result = integer[i] + result;
616+
count += 1;
617+
// Add comma after every 3 digits, except at the start
618+
if (count % 3 === 0 && i !== 0) {
619+
result = ',' + result;
620+
}
621+
}
622+
623+
// Reattach decimal part if present
624+
return decimal ? "".concat(result, ".").concat(decimal) : result;
625+
};
626+
const renderStatusBar = () => {
627+
let showWarning = false;
628+
const pointTotal = state.status !== _constants.TIME_SERIES_VIEWER_STATUS.READY ? '--' : addThousandsSeparator(state.pointTotal);
629+
if (_TimeSeriesViewerContext.default.calcPredictedPointsForNewPosition(state) > _TimeSeriesViewerContext.POINTS_PERFORMANCE_LIMIT || _TimeSeriesViewerContext.default.calcPredictedPointsForNewVariable(state) > _TimeSeriesViewerContext.POINTS_PERFORMANCE_LIMIT) {
630+
showWarning = true;
631+
}
632+
return /*#__PURE__*/_react.default.createElement("div", {
633+
className: classes.statusBar
634+
}, /*#__PURE__*/_react.default.createElement(_Computer.default, {
635+
fontSize: "medium"
636+
}), /*#__PURE__*/_react.default.createElement("span", null, /*#__PURE__*/_react.default.createElement("b", null, "Data Points"), ":\xA0"), pointTotal, /*#__PURE__*/_react.default.createElement("span", null, " of "), addThousandsSeparator(_TimeSeriesViewerContext.POINTS_PERFORMANCE_LIMIT), /*#__PURE__*/_react.default.createElement("span", {
637+
className: "warningMessage",
638+
style: {
639+
visibility: showWarning ? 'visible' : 'hidden'
640+
}
641+
}, /*#__PURE__*/_react.default.createElement(_Warning.default, {
642+
fontSize: "medium"
643+
}), "Data point total approaching limit; some options are disabled."));
644+
};
582645
return /*#__PURE__*/_react.default.createElement("div", {
583646
style: {
584647
width: '100%'
@@ -589,7 +652,7 @@ function TimeSeriesViewerContainer() {
589652
style: {
590653
position: 'relative'
591654
}
592-
}, state.product.productCode === loadedProductCode ? /*#__PURE__*/_react.default.createElement(_TimeSeriesViewerGraph.default, null) : null, renderGraphOverlay()), /*#__PURE__*/_react.default.createElement("div", {
655+
}, state.product.productCode === loadedProductCode ? /*#__PURE__*/_react.default.createElement(_TimeSeriesViewerGraph.default, null) : null, renderGraphOverlay()), renderStatusBar(), /*#__PURE__*/_react.default.createElement("div", {
593656
className: classes.tabsContainer
594657
}, renderTabs(), renderTabPanels())));
595658
}

lib/components/TimeSeriesViewer/TimeSeriesViewerContext.d.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export namespace TIME_SERIES_VIEWER_STATUS_TITLES {
77
const ERROR: null;
88
const READY: null;
99
}
10+
export const POINTS_PERFORMANCE_LIMIT: 250000;
1011
export namespace Y_AXIS_RANGE_MODES {
1112
const CENTERED: string;
1213
const FROM_ZERO: string;
@@ -53,6 +54,7 @@ export namespace DEFAULT_STATE {
5354
export const dataFetches: {};
5455
export const dataFetchProgress: number;
5556
export const variables: {};
57+
export const pointTotal: number;
5658
export namespace product {
5759
const productCode: null;
5860
const productName: null;
@@ -204,6 +206,7 @@ export function getTestableItems(): {
204206
dataFetches: {};
205207
dataFetchProgress: number;
206208
variables: {};
209+
pointTotal: number;
207210
product: {
208211
productCode: null;
209212
productName: null;
@@ -325,6 +328,11 @@ declare namespace TimeSeriesViewerContext {
325328
export { Provider };
326329
export { useTimeSeriesViewerState };
327330
export { TimeSeriesViewerPropTypes };
331+
export { calcPredictedPointsForNewPosition };
332+
export { calcPredictedPointsByTimeStep };
333+
export { calcPredictedPointsForNewVariable };
334+
export { calcPredictedPointsByDateRange };
335+
export { getPositionCount };
328336
}
329337
/**
330338
Context Provider
@@ -367,6 +375,7 @@ declare function useTimeSeriesViewerState(): {
367375
dataFetches: {};
368376
dataFetchProgress: number;
369377
variables: {};
378+
pointTotal: number;
370379
product: {
371380
productCode: null;
372381
productName: null;
@@ -442,6 +451,7 @@ declare function useTimeSeriesViewerState(): {
442451
dataFetches: {};
443452
dataFetchProgress: number;
444453
variables: {};
454+
pointTotal: number;
445455
product: {
446456
productCode: null;
447457
productName: null;
@@ -512,4 +522,9 @@ declare namespace TimeSeriesViewerPropTypes {
512522
export function productData_2(props: any, propName: any, componentName: any): Error | null;
513523
export { productData_2 as productData };
514524
}
525+
declare function calcPredictedPointsForNewPosition(state: any, numPositionsOverride: any): number;
526+
declare function calcPredictedPointsByTimeStep(state: any, timeStep: any): number;
527+
declare function calcPredictedPointsForNewVariable(state: any): number;
528+
declare function calcPredictedPointsByDateRange(state: any, startDate: any, endDate: any): number;
529+
declare function getPositionCount(sitesArray: any, siteCodeToExclude: any): number;
515530
import { number } from "prop-types";

lib/components/TimeSeriesViewer/TimeSeriesViewerContext.js

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Object.defineProperty(exports, "__esModule", {
44
value: true
55
});
6-
exports.summarizeTimeSteps = exports.getTestableItems = exports.default = exports.Y_AXIS_RANGE_MODE_DETAILS = exports.Y_AXIS_RANGE_MODES = exports.TabComponentPropTypes = exports.TIME_STEPS = exports.TIME_SERIES_VIEWER_STATUS_TITLES = exports.DEFAULT_STATE = void 0;
6+
exports.summarizeTimeSteps = exports.getTestableItems = exports.default = exports.Y_AXIS_RANGE_MODE_DETAILS = exports.Y_AXIS_RANGE_MODES = exports.TabComponentPropTypes = exports.TIME_STEPS = exports.TIME_SERIES_VIEWER_STATUS_TITLES = exports.POINTS_PERFORMANCE_LIMIT = exports.DEFAULT_STATE = void 0;
77
var _react = _interopRequireWildcard(require("react"));
88
var _propTypes = _interopRequireWildcard(require("prop-types"));
99
var _moment = _interopRequireDefault(require("moment"));
@@ -53,6 +53,7 @@ const TIME_SERIES_VIEWER_STATUS_TITLES = exports.TIME_SERIES_VIEWER_STATUS_TITLE
5353
ERROR: null,
5454
READY: null
5555
};
56+
const POINTS_PERFORMANCE_LIMIT = exports.POINTS_PERFORMANCE_LIMIT = 250000;
5657

5758
// List of common date-time variable names to verify against
5859
// The variables file ultimately controls the datetime variable that will
@@ -146,6 +147,7 @@ const DEFAULT_STATE = exports.DEFAULT_STATE = {
146147
dataFetches: {},
147148
dataFetchProgress: 0,
148149
variables: {},
150+
pointTotal: 0,
149151
product: {
150152
productCode: null,
151153
productName: null,
@@ -297,9 +299,104 @@ const summarizeTimeSteps = function (steps) {
297299
const plural = pluralize ? 's' : '';
298300
return "".concat(value, " ").concat(intervals[breakIdx]).concat(plural);
299301
};
302+
exports.summarizeTimeSteps = summarizeTimeSteps;
303+
const getPositionCount = (sitesArray, siteCodeToExclude) => {
304+
let total = 0;
305+
sitesArray.forEach(site => {
306+
if (site.siteCode !== siteCodeToExclude) {
307+
total += site.positions.length;
308+
}
309+
});
310+
return total;
311+
};
312+
313+
// yearMonth param is in the format of 'yyyy-mm' which is
314+
// a typical dateRange item
315+
const getLastDayInMonth = yearMonth => {
316+
const date = new Date("".concat(yearMonth, "-01T00:00:00Z"));
317+
date.setUTCMonth(date.getUTCMonth() + 1);
318+
date.setUTCSeconds(date.getUTCSeconds() - 1);
319+
return date.getUTCDate();
320+
};
321+
const getTotalHoursCustom = (startDate, endDate) => {
322+
const date1 = new Date("".concat(startDate, "-01T00:00:00Z"));
323+
const lastDay = getLastDayInMonth(endDate);
324+
const date2 = new Date("".concat(endDate, "-").concat(lastDay, "T23:59:59Z"));
325+
return Math.round((date2.getTime() - date1.getTime()) / 1000 / 60 / 60);
326+
};
327+
const getTotalHours = state => {
328+
const date1 = new Date("".concat(state.selection.dateRange[0], "-01T00:00:00Z"));
329+
let date2;
330+
if (state.selection.continuousDateRange.length === 1) {
331+
const lastDay = getLastDayInMonth(state.selection.continuousDateRange[0]);
332+
date2 = new Date("".concat(state.selection.continuousDateRange[0], "-").concat(lastDay, "T23:59:59Z"));
333+
} else if (state.selection.dateRange.length === 2) {
334+
const lastDay = getLastDayInMonth(state.selection.dateRange[1]);
335+
date2 = new Date("".concat(state.selection.dateRange[1], "-").concat(lastDay, "T23:59:59Z"));
336+
} else {
337+
// eslint-disable-next-line no-console
338+
console.error('Unknown date range');
339+
}
340+
return Math.round((date2.getTime() - date1.getTime()) / 1000 / 60 / 60);
341+
};
342+
const getPointsPerHour = (state, currentTimeStep) => {
343+
const secondsInHour = 3600;
344+
const timeStep = currentTimeStep === 'auto' ? state.selection.autoTimeStep : currentTimeStep;
345+
const timeStepSeconds = TIME_STEPS[timeStep].seconds;
346+
return secondsInHour / timeStepSeconds;
347+
};
348+
const calcPointTotal = data => {
349+
if (!data) {
350+
return 0;
351+
}
352+
let varsAndPositions = 0;
353+
if (data.length > 0 && data[0].length > 1) {
354+
// first array position is dateTime. Count items after it
355+
varsAndPositions = data[0].length - 1;
356+
}
357+
return data.length * varsAndPositions;
358+
};
359+
const calcPredictedPointsByTimeStep = (state, timeStep) => {
360+
if (!state.selection.autoTimeStep) return 0;
361+
362+
// formula: points per hour (seconds in hour / Time Step seconds)
363+
// x hours (months selected converted to hours) x positions * variables
364+
// using seconds for points per hour since that is what TIME_STEPS has.
365+
const positions = getPositionCount(state.selection.sites);
366+
const pointPerHour = getPointsPerHour(state, timeStep);
367+
const variables = state.selection.variables.length === 0 ? 1 : state.selection.variables.length;
368+
const totalHours = getTotalHours(state);
369+
return pointPerHour * totalHours * positions * variables;
370+
};
371+
const calcPredictedPointsForNewPosition = (state, numPositionsOverride) => {
372+
if (!state.selection.autoTimeStep) return 0;
373+
const positions = numPositionsOverride !== null && numPositionsOverride !== void 0 ? numPositionsOverride : getPositionCount(state.selection.sites) + 1;
374+
const pointPerHour = getPointsPerHour(state, state.selection.timeStep);
375+
const variables = state.selection.variables.length === 0 ? 1 : state.selection.variables.length;
376+
const totalHours = getTotalHours(state);
377+
return pointPerHour * totalHours * positions * variables;
378+
};
379+
const calcPredictedPointsForNewVariable = state => {
380+
if (!state.selection.autoTimeStep) return 0;
381+
const positions = getPositionCount(state.selection.sites);
382+
const pointPerHour = getPointsPerHour(state, state.selection.timeStep);
383+
const totalHours = getTotalHours(state);
384+
const variables = state.selection.variables.length === 0 ? 1 : state.selection.variables.length + 1;
385+
return pointPerHour * totalHours * positions * variables;
386+
};
387+
388+
// note that the dates are not JS dates but from dateRange and should be
389+
// in the format of 'yyyy-mm'.
390+
const calcPredictedPointsByDateRange = (state, startDate, endDate) => {
391+
if (!state.selection.autoTimeStep) return 0;
392+
const positions = getPositionCount(state.selection.sites);
393+
const pointPerHour = getPointsPerHour(state, state.selection.timeStep);
394+
const variables = state.selection.variables.length === 0 ? 1 : state.selection.variables.length;
395+
const totalHours = getTotalHoursCustom(startDate, endDate);
396+
return pointPerHour * totalHours * positions * variables;
397+
};
300398

301399
// Array offsets and validators for use when splitting a data file URL
302-
exports.summarizeTimeSteps = summarizeTimeSteps;
303400
const DATA_FILE_PARTS = {
304401
POSITION_H: {
305402
offset: 6,
@@ -1144,6 +1241,7 @@ const reducer = (state, action) => {
11441241
return softFail('Current selection of dates/sites/positions/variables does not have any valid numeric data.');
11451242
}
11461243
newState.graphData = action.graphData;
1244+
newState.pointTotal = calcPointTotal(action.graphData.data);
11471245
newState.status = _constants.TIME_SERIES_VIEWER_STATUS.READY;
11481246
return newState;
11491247

@@ -1218,6 +1316,22 @@ const reducer = (state, action) => {
12181316
case 'fetchDataFileSucceeded':
12191317
newState.product.sites[action.siteCode].positions[action.position].data[action.month][action.downloadPkg][action.timeStep][action.table].status = FETCH_STATUS.SUCCESS;
12201318
newState.product.sites[action.siteCode].positions[action.position].data[action.month][action.downloadPkg][action.timeStep][action.table].series = action.series;
1319+
/* uncomment for troubleshooting to get number of points downloaded
1320+
try {
1321+
if (!newState.product.pointTotal || isNaN(newState.product.pointTotal)) {
1322+
newState.product.pointTotal = 0;
1323+
}
1324+
newState.product.pointTotal += action.series.endDateTime.data.length;
1325+
console.log("newState", newState);
1326+
// console.log("action", action);
1327+
console.log("fetchDataFileSucceeded - pointTotal", newState.product.pointTotal);
1328+
// console.log("newState.selection.continuousDateRange.length",
1329+
// newState.selection.continuousDateRange.length);
1330+
} catch (error) {
1331+
console.log("my derpy code crashed", action);
1332+
}
1333+
*/
1334+
12211335
return newState;
12221336

12231337
// Core Selection Actions
@@ -1819,6 +1933,9 @@ const Provider = props => {
18191933
token: masterFetchToken,
18201934
fetches: dataActions
18211935
});
1936+
// this is the point where we can capture the user fetch criteria
1937+
// eslint-disable-next-line no-console
1938+
// console.log('Fetching data', state, dataFetches);
18221939
(0, _rxUtil.forkJoinWithProgress)(dataFetches).pipe((0, _rxjs.mergeMap)(_ref => {
18231940
let [finalResult, progress] = _ref;
18241941
return (0, _rxjs.merge)(progress.pipe((0, _rxjs.tap)(value => dispatch({
@@ -1906,7 +2023,12 @@ Provider.defaultProps = {
19062023
const TimeSeriesViewerContext = {
19072024
Provider,
19082025
useTimeSeriesViewerState,
1909-
TimeSeriesViewerPropTypes
2026+
TimeSeriesViewerPropTypes,
2027+
calcPredictedPointsForNewPosition,
2028+
calcPredictedPointsByTimeStep,
2029+
calcPredictedPointsForNewVariable,
2030+
calcPredictedPointsByDateRange,
2031+
getPositionCount
19102032
};
19112033
var _default = exports.default = TimeSeriesViewerContext; // Additional items exported for unit testing
19122034
const getTestableItems = () => process.env.NODE_ENV !== 'test' ? {} : {

0 commit comments

Comments
 (0)