diff --git a/MATScommon b/MATScommon index c04c364196..68f74446ef 160000 --- a/MATScommon +++ b/MATScommon @@ -1 +1 @@ -Subproject commit c04c364196da61db6bc409fbec4b6c5be922fc67 +Subproject commit 68f74446ef39d830ac7568e84af77ea5b6115f1b diff --git a/METexpress b/METexpress index 140bd86257..e87f0609c2 160000 --- a/METexpress +++ b/METexpress @@ -1 +1 @@ -Subproject commit 140bd86257330c3f21b327420a3b50a6f78e044a +Subproject commit e87f0609c2f7bd4286cec77a53f01fb9c871a910 diff --git a/apps/anomalycor/.eslintrc.json b/apps/anomalycor/.eslintrc.json index 8b795b7df7..79d49c5bb6 100644 --- a/apps/anomalycor/.eslintrc.json +++ b/apps/anomalycor/.eslintrc.json @@ -28,22 +28,6 @@ "space-before-function-paren": "off", // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", - "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-plusplus": "warn", - "vars-on-top": "warn", - "no-var": "warn", - "block-scoped-var": "warn", - "no-loop-func": "warn", - "no-unused-vars": "warn", - "prefer-destructuring": "warn", - "no-param-reassign": "warn", - "camelcase": "warn", - "no-redeclare": "warn" + "prefer-arrow-callback": "off" } } diff --git a/apps/anomalycor/client/main.js b/apps/anomalycor/client/main.js index a87407a1f4..ecd922b6a2 100644 --- a/apps/anomalycor/client/main.js +++ b/apps/anomalycor/client/main.js @@ -2,6 +2,7 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; import "@fortawesome/fontawesome-free"; import "@fortawesome/fontawesome-free/css/all.css"; diff --git a/apps/anomalycor/server/dataFunctions/data_contour.js b/apps/anomalycor/server/dataFunctions/data_contour.js index ab4b97af0a..02d22d7033 100644 --- a/apps/anomalycor/server/dataFunctions/data_contour.js +++ b/apps/anomalycor/server/dataFunctions/data_contour.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContour = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,60 +23,75 @@ dataContour = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: true, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; - const totalProcessingStart = moment(); + + const curves = JSON.parse(JSON.stringify(plotParams.curves)); + if (curves.length > 1) { + throw new Error("INFO: There must only be one added curve."); + } + + const axisMap = Object.create(null); + + let statement = ""; + let error = ""; + const dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - const curves = JSON.parse(JSON.stringify(plotParams.curves)); - if (curves.length > 1) { - throw new Error("INFO: There must only be one added curve."); - } - const dataset = []; - const axisMap = Object.create(null); - // initialize variables specific to the curve + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - const regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${model}_anomcorr_${region} as m0`; + const { variable } = curve; const variableClause = `and m0.variable = '${variable}'`; + let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; - let levelClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimeStr = curve["valid-time"] === matsTypes.InputTypes.unused ? "both" : curve["valid-time"]; - validTimeClause = matsCollections["valid-time"].findOne( + [validTimeClause] = matsCollections["valid-time"].findOne( { name: "valid-time" }, { optionsMap: 1 } - ).optionsMap[validTimeStr][0]; + ).optionsMap[validTimeStr]; } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const statisticClause = + "avg(m0.wacorr/100) as stat, " + + "stddev(m0.wacorr/100) as stdev, " + + "group_concat(unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, ';', m0.level, ';', m0.wacorr / 100 " + + "order by unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, m0.level) as sub_data, " + + "count(m0.wacorr) as N0"; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -86,104 +102,118 @@ dataContour = function (plotParams, plotFunction) { dateString = "unix_timestamp(m0.valid_date)+3600*m0.valid_hour"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; + + let levelClause = ""; if (xAxisParam !== "Pressure level" && yAxisParam !== "Pressure level") { const levels = curve.level === undefined ? [] : curve.level; - if (levels.length > 0 && levels !== matsTypes.InputTypes.unused) { + if (levels.length !== 0 && levels !== matsTypes.InputTypes.unused) { levelClause = `and m0.level IN(${levels})`; } } - const statisticClause = - "avg(m0.wacorr/100) as stat, " + - "stddev(m0.wacorr/100) as stdev, " + - "group_concat(unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, ';', m0.level, ';', m0.wacorr / 100 " + - "order by unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, m0.level) as sub_data, " + - "count(m0.wacorr) as N0"; - const statType = "ACC"; - curve.statistic = "Correlation"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + + const queryTableClause = `from ${model}_anomcorr_${region} as m0`; // For contours, this functions as the colorbar label. - curve.unitKey = curve.statistic; + const statType = "ACC"; + const axisKey = "Correlation"; + curve.statistic = axisKey; + curve.unitKey = axisKey; let d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{variableClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{levelClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; - - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{variableClause}}", variableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{levelClause}}", levelClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - "Anomaly Correlation" - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{variableClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{levelClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; + + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{variableClause}}", variableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{levelClause}}", levelClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + "Anomaly Correlation" + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - } - if (!dataFoundForCurve) { - // we found no data for any curves so don't bother proceeding - throw new Error("INFO: No valid data for any curves."); - } + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { + // this is NOT an error just a no data condition + dataFoundForCurve = false; + } else { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + } - const postQueryStartMoment = moment(); + if (!dataFoundForCurve) { + // we found no data for any curves so don't bother proceeding + throw new Error("INFO: No valid data for any curves."); + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined diff --git a/apps/anomalycor/server/dataFunctions/data_contour_diff.js b/apps/anomalycor/server/dataFunctions/data_contour_diff.js index faaf06b5ef..1e62ca8735 100644 --- a/apps/anomalycor/server/dataFunctions/data_contour_diff.js +++ b/apps/anomalycor/server/dataFunctions/data_contour_diff.js @@ -14,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContourDiff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -24,64 +25,80 @@ dataContourDiff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: true, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; let dataNotFoundForAnyCurve = false; + + let curves = JSON.parse(JSON.stringify(plotParams.curves)); + const curvesLength = curves.length; + if (curvesLength !== 2) { + throw new Error("INFO: There must be two added curves."); + } + + const axisMap = Object.create(null); const showSignificance = plotParams.significance !== "none"; - const totalProcessingStart = moment(); + + let statType; + + let statement = ""; + let error = ""; + let dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - let curves = JSON.parse(JSON.stringify(plotParams.curves)); - const curvesLength = curves.length; - if (curvesLength !== 2) { - throw new Error("INFO: There must be two added curves."); - } - let dataset = []; - const axisMap = Object.create(null); - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${model}_anomcorr_${region} as m0`; + const { variable } = curve; const variableClause = `and m0.variable = '${variable}'`; + let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; - let levelClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimeStr = curve["valid-time"] === matsTypes.InputTypes.unused ? "both" : curve["valid-time"]; - validTimeClause = matsCollections["valid-time"].findOne( + [validTimeClause] = matsCollections["valid-time"].findOne( { name: "valid-time" }, { optionsMap: 1 } - ).optionsMap[validTimeStr][0]; + ).optionsMap[validTimeStr]; } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const statisticClause = + "avg(m0.wacorr/100) as stat, " + + "stddev(m0.wacorr/100) as stdev, " + + "group_concat(unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, ';', m0.level, ';', m0.wacorr / 100 " + + "order by unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, m0.level) as sub_data, " + + "count(m0.wacorr) as N0"; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -92,100 +109,111 @@ dataContourDiff = function (plotParams, plotFunction) { dateString = "unix_timestamp(m0.valid_date)+3600*m0.valid_hour"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; + + let levelClause = ""; if (xAxisParam !== "Pressure level" && yAxisParam !== "Pressure level") { const levels = curve.level === undefined ? [] : curve.level; - if (levels.length > 0 && levels !== matsTypes.InputTypes.unused) { + if (levels.length !== 0 && levels !== matsTypes.InputTypes.unused) { levelClause = `and m0.level IN(${levels})`; } } - const statisticClause = - "avg(m0.wacorr/100) as stat, " + - "stddev(m0.wacorr/100) as stdev, " + - "group_concat(unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, ';', m0.level, ';', m0.wacorr / 100 " + - "order by unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, m0.level) as sub_data, " + - "count(m0.wacorr) as N0"; - var statType = "ACC"; - curve.statistic = "Correlation"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + + const queryTableClause = `from ${model}_anomcorr_${region} as m0`; // For contours, this functions as the colorbar label. - curves[curveIndex].unitKey = curve.statistic; + statType = "ACC"; + const axisKey = "Correlation"; + curve.statistic = axisKey; + curve.unitKey = axisKey; - var d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{variableClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{levelClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; + let d; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{variableClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{levelClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{variableClause}}", variableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{levelClause}}", levelClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{variableClause}}", variableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{levelClause}}", levelClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; - var queryResult; - const startMoment = moment(); - var finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - "Anomaly Correlation" - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + "Anomaly Correlation" + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - dataNotFoundForAnyCurve = true; - } - const postQueryStartMoment = moment(); + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + dataNotFoundForAnyCurve = true; + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined @@ -233,8 +261,9 @@ dataContourDiff = function (plotParams, plotFunction) { "Anomaly Correlation", statType === "ctc" ); - plotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); - curves = plotParams.curves; + const newPlotParams = plotParams; + newPlotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); + curves = newPlotParams.curves; dataset[0].name = matsPlotUtils.getCurveText( matsTypes.PlotTypes.contourDiff, curves[0] @@ -250,7 +279,7 @@ dataContourDiff = function (plotParams, plotFunction) { const result = matsDataProcessUtils.processDataContour( dataset, curveInfoParams, - plotParams, + newPlotParams, bookkeepingParams ); plotFunction(result); diff --git a/apps/anomalycor/server/dataFunctions/data_dieoff.js b/apps/anomalycor/server/dataFunctions/data_dieoff.js index 3c2a9830ac..e4086867ae 100644 --- a/apps/anomalycor/server/dataFunctions/data_dieoff.js +++ b/apps/anomalycor/server/dataFunctions/data_dieoff.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDieoff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,61 +24,82 @@ dataDieoff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: true, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = [100]; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${model}_anomcorr_${region} as m0`; + const { variable } = curve; const variableClause = `and m0.variable = '${variable}'`; + let validTimeClause = ""; - var utcCycleStart; + let utcCycleStartClause = ""; + let utcCycleStart; + const forecastLengthStr = curve["dieoff-type"]; const forecastLengthOptionsMap = matsCollections["dieoff-type"].findOne( { name: "dieoff-type" }, { optionsMap: 1 } ).optionsMap; const forecastLength = forecastLengthOptionsMap[forecastLengthStr][0]; + + const statisticClause = + "avg(m0.wacorr/100) as stat, " + + "count(m0.wacorr) as N0, " + + "group_concat(unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, ';', m0.level, ';', m0.wacorr / 100 " + + "order by unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, m0.level) as sub_data"; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; - var dateClause; + let dateClause; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + + const queryTableClause = `from ${model}_anomcorr_${region} as m0`; + if (forecastLength === matsTypes.ForecastTypes.dieoff) { const validTimeStr = curve["valid-time"] === matsTypes.InputTypes.unused ? "both" : curve["valid-time"]; - validTimeClause = matsCollections["valid-time"].findOne( + [validTimeClause] = matsCollections["valid-time"].findOne( { name: "valid-time" }, { optionsMap: 1 } - ).optionsMap[validTimeStr][0]; + ).optionsMap[validTimeStr]; dateClause = `and unix_timestamp(m0.valid_date)+3600*m0.valid_hour >= '${fromSecs}' and unix_timestamp(m0.valid_date)+3600*m0.valid_hour <= '${toSecs}' `; } else if (forecastLength === matsTypes.ForecastTypes.utcCycle) { utcCycleStart = @@ -89,67 +111,64 @@ dataDieoff = function (plotParams, plotFunction) { } else { dateClause = `and unix_timestamp(m0.valid_date)+3600*m0.valid_hour-m0.fcst_len*3600 = ${fromSecs}`; } + let levelClause = ""; const levels = curve.level === undefined ? [] : curve.level; if (levels.length !== 0 && levels !== matsTypes.InputTypes.unused) { levelClause = `and m0.level IN(${levels})`; } - const statisticClause = - "avg(m0.wacorr/100) as stat, " + - "count(m0.wacorr) as N0, " + - "group_concat(unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, ';', m0.level, ';', m0.wacorr / 100 " + - "order by unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, m0.level) as sub_data"; - var statType = "ACC"; - curves[curveIndex].statistic = "Correlation"; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + statType = "ACC"; const axisKey = "Correlation"; + curves[curveIndex].statistic = axisKey; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.fcst_len as fcst_lead, " + - "count(distinct unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as N_times, " + - "min(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as min_secs, " + - "max(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{variableClause}} " + - "{{validTimeClause}} " + - "{{utcCycleStartClause}} " + - "{{levelClause}} " + - "group by fcst_lead " + - "order by fcst_lead" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{variableClause}}", variableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{levelClause}}", levelClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.fcst_len as fcst_lead, " + + "count(distinct unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as N_times, " + + "min(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as min_secs, " + + "max(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{variableClause}} " + + "{{validTimeClause}} " + + "{{utcCycleStartClause}} " + + "{{levelClause}} " + + "group by fcst_lead " + + "order by fcst_lead" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{variableClause}}", variableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{levelClause}}", levelClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, "Anomaly Correlation" ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -165,6 +184,7 @@ dataDieoff = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -179,7 +199,6 @@ dataDieoff = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -204,6 +223,7 @@ dataDieoff = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/anomalycor/server/dataFunctions/data_histogram.js b/apps/anomalycor/server/dataFunctions/data_histogram.js index 74affb5d88..41aaa3498b 100644 --- a/apps/anomalycor/server/dataFunctions/data_histogram.js +++ b/apps/anomalycor/server/dataFunctions/data_histogram.js @@ -11,6 +11,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataHistogram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -21,43 +22,43 @@ dataHistogram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: true, }; - const alreadyMatched = false; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries const dataFoundForCurve = []; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const alreadyMatched = false; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; + + const axisMap = Object.create(null); + let statType; + + let statement = ""; + let error = ""; const dataset = []; const allReturnedSubStats = []; const allReturnedSubSecs = []; const allReturnedSubLevs = []; - const axisMap = Object.create(null); // process user bin customizations const binParams = matsDataUtils.setHistogramParameters(plotParams); const { yAxisFormat } = binParams; const { binNum } = binParams; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; dataFoundForCurve[curveIndex] = true; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${model}_anomcorr_${region} as m0`; + const { variable } = curve; const variableClause = `and m0.variable = '${variable}'`; + const validTimeStr = curve["valid-time"] === matsTypes.InputTypes.unused ? "both" @@ -66,28 +67,43 @@ dataHistogram = function (plotParams, plotFunction) { { name: "valid-time" }, { optionsMap: 1 } ).optionsMap[validTimeStr][0]; + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; + + const statisticClause = + "avg(m0.wacorr/100) as stat, " + + "count(m0.wacorr) as N0, " + + "group_concat(unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, ';', m0.level, ';', m0.wacorr / 100 " + + "order by unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, m0.level) as sub_data"; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; const dateClause = `and unix_timestamp(m0.valid_date)+3600*m0.valid_hour >= ${fromSecs} and unix_timestamp(m0.valid_date)+3600*m0.valid_hour <= ${toSecs}`; + let levelClause = ""; const levels = curve.level === undefined ? [] : curve.level; if (levels.length !== 0 && levels !== matsTypes.InputTypes.unused) { levelClause = `and m0.level IN(${levels})`; } - const statisticClause = - "avg(m0.wacorr/100) as stat, " + - "count(m0.wacorr) as N0, " + - "group_concat(unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, ';', m0.level, ';', m0.wacorr / 100 " + - "order by unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, m0.level) as sub_data"; - var statType = "ACC"; - curves[curveIndex].statistic = "Correlation"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + + const queryTableClause = `from ${model}_anomcorr_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + statType = "ACC"; + curves[curveIndex].statistic = "Correlation"; let axisKey = yAxisFormat; if (yAxisFormat === "Relative frequency") { axisKey += " (x100)"; @@ -95,48 +111,48 @@ dataHistogram = function (plotParams, plotFunction) { curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options curves[curveIndex].binNum = binNum; // stash the binNum to use it later for bar chart options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select unix_timestamp(m0.valid_date)+3600*m0.valid_hour as avtime, " + - "count(distinct unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as N_times, " + - "min(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as min_secs, " + - "max(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{variableClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{levelClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{variableClause}}", variableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{levelClause}}", levelClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select unix_timestamp(m0.valid_date)+3600*m0.valid_hour as avtime, " + + "count(distinct unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as N_times, " + + "min(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as min_secs, " + + "max(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{variableClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{levelClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{variableClause}}", variableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{levelClause}}", levelClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, "Anomaly Correlation" ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -155,6 +171,7 @@ dataHistogram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/anomalycor/server/dataFunctions/data_profile.js b/apps/anomalycor/server/dataFunctions/data_profile.js index 60cc13b1c6..1b77bb4f90 100644 --- a/apps/anomalycor/server/dataFunctions/data_profile.js +++ b/apps/anomalycor/server/dataFunctions/data_profile.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataProfile = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,38 +24,39 @@ dataProfile = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: true, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; const idealValues = [100]; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${model}_anomcorr_${region} as m0`; + const { variable } = curve; const variableClause = `and m0.variable = '${variable}'`; + const validTimeStr = curve["valid-time"] === matsTypes.InputTypes.unused ? "both" @@ -63,66 +65,80 @@ dataProfile = function (plotParams, plotFunction) { { name: "valid-time" }, { optionsMap: 1 } ).optionsMap[validTimeStr][0]; + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and unix_timestamp(m0.valid_date)+3600*m0.valid_hour >= ${fromSecs} and unix_timestamp(m0.valid_date)+3600*m0.valid_hour <= ${toSecs}`; + const statisticClause = "avg(m0.wacorr/100) as stat, " + "count(m0.wacorr) as N0, " + "group_concat(unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, ';', m0.level, ';', m0.wacorr / 100 " + "order by unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, m0.level) as sub_data"; - var statType = "ACC"; - curves[curveIndex].statistic = "Correlation"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and unix_timestamp(m0.valid_date)+3600*m0.valid_hour >= ${fromSecs} and unix_timestamp(m0.valid_date)+3600*m0.valid_hour <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + + const queryTableClause = `from ${model}_anomcorr_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + statType = "ACC"; const axisKey = "Correlation"; + curves[curveIndex].statistic = axisKey; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.level as avVal, " + - "count(distinct unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as N_times, " + - "min(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as min_secs, " + - "max(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{variableClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avVal " + - "order by avVal" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{variableClause}}", variableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.level as avVal, " + + "count(distinct unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as N_times, " + + "min(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as min_secs, " + + "max(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{variableClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avVal " + + "order by avVal" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{variableClause}}", variableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, "Anomaly Correlation" ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -138,6 +154,7 @@ dataProfile = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -152,7 +169,6 @@ dataProfile = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -177,6 +193,7 @@ dataProfile = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.y.length; const annotation = mean === undefined diff --git a/apps/anomalycor/server/dataFunctions/data_series.js b/apps/anomalycor/server/dataFunctions/data_series.js index 1a61a3cedd..9a156a60cb 100644 --- a/apps/anomalycor/server/dataFunctions/data_series.js +++ b/apps/anomalycor/server/dataFunctions/data_series.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSeries = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,42 +24,44 @@ dataSeries = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: true, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = [100]; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${model}_anomcorr_${region} as m0`; + const { variable } = curve; const variableClause = `and m0.variable = '${variable}'`; + const validTimeStr = curve["valid-time"] === matsTypes.InputTypes.unused ? "both" @@ -69,77 +72,91 @@ dataSeries = function (plotParams, plotFunction) { { name: "valid-time" }, { optionsMap: 1 } ).optionsMap[validTimeStr][0]; + let forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateClause = `and unix_timestamp(m0.valid_date)+3600*m0.valid_hour >= ${fromSecs} and unix_timestamp(m0.valid_date)+3600*m0.valid_hour <= ${toSecs}`; + + const statisticClause = + "avg(m0.wacorr/100) as stat, " + + "count(m0.wacorr) as N0, " + + "group_concat(unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, ';', m0.level, ';', m0.wacorr / 100 " + + "order by unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, m0.level) as sub_data"; + const averageStr = curve.average; const averageOptionsMap = matsCollections.average.findOne( { name: "average" }, { optionsMap: 1 } ).optionsMap; const average = averageOptionsMap[averageStr][0]; + + const dateClause = `and unix_timestamp(m0.valid_date)+3600*m0.valid_hour >= ${fromSecs} and unix_timestamp(m0.valid_date)+3600*m0.valid_hour <= ${toSecs}`; + let levelClause = ""; const levels = curve.level === undefined ? [] : curve.level; if (levels.length !== 0 && levels !== matsTypes.InputTypes.unused) { levelClause = `and m0.level IN(${levels})`; } - const statisticClause = - "avg(m0.wacorr/100) as stat, " + - "count(m0.wacorr) as N0, " + - "group_concat(unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, ';', m0.level, ';', m0.wacorr / 100 " + - "order by unix_timestamp(m0.valid_date) + 3600 * m0.valid_hour, m0.level) as sub_data"; - var statType = "ACC"; - curves[curveIndex].statistic = "Correlation"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + + const queryTableClause = `from ${model}_anomcorr_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + statType = "ACC"; const axisKey = "Correlation"; + curves[curveIndex].statistic = axisKey; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select {{average}} as avtime, " + - "count(distinct unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as N_times, " + - "min(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as min_secs, " + - "max(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{variableClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{levelClause}} " + - "group by avtime " + - "order by avtime" + - ";"; + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select {{average}} as avtime, " + + "count(distinct unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as N_times, " + + "min(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as min_secs, " + + "max(unix_timestamp(m0.valid_date)+3600*m0.valid_hour) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{variableClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{levelClause}} " + + "group by avtime " + + "order by avtime" + + ";"; - statement = statement.replace("{{average}}", average); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{variableClause}}", variableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{levelClause}}", levelClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; + statement = statement.replace("{{average}}", average); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{variableClause}}", variableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{levelClause}}", levelClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; - // math is done on forecastLength later on -- set all analyses to 0 - if (forecastLength === "-99") { - forecastLength = "0"; - } + // math is done on forecastLength later on -- set all analyses to 0 + if (forecastLength === "-99") { + forecastLength = "0"; + } - var queryResult; - const startMoment = moment(); - var finishMoment; - try { // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBTimeSeries( - sumPool, + sumPool, // eslint-disable-line no-undef statement, model, forecastLength, @@ -151,7 +168,9 @@ dataSeries = function (plotParams, plotFunction) { appParams, false ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -167,6 +186,7 @@ dataSeries = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -181,7 +201,6 @@ dataSeries = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -206,6 +225,7 @@ dataSeries = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/anomalycor/server/main.js b/apps/anomalycor/server/main.js index 2d3759b666..656689800a 100644 --- a/apps/anomalycor/server/main.js +++ b/apps/anomalycor/server/main.js @@ -4,7 +4,9 @@ import { Meteor } from "meteor/meteor"; import { mysql } from "meteor/pcel:mysql"; +import { moment } from "meteor/momentjs:moment"; import { + matsMethods, matsTypes, matsCollections, matsDataUtils, @@ -323,89 +325,82 @@ const doCurveParams = function () { const params = matsCollections.CurveParamsInfo.find({ curve_params: { $exists: true }, }).fetch()[0].curve_params; - for (let cp = 0; cp < params.length; cp++) { + for (let cp = 0; cp < params.length; cp += 1) { matsCollections[params[cp]].remove({}); } } + const modelOptionsMap = {}; let modelDateRangeMap = {}; const regionModelOptionsMap = {}; const forecastLengthOptionsMap = {}; const levelOptionsMap = {}; const variableModelOptionsMap = {}; - const masterRegionValuesMap = {}; + const allRegionValuesMap = {}; try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - metadataPool, + metadataPool, // eslint-disable-line no-undef "select id,description from region_descriptions;" ); - let masterRegDescription; - let masterId; - for (var j = 0; j < rows.length; j++) { - masterRegDescription = rows[j].description.trim(); - masterId = rows[j].id; - masterRegionValuesMap[masterId] = masterRegDescription; + for (let j = 0; j < rows.length; j += 1) { + allRegionValuesMap[rows[j].id] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select model,regions,display_text,fcst_lens,levels,variable,mindate,maxdate from regions_per_model_mats_all_categories order by display_category, display_order;" ); - for (let i = 0; i < rows.length; i++) { - const model_value = rows[i].model.trim(); + for (let i = 0; i < rows.length; i += 1) { + const modelValue = rows[i].model.trim(); const model = rows[i].display_text.trim(); - modelOptionsMap[model] = [model_value]; + modelOptionsMap[model] = [modelValue]; const rowMinDate = moment.utc(rows[i].mindate * 1000).format("MM/DD/YYYY HH:mm"); const rowMaxDate = moment.utc(rows[i].maxdate * 1000).format("MM/DD/YYYY HH:mm"); - modelDateRangeMap[model] = { minDate: rowMinDate, maxDate: rowMaxDate }; + modelDateRangeMap[model] = { + minDate: rowMinDate, + maxDate: rowMaxDate, + }; const forecastLengths = rows[i].fcst_lens; - const forecastLengthArr = forecastLengths + forecastLengthOptionsMap[model] = forecastLengths .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < forecastLengthArr.length; j++) { - forecastLengthArr[j] = forecastLengthArr[j].replace(/'|\[|\]/g, ""); - } - forecastLengthOptionsMap[model] = forecastLengthArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (fhr) { + return fhr.replace(/'|\[|\]/g, ""); + }); const { levels } = rows[i]; - const levelArr = levels + levelOptionsMap[model] = levels .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < levelArr.length; j++) { - levelArr[j] = levelArr[j].replace(/'|\[|\]/g, ""); - } - levelOptionsMap[model] = levelArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (level) { + return level.replace(/'|\[|\]/g, ""); + }); const variables = rows[i].variable; - const variableArr = variables + variableModelOptionsMap[model] = variables .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < variableArr.length; j++) { - variableArr[j] = variableArr[j].replace(/'|\[|\]/g, ""); - } - variableModelOptionsMap[model] = variableArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (variable) { + return variable.replace(/'|\[|\]/g, ""); + }); const { regions } = rows[i]; - const regionsArrRaw = regions + regionModelOptionsMap[model] = regions .split(",") - .map(Function.prototype.call, String.prototype.trim); - const regionsArr = []; - var dummyRegion; - for (var j = 0; j < regionsArrRaw.length; j++) { - dummyRegion = regionsArrRaw[j].replace(/'|\[|\]/g, ""); - regionsArr.push(masterRegionValuesMap[dummyRegion]); - } - regionModelOptionsMap[model] = regionsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (region) { + return allRegionValuesMap[region.replace(/'|\[|\]/g, "")]; + }); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } if (matsCollections.label.findOne({ name: "label" }) === undefined) { @@ -450,7 +445,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["data-source"].findOne({ name: "data-source" }); + const currentParam = matsCollections["data-source"].findOne({ + name: "data-source", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, modelOptionsMap) || !matsDataUtils.areObjectsEqual(currentParam.dates, modelDateRangeMap) @@ -476,7 +473,7 @@ const doCurveParams = function () { type: matsTypes.InputTypes.select, optionsMap: regionModelOptionsMap, options: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]], - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, superiorNames: ["data-source"], controlButtonCovered: true, unique: false, @@ -488,10 +485,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.region.findOne({ name: "region" }); + const currentParam = matsCollections.region.findOne({ name: "region" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, regionModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterRegionValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allRegionValuesMap) ) { // have to reload region data matsCollections.region.update( @@ -499,7 +496,7 @@ const doCurveParams = function () { { $set: { optionsMap: regionModelOptionsMap, - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, options: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]], default: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]][0], }, @@ -547,7 +544,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["forecast-length"].findOne({ + const currentParam = matsCollections["forecast-length"].findOne({ name: "forecast-length", }); if ( @@ -734,7 +731,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.level.findOne({ name: "level" }); + const currentParam = matsCollections.level.findOne({ name: "level" }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, levelOptionsMap)) { // have to reload level data matsCollections.level.update( @@ -799,7 +796,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["curve-dates"].findOne({ name: "curve-dates" }); + const currentParam = matsCollections["curve-dates"].findOne({ + name: "curve-dates", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.startDate, minDate) || !matsDataUtils.areObjectsEqual(currentParam.stopDate, maxDate) || @@ -1046,7 +1045,8 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { - console.log( + // eslint-disable-next-line no-console + console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); matsCollections.Databases.drop(); @@ -1063,7 +1063,7 @@ Meteor.startup(function () { databases = Meteor.settings.private.databases; } if (databases !== null && databases !== undefined && Array.isArray(databases)) { - for (let di = 0; di < databases.length; di++) { + for (let di = 0; di < databases.length; di += 1) { matsCollections.Databases.insert(databases[di]); } } @@ -1090,6 +1090,7 @@ Meteor.startup(function () { ); if (cbConnection) { // global cbScorecardSettingsPool + // eslint-disable-next-line no-undef cbScorecardSettingsPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1116,6 +1117,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (metadataSettings) { + // eslint-disable-next-line no-undef metadataPool = mysql.createPool(metadataSettings); allPools.push({ pool: "metadataPool", role: matsTypes.DatabaseRoles.META_DATA }); } @@ -1136,6 +1138,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (sumSettings) { + // eslint-disable-next-line no-undef sumPool = mysql.createPool(sumSettings); allPools.push({ pool: "sumPool", role: matsTypes.DatabaseRoles.SUMS_DATA }); } @@ -1152,7 +1155,7 @@ Meteor.startup(function () { appType: matsTypes.AppTypes.mats, }); } catch (error) { - console.log(error.message); + throw new Error(error.message); } }); @@ -1160,6 +1163,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doPlotGraph, doCurveParams, diff --git a/apps/cb-metar/.eslintrc.json b/apps/cb-metar/.eslintrc.json index 5aab474ac1..b7e4dd3a3d 100644 --- a/apps/cb-metar/.eslintrc.json +++ b/apps/cb-metar/.eslintrc.json @@ -14,6 +14,8 @@ "settings": { "import/resolver": "meteor" }, + // ignore directory with Gopa's experimental tests + "ignorePatterns": ["test"], "rules": { "prettier/prettier": "error", "react/jsx-filename-extension": "off", @@ -29,27 +31,6 @@ // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-plusplus": "warn", - "vars-on-top": "warn", - "no-var": "warn", - "block-scoped-var": "warn", - "no-loop-func": "warn", - "no-unused-vars": "warn", - "no-console": "warn", - "prefer-destructuring": "warn", - "no-param-reassign": "warn", - "camelcase": "warn", - "no-redeclare": "warn", - "no-shadow": "warn", - "no-await-in-loop": "warn", - "no-continue": "warn", - "global-require": "warn", - "no-use-before-define": "warn" + "no-await-in-loop": "warn" } } diff --git a/apps/cb-metar/client/main.js b/apps/cb-metar/client/main.js index a87407a1f4..ecd922b6a2 100644 --- a/apps/cb-metar/client/main.js +++ b/apps/cb-metar/client/main.js @@ -2,6 +2,7 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; import "@fortawesome/fontawesome-free"; import "@fortawesome/fontawesome-free/css/all.css"; diff --git a/apps/cb-metar/server/dataFunctions/data_contour.js b/apps/cb-metar/server/dataFunctions/data_contour.js index ed5ab3abf8..c5955931a1 100644 --- a/apps/cb-metar/server/dataFunctions/data_contour.js +++ b/apps/cb-metar/server/dataFunctions/data_contour.js @@ -2,6 +2,8 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +/* global cbPool, Assets */ + import { matsCollections, matsTypes, @@ -12,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContour = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { diff --git a/apps/cb-metar/server/dataFunctions/data_contour_diff.js b/apps/cb-metar/server/dataFunctions/data_contour_diff.js index ab584f9683..536d9553d0 100644 --- a/apps/cb-metar/server/dataFunctions/data_contour_diff.js +++ b/apps/cb-metar/server/dataFunctions/data_contour_diff.js @@ -2,6 +2,8 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +/* global cbPool, Assets */ + import { matsCollections, matsTypes, @@ -14,6 +16,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContourDiff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -27,7 +30,6 @@ dataContourDiff = function (plotParams, plotFunction) { const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; let dataNotFoundForAnyCurve = false; let curves = JSON.parse(JSON.stringify(plotParams.curves)); @@ -219,10 +221,7 @@ dataContourDiff = function (plotParams, plotFunction) { } if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { // this is an error returned by the mysql database error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; throw new Error(error); diff --git a/apps/cb-metar/server/dataFunctions/data_dailymodelcycle.js b/apps/cb-metar/server/dataFunctions/data_dailymodelcycle.js index 1e8aec8754..74d57275d6 100644 --- a/apps/cb-metar/server/dataFunctions/data_dailymodelcycle.js +++ b/apps/cb-metar/server/dataFunctions/data_dailymodelcycle.js @@ -2,6 +2,8 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +/* global cbPool, Assets */ + import { matsCollections, matsTypes, @@ -14,6 +16,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDailyModelCycle = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { diff --git a/apps/cb-metar/server/dataFunctions/data_dieoff.js b/apps/cb-metar/server/dataFunctions/data_dieoff.js index d19e469280..421749f012 100644 --- a/apps/cb-metar/server/dataFunctions/data_dieoff.js +++ b/apps/cb-metar/server/dataFunctions/data_dieoff.js @@ -2,6 +2,8 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +/* global cbPool, Assets */ + import { matsCollections, matsTypes, @@ -14,6 +16,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDieoff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -178,8 +181,8 @@ dataDieoff = function (plotParams, plotFunction) { if (regionType === "Predefined region") { statement = cbPool.trfmSQLForDbTarget(queryTemplate); } else { - statement = "Station plot -- no one query."; // send to matsMiddle + statement = "Station plot -- no one query."; const tss = new matsMiddleDieoff.MatsMiddleDieoff(cbPool); rows = tss.processStationQuery( variable, diff --git a/apps/cb-metar/server/dataFunctions/data_histogram.js b/apps/cb-metar/server/dataFunctions/data_histogram.js index 0d38633d11..a67412ecde 100644 --- a/apps/cb-metar/server/dataFunctions/data_histogram.js +++ b/apps/cb-metar/server/dataFunctions/data_histogram.js @@ -2,6 +2,8 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +/* global cbPool, Assets */ + import { matsCollections, matsTypes, @@ -11,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataHistogram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { diff --git a/apps/cb-metar/server/dataFunctions/data_map.js b/apps/cb-metar/server/dataFunctions/data_map.js index f728fc453f..c57f9f4538 100644 --- a/apps/cb-metar/server/dataFunctions/data_map.js +++ b/apps/cb-metar/server/dataFunctions/data_map.js @@ -2,6 +2,8 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +/* global cbPool */ + import { matsCollections, matsTypes, @@ -13,6 +15,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataMap = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { diff --git a/apps/cb-metar/server/dataFunctions/data_perfDiagram.js b/apps/cb-metar/server/dataFunctions/data_perfDiagram.js index 433697a0c8..2cade5551e 100644 --- a/apps/cb-metar/server/dataFunctions/data_perfDiagram.js +++ b/apps/cb-metar/server/dataFunctions/data_perfDiagram.js @@ -2,6 +2,8 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +/* global cbPool, Assets */ + import { matsCollections, matsTypes, @@ -12,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataPerformanceDiagram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { diff --git a/apps/cb-metar/server/dataFunctions/data_series.js b/apps/cb-metar/server/dataFunctions/data_series.js index 8a309a6c71..9789002bc6 100644 --- a/apps/cb-metar/server/dataFunctions/data_series.js +++ b/apps/cb-metar/server/dataFunctions/data_series.js @@ -2,6 +2,8 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +/* global cbPool, Assets */ + import { matsCollections, matsTypes, @@ -14,6 +16,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSeries = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -148,7 +151,7 @@ dataSeries = function (plotParams, plotFunction) { const startMoment = moment(); let finishMoment; try { - // math is done on forecastLength later on -= 1 set all analyses to 0 + // math is done on forecastLength later on -- set all analyses to 0 if (forecastLength === "-99") { forecastLength = "0"; } diff --git a/apps/cb-metar/server/dataFunctions/data_threshold.js b/apps/cb-metar/server/dataFunctions/data_threshold.js index f1b2c87e3b..ad1c84bf5a 100644 --- a/apps/cb-metar/server/dataFunctions/data_threshold.js +++ b/apps/cb-metar/server/dataFunctions/data_threshold.js @@ -2,6 +2,8 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +/* global cbPool, Assets */ + import { matsCollections, matsTypes, @@ -13,6 +15,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataThreshold = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -60,7 +63,7 @@ dataThreshold = function (plotParams, plotFunction) { const allThresholdsStr = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ); - for (let tidx = 0; tidx < allThresholdsStr.length; tidx++) { + for (let tidx = 0; tidx < allThresholdsStr.length; tidx += 1) { allThresholdsStr[tidx] = allThresholdsStr[tidx].replace(/_/g, "."); } const allThresholds = allThresholdsStr.sort(function (a, b) { diff --git a/apps/cb-metar/server/dataFunctions/data_validtime.js b/apps/cb-metar/server/dataFunctions/data_validtime.js index e833a1e858..35cc56fd51 100644 --- a/apps/cb-metar/server/dataFunctions/data_validtime.js +++ b/apps/cb-metar/server/dataFunctions/data_validtime.js @@ -2,6 +2,8 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +/* global cbPool, Assets */ + import { matsCollections, matsTypes, @@ -14,6 +16,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataValidTime = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { diff --git a/apps/cb-metar/server/main.js b/apps/cb-metar/server/main.js index 4d7eb61ce8..306b0cd605 100644 --- a/apps/cb-metar/server/main.js +++ b/apps/cb-metar/server/main.js @@ -337,49 +337,48 @@ const doCurveParams = async function () { const sitesLocationMap = []; const forecastLengthOptionsMap = {}; const thresholdsModelOptionsMap = {}; - const masterRegionValuesMap = {}; - const masterThresholdValuesMap = {}; + const allRegionValuesMap = {}; + const allThresholdValuesMap = {}; try { + // eslint-disable-next-line no-undef const queryStr = cbPool.trfmSQLForDbTarget( 'select name, description from {{vxDBTARGET}} where type="MD" and docType="region" and version = "V01" and subset="COMMON"' ); + // eslint-disable-next-line no-undef const rows = await cbPool.queryCB(queryStr); if (rows.includes("queryCB ERROR: ")) { // have this local try catch fail properly if the metadata isn't there throw new Error(rows); } - let masterRegDescription; - let masterShortName; + for (let j = 0; j < rows.length; j += 1) { - masterRegDescription = rows[j].description.trim(); - masterShortName = rows[j].name.trim(); - masterRegionValuesMap[masterShortName] = masterRegDescription; + allRegionValuesMap[rows[j].name.trim()] = rows[j].description.trim(); } } catch (err) { throw new Error(err.message); } - let didx; try { - for (didx = 0; didx < variables.length; didx += 1) { + for (let didx = 0; didx < variables.length; didx += 1) { const variable = variables[didx]; - masterThresholdValuesMap[variable] = {}; + allThresholdValuesMap[variable] = {}; + // eslint-disable-next-line no-undef const queryStr = cbPool.trfmSQLForDbTarget( - // `select raw thresholdDescriptions.${variable.toLowerCase()} from {{vxDBTARGET}} where type="MD" and docType="matsAux" and subset="COMMON" and version="V01"` - `select raw thresholdDescriptions.ceiling from {{vxDBTARGET}} where type="MD" and docType="matsAux" and subset="COMMON" and version="V01"` + `select raw thresholdDescriptions.${variable.toLowerCase()} from {{vxDBTARGET}} where type="MD" and docType="matsAux" and subset="COMMON" and version="V01"` + // `select raw thresholdDescriptions.ceiling from {{vxDBTARGET}} where type="MD" and docType="matsAux" and subset="COMMON" and version="V01"` ); + // eslint-disable-next-line no-undef const rows = await cbPool.queryCB(queryStr); if (rows.includes("queryCB ERROR: ")) { // have this local try catch fail properly if the metadata isn't there throw new Error(rows); } - let jsonFriendlyTrsh; - for (let j = 0; j < Object.keys(rows[0]).length; j += 1) { - const masterDescription = rows[0][Object.keys(rows[0])[j]].trim(); - const masterTrsh = Object.keys(rows[0])[j].trim(); - jsonFriendlyTrsh = masterTrsh.replace(/\./g, "_"); - masterThresholdValuesMap[variable][jsonFriendlyTrsh] = masterDescription; + const allThresholds = Object.keys(rows[0]); + for (let j = 0; j < allThresholds.length; j += 1) { + // The replace here is because JSON doesn't like dots in the middle of keys + allThresholdValuesMap[variable][allThresholds[j].trim().replace(/\./g, "_")] = + rows[0][allThresholds[j]].trim(); } } } catch (err) { @@ -387,7 +386,7 @@ const doCurveParams = async function () { } try { - for (didx = 0; didx < variables.length; didx += 1) { + for (let didx = 0; didx < variables.length; didx += 1) { const variable = variables[didx]; modelOptionsMap[variable] = {}; modelDateRangeMap[variable] = {}; @@ -395,12 +394,14 @@ const doCurveParams = async function () { thresholdsModelOptionsMap[variable] = {}; regionModelOptionsMap[variable] = {}; + // eslint-disable-next-line no-undef const queryStr = cbPool.trfmSQLForDbTarget( "select model, displayText, mindate, maxdate, fcstLens, " + "regions, thresholds " + `from {{vxDBTARGET}} where type="MD" and docType="matsGui" and subset="COMMON" and version="V01" and app="${variableMetadataDocs[variable]}" and numrecs>0 ` + "order by displayCategory, displayOrder" ); + // eslint-disable-next-line no-undef const rows = await cbPool.queryCB(queryStr); if (rows.includes("queryCB ERROR: ")) { @@ -426,24 +427,20 @@ const doCurveParams = async function () { forecastLengthOptionsMap[variable][model] = rows[i].fcstLens.map(String); // we want the full threshold descriptions in thresholdsModelOptionsMap, not just the thresholds - rows[i].thresholds.sort(function (a, b) { - return Number(a) - Number(b); + const { thresholds } = rows[i]; + thresholdsModelOptionsMap[variable][model] = thresholds + .sort(function (a, b) { + return Number(a) - Number(b); + }) + .map(function (threshold) { + return allThresholdValuesMap[variable][threshold.replace(/\./g, "_")]; + }); + + // we want the full region descriptions in thresholdsModelOptionsMap, not just the regions + const { regions } = rows[i]; + regionModelOptionsMap[variable][model] = regions.map(function (region) { + return allRegionValuesMap[region]; }); - const thresholdArr = []; - for (let t = 0; t < rows[i].thresholds.length; t += 1) { - thresholdArr.push( - masterThresholdValuesMap[variable][ - rows[i].thresholds[t].replace(/\./g, "_") - ] - ); - } - thresholdsModelOptionsMap[variable][model] = thresholdArr; - - const regionsArr = []; - for (let ri = 0; ri < rows[i].regions.length; ri += 1) { - regionsArr.push(masterRegionValuesMap[rows[i].regions[ri]]); - } - regionModelOptionsMap[variable][model] = regionsArr; } } } catch (err) { @@ -452,7 +449,9 @@ const doCurveParams = async function () { try { matsCollections.SiteMap.remove({}); + // eslint-disable-next-line no-undef let rows = await cbPool.queryCB( + // eslint-disable-next-line no-undef cbPool.trfmSQLForDbTarget( 'select meta().id, {{vxCOLLECTION}}.* from {{vxDBTARGET}} where type="MD" and docType="station" and version = "V01" and subset="{{vxCOLLECTION}}";' ) @@ -463,34 +462,38 @@ const doCurveParams = async function () { } rows = rows.sort((a, b) => (a.name > b.name ? 1 : -1)); for (let i = 0; i < rows.length; i += 1) { - const siteId = rows[i].id; const siteName = rows[i].name === undefined ? "unknown" : rows[i].name; const siteDescription = rows[i].description === undefined ? "unknown" : rows[i].description; - const siteLat = rows[i].geo === undefined ? undefined : rows[i].geo[0].lat; - const siteLon = rows[i].geo === undefined ? undefined : rows[i].geo[0].lon; - const siteElev = rows[i].geo === undefined ? "unknown" : rows[i].geo[0].elev; - if (siteLat >= 90 || siteLat <= -90) continue; // there's one station right at the south pole that the map doesn't know how to render at all - siteOptionsMap[siteName] = [siteId]; - - const point = [siteLat, siteLon]; - const obj = { - name: siteName, - origName: siteName, - point, - elevation: siteElev, - options: { - title: siteDescription, - color: "red", - size: 5, - network: "METAR", - peerOption: siteName, - id: siteId, - highLightColor: "blue", - }, - }; - sitesLocationMap.push(obj); - matsCollections.SiteMap.insert({ siteName, siteId }); + const siteId = rows[i].id; + const siteLat = rows[i].geo === undefined ? -90 : Number(rows[i].geo[0].lat); + const siteLon = rows[i].geo === undefined ? 0 : Number(rows[i].geo[0].lon); + const siteElev = rows[i].geo === undefined ? 0 : rows[i].geo[0].elev; + + // There's one station right at the south pole that the map doesn't know how to render at all, so exclude it. + // Also exclude stations with missing data + if (siteLat < 90 && siteLat > -90) { + siteOptionsMap[siteName] = [siteId]; + + const point = [siteLat, siteLon]; + const obj = { + name: siteName, + origName: siteName, + point, + elevation: siteElev, + options: { + title: siteDescription, + color: "red", + size: 5, + network: "METAR", + peerOption: siteName, + id: siteId, + highLightColor: "blue", + }, + }; + sitesLocationMap.push(obj); + matsCollections.SiteMap.insert({ siteName, siteId }); + } } } catch (err) { throw new Error(err.message); @@ -620,7 +623,7 @@ const doCurveParams = async function () { regionModelOptionsMap[variables[0]][ Object.keys(regionModelOptionsMap[variables[0]])[0] ], - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -638,7 +641,7 @@ const doCurveParams = async function () { const currentParam = matsCollections.region.findOne({ name: "region" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, regionModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterRegionValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allRegionValuesMap) ) { // have to reload region data matsCollections.region.update( @@ -646,7 +649,7 @@ const doCurveParams = async function () { { $set: { optionsMap: regionModelOptionsMap, - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, options: regionModelOptionsMap[variables[0]][ Object.keys(regionModelOptionsMap[variables[0]])[0] @@ -723,7 +726,7 @@ const doCurveParams = async function () { thresholdsModelOptionsMap[variables[0]][ Object.keys(thresholdsModelOptionsMap[variables[0]])[0] ], - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -744,7 +747,7 @@ const doCurveParams = async function () { currentParam.optionsMap, thresholdsModelOptionsMap ) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterThresholdValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allThresholdValuesMap) ) { // have to reload threshold data matsCollections.threshold.update( @@ -752,7 +755,7 @@ const doCurveParams = async function () { { $set: { optionsMap: thresholdsModelOptionsMap, - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, options: thresholdsModelOptionsMap[variables[0]][ Object.keys(thresholdsModelOptionsMap[variables[0]])[0] @@ -1073,14 +1076,14 @@ const doCurveParams = async function () { } // determine date defaults for dates and curveDates - modelDateRangeMap = matsCollections.variable.findOne( - { name: "variable" }, - { dates: 1 } - ).dates; const defaultDataSource = matsCollections["data-source"].findOne( { name: "data-source" }, { default: 1 } ).default; + modelDateRangeMap = matsCollections.variable.findOne( + { name: "variable" }, + { dates: 1 } + ).dates; minDate = modelDateRangeMap[variables[0]][defaultDataSource].minDate; maxDate = modelDateRangeMap[variables[0]][defaultDataSource].maxDate; @@ -1516,6 +1519,7 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { + // eslint-disable-next-line no-console console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); @@ -1541,6 +1545,7 @@ Meteor.startup(function () { // create list of all pools const allPools = []; + // connect to the couchbase cluster const cbConnection = matsCollections.Databases.findOne( { @@ -1560,6 +1565,7 @@ Meteor.startup(function () { // the cluster and bucket are intended to be global if (cbConnection) { + // eslint-disable-next-line no-undef cbPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1592,7 +1598,7 @@ Meteor.startup(function () { dbType: matsTypes.DbTypes.couchbase, }); } catch (error) { - console.log(error.message); + throw new Error(error.message); } }); @@ -1600,6 +1606,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doPlotGraph, doCurveParams, diff --git a/apps/ceil-vis/.eslintrc.json b/apps/ceil-vis/.eslintrc.json index e5e813ea94..79d49c5bb6 100644 --- a/apps/ceil-vis/.eslintrc.json +++ b/apps/ceil-vis/.eslintrc.json @@ -28,23 +28,6 @@ "space-before-function-paren": "off", // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", - "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-plusplus": "warn", - "vars-on-top": "warn", - "no-var": "warn", - "block-scoped-var": "warn", - "no-loop-func": "warn", - "no-unused-vars": "warn", - "prefer-destructuring": "warn", - "no-param-reassign": "warn", - "camelcase": "warn", - "no-redeclare": "warn", - "no-shadow": "warn" + "prefer-arrow-callback": "off" } } diff --git a/apps/ceil-vis/client/main.js b/apps/ceil-vis/client/main.js index 0a7a17ea3b..ecd922b6a2 100644 --- a/apps/ceil-vis/client/main.js +++ b/apps/ceil-vis/client/main.js @@ -2,4 +2,8 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; +import "@fortawesome/fontawesome-free"; +import "@fortawesome/fontawesome-free/css/all.css"; +import "@fortawesome/fontawesome-free/js/all.js"; diff --git a/apps/ceil-vis/server/dataFunctions/data_contour.js b/apps/ceil-vis/server/dataFunctions/data_contour.js index 7ee6688d8f..954284e670 100644 --- a/apps/ceil-vis/server/dataFunctions/data_contour.js +++ b/apps/ceil-vis/server/dataFunctions/data_contour.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContour = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,50 +23,53 @@ dataContour = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; - const totalProcessingStart = moment(); + + const curves = JSON.parse(JSON.stringify(plotParams.curves)); + if (curves.length > 1) { + throw new Error("INFO: There must only be one added curve."); + } + + const axisMap = Object.create(null); + + let statement = ""; + let error = ""; + const dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - const curves = JSON.parse(JSON.stringify(plotParams.curves)); - if (curves.length > 1) { - throw new Error("INFO: There must only be one added curve."); - } - const dataset = []; - const axisMap = Object.create(null); - // initialize variables specific to the curve + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }).optionsMap[ variable ]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - const regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { const thresholdStr = curve.threshold; + if (thresholdStr === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -76,16 +80,36 @@ dataContour = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold}`; } + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { - validTimeClause = `and m0.time%(24*3600)/3600 IN(${validTimes})`; + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { + validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -96,95 +120,105 @@ dataContour = function (plotParams, plotFunction) { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + // For contours, this functions as the colorbar label. const statType = statisticOptionsMap[statisticSelect][0]; - curve.unitKey = statisticOptionsMap[statisticSelect][1]; + [, curve.unitKey] = statisticOptionsMap[statisticSelect]; let d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; - - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; + + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - } - if (!dataFoundForCurve) { - // we found no data for any curves so don't bother proceeding - throw new Error("INFO: No valid data for any curves."); - } + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { + // this is NOT an error just a no data condition + dataFoundForCurve = false; + } else { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + } - const postQueryStartMoment = moment(); + if (!dataFoundForCurve) { + // we found no data for any curves so don't bother proceeding + throw new Error("INFO: No valid data for any curves."); + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined diff --git a/apps/ceil-vis/server/dataFunctions/data_contour_diff.js b/apps/ceil-vis/server/dataFunctions/data_contour_diff.js index f6e4c1ee4d..f89b252d66 100644 --- a/apps/ceil-vis/server/dataFunctions/data_contour_diff.js +++ b/apps/ceil-vis/server/dataFunctions/data_contour_diff.js @@ -14,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContourDiff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -24,53 +25,58 @@ dataContourDiff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; let dataNotFoundForAnyCurve = false; + + let curves = JSON.parse(JSON.stringify(plotParams.curves)); + const curvesLength = curves.length; + if (curvesLength !== 2) { + throw new Error("INFO: There must be two added curves."); + } + + const axisMap = Object.create(null); const showSignificance = plotParams.significance !== "none"; - const totalProcessingStart = moment(); + + let statType; + let statisticSelect; + + let statement = ""; + let error = ""; + let dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - let curves = JSON.parse(JSON.stringify(plotParams.curves)); - const curvesLength = curves.length; - if (curvesLength !== 2) { - throw new Error("INFO: There must be two added curves."); - } - let dataset = []; - const axisMap = Object.create(null); - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { - var thresholdStr = curve.threshold; + const thresholdStr = curve.threshold; + if (thresholdStr === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -81,16 +87,36 @@ dataContourDiff = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold}`; } + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { - validTimeClause = `and m0.time%(24*3600)/3600 IN(${validTimes})`; + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { + validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -101,91 +127,98 @@ dataContourDiff = function (plotParams, plotFunction) { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - var statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + // For contours, this functions as the colorbar label. - var statType = statisticOptionsMap[statisticSelect][0]; - curves[curveIndex].unitKey = statisticOptionsMap[statisticSelect][1]; + [statType] = statisticOptionsMap[statisticSelect]; + [, curve.unitKey] = statisticOptionsMap[statisticSelect]; - var d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; + let d; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; - var queryResult; - const startMoment = moment(); - var finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - dataNotFoundForAnyCurve = true; - } - const postQueryStartMoment = moment(); + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + dataNotFoundForAnyCurve = true; + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined @@ -234,8 +267,9 @@ dataContourDiff = function (plotParams, plotFunction) { statType === "ctc", statType === "scalar" ); - plotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); - curves = plotParams.curves; + const newPlotParams = plotParams; + newPlotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); + curves = newPlotParams.curves; dataset[0].name = matsPlotUtils.getCurveText( matsTypes.PlotTypes.contourDiff, curves[0] @@ -251,7 +285,7 @@ dataContourDiff = function (plotParams, plotFunction) { const result = matsDataProcessUtils.processDataContour( dataset, curveInfoParams, - plotParams, + newPlotParams, bookkeepingParams ); plotFunction(result); diff --git a/apps/ceil-vis/server/dataFunctions/data_dailymodelcycle.js b/apps/ceil-vis/server/dataFunctions/data_dailymodelcycle.js index 1cb5c65caf..7d21f9f653 100644 --- a/apps/ceil-vis/server/dataFunctions/data_dailymodelcycle.js +++ b/apps/ceil-vis/server/dataFunctions/data_dailymodelcycle.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDailyModelCycle = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,37 +24,48 @@ dataDailyModelCycle = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; let queryTableClause = ""; - var thresholdStr = curve.threshold; + + let thresholdClause = ""; + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -62,7 +74,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { key ] === thresholdStr ); - let thresholdClause = ""; + if (curve["utc-cycle-start"].length !== 1) { throw new Error( "INFO: Please select exactly one UTC Cycle Init Hour for this plot type." @@ -71,24 +83,24 @@ dataDailyModelCycle = function (plotParams, plotFunction) { const utcCycleStart = Number(curve["utc-cycle-start"][0]); utcCycleStarts[curveIndex] = utcCycleStart; const utcCycleStartClause = `and floor(((m0.time+1800) - m0.fcst_len*3600)%(24*3600)/3600) IN(${utcCycleStart})`; + const forecastLengthClause = "and m0.fcst_len < 24"; - var dateClause; - let siteDateClause = ""; - let siteMatchClause = ""; - let sitesClause = ""; - const siteMap = matsCollections.StationMap.findOne( - { name: "stations" }, - { optionsMap: 1 } - ).optionsMap; + + let statisticClause; const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap; - var statisticClause; + + let dateClause; + let siteDateClause = ""; + let siteMatchClause = ""; + let sitesClause = ""; + const regionType = curve["region-type"]; if (regionType === "Predefined region") { - var regionStr = curve.region; + const regionStr = curve.region; const region = Object.keys( matsCollections.region.findOne({ name: "region" }).valuesMap ).find( @@ -116,16 +128,17 @@ dataDailyModelCycle = function (plotParams, plotFunction) { statisticClause = statisticClause.replace(/m0\.ceil/g, "m0.vis100"); statisticClause = statisticClause.replace(/o\.ceil/g, "o.vis100"); } + + const siteMap = matsCollections.StationMap.findOne( + { name: "stations" }, + { optionsMap: 1 } + ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - var thisSite; - var thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.madis_id in('${querySites.join("','")}')`; } else { throw new Error( @@ -136,11 +149,12 @@ dataDailyModelCycle = function (plotParams, plotFunction) { siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; siteMatchClause = "and m0.madis_id = o.madis_id and m0.time = o.time "; } + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -148,52 +162,52 @@ dataDailyModelCycle = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select ceil(3600*floor((m0.time+1800)/3600)) as avtime, " + - "count(distinct ceil(3600*floor((m0.time+1800)/3600))) as N_times, " + - "min(ceil(3600*floor((m0.time+1800)/3600))) as min_secs, " + - "max(ceil(3600*floor((m0.time+1800)/3600))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{utcCycleStartClause}} " + - "{{thresholdClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select ceil(3600*floor((m0.time+1800)/3600)) as avtime, " + + "count(distinct ceil(3600*floor((m0.time+1800)/3600))) as N_times, " + + "min(ceil(3600*floor((m0.time+1800)/3600))) as min_secs, " + + "max(ceil(3600*floor((m0.time+1800)/3600))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{utcCycleStartClause}} " + + "{{thresholdClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -209,6 +223,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -223,7 +238,6 @@ dataDailyModelCycle = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -248,6 +262,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ceil-vis/server/dataFunctions/data_dieoff.js b/apps/ceil-vis/server/dataFunctions/data_dieoff.js index b67e8e19e2..cd9c532da5 100644 --- a/apps/ceil-vis/server/dataFunctions/data_dieoff.js +++ b/apps/ceil-vis/server/dataFunctions/data_dieoff.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDieoff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,34 +24,44 @@ dataDieoff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; let queryTableClause = ""; - var thresholdStr = curve.threshold; + + let thresholdClause = ""; + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -59,38 +70,39 @@ dataDieoff = function (plotParams, plotFunction) { key ] === thresholdStr ); - let thresholdClause = ""; - var validTimes; + let validTimeClause = ""; - var utcCycleStart; + let validTimes; + let utcCycleStartClause = ""; + let utcCycleStart; + const forecastLengthStr = curve["dieoff-type"]; const forecastLengthOptionsMap = matsCollections["dieoff-type"].findOne( { name: "dieoff-type" }, { optionsMap: 1 } ).optionsMap; const forecastLength = forecastLengthOptionsMap[forecastLengthStr][0]; - const forecastLengthClause = ""; + + let statisticClause; + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; - var dateClause; + + let dateClause; let siteDateClause = ""; let siteMatchClause = ""; let sitesClause = ""; - const siteMap = matsCollections.StationMap.findOne( - { name: "stations" }, - { optionsMap: 1 } - ).optionsMap; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - var statisticClause; + const regionType = curve["region-type"]; if (regionType === "Predefined region") { - var regionStr = curve.region; + const regionStr = curve.region; const region = Object.keys( matsCollections.region.findOne({ name: "region" }).valuesMap ).find( @@ -118,16 +130,17 @@ dataDieoff = function (plotParams, plotFunction) { statisticClause = statisticClause.replace(/m0\.ceil/g, "m0.vis100"); statisticClause = statisticClause.replace(/o\.ceil/g, "o.vis100"); } + + const siteMap = matsCollections.StationMap.findOne( + { name: "stations" }, + { optionsMap: 1 } + ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - var thisSite; - var thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.madis_id in('${querySites.join("','")}')`; } else { throw new Error( @@ -138,6 +151,7 @@ dataDieoff = function (plotParams, plotFunction) { siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; siteMatchClause = "and m0.madis_id = o.madis_id and m0.time = o.time "; } + if (forecastLength === matsTypes.ForecastTypes.dieoff) { validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { @@ -157,11 +171,12 @@ dataDieoff = function (plotParams, plotFunction) { } else { dateClause = `and m0.time-m0.fcst_len*3600 = ${fromSecs}`; } + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -169,54 +184,52 @@ dataDieoff = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.fcst_len as fcst_lead, " + - "count(distinct ceil(3600*floor((m0.time+1800)/3600))) as N_times, " + - "min(ceil(3600*floor((m0.time+1800)/3600))) as min_secs, " + - "max(ceil(3600*floor((m0.time+1800)/3600))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{utcCycleStartClause}} " + - "group by fcst_lead " + - "order by fcst_lead" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.fcst_len as fcst_lead, " + + "count(distinct ceil(3600*floor((m0.time+1800)/3600))) as N_times, " + + "min(ceil(3600*floor((m0.time+1800)/3600))) as min_secs, " + + "max(ceil(3600*floor((m0.time+1800)/3600))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{utcCycleStartClause}} " + + "group by fcst_lead " + + "order by fcst_lead" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -232,6 +245,7 @@ dataDieoff = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -246,7 +260,6 @@ dataDieoff = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -271,6 +284,7 @@ dataDieoff = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ceil-vis/server/dataFunctions/data_histogram.js b/apps/ceil-vis/server/dataFunctions/data_histogram.js index 4bc7a94ca3..adef84110c 100644 --- a/apps/ceil-vis/server/dataFunctions/data_histogram.js +++ b/apps/ceil-vis/server/dataFunctions/data_histogram.js @@ -11,6 +11,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataHistogram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -21,44 +22,45 @@ dataHistogram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const alreadyMatched = false; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries const dataFoundForCurve = []; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const alreadyMatched = false; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; + + const axisMap = Object.create(null); + let statType; + let varUnits; + + let statement = ""; + let error = ""; const dataset = []; const allReturnedSubStats = []; const allReturnedSubSecs = []; - const axisMap = Object.create(null); // process user bin customizations const binParams = matsDataUtils.setHistogramParameters(plotParams); const { yAxisFormat } = binParams; const { binNum } = binParams; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; dataFoundForCurve[curveIndex] = true; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -68,17 +70,16 @@ dataHistogram = function (plotParams, plotFunction) { ] === thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold}`; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; } + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -86,12 +87,27 @@ dataHistogram = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; - var varUnits = statisticOptionsMap[statisticSelect][1]; + [statType] = statisticOptionsMap[statisticSelect]; + [, varUnits] = statisticOptionsMap[statisticSelect]; let axisKey = yAxisFormat; if (yAxisFormat === "Relative frequency") { axisKey += " (x100)"; @@ -99,46 +115,46 @@ dataHistogram = function (plotParams, plotFunction) { curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options curves[curveIndex].binNum = binNum; // stash the binNum to use it later for bar chart options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.time as avtime, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.time as avtime, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -156,6 +172,7 @@ dataHistogram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/ceil-vis/server/dataFunctions/data_map.js b/apps/ceil-vis/server/dataFunctions/data_map.js index 478d5c02f1..69e6b197a5 100644 --- a/apps/ceil-vis/server/dataFunctions/data_map.js +++ b/apps/ceil-vis/server/dataFunctions/data_map.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataMap = function (plotParams, plotFunction) { const appParams = { plotType: matsTypes.PlotTypes.map, @@ -21,20 +22,28 @@ dataMap = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; + const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const dataRequests = {}; // used to store data queries + const curves = JSON.parse(JSON.stringify(plotParams.curves)); if (curves.length > 1) { throw new Error("INFO: There must only be one added curve."); } + + let statement = ""; + let error = ""; const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }).optionsMap[ variable @@ -44,11 +53,7 @@ dataMap = function (plotParams, plotFunction) { const obsTable = modelTable.includes("ret_") || modelTable.includes("Ret_") ? "obs_retro" : "obs"; const queryTableClause = `from ${databaseRef.modelDB}.${obsTable} as o, ${databaseRef.modelDB}.${modelTable} as m0 `; - let sitesClause = ""; - const siteMap = matsCollections.StationMap.findOne( - { name: "stations" }, - { optionsMap: 1 } - ).optionsMap; + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] @@ -58,13 +63,16 @@ dataMap = function (plotParams, plotFunction) { key ] === thresholdStr ); + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time+1800)%(24*3600)/3600) IN(${validTimes})`; } + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; + const { statistic } = curve; let statisticClause = "sum(if((m0.ceil < {{threshold}}) and (o.ceil < {{threshold}}),1,0)) as hit, sum(if((m0.ceil < {{threshold}}) and NOT (o.ceil < {{threshold}}),1,0)) as fa, " + @@ -77,16 +85,19 @@ dataMap = function (plotParams, plotFunction) { statisticClause = statisticClause.replace(/m0\.ceil/g, "m0.vis100"); statisticClause = statisticClause.replace(/o\.ceil/g, "o.vis100"); } + + let sitesClause = ""; + + const siteMap = matsCollections.StationMap.findOne( + { name: "stations" }, + { optionsMap: 1 } + ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - let thisSite; - let thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.madis_id in('${querySites.join("','")}')`; } else { throw new Error( @@ -97,85 +108,105 @@ dataMap = function (plotParams, plotFunction) { const siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; const siteMatchClause = "and m0.madis_id = o.madis_id and m0.time = o.time"; - let statement = - "select m0.madis_id as sta_id, " + - "count(distinct ceil(3600*floor((m0.time+1800)/3600))) as N_times, " + - "min(ceil(3600*floor((m0.time+1800)/3600))) as min_secs, " + - "max(ceil(3600*floor((m0.time+1800)/3600))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by sta_id " + - "order by N0" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - dataRequests[label] = statement; - - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBMapCTC( - sumPool, - statement, - modelTable, - statistic, - siteMap, - appParams - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.length, - }; - // get the data back from the query - var d = queryResult.data; - var dPurple = queryResult.dataPurple; - var dPurpleBlue = queryResult.dataPurpleBlue; - var dBlue = queryResult.dataBlue; - var dBlueGreen = queryResult.dataBlueGreen; - var dGreen = queryResult.dataGreen; - var dGreenYellow = queryResult.dataGreenYellow; - var dYellow = queryResult.dataYellow; - var dOrange = queryResult.dataOrange; - var dOrangeRed = queryResult.dataOrangeRed; - var dRed = queryResult.dataRed; - var { valueLimits } = queryResult; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + let d; + let dPurple; + let dPurpleBlue; + let dBlue; + let dBlueGreen; + let dGreen; + let dGreenYellow; + let dYellow; + let dOrange; + let dOrangeRed; + let dRed; + let valueLimits; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select m0.madis_id as sta_id, " + + "count(distinct ceil(3600*floor((m0.time+1800)/3600))) as N_times, " + + "min(ceil(3600*floor((m0.time+1800)/3600))) as min_secs, " + + "max(ceil(3600*floor((m0.time+1800)/3600))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by sta_id " + + "order by N0" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + dataRequests[label] = statement; + + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBMapCTC( + sumPool, // eslint-disable-line no-undef + statement, + modelTable, + statistic, + siteMap, + appParams + ); + + finishMoment = moment(); + dataRequests[label] = "Station plot -- no one query."; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.length, + }; + // get the data back from the query + d = queryResult.data; + dPurple = queryResult.dataPurple; + dPurpleBlue = queryResult.dataPurpleBlue; + dBlue = queryResult.dataBlue; + dBlueGreen = queryResult.dataBlueGreen; + dGreen = queryResult.dataGreen; + dGreenYellow = queryResult.dataGreenYellow; + dYellow = queryResult.dataYellow; + dOrange = queryResult.dataOrange; + dOrangeRed = queryResult.dataOrangeRed; + dRed = queryResult.dataRed; + valueLimits = queryResult.valueLimits; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); + } + + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } } + } else { + // this is a difference curve -- not supported for maps + throw new Error( + "INFO: Difference curves are not supported for maps, as there is only one curve." + ); } + const postQueryStartMoment = moment(); let cOptions = matsDataCurveOpsUtils.generateCTCMapCurveOptions(curve, d, appParams); // generate map with site data dataset.push(cOptions); @@ -303,6 +334,15 @@ dataMap = function (plotParams, plotFunction) { ); // generate red text layer dataset.push(cOptions); + const postQueryFinishMoment = moment(); + dataRequests[`post data retrieval (query) process time - ${label}`] = { + begin: postQueryStartMoment.format(), + finish: postQueryFinishMoment.format(), + duration: `${moment + .duration(postQueryFinishMoment.diff(postQueryStartMoment)) + .asSeconds()} seconds`, + }; + const resultOptions = matsDataPlotOpsUtils.generateMapPlotOptions(true); const totalProcessingFinish = moment(); dataRequests["total retrieval and processing time for curve set"] = { diff --git a/apps/ceil-vis/server/dataFunctions/data_perfDiagram.js b/apps/ceil-vis/server/dataFunctions/data_perfDiagram.js index 0023dfc36f..8b167a1434 100644 --- a/apps/ceil-vis/server/dataFunctions/data_perfDiagram.js +++ b/apps/ceil-vis/server/dataFunctions/data_perfDiagram.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataPerformanceDiagram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,52 +23,47 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statType; + + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const binParam = curve["bin-parameter"]; const binClause = matsCollections["bin-parameter"].findOne({ name: "bin-parameter", }).optionsMap[binParam]; - var { variable } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let dateString = ""; - let dateClause = ""; if (binParam !== "Threshold") { - var thresholdStr = curve.threshold; + const thresholdStr = curve.threshold; if (thresholdStr === undefined) { throw new Error( `INFO: ${label}'s threshold is undefined. Please assign it a value.` @@ -83,12 +79,16 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold}`; } + + let validTimeClause = ""; if (binParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { - validTimeClause = `and m0.time%(24*3600)/3600 IN(${validTimes})`; + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { + validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (binParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; if (forecastLength === undefined) { @@ -98,62 +98,79 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const statisticSelect = "PerformanceDiagram"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + let dateString = ""; + let dateClause = ""; if (binParam === "Init Date") { dateString = "m0.time-m0.fcst_len*3600"; } else { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const statisticSelect = "PerformanceDiagram"; - var statType = "ctc"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // variable + statistic (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + statType = "ctc"; curves[curveIndex].axisKey = statisticSelect; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{binClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "((sum(m0.yy)+0.00)/sum(m0.yy+m0.ny)) as pod, ((sum(m0.yn)+0.00)/sum(m0.yn+m0.yy)) as far, " + - "sum(m0.yy+m0.ny) as oy_all, sum(m0.yn+m0.nn) as on_all, group_concat(m0.time, ';', m0.yy, ';', " + - "m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0 " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by binVal " + - "order by binVal" + - ";"; - - statement = statement.replace("{{binClause}}", binClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "{{binClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "((sum(m0.yy)+0.00)/sum(m0.yy+m0.ny)) as pod, ((sum(m0.yn)+0.00)/sum(m0.yn+m0.yy)) as far, " + + "sum(m0.yy+m0.ny) as oy_all, sum(m0.yn+m0.nn) as on_all, group_concat(m0.time, ';', m0.yy, ';', " + + "m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0 " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by binVal " + + "order by binVal" + + ";"; + + statement = statement.replace("{{binClause}}", binClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBPerformanceDiagram( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -169,6 +186,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -183,7 +201,6 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -191,7 +208,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { ymax = ymax > d.ymax ? ymax : d.ymax; } } else { - // this is a difference curve -- not supported for ROC plots + // this is a difference curve -- not supported for performance diagrams throw new Error( "INFO: Difference curves are not supported for performance diagrams, as they do not feature consistent x or y values across all curves." ); @@ -199,6 +216,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ceil-vis/server/dataFunctions/data_series.js b/apps/ceil-vis/server/dataFunctions/data_series.js index 4dc9470a8d..a9ab761d0d 100644 --- a/apps/ceil-vis/server/dataFunctions/data_series.js +++ b/apps/ceil-vis/server/dataFunctions/data_series.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSeries = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,37 +24,48 @@ dataSeries = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; let queryTableClause = ""; - var thresholdStr = curve.threshold; + + let thresholdClause = ""; + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -62,31 +74,38 @@ dataSeries = function (plotParams, plotFunction) { key ] === thresholdStr ); - let thresholdClause = ""; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time+1800)%(24*3600)/3600) IN(${validTimes})`; } + let forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - var dateClause; - let siteDateClause = ""; - let siteMatchClause = ""; - let sitesClause = ""; - const siteMap = matsCollections.StationMap.findOne( - { name: "stations" }, - { optionsMap: 1 } - ).optionsMap; + + let statisticClause; const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap; - var statisticClause; + + const averageStr = curve.average; + const averageOptionsMap = matsCollections.average.findOne( + { name: "average" }, + { optionsMap: 1 } + ).optionsMap; + const average = averageOptionsMap[averageStr][0]; + + let dateClause; + let siteDateClause = ""; + let siteMatchClause = ""; + let sitesClause = ""; + const regionType = curve["region-type"]; if (regionType === "Predefined region") { - var regionStr = curve.region; + const regionStr = curve.region; const region = Object.keys( matsCollections.region.findOne({ name: "region" }).valuesMap ).find( @@ -114,16 +133,17 @@ dataSeries = function (plotParams, plotFunction) { statisticClause = statisticClause.replace(/m0\.ceil/g, "m0.vis100"); statisticClause = statisticClause.replace(/o\.ceil/g, "o.vis100"); } + + const siteMap = matsCollections.StationMap.findOne( + { name: "stations" }, + { optionsMap: 1 } + ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - var thisSite; - var thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.madis_id in('${querySites.join("','")}')`; } else { throw new Error( @@ -134,17 +154,12 @@ dataSeries = function (plotParams, plotFunction) { siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; siteMatchClause = "and m0.madis_id = o.madis_id and m0.time = o.time "; } - const averageStr = curve.average; - const averageOptionsMap = matsCollections.average.findOne( - { name: "average" }, - { optionsMap: 1 } - ).optionsMap; - const average = averageOptionsMap[averageStr][0]; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -152,53 +167,51 @@ dataSeries = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select {{average}} as avtime, " + - "count(distinct ceil(3600*floor((m0.time+1800)/3600))) as N_times, " + - "min(ceil(3600*floor((m0.time+1800)/3600))) as min_secs, " + - "max(ceil(3600*floor((m0.time+1800)/3600))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select {{average}} as avtime, " + + "count(distinct ceil(3600*floor((m0.time+1800)/3600))) as N_times, " + + "min(ceil(3600*floor((m0.time+1800)/3600))) as min_secs, " + + "max(ceil(3600*floor((m0.time+1800)/3600))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; - statement = statement.replace("{{average}}", average); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - dataRequests[label] = statement; + statement = statement.replace("{{average}}", average); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + dataRequests[label] = statement; - // math is done on forecastLength later on -- set all analyses to 0 - if (forecastLength === "-99") { - forecastLength = "0"; - } + // math is done on forecastLength later on -- set all analyses to 0 + if (forecastLength === "-99") { + forecastLength = "0"; + } - var queryResult; - const startMoment = moment(); - var finishMoment; - try { // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBTimeSeries( - sumPool, + sumPool, // eslint-disable-line no-undef statement, model, forecastLength, @@ -210,7 +223,9 @@ dataSeries = function (plotParams, plotFunction) { appParams, false ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -226,6 +241,7 @@ dataSeries = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -240,7 +256,6 @@ dataSeries = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -265,6 +280,7 @@ dataSeries = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ceil-vis/server/dataFunctions/data_threshold.js b/apps/ceil-vis/server/dataFunctions/data_threshold.js index 737226b47e..f0aac94918 100644 --- a/apps/ceil-vis/server/dataFunctions/data_threshold.js +++ b/apps/ceil-vis/server/dataFunctions/data_threshold.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataThreshold = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,51 +24,50 @@ dataThreshold = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; } + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -75,11 +75,26 @@ dataThreshold = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -87,44 +102,44 @@ dataThreshold = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.trsh/100 as thresh, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by thresh " + - "order by thresh" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.trsh/100 as thresh, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by thresh " + + "order by thresh" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -140,6 +155,7 @@ dataThreshold = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -154,7 +170,6 @@ dataThreshold = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -179,6 +194,7 @@ dataThreshold = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ceil-vis/server/dataFunctions/data_validtime.js b/apps/ceil-vis/server/dataFunctions/data_validtime.js index 05cb8e2365..2a5d68437c 100644 --- a/apps/ceil-vis/server/dataFunctions/data_validtime.js +++ b/apps/ceil-vis/server/dataFunctions/data_validtime.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataValidTime = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,34 +24,44 @@ dataValidTime = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; let queryTableClause = ""; - var thresholdStr = curve.threshold; + + let thresholdClause = ""; + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -59,29 +70,29 @@ dataValidTime = function (plotParams, plotFunction) { key ] === thresholdStr ); - let thresholdClause = ""; + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; + + let statisticClause; + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; - var dateClause; + + let dateClause; let siteDateClause = ""; let siteMatchClause = ""; let sitesClause = ""; - const siteMap = matsCollections.StationMap.findOne( - { name: "stations" }, - { optionsMap: 1 } - ).optionsMap; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - var statisticClause; + const regionType = curve["region-type"]; if (regionType === "Predefined region") { - var regionStr = curve.region; + const regionStr = curve.region; const region = Object.keys( matsCollections.region.findOne({ name: "region" }).valuesMap ).find( @@ -109,16 +120,17 @@ dataValidTime = function (plotParams, plotFunction) { statisticClause = statisticClause.replace(/m0\.ceil/g, "m0.vis100"); statisticClause = statisticClause.replace(/o\.ceil/g, "o.vis100"); } + + const siteMap = matsCollections.StationMap.findOne( + { name: "stations" }, + { optionsMap: 1 } + ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - var thisSite; - var thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.madis_id in('${querySites.join("','")}')`; } else { throw new Error( @@ -129,11 +141,12 @@ dataValidTime = function (plotParams, plotFunction) { siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; siteMatchClause = "and m0.madis_id = o.madis_id and m0.time = o.time "; } + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -141,50 +154,50 @@ dataValidTime = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select floor((m0.time+1800)%(24*3600)/3600) as hr_of_day, " + - "count(distinct ceil(3600*floor((m0.time+1800)/3600))) as N_times, " + - "min(ceil(3600*floor((m0.time+1800)/3600))) as min_secs, " + - "max(ceil(3600*floor((m0.time+1800)/3600))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{thresholdClause}} " + - "{{forecastLengthClause}} " + - "group by hr_of_day " + - "order by hr_of_day" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select floor((m0.time+1800)%(24*3600)/3600) as hr_of_day, " + + "count(distinct ceil(3600*floor((m0.time+1800)/3600))) as N_times, " + + "min(ceil(3600*floor((m0.time+1800)/3600))) as min_secs, " + + "max(ceil(3600*floor((m0.time+1800)/3600))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{thresholdClause}} " + + "{{forecastLengthClause}} " + + "group by hr_of_day " + + "order by hr_of_day" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -200,6 +213,7 @@ dataValidTime = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -214,7 +228,6 @@ dataValidTime = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -239,6 +252,7 @@ dataValidTime = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ceil-vis/server/main.js b/apps/ceil-vis/server/main.js index 2145a1482a..9ee7b4dcd3 100644 --- a/apps/ceil-vis/server/main.js +++ b/apps/ceil-vis/server/main.js @@ -4,7 +4,9 @@ import { Meteor } from "meteor/meteor"; import { mysql } from "meteor/pcel:mysql"; +import { moment } from "meteor/momentjs:moment"; import { + matsMethods, matsTypes, matsCollections, matsDataUtils, @@ -327,7 +329,7 @@ const doCurveParams = function () { const params = matsCollections.CurveParamsInfo.find({ curve_params: { $exists: true }, }).fetch()[0].curve_params; - for (let cp = 0; cp < params.length; cp++) { + for (let cp = 0; cp < params.length; cp += 1) { matsCollections[params[cp]].remove({}); } } @@ -339,67 +341,56 @@ const doCurveParams = function () { const sitesLocationMap = []; const forecastLengthOptionsMap = {}; const thresholdsModelOptionsMap = {}; - const masterRegionValuesMap = {}; - const masterThresholdValuesMap = {}; + const allRegionValuesMap = {}; + const allThresholdValuesMap = {}; try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - metadataPool, + metadataPool, // eslint-disable-line no-undef "select short_name,description from region_descriptions;" ); - let masterRegDescription; - let masterShortName; - for (var j = 0; j < rows.length; j++) { - masterRegDescription = rows[j].description.trim(); - masterShortName = rows[j].short_name.trim(); - masterRegionValuesMap[masterShortName] = masterRegDescription; + for (let j = 0; j < rows.length; j += 1) { + allRegionValuesMap[rows[j].short_name.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } - let rows; - let didx; - try { - for (didx = 0; didx < variables.length; didx++) { - masterThresholdValuesMap[variables[didx]] = {}; - rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + for (let didx = 0; didx < variables.length; didx += 1) { + allThresholdValuesMap[variables[didx]] = {}; + const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( + sumPool, // eslint-disable-line no-undef `select trsh,description from ${ variableDBNames[variables[didx]].modelDB }.threshold_descriptions;` ); - var masterDescription; - var masterTrsh; - for (var j = 0; j < rows.length; j++) { - masterDescription = rows[j].description.trim(); - masterTrsh = rows[j].trsh.trim(); - masterThresholdValuesMap[variables[didx]][masterTrsh] = masterDescription; + for (let j = 0; j < rows.length; j += 1) { + allThresholdValuesMap[variables[didx]][rows[j].trsh.trim()] = + rows[j].description.trim(); } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { - for (didx = 0; didx < variables.length; didx++) { - modelOptionsMap[variables[didx]] = {}; - modelDateRangeMap[variables[didx]] = {}; - forecastLengthOptionsMap[variables[didx]] = {}; - thresholdsModelOptionsMap[variables[didx]] = {}; - regionModelOptionsMap[variables[didx]] = {}; - - rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, - `select model,regions,display_text,fcst_lens,trsh,mindate,maxdate from ${ - variableDBNames[variables[didx]].sumsDB - }.regions_per_model_mats_all_categories order by display_category, display_order;` + for (let didx = 0; didx < variables.length; didx += 1) { + const variable = variables[didx]; + modelOptionsMap[variable] = {}; + modelDateRangeMap[variable] = {}; + forecastLengthOptionsMap[variable] = {}; + thresholdsModelOptionsMap[variable] = {}; + regionModelOptionsMap[variable] = {}; + + const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( + sumPool, // eslint-disable-line no-undef + `select model,regions,display_text,fcst_lens,trsh,mindate,maxdate from ${variableDBNames[variable].sumsDB}.regions_per_model_mats_all_categories order by display_category, display_order;` ); - for (var i = 0; i < rows.length; i++) { - const model_value = rows[i].model.trim(); + for (let i = 0; i < rows.length; i += 1) { + const modelValue = rows[i].model.trim(); const model = rows[i].display_text.trim(); - modelOptionsMap[variables[didx]][model] = [model_value]; + modelOptionsMap[variable][model] = [modelValue]; const rowMinDate = moment .utc(rows[i].mindate * 1000) @@ -407,86 +398,82 @@ const doCurveParams = function () { const rowMaxDate = moment .utc(rows[i].maxdate * 1000) .format("MM/DD/YYYY HH:mm"); - modelDateRangeMap[variables[didx]][model] = { + modelDateRangeMap[variable][model] = { minDate: rowMinDate, maxDate: rowMaxDate, }; const forecastLengths = rows[i].fcst_lens; - const forecastLengthArr = forecastLengths + forecastLengthOptionsMap[variable][model] = forecastLengths .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < forecastLengthArr.length; j++) { - forecastLengthArr[j] = forecastLengthArr[j].replace(/'|\[|\]/g, ""); - } - forecastLengthOptionsMap[variables[didx]][model] = forecastLengthArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (fhr) { + return fhr.replace(/'|\[|\]/g, ""); + }); const thresholds = rows[i].trsh; - const thresholdsArrRaw = thresholds + thresholdsModelOptionsMap[variable][model] = thresholds .split(",") - .map(Function.prototype.call, String.prototype.trim); - const thresholdsArr = []; - var dummyThresh; - for (var j = 0; j < thresholdsArrRaw.length; j++) { - dummyThresh = thresholdsArrRaw[j].replace(/'|\[|\]/g, ""); - thresholdsArr.push(masterThresholdValuesMap[variables[didx]][dummyThresh]); - } - thresholdsModelOptionsMap[variables[didx]][model] = thresholdsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (threshold) { + return allThresholdValuesMap[variable][threshold.replace(/'|\[|\]/g, "")]; + }); const { regions } = rows[i]; - const regionsArrRaw = regions + regionModelOptionsMap[variable][model] = regions .split(",") - .map(Function.prototype.call, String.prototype.trim); - const regionsArr = []; - var dummyRegion; - for (var j = 0; j < regionsArrRaw.length; j++) { - dummyRegion = regionsArrRaw[j].replace(/'|\[|\]/g, ""); - regionsArr.push(masterRegionValuesMap[dummyRegion]); - } - regionModelOptionsMap[variables[didx]][model] = regionsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (region) { + return allRegionValuesMap[region.replace(/'|\[|\]/g, "")]; + }); } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { matsCollections.SiteMap.remove({}); const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select madis_id,name,lat,lon,elev,description from madis3.metars_mats_global where lat > -16380 and lat < 16380 and lon > -32760 and lon < 32760 order by name;" ); - for (var i = 0; i < rows.length; i++) { - const site_name = rows[i].name; - const site_description = rows[i].description; - const site_id = rows[i].madis_id; - const site_lat = rows[i].lat / 182; - const site_lon = rows[i].lon / 182; - const site_elev = rows[i].elev; - siteOptionsMap[site_name] = [site_id]; - - const point = [site_lat, site_lon]; - const obj = { - name: site_name, - origName: site_name, - point, - elevation: site_elev, - options: { - title: site_description, - color: "red", - size: 5, - network: "METAR", - peerOption: site_name, - id: site_id, - highLightColor: "blue", - }, - }; - sitesLocationMap.push(obj); - - matsCollections.SiteMap.insert({ siteName: site_name, siteId: site_id }); + for (let i = 0; i < rows.length; i += 1) { + const siteName = rows[i].name === undefined ? "unknown" : rows[i].name; + const siteDescription = + rows[i].description === undefined ? "unknown" : rows[i].description; + const siteId = rows[i].madis_id; + const siteLat = rows[i].lat === undefined ? -90 : rows[i].lat / 182; + const siteLon = rows[i].lon === undefined ? 0 : rows[i].lon / 182; + const siteElev = rows[i].elev === undefined ? 0 : rows[i].elev; + + // There's one station right at the south pole that the map doesn't know how to render at all, so exclude it. + // Also exclude stations with missing data + if (siteLat < 90 && siteLat > -90) { + siteOptionsMap[siteName] = [siteId]; + + const point = [siteLat, siteLon]; + const obj = { + name: siteName, + origName: siteName, + point, + elevation: siteElev, + options: { + title: siteDescription, + color: "red", + size: 5, + network: "METAR", + peerOption: siteName, + id: siteId, + highLightColor: "blue", + }, + }; + sitesLocationMap.push(obj); + matsCollections.SiteMap.insert({ siteName, siteId }); + } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } matsCollections.StationMap.remove({}); @@ -530,7 +517,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.variable.findOne({ name: "variable" }); + const currentParam = matsCollections.variable.findOne({ name: "variable" }); if (!matsDataUtils.areObjectsEqual(currentParam.dates, modelDateRangeMap)) { // have to reload variable data matsCollections.variable.update( @@ -587,7 +574,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["data-source"].findOne({ name: "data-source" }); + const currentParam = matsCollections["data-source"].findOne({ + name: "data-source", + }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, modelOptionsMap)) { // have to reload model data matsCollections["data-source"].update( @@ -612,7 +601,7 @@ const doCurveParams = function () { regionModelOptionsMap[variables[0]][ Object.keys(regionModelOptionsMap[variables[0]])[0] ], - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -627,10 +616,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.region.findOne({ name: "region" }); + const currentParam = matsCollections.region.findOne({ name: "region" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, regionModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterRegionValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allRegionValuesMap) ) { // have to reload region data matsCollections.region.update( @@ -638,7 +627,7 @@ const doCurveParams = function () { { $set: { optionsMap: regionModelOptionsMap, - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, options: regionModelOptionsMap[variables[0]][ Object.keys(regionModelOptionsMap[variables[0]])[0] @@ -715,7 +704,7 @@ const doCurveParams = function () { thresholdsModelOptionsMap[variables[0]][ Object.keys(thresholdsModelOptionsMap[variables[0]])[0] ], - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -730,13 +719,13 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.threshold.findOne({ name: "threshold" }); + const currentParam = matsCollections.threshold.findOne({ name: "threshold" }); if ( !matsDataUtils.areObjectsEqual( currentParam.optionsMap, thresholdsModelOptionsMap ) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterThresholdValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allThresholdValuesMap) ) { // have to reload threshold data matsCollections.threshold.update( @@ -744,7 +733,7 @@ const doCurveParams = function () { { $set: { optionsMap: thresholdsModelOptionsMap, - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, options: thresholdsModelOptionsMap[variables[0]][ Object.keys(thresholdsModelOptionsMap[variables[0]])[0] @@ -784,7 +773,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["forecast-length"].findOne({ + const currentParam = matsCollections["forecast-length"].findOne({ name: "forecast-length", }); if ( @@ -1053,18 +1042,14 @@ const doCurveParams = function () { } // determine date defaults for dates and curveDates - const defaultDb = matsCollections.variable.findOne( - { name: "variable" }, + const defaultDataSource = matsCollections["data-source"].findOne( + { name: "data-source" }, { default: 1 } ).default; modelDateRangeMap = matsCollections.variable.findOne( { name: "variable" }, { dates: 1 } ).dates; - const defaultDataSource = matsCollections["data-source"].findOne( - { name: "data-source" }, - { default: 1 } - ).default; minDate = modelDateRangeMap[variables[0]][defaultDataSource].minDate; maxDate = modelDateRangeMap[variables[0]][defaultDataSource].maxDate; @@ -1105,7 +1090,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["curve-dates"].findOne({ name: "curve-dates" }); + const currentParam = matsCollections["curve-dates"].findOne({ + name: "curve-dates", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.startDate, minDate) || !matsDataUtils.areObjectsEqual(currentParam.stopDate, maxDate) || @@ -1498,7 +1485,8 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { - console.log( + // eslint-disable-next-line no-console + console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); matsCollections.Databases.drop(); @@ -1515,7 +1503,7 @@ Meteor.startup(function () { databases = Meteor.settings.private.databases; } if (databases !== null && databases !== undefined && Array.isArray(databases)) { - for (let di = 0; di < databases.length; di++) { + for (let di = 0; di < databases.length; di += 1) { matsCollections.Databases.insert(databases[di]); } } @@ -1542,6 +1530,7 @@ Meteor.startup(function () { ); if (cbConnection) { // global cbScorecardSettingsPool + // eslint-disable-next-line no-undef cbScorecardSettingsPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1568,6 +1557,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (metadataSettings) { + // eslint-disable-next-line no-undef metadataPool = mysql.createPool(metadataSettings); allPools.push({ pool: "metadataPool", role: matsTypes.DatabaseRoles.META_DATA }); } @@ -1588,6 +1578,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (sumSettings) { + // eslint-disable-next-line no-undef sumPool = mysql.createPool(sumSettings); allPools.push({ pool: "sumPool", role: matsTypes.DatabaseRoles.SUMS_DATA }); } @@ -1596,7 +1587,7 @@ Meteor.startup(function () { const mdr = new matsTypes.MetaDataDBRecord("metadataPool", "mats_common", [ "region_descriptions", ]); - for (let didx = 0; didx < variables.length; didx++) { + for (let didx = 0; didx < variables.length; didx += 1) { mdr.addRecord("sumPool", variableDBNames[variables[didx]].modelDB, [ "threshold_descriptions", ]); @@ -1611,7 +1602,7 @@ Meteor.startup(function () { appType: matsTypes.AppTypes.mats, }); } catch (error) { - console.log(error.message); + throw new Error(error.message); } }); @@ -1619,6 +1610,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doPlotGraph, doCurveParams, diff --git a/apps/ceil-vis15/.eslintrc.json b/apps/ceil-vis15/.eslintrc.json index e5e813ea94..79d49c5bb6 100644 --- a/apps/ceil-vis15/.eslintrc.json +++ b/apps/ceil-vis15/.eslintrc.json @@ -28,23 +28,6 @@ "space-before-function-paren": "off", // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", - "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-plusplus": "warn", - "vars-on-top": "warn", - "no-var": "warn", - "block-scoped-var": "warn", - "no-loop-func": "warn", - "no-unused-vars": "warn", - "prefer-destructuring": "warn", - "no-param-reassign": "warn", - "camelcase": "warn", - "no-redeclare": "warn", - "no-shadow": "warn" + "prefer-arrow-callback": "off" } } diff --git a/apps/ceil-vis15/client/main.js b/apps/ceil-vis15/client/main.js index a87407a1f4..ecd922b6a2 100644 --- a/apps/ceil-vis15/client/main.js +++ b/apps/ceil-vis15/client/main.js @@ -2,6 +2,7 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; import "@fortawesome/fontawesome-free"; import "@fortawesome/fontawesome-free/css/all.css"; diff --git a/apps/ceil-vis15/server/dataFunctions/data_contour.js b/apps/ceil-vis15/server/dataFunctions/data_contour.js index 9ebf0bd5a8..b5524ec99d 100644 --- a/apps/ceil-vis15/server/dataFunctions/data_contour.js +++ b/apps/ceil-vis15/server/dataFunctions/data_contour.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContour = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,62 +23,53 @@ dataContour = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; - const totalProcessingStart = moment(); + + const curves = JSON.parse(JSON.stringify(plotParams.curves)); + if (curves.length > 1) { + throw new Error("INFO: There must only be one added curve."); + } + + const axisMap = Object.create(null); + + let statement = ""; + let error = ""; + const dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - const curves = JSON.parse(JSON.stringify(plotParams.curves)); - if (curves.length > 1) { - throw new Error("INFO: There must only be one added curve."); - } - const dataset = []; - const axisMap = Object.create(null); - // initialize variables specific to the curve + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }).optionsMap[ variable ]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - const regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + let thresholdClause = ""; - let truthClause = ""; - if (variable === "15 Minute Visibility") { - const truthStr = curve.truth; - const truth = Object.keys( - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] - ).find( - (key) => - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === - truthStr - ); - truthClause = `and m0.truth = '${truth}'`; - } - let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { const thresholdStr = curve.threshold; + if (thresholdStr === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -88,18 +80,52 @@ dataContour = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold}`; } + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time)%(24*3600)/900)/4 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = Number(curve["forecast-length"]); + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } const forecastHour = Math.floor(forecastLength); const forecastMinute = (forecastLength - forecastHour) * 60; forecastLengthClause = `and m0.fcst_len = ${forecastHour} and m0.fcst_min = ${forecastMinute}`; } + + let truthClause = ""; + let truth; + if (variable === "15 Minute Visibility") { + const truthStr = curve.truth; + truth = Object.keys( + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] + ).find( + (key) => + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === + truthStr + ); + truthClause = `and m0.truth = '${truth}'`; + } + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -110,97 +136,107 @@ dataContour = function (plotParams, plotFunction) { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + // For contours, this functions as the colorbar label. const statType = statisticOptionsMap[statisticSelect][0]; - curve.unitKey = statisticOptionsMap[statisticSelect][1]; + [, curve.unitKey] = statisticOptionsMap[statisticSelect]; let d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{truthClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; - - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{truthClause}}", truthClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{truthClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; + + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{truthClause}}", truthClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - } - if (!dataFoundForCurve) { - // we found no data for any curves so don't bother proceeding - throw new Error("INFO: No valid data for any curves."); - } + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { + // this is NOT an error just a no data condition + dataFoundForCurve = false; + } else { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + } - const postQueryStartMoment = moment(); + if (!dataFoundForCurve) { + // we found no data for any curves so don't bother proceeding + throw new Error("INFO: No valid data for any curves."); + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined diff --git a/apps/ceil-vis15/server/dataFunctions/data_contour_diff.js b/apps/ceil-vis15/server/dataFunctions/data_contour_diff.js index cfd0a22887..bc08ac35c4 100644 --- a/apps/ceil-vis15/server/dataFunctions/data_contour_diff.js +++ b/apps/ceil-vis15/server/dataFunctions/data_contour_diff.js @@ -14,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContourDiff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -24,65 +25,58 @@ dataContourDiff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; let dataNotFoundForAnyCurve = false; + + let curves = JSON.parse(JSON.stringify(plotParams.curves)); + const curvesLength = curves.length; + if (curvesLength !== 2) { + throw new Error("INFO: There must be two added curves."); + } + + const axisMap = Object.create(null); const showSignificance = plotParams.significance !== "none"; - const totalProcessingStart = moment(); + + let statType; + let statisticSelect; + + let statement = ""; + let error = ""; + let dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - let curves = JSON.parse(JSON.stringify(plotParams.curves)); - const curvesLength = curves.length; - if (curvesLength !== 2) { - throw new Error("INFO: There must be two added curves."); - } - let dataset = []; - const axisMap = Object.create(null); - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + let thresholdClause = ""; - let truthClause = ""; - if (variable === "15 Minute Visibility") { - var truthStr = curve.truth; - const truth = Object.keys( - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] - ).find( - (key) => - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === - truthStr - ); - truthClause = `and m0.truth = '${truth}'`; - } - let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { - var thresholdStr = curve.threshold; + const thresholdStr = curve.threshold; + if (thresholdStr === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -93,18 +87,52 @@ dataContourDiff = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold}`; } + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time)%(24*3600)/900)/4 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = Number(curve["forecast-length"]); + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } const forecastHour = Math.floor(forecastLength); const forecastMinute = (forecastLength - forecastHour) * 60; forecastLengthClause = `and m0.fcst_len = ${forecastHour} and m0.fcst_min = ${forecastMinute}`; } + + let truthClause = ""; + let truth; + if (variable === "15 Minute Visibility") { + const truthStr = curve.truth; + truth = Object.keys( + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] + ).find( + (key) => + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === + truthStr + ); + truthClause = `and m0.truth = '${truth}'`; + } + + statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -115,93 +143,100 @@ dataContourDiff = function (plotParams, plotFunction) { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - var statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + // For contours, this functions as the colorbar label. - var statType = statisticOptionsMap[statisticSelect][0]; - curves[curveIndex].unitKey = statisticOptionsMap[statisticSelect][1]; + [statType] = statisticOptionsMap[statisticSelect]; + [, curve.unitKey] = statisticOptionsMap[statisticSelect]; - var d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{truthClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; + let d; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{truthClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{truthClause}}", truthClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{truthClause}}", truthClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; - var queryResult; - const startMoment = moment(); - var finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - dataNotFoundForAnyCurve = true; - } - const postQueryStartMoment = moment(); + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + dataNotFoundForAnyCurve = true; + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined @@ -250,8 +285,9 @@ dataContourDiff = function (plotParams, plotFunction) { statType === "ctc", statType === "scalar" ); - plotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); - curves = plotParams.curves; + const newPlotParams = plotParams; + newPlotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); + curves = newPlotParams.curves; dataset[0].name = matsPlotUtils.getCurveText( matsTypes.PlotTypes.contourDiff, curves[0] @@ -267,7 +303,7 @@ dataContourDiff = function (plotParams, plotFunction) { const result = matsDataProcessUtils.processDataContour( dataset, curveInfoParams, - plotParams, + newPlotParams, bookkeepingParams ); plotFunction(result); diff --git a/apps/ceil-vis15/server/dataFunctions/data_dailymodelcycle.js b/apps/ceil-vis15/server/dataFunctions/data_dailymodelcycle.js index dd4255e2a2..846a0e074c 100644 --- a/apps/ceil-vis15/server/dataFunctions/data_dailymodelcycle.js +++ b/apps/ceil-vis15/server/dataFunctions/data_dailymodelcycle.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDailyModelCycle = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,48 +24,48 @@ dataDailyModelCycle = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; let queryTableClause = ""; - let truthClause = ""; - if (variable === "15 Minute Visibility") { - var truthStr = curve.truth; - var truth = Object.keys( - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] - ).find( - (key) => - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === - truthStr - ); - } - var thresholdStr = curve.threshold; + + let thresholdClause = ""; + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -73,7 +74,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { key ] === thresholdStr ); - let thresholdClause = ""; + if (curve["utc-cycle-start"].length !== 1) { throw new Error( "INFO: Please select exactly one UTC Cycle Init Hour for this plot type." @@ -82,24 +83,36 @@ dataDailyModelCycle = function (plotParams, plotFunction) { const utcCycleStart = Number(curve["utc-cycle-start"][0]); utcCycleStarts[curveIndex] = utcCycleStart; const utcCycleStartClause = `and floor(((m0.time+450) - (m0.fcst_len*60+m0.fcst_min)*60)%(24*3600)/900)/4 IN(${utcCycleStart})`; + const forecastLengthClause = "and m0.fcst_len < 24"; - var dateClause; - let siteDateClause = ""; - let siteMatchClause = ""; - let sitesClause = ""; - const siteMap = matsCollections.StationMap.findOne( - { name: "stations" }, - { optionsMap: 1 } - ).optionsMap; + + let truthClause = ""; + let truth; + if (variable === "15 Minute Visibility") { + const truthStr = curve.truth; + truth = Object.keys( + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] + ).find( + (key) => + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === + truthStr + ); + } + let statisticClause; const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap; - var statisticClause; + + let dateClause; + let siteDateClause = ""; + let siteMatchClause = ""; + let sitesClause = ""; + const regionType = curve["region-type"]; if (regionType === "Predefined region") { - var regionStr = curve.region; + const regionStr = curve.region; const region = Object.keys( matsCollections.region.findOne({ name: "region" }).valuesMap ).find( @@ -135,16 +148,17 @@ dataDailyModelCycle = function (plotParams, plotFunction) { truthClause = "and o.vis_std < 2.4"; } } + + const siteMap = matsCollections.StationMap.findOne( + { name: "stations" }, + { optionsMap: 1 } + ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - var thisSite; - var thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.madis_id in('${querySites.join("','")}')`; } else { throw new Error( @@ -155,11 +169,12 @@ dataDailyModelCycle = function (plotParams, plotFunction) { siteDateClause = `and o.time >= ${fromSecs} - 300 and o.time <= ${toSecs} + 300`; siteMatchClause = "and m0.madis_id = o.madis_id and m0.time = o.time "; } + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -167,57 +182,57 @@ dataDailyModelCycle = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select ceil(900*floor((m0.time+450)/900)) as avtime, " + - "count(distinct ceil(900*floor((m0.time+450)/900))) as N_times, " + - "min(ceil(900*floor((m0.time+450)/900))) as min_secs, " + - "max(ceil(900*floor((m0.time+450)/900))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{utcCycleStartClause}} " + - "{{thresholdClause}} " + - "{{forecastLengthClause}} " + - "{{truthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{truthClause}}", truthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - if (variable.includes("Visibility")) { - statement = statement.replace(/o\.time/g, "o.valid_time"); - } - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select ceil(900*floor((m0.time+450)/900)) as avtime, " + + "count(distinct ceil(900*floor((m0.time+450)/900))) as N_times, " + + "min(ceil(900*floor((m0.time+450)/900))) as min_secs, " + + "max(ceil(900*floor((m0.time+450)/900))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{utcCycleStartClause}} " + + "{{thresholdClause}} " + + "{{forecastLengthClause}} " + + "{{truthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{truthClause}}", truthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + if (variable.includes("Visibility")) { + statement = statement.replace(/o\.time/g, "o.valid_time"); + } + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -233,6 +248,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -247,7 +263,6 @@ dataDailyModelCycle = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -272,6 +287,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ceil-vis15/server/dataFunctions/data_dieoff.js b/apps/ceil-vis15/server/dataFunctions/data_dieoff.js index 62f1842e35..9365e12363 100644 --- a/apps/ceil-vis15/server/dataFunctions/data_dieoff.js +++ b/apps/ceil-vis15/server/dataFunctions/data_dieoff.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDieoff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,45 +24,44 @@ dataDieoff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; let queryTableClause = ""; - let truthClause = ""; - if (variable === "15 Minute Visibility") { - var truthStr = curve.truth; - var truth = Object.keys( - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] - ).find( - (key) => - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === - truthStr - ); - } - var thresholdStr = curve.threshold; + + let thresholdClause = ""; + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -70,38 +70,52 @@ dataDieoff = function (plotParams, plotFunction) { key ] === thresholdStr ); - let thresholdClause = ""; - var validTimes; + let validTimeClause = ""; - var utcCycleStart; + let validTimes; + let utcCycleStartClause = ""; + let utcCycleStart; + const forecastLengthStr = curve["dieoff-type"]; const forecastLengthOptionsMap = matsCollections["dieoff-type"].findOne( { name: "dieoff-type" }, { optionsMap: 1 } ).optionsMap; const forecastLength = forecastLengthOptionsMap[forecastLengthStr][0]; - const forecastLengthClause = ""; + + let truthClause = ""; + let truth; + if (variable === "15 Minute Visibility") { + const truthStr = curve.truth; + truth = Object.keys( + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] + ).find( + (key) => + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === + truthStr + ); + } + + let statisticClause; + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; - var dateClause; + + let dateClause; let siteDateClause = ""; let siteMatchClause = ""; let sitesClause = ""; - const siteMap = matsCollections.StationMap.findOne( - { name: "stations" }, - { optionsMap: 1 } - ).optionsMap; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - var statisticClause; + const regionType = curve["region-type"]; if (regionType === "Predefined region") { - var regionStr = curve.region; + const regionStr = curve.region; const region = Object.keys( matsCollections.region.findOne({ name: "region" }).valuesMap ).find( @@ -137,16 +151,17 @@ dataDieoff = function (plotParams, plotFunction) { truthClause = "and o.vis_std < 2.4"; } } + + const siteMap = matsCollections.StationMap.findOne( + { name: "stations" }, + { optionsMap: 1 } + ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - var thisSite; - var thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.madis_id in('${querySites.join("','")}')`; } else { throw new Error( @@ -157,6 +172,7 @@ dataDieoff = function (plotParams, plotFunction) { siteDateClause = `and o.time >= ${fromSecs} - 300 and o.time <= ${toSecs} + 300`; siteMatchClause = "and m0.madis_id = o.madis_id and m0.time = o.time "; } + if (forecastLength === matsTypes.ForecastTypes.dieoff) { validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { @@ -176,11 +192,12 @@ dataDieoff = function (plotParams, plotFunction) { } else { dateClause = `and m0.time-(m0.fcst_len*60+m0.fcst_min)*60 = ${fromSecs}`; } + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -188,59 +205,57 @@ dataDieoff = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.fcst_len + (m0.fcst_min/60) as fcst_lead, " + - "count(distinct ceil(900*floor((m0.time+450)/900))) as N_times, " + - "min(ceil(900*floor((m0.time+450)/900))) as min_secs, " + - "max(ceil(900*floor((m0.time+450)/900))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{utcCycleStartClause}} " + - "{{truthClause}} " + - "group by fcst_lead " + - "order by fcst_lead" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{truthClause}}", truthClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - if (variable.includes("Visibility")) { - statement = statement.replace(/o\.time/g, "o.valid_time"); - } - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.fcst_len + (m0.fcst_min/60) as fcst_lead, " + + "count(distinct ceil(900*floor((m0.time+450)/900))) as N_times, " + + "min(ceil(900*floor((m0.time+450)/900))) as min_secs, " + + "max(ceil(900*floor((m0.time+450)/900))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{utcCycleStartClause}} " + + "{{truthClause}} " + + "group by fcst_lead " + + "order by fcst_lead" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{truthClause}}", truthClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + if (variable.includes("Visibility")) { + statement = statement.replace(/o\.time/g, "o.valid_time"); + } + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -256,6 +271,7 @@ dataDieoff = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -270,7 +286,6 @@ dataDieoff = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -295,6 +310,7 @@ dataDieoff = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ceil-vis15/server/dataFunctions/data_histogram.js b/apps/ceil-vis15/server/dataFunctions/data_histogram.js index 0eba5ab67f..db7bb094da 100644 --- a/apps/ceil-vis15/server/dataFunctions/data_histogram.js +++ b/apps/ceil-vis15/server/dataFunctions/data_histogram.js @@ -11,6 +11,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataHistogram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -21,44 +22,45 @@ dataHistogram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const alreadyMatched = false; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries const dataFoundForCurve = []; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const alreadyMatched = false; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; + + const axisMap = Object.create(null); + let statType; + let varUnits; + + let statement = ""; + let error = ""; const dataset = []; const allReturnedSubStats = []; const allReturnedSubSecs = []; - const axisMap = Object.create(null); // process user bin customizations const binParams = matsDataUtils.setHistogramParameters(plotParams); const { yAxisFormat } = binParams; const { binNum } = binParams; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; dataFoundForCurve[curveIndex] = true; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -68,10 +70,23 @@ dataHistogram = function (plotParams, plotFunction) { ] === thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold}`; + + let validTimeClause = ""; + const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { + validTimeClause = `and floor((m0.time)%(24*3600)/900)/4 IN(${validTimes})`; + } + + const forecastLength = Number(curve["forecast-length"]); + const forecastHour = Math.floor(forecastLength); + const forecastMinute = (forecastLength - forecastHour) * 60; + const forecastLengthClause = `and m0.fcst_len = ${forecastLength} and m0.fcst_min = ${forecastMinute}`; + let truthClause = ""; + let truth; if (variable === "15 Minute Visibility") { - var truthStr = curve.truth; - const truth = Object.keys( + const truthStr = curve.truth; + truth = Object.keys( matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] ).find( (key) => @@ -80,19 +95,7 @@ dataHistogram = function (plotParams, plotFunction) { ); truthClause = `and m0.truth = '${truth}'`; } - let validTimeClause = ""; - const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { - validTimeClause = `and floor((m0.time)%(24*3600)/900)/4 IN(${validTimes})`; - } - const forecastLength = Number(curve["forecast-length"]); - const forecastHour = Math.floor(forecastLength); - const forecastMinute = (forecastLength - forecastHour) * 60; - const forecastLengthClause = `and m0.fcst_len = ${forecastLength} and m0.fcst_min = ${forecastMinute}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -100,12 +103,27 @@ dataHistogram = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; - var varUnits = statisticOptionsMap[statisticSelect][1]; + [statType] = statisticOptionsMap[statisticSelect]; + [, varUnits] = statisticOptionsMap[statisticSelect]; let axisKey = yAxisFormat; if (yAxisFormat === "Relative frequency") { axisKey += " (x100)"; @@ -113,48 +131,48 @@ dataHistogram = function (plotParams, plotFunction) { curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options curves[curveIndex].binNum = binNum; // stash the binNum to use it later for bar chart options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.time as avtime, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{truthClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{truthClause}}", truthClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.time as avtime, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{truthClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{truthClause}}", truthClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -172,6 +190,7 @@ dataHistogram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/ceil-vis15/server/dataFunctions/data_map.js b/apps/ceil-vis15/server/dataFunctions/data_map.js index e0f911c61b..6e1399dc8b 100644 --- a/apps/ceil-vis15/server/dataFunctions/data_map.js +++ b/apps/ceil-vis15/server/dataFunctions/data_map.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataMap = function (plotParams, plotFunction) { const appParams = { plotType: matsTypes.PlotTypes.map, @@ -21,20 +22,28 @@ dataMap = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; + const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const dataRequests = {}; // used to store data queries + const curves = JSON.parse(JSON.stringify(plotParams.curves)); if (curves.length > 1) { throw new Error("INFO: There must only be one added curve."); } + + let statement = ""; + let error = ""; const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }).optionsMap[ variable @@ -44,22 +53,7 @@ dataMap = function (plotParams, plotFunction) { const obsTable = modelTable.includes("ret_") || modelTable.includes("Ret_") ? "obs_retro" : "obs"; const queryTableClause = `from ${databaseRef.modelDB}.${obsTable} as o, ${databaseRef.modelDB}.${modelTable} as m0 `; - let sitesClause = ""; - const siteMap = matsCollections.StationMap.findOne( - { name: "stations" }, - { optionsMap: 1 } - ).optionsMap; - let truthClause = ""; - if (variable === "15 Minute Visibility") { - const truthStr = curve.truth; - var truth = Object.keys( - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] - ).find( - (key) => - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === - truthStr - ); - } + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] @@ -69,6 +63,7 @@ dataMap = function (plotParams, plotFunction) { key ] === thresholdStr ); + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { @@ -78,6 +73,20 @@ dataMap = function (plotParams, plotFunction) { const forecastHour = Math.floor(forecastLength); const forecastMinute = (forecastLength - forecastHour) * 60; const forecastLengthClause = `and m0.fcst_len = ${forecastLength} and m0.fcst_min = ${forecastMinute}`; + + let truthClause = ""; + let truth; + if (variable === "15 Minute Visibility") { + const truthStr = curve.truth; + truth = Object.keys( + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] + ).find( + (key) => + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === + truthStr + ); + } + const { statistic } = curve; let statisticClause = "sum(if((m0.ceil < {{threshold}}) and (o.ceil < {{threshold}}),1,0)) as hit, sum(if((m0.ceil < {{threshold}}) and NOT (o.ceil < {{threshold}}),1,0)) as fa, " + @@ -95,16 +104,19 @@ dataMap = function (plotParams, plotFunction) { truthClause = "and o.vis_std < 2.4"; } } + + let sitesClause = ""; + + const siteMap = matsCollections.StationMap.findOne( + { name: "stations" }, + { optionsMap: 1 } + ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - let thisSite; - let thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.madis_id in('${querySites.join("','")}')`; } else { throw new Error( @@ -115,90 +127,110 @@ dataMap = function (plotParams, plotFunction) { const siteDateClause = `and o.time >= ${fromSecs} - 300 and o.time <= ${toSecs} + 300`; const siteMatchClause = "and m0.madis_id = o.madis_id and m0.time = o.time"; - let statement = - "select m0.madis_id as sta_id, " + - "count(distinct ceil(900*floor((m0.time+450)/900))) as N_times, " + - "min(ceil(900*floor((m0.time+450)/900))) as min_secs, " + - "max(ceil(900*floor((m0.time+450)/900))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{truthClause}} " + - "group by sta_id " + - "order by N0" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{truthClause}}", truthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - if (variable.includes("Visibility")) { - statement = statement.replace(/o\.time/g, "o.valid_time"); - } - dataRequests[label] = statement; - - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBMapCTC( - sumPool, - statement, - modelTable, - statistic, - siteMap, - appParams - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.length, - }; - // get the data back from the query - var d = queryResult.data; - var dPurple = queryResult.dataPurple; - var dPurpleBlue = queryResult.dataPurpleBlue; - var dBlue = queryResult.dataBlue; - var dBlueGreen = queryResult.dataBlueGreen; - var dGreen = queryResult.dataGreen; - var dGreenYellow = queryResult.dataGreenYellow; - var dYellow = queryResult.dataYellow; - var dOrange = queryResult.dataOrange; - var dOrangeRed = queryResult.dataOrangeRed; - var dRed = queryResult.dataRed; - var { valueLimits } = queryResult; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + let d; + let dPurple; + let dPurpleBlue; + let dBlue; + let dBlueGreen; + let dGreen; + let dGreenYellow; + let dYellow; + let dOrange; + let dOrangeRed; + let dRed; + let valueLimits; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select m0.madis_id as sta_id, " + + "count(distinct ceil(900*floor((m0.time+450)/900))) as N_times, " + + "min(ceil(900*floor((m0.time+450)/900))) as min_secs, " + + "max(ceil(900*floor((m0.time+450)/900))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{truthClause}} " + + "group by sta_id " + + "order by N0" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{truthClause}}", truthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + if (variable.includes("Visibility")) { + statement = statement.replace(/o\.time/g, "o.valid_time"); + } + dataRequests[label] = statement; + + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBMapCTC( + sumPool, // eslint-disable-line no-undef + statement, + modelTable, + statistic, + siteMap, + appParams + ); + + finishMoment = moment(); + dataRequests[label] = "Station plot -- no one query."; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.length, + }; + // get the data back from the query + d = queryResult.data; + dPurple = queryResult.dataPurple; + dPurpleBlue = queryResult.dataPurpleBlue; + dBlue = queryResult.dataBlue; + dBlueGreen = queryResult.dataBlueGreen; + dGreen = queryResult.dataGreen; + dGreenYellow = queryResult.dataGreenYellow; + dYellow = queryResult.dataYellow; + dOrange = queryResult.dataOrange; + dOrangeRed = queryResult.dataOrangeRed; + dRed = queryResult.dataRed; + valueLimits = queryResult.valueLimits; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } + + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + } + } else { + // this is a difference curve -- not supported for maps + throw new Error( + "INFO: Difference curves are not supported for maps, as there is only one curve." + ); } + const postQueryStartMoment = moment(); let cOptions = matsDataCurveOpsUtils.generateCTCMapCurveOptions(curve, d, appParams); // generate map with site data dataset.push(cOptions); @@ -326,6 +358,15 @@ dataMap = function (plotParams, plotFunction) { ); // generate red text layer dataset.push(cOptions); + const postQueryFinishMoment = moment(); + dataRequests[`post data retrieval (query) process time - ${label}`] = { + begin: postQueryStartMoment.format(), + finish: postQueryFinishMoment.format(), + duration: `${moment + .duration(postQueryFinishMoment.diff(postQueryStartMoment)) + .asSeconds()} seconds`, + }; + const resultOptions = matsDataPlotOpsUtils.generateMapPlotOptions(true); const totalProcessingFinish = moment(); dataRequests["total retrieval and processing time for curve set"] = { diff --git a/apps/ceil-vis15/server/dataFunctions/data_perfDiagram.js b/apps/ceil-vis15/server/dataFunctions/data_perfDiagram.js index ce583912cd..e04a267013 100644 --- a/apps/ceil-vis15/server/dataFunctions/data_perfDiagram.js +++ b/apps/ceil-vis15/server/dataFunctions/data_perfDiagram.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataPerformanceDiagram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,64 +23,47 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statType; + + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const binParam = curve["bin-parameter"]; const binClause = matsCollections["bin-parameter"].findOne({ name: "bin-parameter", }).optionsMap[binParam]; - var { variable } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + let thresholdClause = ""; - let truthClause = ""; - if (variable === "15 Minute Visibility") { - var truthStr = curve.truth; - const truth = Object.keys( - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] - ).find( - (key) => - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === - truthStr - ); - truthClause = `and m0.truth = '${truth}'`; - } - let validTimeClause = ""; - let forecastLengthClause = ""; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let dateString = ""; - let dateClause = ""; if (binParam !== "Threshold") { - var thresholdStr = curve.threshold; + const thresholdStr = curve.threshold; if (thresholdStr === undefined) { throw new Error( `INFO: ${label}'s threshold is undefined. Please assign it a value.` @@ -95,12 +79,16 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold}`; } + + let validTimeClause = ""; if (binParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time)%(24*3600)/900)/4 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (binParam !== "Fcst lead time") { const forecastLength = Number(curve["forecast-length"]); const forecastHour = Math.floor(forecastLength); @@ -112,64 +100,95 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { } forecastLengthClause = `and m0.fcst_len = ${forecastLength} and m0.fcst_min = ${forecastMinute}`; } + + let truthClause = ""; + let truth; + if (variable === "15 Minute Visibility") { + const truthStr = curve.truth; + truth = Object.keys( + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] + ).find( + (key) => + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === + truthStr + ); + truthClause = `and m0.truth = '${truth}'`; + } + + const statisticSelect = "PerformanceDiagram"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + let dateString = ""; + let dateClause = ""; if (binParam === "Init Date") { dateString = "m0.time-(m0.fcst_len*3600+m0.fcst_min*60)"; } else { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const statisticSelect = "PerformanceDiagram"; - var statType = "ctc"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // variable + statistic (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + statType = "ctc"; curves[curveIndex].axisKey = statisticSelect; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{binClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "((sum(m0.yy)+0.00)/sum(m0.yy+m0.ny)) as pod, ((sum(m0.yn)+0.00)/sum(m0.yn+m0.yy)) as far, " + - "sum(m0.yy+m0.ny) as oy_all, sum(m0.yn+m0.nn) as on_all, group_concat(m0.time, ';', m0.yy, ';', " + - "m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0 " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{truthClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by binVal " + - "order by binVal" + - ";"; - - statement = statement.replace("{{binClause}}", binClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{truthClause}}", truthClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "{{binClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "((sum(m0.yy)+0.00)/sum(m0.yy+m0.ny)) as pod, ((sum(m0.yn)+0.00)/sum(m0.yn+m0.yy)) as far, " + + "sum(m0.yy+m0.ny) as oy_all, sum(m0.yn+m0.nn) as on_all, group_concat(m0.time, ';', m0.yy, ';', " + + "m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0 " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{truthClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by binVal " + + "order by binVal" + + ";"; + + statement = statement.replace("{{binClause}}", binClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{truthClause}}", truthClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBPerformanceDiagram( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -185,6 +204,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -199,7 +219,6 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -207,7 +226,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { ymax = ymax > d.ymax ? ymax : d.ymax; } } else { - // this is a difference curve -- not supported for ROC plots + // this is a difference curve -- not supported for performance diagrams throw new Error( "INFO: Difference curves are not supported for performance diagrams, as they do not feature consistent x or y values across all curves." ); @@ -215,6 +234,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ceil-vis15/server/dataFunctions/data_series.js b/apps/ceil-vis15/server/dataFunctions/data_series.js index 6373583fbd..a9b57bba58 100644 --- a/apps/ceil-vis15/server/dataFunctions/data_series.js +++ b/apps/ceil-vis15/server/dataFunctions/data_series.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSeries = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,48 +24,48 @@ dataSeries = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; let queryTableClause = ""; - let truthClause = ""; - if (variable === "15 Minute Visibility") { - var truthStr = curve.truth; - var truth = Object.keys( - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] - ).find( - (key) => - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === - truthStr - ); - } - var thresholdStr = curve.threshold; + + let thresholdClause = ""; + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -73,33 +74,53 @@ dataSeries = function (plotParams, plotFunction) { key ] === thresholdStr ); - let thresholdClause = ""; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time+450)%(24*3600)/900)/4 IN(${validTimes})`; } + let forecastLength = Number(curve["forecast-length"]); const forecastHour = Math.floor(forecastLength); const forecastMinute = (forecastLength - forecastHour) * 60; const forecastLengthClause = `and m0.fcst_len = ${forecastLength} and m0.fcst_min = ${forecastMinute}`; - var dateClause; - let siteDateClause = ""; - let siteMatchClause = ""; - let sitesClause = ""; - const siteMap = matsCollections.StationMap.findOne( - { name: "stations" }, - { optionsMap: 1 } - ).optionsMap; + + let truthClause = ""; + let truth; + if (variable === "15 Minute Visibility") { + const truthStr = curve.truth; + truth = Object.keys( + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] + ).find( + (key) => + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === + truthStr + ); + } + + let statisticClause; const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap; - var statisticClause; + + const averageStr = curve.average; + const averageOptionsMap = matsCollections.average.findOne( + { name: "average" }, + { optionsMap: 1 } + ).optionsMap; + const average = averageOptionsMap[averageStr][0]; + + let dateClause; + let siteDateClause = ""; + let siteMatchClause = ""; + let sitesClause = ""; + const regionType = curve["region-type"]; if (regionType === "Predefined region") { - var regionStr = curve.region; + const regionStr = curve.region; const region = Object.keys( matsCollections.region.findOne({ name: "region" }).valuesMap ).find( @@ -135,16 +156,17 @@ dataSeries = function (plotParams, plotFunction) { truthClause = "and o.vis_std < 2.4"; } } + + const siteMap = matsCollections.StationMap.findOne( + { name: "stations" }, + { optionsMap: 1 } + ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - var thisSite; - var thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.madis_id in('${querySites.join("','")}')`; } else { throw new Error( @@ -155,17 +177,12 @@ dataSeries = function (plotParams, plotFunction) { siteDateClause = `and o.time >= ${fromSecs} - 300 and o.time <= ${toSecs} + 300`; siteMatchClause = "and m0.madis_id = o.madis_id and m0.time = o.time "; } - const averageStr = curve.average; - const averageOptionsMap = matsCollections.average.findOne( - { name: "average" }, - { optionsMap: 1 } - ).optionsMap; - const average = averageOptionsMap[averageStr][0]; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -173,58 +190,56 @@ dataSeries = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select {{average}} as avtime, " + - "count(distinct ceil(900*floor((m0.time+450)/900))) as N_times, " + - "min(ceil(900*floor((m0.time+450)/900))) as min_secs, " + - "max(ceil(900*floor((m0.time+450)/900))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{truthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select {{average}} as avtime, " + + "count(distinct ceil(900*floor((m0.time+450)/900))) as N_times, " + + "min(ceil(900*floor((m0.time+450)/900))) as min_secs, " + + "max(ceil(900*floor((m0.time+450)/900))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{truthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; - statement = statement.replace("{{average}}", average); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{truthClause}}", truthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - if (variable.includes("Visibility")) { - statement = statement.replace(/o\.time/g, "o.valid_time"); - } - dataRequests[label] = statement; + statement = statement.replace("{{average}}", average); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{truthClause}}", truthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + if (variable.includes("Visibility")) { + statement = statement.replace(/o\.time/g, "o.valid_time"); + } + dataRequests[label] = statement; - // math is done on forecastLength later on -- set all analyses to 0 - if (forecastLength === "-99") { - forecastLength = "0"; - } + // math is done on forecastLength later on -- set all analyses to 0 + if (forecastLength === "-99") { + forecastLength = "0"; + } - var queryResult; - const startMoment = moment(); - var finishMoment; - try { // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBTimeSeries( - sumPool, + sumPool, // eslint-disable-line no-undef statement, model, forecastLength, @@ -236,7 +251,9 @@ dataSeries = function (plotParams, plotFunction) { appParams, false ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -252,6 +269,7 @@ dataSeries = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -266,7 +284,6 @@ dataSeries = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -291,6 +308,7 @@ dataSeries = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ceil-vis15/server/dataFunctions/data_threshold.js b/apps/ceil-vis15/server/dataFunctions/data_threshold.js index 9e4a14c5bb..57e3aa6610 100644 --- a/apps/ceil-vis15/server/dataFunctions/data_threshold.js +++ b/apps/ceil-vis15/server/dataFunctions/data_threshold.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataThreshold = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,44 +24,57 @@ dataThreshold = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + + let validTimeClause = ""; + const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { + validTimeClause = `and floor((m0.time)%(24*3600)/900)/4 IN(${validTimes})`; + } + + const forecastLength = Number(curve["forecast-length"]); + const forecastHour = Math.floor(forecastLength); + const forecastMinute = (forecastLength - forecastHour) * 60; + const forecastLengthClause = `and m0.fcst_len = ${forecastLength} and m0.fcst_min = ${forecastMinute}`; + let truthClause = ""; + let truth; if (variable === "15 Minute Visibility") { - var truthStr = curve.truth; - const truth = Object.keys( + const truthStr = curve.truth; + truth = Object.keys( matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] ).find( (key) => @@ -69,19 +83,7 @@ dataThreshold = function (plotParams, plotFunction) { ); truthClause = `and m0.truth = '${truth}'`; } - let validTimeClause = ""; - const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { - validTimeClause = `and floor((m0.time)%(24*3600)/900)/4 IN(${validTimes})`; - } - const forecastLength = Number(curve["forecast-length"]); - const forecastHour = Math.floor(forecastLength); - const forecastMinute = (forecastLength - forecastHour) * 60; - const forecastLengthClause = `and m0.fcst_len = ${forecastLength} and m0.fcst_min = ${forecastMinute}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -89,11 +91,26 @@ dataThreshold = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef.sumsDB}.${model}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -101,46 +118,46 @@ dataThreshold = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.trsh/100 as thresh, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{truthClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by thresh " + - "order by thresh" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{truthClause}}", truthClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.trsh/100 as thresh, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{truthClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by thresh " + + "order by thresh" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{truthClause}}", truthClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -156,6 +173,7 @@ dataThreshold = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -170,7 +188,6 @@ dataThreshold = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -195,6 +212,7 @@ dataThreshold = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ceil-vis15/server/dataFunctions/data_validtime.js b/apps/ceil-vis15/server/dataFunctions/data_validtime.js index 0c8fe6f098..05ae590be1 100644 --- a/apps/ceil-vis15/server/dataFunctions/data_validtime.js +++ b/apps/ceil-vis15/server/dataFunctions/data_validtime.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataValidTime = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,45 +24,44 @@ dataValidTime = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; let queryTableClause = ""; - let truthClause = ""; - if (variable === "15 Minute Visibility") { - var truthStr = curve.truth; - var truth = Object.keys( - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] - ).find( - (key) => - matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === - truthStr - ); - } - var thresholdStr = curve.threshold; + + let thresholdClause = ""; + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -70,31 +70,44 @@ dataValidTime = function (plotParams, plotFunction) { key ] === thresholdStr ); - let thresholdClause = ""; + const forecastLength = Number(curve["forecast-length"]); const forecastHour = Math.floor(forecastLength); const forecastMinute = (forecastLength - forecastHour) * 60; const forecastLengthClause = `and m0.fcst_len = ${forecastLength} and m0.fcst_min = ${forecastMinute}`; + + let truthClause = ""; + let truth; + if (variable === "15 Minute Visibility") { + const truthStr = curve.truth; + truth = Object.keys( + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable] + ).find( + (key) => + matsCollections.truth.findOne({ name: "truth" }).valuesMap[variable][key] === + truthStr + ); + } + + let statisticClause; + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; - var dateClause; + + let dateClause; let siteDateClause = ""; let siteMatchClause = ""; let sitesClause = ""; - const siteMap = matsCollections.StationMap.findOne( - { name: "stations" }, - { optionsMap: 1 } - ).optionsMap; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - var statisticClause; + const regionType = curve["region-type"]; if (regionType === "Predefined region") { - var regionStr = curve.region; + const regionStr = curve.region; const region = Object.keys( matsCollections.region.findOne({ name: "region" }).valuesMap ).find( @@ -130,16 +143,17 @@ dataValidTime = function (plotParams, plotFunction) { truthClause = "and o.vis_std < 2.4"; } } + + const siteMap = matsCollections.StationMap.findOne( + { name: "stations" }, + { optionsMap: 1 } + ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - var thisSite; - var thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.madis_id in('${querySites.join("','")}')`; } else { throw new Error( @@ -150,11 +164,12 @@ dataValidTime = function (plotParams, plotFunction) { siteDateClause = `and o.time >= ${fromSecs} - 300 and o.time <= ${toSecs} + 300`; siteMatchClause = "and m0.madis_id = o.madis_id and m0.time = o.time "; } + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -162,55 +177,55 @@ dataValidTime = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select floor((m0.time+450)%(24*3600)/900)/4 as hr_of_day, " + - "count(distinct ceil(900*floor((m0.time+450)/900))) as N_times, " + - "min(ceil(900*floor((m0.time+450)/900))) as min_secs, " + - "max(ceil(900*floor((m0.time+450)/900))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{thresholdClause}} " + - "{{forecastLengthClause}} " + - "{{truthClause}} " + - "group by hr_of_day " + - "order by hr_of_day" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{truthClause}}", truthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - if (variable.includes("Visibility")) { - statement = statement.replace(/o\.time/g, "o.valid_time"); - } - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select floor((m0.time+450)%(24*3600)/900)/4 as hr_of_day, " + + "count(distinct ceil(900*floor((m0.time+450)/900))) as N_times, " + + "min(ceil(900*floor((m0.time+450)/900))) as min_secs, " + + "max(ceil(900*floor((m0.time+450)/900))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{thresholdClause}} " + + "{{forecastLengthClause}} " + + "{{truthClause}} " + + "group by hr_of_day " + + "order by hr_of_day" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{truthClause}}", truthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + if (variable.includes("Visibility")) { + statement = statement.replace(/o\.time/g, "o.valid_time"); + } + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -226,6 +241,7 @@ dataValidTime = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -240,7 +256,6 @@ dataValidTime = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -265,6 +280,7 @@ dataValidTime = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ceil-vis15/server/main.js b/apps/ceil-vis15/server/main.js index 213cf6484f..8244262664 100644 --- a/apps/ceil-vis15/server/main.js +++ b/apps/ceil-vis15/server/main.js @@ -4,7 +4,9 @@ import { Meteor } from "meteor/meteor"; import { mysql } from "meteor/pcel:mysql"; +import { moment } from "meteor/momentjs:moment"; import { + matsMethods, matsTypes, matsCollections, matsDataUtils, @@ -329,7 +331,7 @@ const doCurveParams = function () { const params = matsCollections.CurveParamsInfo.find({ curve_params: { $exists: true }, }).fetch()[0].curve_params; - for (let cp = 0; cp < params.length; cp++) { + for (let cp = 0; cp < params.length; cp += 1) { matsCollections[params[cp]].remove({}); } } @@ -342,103 +344,88 @@ const doCurveParams = function () { const forecastLengthOptionsMap = {}; const thresholdsModelOptionsMap = {}; const truthsModelOptionsMap = {}; - const masterRegionValuesMap = {}; - const masterThresholdValuesMap = {}; - const masterTruthValuesMap = {}; + const allRegionValuesMap = {}; + const allThresholdValuesMap = {}; + const allTruthValuesMap = {}; try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - metadataPool, + metadataPool, // eslint-disable-line no-undef "select short_name,description from region_descriptions;" ); - let masterRegDescription; - let masterShortName; - for (var j = 0; j < rows.length; j++) { - masterRegDescription = rows[j].description.trim(); - masterShortName = rows[j].short_name.trim(); - masterRegionValuesMap[masterShortName] = masterRegDescription; + for (let j = 0; j < rows.length; j += 1) { + allRegionValuesMap[rows[j].short_name.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } - let rows; - let didx; - try { - for (didx = 0; didx < variables.length; didx++) { - masterThresholdValuesMap[variables[didx]] = {}; - rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + for (let didx = 0; didx < variables.length; didx += 1) { + allThresholdValuesMap[variables[didx]] = {}; + const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( + sumPool, // eslint-disable-line no-undef `select trsh,description from ${ variableDBNames[variables[didx]].modelDB }.threshold_descriptions;` ); - var masterDescription; - var masterTrsh; - for (var j = 0; j < rows.length; j++) { - masterDescription = rows[j].description.trim(); - masterTrsh = rows[j].trsh.trim(); - masterThresholdValuesMap[variables[didx]][masterTrsh] = masterDescription; + for (let j = 0; j < rows.length; j += 1) { + allThresholdValuesMap[variables[didx]][rows[j].trsh.trim()] = + rows[j].description.trim(); } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { - for (didx = 0; didx < variables.length; didx++) { - masterTruthValuesMap[variables[didx]] = {}; + for (let didx = 0; didx < variables.length; didx += 1) { + allTruthValuesMap[variables[didx]] = {}; if (variables[didx] === "15 Minute Visibility") { - rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( + sumPool, // eslint-disable-line no-undef `select truth,description from ${ variableDBNames[variables[didx]].modelDB }.truth_descriptions;` ); - var masterDescription; - var masterTruth; - for (var j = 0; j < rows.length; j++) { - masterDescription = rows[j].description.trim(); - masterTruth = rows[j].truth.trim(); - masterTruthValuesMap[variables[didx]][masterTruth] = masterDescription; + for (let j = 0; j < rows.length; j += 1) { + allTruthValuesMap[variables[didx]][rows[j].truth.trim()] = + rows[j].description.trim(); } } else { - masterTruthValuesMap[variables[didx]].default = "Default truth data source"; + allTruthValuesMap[variables[didx]].default = "Default truth data source"; } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { - for (didx = 0; didx < variables.length; didx++) { - modelOptionsMap[variables[didx]] = {}; - modelDateRangeMap[variables[didx]] = {}; - forecastLengthOptionsMap[variables[didx]] = {}; - thresholdsModelOptionsMap[variables[didx]] = {}; - truthsModelOptionsMap[variables[didx]] = {}; - regionModelOptionsMap[variables[didx]] = {}; - - if (variables[didx] === "15 Minute Visibility") { + for (let didx = 0; didx < variables.length; didx += 1) { + const variable = variables[didx]; + modelOptionsMap[variable] = {}; + modelDateRangeMap[variable] = {}; + forecastLengthOptionsMap[variable] = {}; + thresholdsModelOptionsMap[variable] = {}; + truthsModelOptionsMap[variable] = {}; + regionModelOptionsMap[variable] = {}; + + let rows; + if (variable === "15 Minute Visibility") { rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, - `select model,regions,display_text,fcst_lens,trsh,truth,mindate,maxdate from ${ - variableDBNames[variables[didx]].sumsDB - }.regions_per_model_mats_all_categories order by display_category, display_order;` + sumPool, // eslint-disable-line no-undef + `select model,regions,display_text,fcst_lens,trsh,truth,mindate,maxdate from ${variableDBNames[variable].sumsDB}.regions_per_model_mats_all_categories order by display_category, display_order;` ); } else { rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, - `select model,regions,display_text,fcst_lens,trsh,mindate,maxdate from ${ - variableDBNames[variables[didx]].sumsDB - }.regions_per_model_mats_all_categories order by display_category, display_order;` + sumPool, // eslint-disable-line no-undef + `select model,regions,display_text,fcst_lens,trsh,mindate,maxdate from ${variableDBNames[variable].sumsDB}.regions_per_model_mats_all_categories order by display_category, display_order;` ); } - for (var i = 0; i < rows.length; i++) { - const model_value = rows[i].model.trim(); + for (let i = 0; i < rows.length; i += 1) { + const modelValue = rows[i].model.trim(); const model = rows[i].display_text.trim(); - modelOptionsMap[variables[didx]][model] = [model_value]; + modelOptionsMap[variable][model] = [modelValue]; const rowMinDate = moment .utc(rows[i].mindate * 1000) @@ -446,104 +433,96 @@ const doCurveParams = function () { const rowMaxDate = moment .utc(rows[i].maxdate * 1000) .format("MM/DD/YYYY HH:mm"); - modelDateRangeMap[variables[didx]][model] = { + modelDateRangeMap[variable][model] = { minDate: rowMinDate, maxDate: rowMaxDate, }; - const truthsArr = []; if (variables[didx] === "15 Minute Visibility") { const truths = rows[i].truth; - const truthsArrRaw = truths + truthsModelOptionsMap[variables[didx]][model] = truths .split(",") - .map(Function.prototype.call, String.prototype.trim); - var dummyTruth; - for (var j = 0; j < truthsArrRaw.length; j++) { - dummyTruth = truthsArrRaw[j].replace(/'|\[|\]/g, ""); - truthsArr.push(masterTruthValuesMap[variables[didx]][dummyTruth]); - } + .map(Function.prototype.call, String.prototype.trim) + .map(function (truth) { + return allTruthValuesMap[variable][truth.replace(/'|\[|\]/g, "")]; + }); } else { - truthsArr.push(masterTruthValuesMap[variables[didx]].default); + truthsModelOptionsMap[variables[didx]][model] = [ + allTruthValuesMap[variable].default, + ]; } - truthsModelOptionsMap[variables[didx]][model] = truthsArr; const forecastLengths = rows[i].fcst_lens; - const forecastLengthArr = forecastLengths + forecastLengthOptionsMap[variable][model] = forecastLengths .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < forecastLengthArr.length; j++) { - forecastLengthArr[j] = ( - Number(forecastLengthArr[j].replace(/'|\[|\]/g, "")) / 60 - ).toString(); - } - forecastLengthOptionsMap[variables[didx]][model] = forecastLengthArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (fhr) { + return Number(fhr.replace(/'|\[|\]/g, "")) / 60; + }); const thresholds = rows[i].trsh; - const thresholdsArrRaw = thresholds + thresholdsModelOptionsMap[variable][model] = thresholds .split(",") - .map(Function.prototype.call, String.prototype.trim); - const thresholdsArr = []; - var dummyThresh; - for (var j = 0; j < thresholdsArrRaw.length; j++) { - dummyThresh = thresholdsArrRaw[j].replace(/'|\[|\]/g, ""); - thresholdsArr.push(masterThresholdValuesMap[variables[didx]][dummyThresh]); - } - thresholdsModelOptionsMap[variables[didx]][model] = thresholdsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (threshold) { + return allThresholdValuesMap[variable][threshold.replace(/'|\[|\]/g, "")]; + }); const { regions } = rows[i]; - const regionsArrRaw = regions + regionModelOptionsMap[variable][model] = regions .split(",") - .map(Function.prototype.call, String.prototype.trim); - const regionsArr = []; - var dummyRegion; - for (var j = 0; j < regionsArrRaw.length; j++) { - dummyRegion = regionsArrRaw[j].replace(/'|\[|\]/g, ""); - regionsArr.push(masterRegionValuesMap[dummyRegion]); - } - regionModelOptionsMap[variables[didx]][model] = regionsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (region) { + return allRegionValuesMap[region.replace(/'|\[|\]/g, "")]; + }); } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { matsCollections.SiteMap.remove({}); const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select madis_id,name,lat,lon,elev,description from madis3.metars_mats_global where lat > -16380 and lat < 16380 and lon > -32760 and lon < 32760 order by name;" ); - for (var i = 0; i < rows.length; i++) { - const site_name = rows[i].name; - const site_description = rows[i].description; - const site_id = rows[i].madis_id; - const site_lat = rows[i].lat / 182; - const site_lon = rows[i].lon / 182; - const site_elev = rows[i].elev; - siteOptionsMap[site_name] = [site_id]; - - const point = [site_lat, site_lon]; - const obj = { - name: site_name, - origName: site_name, - point, - elevation: site_elev, - options: { - title: site_description, - color: "red", - size: 5, - network: "METAR", - peerOption: site_name, - id: site_id, - highLightColor: "blue", - }, - }; - sitesLocationMap.push(obj); - - matsCollections.SiteMap.insert({ siteName: site_name, siteId: site_id }); + for (let i = 0; i < rows.length; i += 1) { + const siteName = rows[i].name === undefined ? "unknown" : rows[i].name; + const siteDescription = + rows[i].description === undefined ? "unknown" : rows[i].description; + const siteId = rows[i].madis_id; + const siteLat = rows[i].lat === undefined ? -90 : rows[i].lat / 182; + const siteLon = rows[i].lon === undefined ? 0 : rows[i].lon / 182; + const siteElev = rows[i].elev === undefined ? 0 : rows[i].elev; + + // There's one station right at the south pole that the map doesn't know how to render at all, so exclude it. + // Also exclude stations with missing data + if (siteLat < 90 && siteLat > -90) { + siteOptionsMap[siteName] = [siteId]; + + const point = [siteLat, siteLon]; + const obj = { + name: siteName, + origName: siteName, + point, + elevation: siteElev, + options: { + title: siteDescription, + color: "red", + size: 5, + network: "METAR", + peerOption: siteName, + id: siteId, + highLightColor: "blue", + }, + }; + sitesLocationMap.push(obj); + matsCollections.SiteMap.insert({ siteName, siteId }); + } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } matsCollections.StationMap.remove({}); @@ -590,7 +569,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.variable.findOne({ name: "variable" }); + const currentParam = matsCollections.variable.findOne({ name: "variable" }); if (!matsDataUtils.areObjectsEqual(currentParam.dates, modelDateRangeMap)) { // have to reload variable data matsCollections.variable.update( @@ -648,7 +627,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["data-source"].findOne({ name: "data-source" }); + const currentParam = matsCollections["data-source"].findOne({ + name: "data-source", + }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, modelOptionsMap)) { // have to reload model data matsCollections["data-source"].update( @@ -673,7 +654,7 @@ const doCurveParams = function () { regionModelOptionsMap[variables[0]][ Object.keys(regionModelOptionsMap[variables[0]])[0] ], - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -688,10 +669,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.region.findOne({ name: "region" }); + const currentParam = matsCollections.region.findOne({ name: "region" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, regionModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterRegionValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allRegionValuesMap) ) { // have to reload region data matsCollections.region.update( @@ -699,7 +680,7 @@ const doCurveParams = function () { { $set: { optionsMap: regionModelOptionsMap, - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, options: regionModelOptionsMap[variables[0]][ Object.keys(regionModelOptionsMap[variables[0]])[0] @@ -776,7 +757,7 @@ const doCurveParams = function () { thresholdsModelOptionsMap[variables[0]][ Object.keys(thresholdsModelOptionsMap[variables[0]])[0] ], - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -791,13 +772,13 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.threshold.findOne({ name: "threshold" }); + const currentParam = matsCollections.threshold.findOne({ name: "threshold" }); if ( !matsDataUtils.areObjectsEqual( currentParam.optionsMap, thresholdsModelOptionsMap ) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterThresholdValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allThresholdValuesMap) ) { // have to reload threshold data matsCollections.threshold.update( @@ -805,7 +786,7 @@ const doCurveParams = function () { { $set: { optionsMap: thresholdsModelOptionsMap, - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, options: thresholdsModelOptionsMap[variables[0]][ Object.keys(thresholdsModelOptionsMap[variables[0]])[0] @@ -829,7 +810,7 @@ const doCurveParams = function () { truthsModelOptionsMap[variables[0]][ Object.keys(truthsModelOptionsMap[variables[0]])[0] ], - valuesMap: masterTruthValuesMap, + valuesMap: allTruthValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -844,10 +825,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.truth.findOne({ name: "truth" }); + const currentParam = matsCollections.truth.findOne({ name: "truth" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, truthsModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterTruthValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allTruthValuesMap) ) { // have to reload truth data matsCollections.truth.update( @@ -855,7 +836,7 @@ const doCurveParams = function () { { $set: { optionsMap: truthsModelOptionsMap, - valuesMap: masterTruthValuesMap, + valuesMap: allTruthValuesMap, options: truthsModelOptionsMap[variables[0]][ Object.keys(truthsModelOptionsMap[variables[0]])[0] @@ -895,7 +876,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["forecast-length"].findOne({ + const currentParam = matsCollections["forecast-length"].findOne({ name: "forecast-length", }); if ( @@ -1147,18 +1128,14 @@ const doCurveParams = function () { } // determine date defaults for dates and curveDates - const defaultDb = matsCollections.variable.findOne( - { name: "variable" }, + const defaultDataSource = matsCollections["data-source"].findOne( + { name: "data-source" }, { default: 1 } ).default; modelDateRangeMap = matsCollections.variable.findOne( { name: "variable" }, { dates: 1 } ).dates; - const defaultDataSource = matsCollections["data-source"].findOne( - { name: "data-source" }, - { default: 1 } - ).default; minDate = modelDateRangeMap[variables[0]][defaultDataSource].minDate; maxDate = modelDateRangeMap[variables[0]][defaultDataSource].maxDate; @@ -1199,7 +1176,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["curve-dates"].findOne({ name: "curve-dates" }); + const currentParam = matsCollections["curve-dates"].findOne({ + name: "curve-dates", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.startDate, minDate) || !matsDataUtils.areObjectsEqual(currentParam.stopDate, maxDate) || @@ -1612,7 +1591,8 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { - console.log( + // eslint-disable-next-line no-console + console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); matsCollections.Databases.drop(); @@ -1629,7 +1609,7 @@ Meteor.startup(function () { databases = Meteor.settings.private.databases; } if (databases !== null && databases !== undefined && Array.isArray(databases)) { - for (let di = 0; di < databases.length; di++) { + for (let di = 0; di < databases.length; di += 1) { matsCollections.Databases.insert(databases[di]); } } @@ -1656,6 +1636,7 @@ Meteor.startup(function () { ); if (cbConnection) { // global cbScorecardSettingsPool + // eslint-disable-next-line no-undef cbScorecardSettingsPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1682,6 +1663,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (metadataSettings) { + // eslint-disable-next-line no-undef metadataPool = mysql.createPool(metadataSettings); allPools.push({ pool: "metadataPool", role: matsTypes.DatabaseRoles.META_DATA }); } @@ -1702,6 +1684,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (sumSettings) { + // eslint-disable-next-line no-undef sumPool = mysql.createPool(sumSettings); allPools.push({ pool: "sumPool", role: matsTypes.DatabaseRoles.SUMS_DATA }); } @@ -1710,7 +1693,7 @@ Meteor.startup(function () { const mdr = new matsTypes.MetaDataDBRecord("metadataPool", "mats_common", [ "region_descriptions", ]); - for (let didx = 0; didx < variables.length; didx++) { + for (let didx = 0; didx < variables.length; didx += 1) { mdr.addRecord("sumPool", variableDBNames[variables[didx]].modelDB, [ "threshold_descriptions", ]); @@ -1725,7 +1708,7 @@ Meteor.startup(function () { appType: matsTypes.AppTypes.mats, }); } catch (error) { - console.log(error.message); + throw new Error(error.message); } }); @@ -1733,6 +1716,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doPlotGraph, doCurveParams, diff --git a/apps/ensemble/.eslintrc.json b/apps/ensemble/.eslintrc.json index a33ac158ff..79d49c5bb6 100644 --- a/apps/ensemble/.eslintrc.json +++ b/apps/ensemble/.eslintrc.json @@ -28,13 +28,6 @@ "space-before-function-paren": "off", // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", - "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-unused-vars": "warn" + "prefer-arrow-callback": "off" } } diff --git a/apps/ensemble/client/main.js b/apps/ensemble/client/main.js index a87407a1f4..ecd922b6a2 100644 --- a/apps/ensemble/client/main.js +++ b/apps/ensemble/client/main.js @@ -2,6 +2,7 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; import "@fortawesome/fontawesome-free"; import "@fortawesome/fontawesome-free/css/all.css"; diff --git a/apps/ensemble/server/dataFunctions/data_contour.js b/apps/ensemble/server/dataFunctions/data_contour.js index 06573192c3..9b85835a27 100644 --- a/apps/ensemble/server/dataFunctions/data_contour.js +++ b/apps/ensemble/server/dataFunctions/data_contour.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContour = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,90 +23,91 @@ dataContour = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; - const totalProcessingStart = moment(); + + const curves = JSON.parse(JSON.stringify(plotParams.curves)); + if (curves.length > 1) { + throw new Error("INFO: There must only be one added curve."); + } + + const axisMap = Object.create(null); + + let statement = ""; + let error = ""; + const dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - const curves = JSON.parse(JSON.stringify(plotParams.curves)); - if (curves.length > 1) { - throw new Error("INFO: There must only be one added curve."); - } - const dataset = []; - const axisMap = Object.create(null); - // initialize variables specific to the curve + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }).optionsMap[ variable ]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - const regionStr = curve.region; - let region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap[appParams.plotType]; - const tableStatPrefix = statisticOptionsMap[statisticSelect][2]; - const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; - const { members } = curve; - const memberClause = `and m0.mem = ${members}`; - const neighborhoodSize = curve["neighborhood-size"]; - const neighborhoodClause = `and m0.nhd_size = ${neighborhoodSize}`; - let kernelClause = ""; - let probBinClause = ""; - let radiusClause = ""; - if (tableStatPrefix === "count") { - const { kernel } = curve; - kernelClause = `and m0.kernel = ${kernel}`; - const probBins = - curve["probability-bins"] === undefined ? [] : curve["probability-bins"]; - if (probBins.length !== 0 && probBins !== matsTypes.InputTypes.unused) { - probBinClause = `and m0.prob IN(${probBins})`; - } else { - throw new Error("INFO: You need to select at least one probability bin."); - } - } else { - const { radius } = curve; - radiusClause = `and m0.radius = ${radius}`; - } + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { const { threshold } = curve; + if (threshold === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } thresholdClause = `and m0.trsh = ${threshold}`; } + + const { members } = curve; + const memberClause = `and m0.mem = ${members}`; + + const neighborhoodSize = curve["neighborhood-size"]; + const neighborhoodClause = `and m0.nhd_size = ${neighborhoodSize}`; + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.time%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap[appParams.plotType]; + const [statisticClause] = statisticOptionsMap[statisticSelect]; + const tableStatPrefix = statisticOptionsMap[statisticSelect][2]; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -116,93 +118,130 @@ dataContour = function (plotParams, plotFunction) { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const [statisticClause] = statisticOptionsMap[statisticSelect]; + + let kernelClause = ""; + let probBinClause = ""; + let radiusClause = ""; + if (tableStatPrefix === "count") { + const { kernel } = curve; + kernelClause = `and m0.kernel = ${kernel}`; + const probBins = + curve["probability-bins"] === undefined ? [] : curve["probability-bins"]; + if (probBins.length !== 0 && probBins !== matsTypes.InputTypes.unused) { + probBinClause = `and m0.prob IN(${probBins})`; + } else { + throw new Error("INFO: You need to select at least one probability bin."); + } + } else { + const { radius } = curve; + radiusClause = `and m0.radius = ${radius}`; + } + + const regionStr = curve.region; + let region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do + + const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + // For contours, this functions as the colorbar label. const statType = statisticOptionsMap[statisticSelect][0]; [, , , curve.unitKey] = statisticOptionsMap[statisticSelect]; let d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{memberClause}} " + - "{{neighborhoodClause}} " + - "{{thresholdClause}} " + - "{{kernelClause}} " + - "{{probBinClause}} " + - "{{radiusClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; - - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{memberClause}}", memberClause); - statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{kernelClause}}", kernelClause); - statement = statement.replace("{{probBinClause}}", probBinClause); - statement = statement.replace("{{radiusClause}}", radiusClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{memberClause}} " + + "{{neighborhoodClause}} " + + "{{thresholdClause}} " + + "{{kernelClause}} " + + "{{probBinClause}} " + + "{{radiusClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; + + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{memberClause}}", memberClause); + statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{kernelClause}}", kernelClause); + statement = statement.replace("{{probBinClause}}", probBinClause); + statement = statement.replace("{{radiusClause}}", radiusClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); + } + + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { + // this is NOT an error just a no data condition + dataFoundForCurve = false; + } else { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } } - } - if (!dataFoundForCurve) { - // we found no data for any curves so don't bother proceeding - throw new Error("INFO: No valid data for any curves."); + if (!dataFoundForCurve) { + // we found no data for any curves so don't bother proceeding + throw new Error("INFO: No valid data for any curves."); + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); } // set curve annotation to be the curve mean -- may be recalculated later diff --git a/apps/ensemble/server/dataFunctions/data_contour_diff.js b/apps/ensemble/server/dataFunctions/data_contour_diff.js index 1e7a2a04f0..40b0dc0b33 100644 --- a/apps/ensemble/server/dataFunctions/data_contour_diff.js +++ b/apps/ensemble/server/dataFunctions/data_contour_diff.js @@ -14,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContourDiff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -24,95 +25,96 @@ dataContourDiff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; let dataNotFoundForAnyCurve = false; + + let curves = JSON.parse(JSON.stringify(plotParams.curves)); + const curvesLength = curves.length; + if (curvesLength !== 2) { + throw new Error("INFO: There must be two added curves."); + } + + const axisMap = Object.create(null); const showSignificance = plotParams.significance !== "none"; - const totalProcessingStart = moment(); + + let statType; + let statisticSelect; + + let statement = ""; + let error = ""; + let dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - let curves = JSON.parse(JSON.stringify(plotParams.curves)); - const curvesLength = curves.length; - if (curvesLength !== 2) { - throw new Error("INFO: There must be two added curves."); - } - let statType; - let statisticSelect; - let dataset = []; - const axisMap = Object.create(null); for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - const regionStr = curve.region; - let region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do - statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap[appParams.plotType]; - const tableStatPrefix = statisticOptionsMap[statisticSelect][2]; - const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; - const { members } = curve; - const memberClause = `and m0.mem = ${members}`; - const neighborhoodSize = curve["neighborhood-size"]; - const neighborhoodClause = `and m0.nhd_size = ${neighborhoodSize}`; - let kernelClause = ""; - let probBinClause = ""; - let radiusClause = ""; - if (tableStatPrefix === "count") { - const { kernel } = curve; - kernelClause = `and m0.kernel = ${kernel}`; - const probBins = - curve["probability-bins"] === undefined ? [] : curve["probability-bins"]; - if (probBins.length !== 0 && probBins !== matsTypes.InputTypes.unused) { - probBinClause = `and m0.prob IN(${probBins})`; - } else { - throw new Error("INFO: You need to select at least one probability bin."); - } - } else { - const { radius } = curve; - radiusClause = `and m0.radius = ${radius}`; - } + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { const { threshold } = curve; + if (threshold === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } thresholdClause = `and m0.trsh = ${threshold}`; } + + const { members } = curve; + const memberClause = `and m0.mem = ${members}`; + + const neighborhoodSize = curve["neighborhood-size"]; + const neighborhoodClause = `and m0.nhd_size = ${neighborhoodSize}`; + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.time%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap[appParams.plotType]; + const [statisticClause] = statisticOptionsMap[statisticSelect]; + const tableStatPrefix = statisticOptionsMap[statisticSelect][2]; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -123,89 +125,123 @@ dataContourDiff = function (plotParams, plotFunction) { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const [statisticClause] = statisticOptionsMap[statisticSelect]; + + let kernelClause = ""; + let probBinClause = ""; + let radiusClause = ""; + if (tableStatPrefix === "count") { + const { kernel } = curve; + kernelClause = `and m0.kernel = ${kernel}`; + const probBins = + curve["probability-bins"] === undefined ? [] : curve["probability-bins"]; + if (probBins.length !== 0 && probBins !== matsTypes.InputTypes.unused) { + probBinClause = `and m0.prob IN(${probBins})`; + } else { + throw new Error("INFO: You need to select at least one probability bin."); + } + } else { + const { radius } = curve; + radiusClause = `and m0.radius = ${radius}`; + } + + const regionStr = curve.region; + let region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do + + const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + // For contours, this functions as the colorbar label. [statType] = statisticOptionsMap[statisticSelect]; [, , , curve.unitKey] = statisticOptionsMap[statisticSelect]; let d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{memberClause}} " + - "{{neighborhoodClause}} " + - "{{thresholdClause}} " + - "{{kernelClause}} " + - "{{probBinClause}} " + - "{{radiusClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{memberClause}} " + + "{{neighborhoodClause}} " + + "{{thresholdClause}} " + + "{{kernelClause}} " + + "{{probBinClause}} " + + "{{radiusClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{memberClause}}", memberClause); - statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{kernelClause}}", kernelClause); - statement = statement.replace("{{probBinClause}}", probBinClause); - statement = statement.replace("{{radiusClause}}", radiusClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{memberClause}}", memberClause); + statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{kernelClause}}", kernelClause); + statement = statement.replace("{{probBinClause}}", probBinClause); + statement = statement.replace("{{radiusClause}}", radiusClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); + } + + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + dataNotFoundForAnyCurve = true; } - dataNotFoundForAnyCurve = true; + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); } // set curve annotation to be the curve mean -- may be recalculated later @@ -259,13 +295,9 @@ dataContourDiff = function (plotParams, plotFunction) { statType === "ctc", statType === "scalar" ); - - // make a copy of the plotParams that we can modify - const diffPlotParams = JSON.parse(JSON.stringify(plotParams)); - diffPlotParams.curves = matsDataUtils.getDiffContourCurveParams( - diffPlotParams.curves - ); - curves = diffPlotParams.curves; + const newPlotParams = plotParams; + newPlotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); + curves = newPlotParams.curves; dataset[0].name = matsPlotUtils.getCurveText( matsTypes.PlotTypes.contourDiff, curves[0] @@ -281,7 +313,7 @@ dataContourDiff = function (plotParams, plotFunction) { const result = matsDataProcessUtils.processDataContour( dataset, curveInfoParams, - diffPlotParams, + newPlotParams, bookkeepingParams ); plotFunction(result); diff --git a/apps/ensemble/server/dataFunctions/data_dieoff.js b/apps/ensemble/server/dataFunctions/data_dieoff.js index 96d6301276..fe415261b3 100644 --- a/apps/ensemble/server/dataFunctions/data_dieoff.js +++ b/apps/ensemble/server/dataFunctions/data_dieoff.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDieoff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,54 +24,77 @@ dataDieoff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - let statType; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; + let statement = ""; + let error = ""; + const dataset = []; + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - const regionStr = curve.region; - let region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap[appParams.plotType]; - const tableStatPrefix = statisticOptionsMap[statisticSelect][2]; - const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + const { threshold } = curve; const thresholdClause = `and m0.trsh = ${threshold}`; + const { members } = curve; const memberClause = `and m0.mem = ${members}`; + const neighborhoodSize = curve["neighborhood-size"]; const neighborhoodClause = `and m0.nhd_size = ${neighborhoodSize}`; + + let validTimeClause = ""; + let validTimes; + + let utcCycleStartClause = ""; + let utcCycleStart; + + const forecastLengthClause = ""; + const forecastLengthStr = curve["dieoff-type"]; + const forecastLengthOptionsMap = matsCollections["dieoff-type"].findOne( + { name: "dieoff-type" }, + { optionsMap: 1 } + ).optionsMap; + const forecastLength = forecastLengthOptionsMap[forecastLengthStr][0]; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap[appParams.plotType]; + const [statisticClause] = statisticOptionsMap[statisticSelect]; + const tableStatPrefix = statisticOptionsMap[statisticSelect][2]; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + let dateClause; + let kernelClause = ""; let probBinClause = ""; let radiusClause = ""; @@ -88,21 +112,18 @@ dataDieoff = function (plotParams, plotFunction) { const { radius } = curve; radiusClause = `and m0.radius = ${radius}`; } - let validTimes; - let validTimeClause = ""; - let utcCycleStart; - let utcCycleStartClause = ""; - const forecastLengthStr = curve["dieoff-type"]; - const forecastLengthOptionsMap = matsCollections["dieoff-type"].findOne( - { name: "dieoff-type" }, - { optionsMap: 1 } - ).optionsMap; - const forecastLength = forecastLengthOptionsMap[forecastLengthStr][0]; - const forecastLengthClause = ""; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let dateClause; + + const regionStr = curve.region; + let region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do + + const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + if (forecastLength === matsTypes.ForecastTypes.dieoff) { validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { @@ -119,7 +140,7 @@ dataDieoff = function (plotParams, plotFunction) { } else { dateClause = `and m0.time-m0.fcst_len*3600 = ${fromSecs}`; } - const [statisticClause] = statisticOptionsMap[statisticSelect]; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. @@ -134,56 +155,56 @@ dataDieoff = function (plotParams, plotFunction) { let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.fcst_len as fcst_lead, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{memberClause}} " + - "{{neighborhoodClause}} " + - "{{thresholdClause}} " + - "{{kernelClause}} " + - "{{probBinClause}} " + - "{{radiusClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{utcCycleStartClause}} " + - "group by fcst_lead " + - "order by fcst_lead" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{memberClause}}", memberClause); - statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{kernelClause}}", kernelClause); - statement = statement.replace("{{probBinClause}}", probBinClause); - statement = statement.replace("{{radiusClause}}", radiusClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - let queryResult; const startMoment = moment(); let finishMoment; try { + statement = + "select m0.fcst_len as fcst_lead, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{memberClause}} " + + "{{neighborhoodClause}} " + + "{{thresholdClause}} " + + "{{kernelClause}} " + + "{{probBinClause}} " + + "{{radiusClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{utcCycleStartClause}} " + + "group by fcst_lead " + + "order by fcst_lead" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{memberClause}}", memberClause); + statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{kernelClause}}", kernelClause); + statement = statement.replace("{{probBinClause}}", probBinClause); + statement = statement.replace("{{radiusClause}}", radiusClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -199,6 +220,7 @@ dataDieoff = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/ensemble/server/dataFunctions/data_gridscale_prob.js b/apps/ensemble/server/dataFunctions/data_gridscale_prob.js index 6ddfd0c9e9..0b75edc4a2 100644 --- a/apps/ensemble/server/dataFunctions/data_gridscale_prob.js +++ b/apps/ensemble/server/dataFunctions/data_gridscale_prob.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataGridScaleProb = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,66 +24,84 @@ dataGridScaleProb = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - let statType; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; + let statement = ""; + let error = ""; + const dataset = []; + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - const regionStr = curve.region; - let region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do - const statisticSelect = "Grid Scale Count"; - const tableStatPrefix = "count"; - const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + const { threshold } = curve; const thresholdClause = `and m0.trsh = ${threshold}`; + const { members } = curve; const memberClause = `and m0.mem = ${members}`; + const neighborhoodSize = curve["neighborhood-size"]; const neighborhoodClause = `and m0.nhd_size = ${neighborhoodSize}`; - let kernelClause = ""; - const { kernel } = curve; - kernelClause = `and m0.kernel = ${kernel}`; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; } + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; + + const statisticSelect = "Grid Scale Count"; + const statisticClause = + "sum(m0.nhdfcstcount) as stat, group_concat(m0.time, ';', m0.nhdfcstcount order by m0.time) as sub_data, count(m0.nhdfcstcount) as N0"; + const tableStatPrefix = "count"; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; - const statisticClause = - "sum(m0.nhdfcstcount) as stat, group_concat(m0.time, ';', m0.nhdfcstcount order by m0.time) as sub_data, count(m0.nhdfcstcount) as N0"; + + let kernelClause = ""; + const { kernel } = curve; + kernelClause = `and m0.kernel = ${kernel}`; + + const regionStr = curve.region; + let region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do + + const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. @@ -93,50 +112,50 @@ dataGridScaleProb = function (plotParams, plotFunction) { let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.prob as binValue, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{memberClause}} " + - "{{neighborhoodClause}} " + - "{{thresholdClause}} " + - "{{kernelClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by binValue " + - "order by binValue" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{memberClause}}", memberClause); - statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{kernelClause}}", kernelClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - let queryResult; const startMoment = moment(); let finishMoment; try { + statement = + "select m0.prob as binValue, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{memberClause}} " + + "{{neighborhoodClause}} " + + "{{thresholdClause}} " + + "{{kernelClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by binValue " + + "order by binValue" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{memberClause}}", memberClause); + statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{kernelClause}}", kernelClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -152,6 +171,7 @@ dataGridScaleProb = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -224,6 +244,7 @@ dataGridScaleProb = function (plotParams, plotFunction) { // we found no data for any curves so don't bother proceeding throw new Error("INFO: No valid data for any curves."); } + // process the data returned by the query const curveInfoParams = { curves, diff --git a/apps/ensemble/server/dataFunctions/data_histogram.js b/apps/ensemble/server/dataFunctions/data_histogram.js index f8bf5c73c7..ec871d0efc 100644 --- a/apps/ensemble/server/dataFunctions/data_histogram.js +++ b/apps/ensemble/server/dataFunctions/data_histogram.js @@ -11,6 +11,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataHistogram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -21,20 +22,25 @@ dataHistogram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const alreadyMatched = false; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries const dataFoundForCurve = []; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const alreadyMatched = false; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; + + const axisMap = Object.create(null); let statType; let varUnits; + + let statement = ""; + let error = ""; const dataset = []; const allReturnedSubStats = []; const allReturnedSubSecs = []; - const axisMap = Object.create(null); // process user bin customizations const binParams = matsDataUtils.setHistogramParameters(plotParams); @@ -44,35 +50,47 @@ dataHistogram = function (plotParams, plotFunction) { for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; dataFoundForCurve[curveIndex] = true; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - const regionStr = curve.region; - let region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap[appParams.plotType]; - const tableStatPrefix = statisticOptionsMap[statisticSelect][2]; - const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + const { threshold } = curve; const thresholdClause = `and m0.trsh = ${threshold}`; + const { members } = curve; const memberClause = `and m0.mem = ${members}`; + const neighborhoodSize = curve["neighborhood-size"]; const neighborhoodClause = `and m0.nhd_size = ${neighborhoodSize}`; + + let validTimeClause = ""; + const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { + validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; + } + + const forecastLength = curve["forecast-length"]; + const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap[appParams.plotType]; + const [statisticClause] = statisticOptionsMap[statisticSelect]; + const tableStatPrefix = statisticOptionsMap[statisticSelect][2]; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + let kernelClause = ""; let probBinClause = ""; let radiusClause = ""; @@ -90,18 +108,18 @@ dataHistogram = function (plotParams, plotFunction) { const { radius } = curve; radiusClause = `and m0.radius = ${radius}`; } - let validTimeClause = ""; - const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { - validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; - } - const forecastLength = curve["forecast-length"]; - const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; - const [statisticClause] = statisticOptionsMap[statisticSelect]; + + const regionStr = curve.region; + let region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do + + const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. @@ -117,54 +135,54 @@ dataHistogram = function (plotParams, plotFunction) { let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.time as avtime, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{memberClause}} " + - "{{neighborhoodClause}} " + - "{{thresholdClause}} " + - "{{kernelClause}} " + - "{{probBinClause}} " + - "{{radiusClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{memberClause}}", memberClause); - statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{kernelClause}}", kernelClause); - statement = statement.replace("{{probBinClause}}", probBinClause); - statement = statement.replace("{{radiusClause}}", radiusClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - let queryResult; const startMoment = moment(); let finishMoment; try { + statement = + "select m0.time as avtime, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{memberClause}} " + + "{{neighborhoodClause}} " + + "{{thresholdClause}} " + + "{{kernelClause}} " + + "{{probBinClause}} " + + "{{radiusClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{memberClause}}", memberClause); + statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{kernelClause}}", kernelClause); + statement = statement.replace("{{probBinClause}}", probBinClause); + statement = statement.replace("{{radiusClause}}", radiusClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -182,6 +200,7 @@ dataHistogram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/ensemble/server/dataFunctions/data_reliability.js b/apps/ensemble/server/dataFunctions/data_reliability.js index 1c788a265f..6fbb4e823c 100644 --- a/apps/ensemble/server/dataFunctions/data_reliability.js +++ b/apps/ensemble/server/dataFunctions/data_reliability.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataReliability = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,65 +23,84 @@ dataReliability = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - let statType; - const dataset = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + let statType; + + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - const regionStr = curve.region; - let region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do - const tableStatPrefix = "count"; - const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + const { threshold } = curve; const thresholdClause = `and m0.trsh = ${threshold}`; + const { members } = curve; const memberClause = `and m0.mem = ${members}`; + const neighborhoodSize = curve["neighborhood-size"]; const neighborhoodClause = `and m0.nhd_size = ${neighborhoodSize}`; - let kernelClause = ""; - const { kernel } = curve; - kernelClause = `and m0.kernel in (0, ${kernel})`; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; } + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + const statisticClause = "sum(m0.nhdfcstcount) as fcstcount, sum(m0.fcstcount) as rawfcstcount, sum(m0.nhdhitcount) " + "as hitcount, group_concat(m0.time, ';', m0.nhdfcstcount, ';', " + "m0.fcstcount, ';', m0.nhdhitcount order by m0.time) as sub_data, count(m0.nhdfcstcount) as N0"; + const tableStatPrefix = "count"; + + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + let kernelClause = ""; + const { kernel } = curve; + kernelClause = `and m0.kernel in (0, ${kernel})`; + + const regionStr = curve.region; + let region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do + + const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. @@ -91,50 +111,50 @@ dataReliability = function (plotParams, plotFunction) { let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.prob as binValue, m0.kernel, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{memberClause}} " + - "{{neighborhoodClause}} " + - "{{thresholdClause}} " + - "{{kernelClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by binValue, kernel " + - "order by binValue, kernel" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{memberClause}}", memberClause); - statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{kernelClause}}", kernelClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - let queryResult; const startMoment = moment(); let finishMoment; try { + statement = + "select m0.prob as binValue, m0.kernel, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{memberClause}} " + + "{{neighborhoodClause}} " + + "{{thresholdClause}} " + + "{{kernelClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by binValue, kernel " + + "order by binValue, kernel" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{memberClause}}", memberClause); + statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{kernelClause}}", kernelClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBReliability( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, kernel ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -150,6 +170,7 @@ dataReliability = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/ensemble/server/dataFunctions/data_series.js b/apps/ensemble/server/dataFunctions/data_series.js index b865047870..51c2759f85 100644 --- a/apps/ensemble/server/dataFunctions/data_series.js +++ b/apps/ensemble/server/dataFunctions/data_series.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSeries = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,57 +24,80 @@ dataSeries = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - let statType; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - const regionStr = curve.region; - let region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap[appParams.plotType]; - const tableStatPrefix = statisticOptionsMap[statisticSelect][2]; - const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + const { threshold } = curve; const thresholdClause = `and m0.trsh = ${threshold}`; + const { members } = curve; const memberClause = `and m0.mem = ${members}`; + const neighborhoodSize = curve["neighborhood-size"]; const neighborhoodClause = `and m0.nhd_size = ${neighborhoodSize}`; + + let validTimeClause = ""; + const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { + validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; + } + + let forecastLength = curve["forecast-length"]; + const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap[appParams.plotType]; + const [statisticClause] = statisticOptionsMap[statisticSelect]; + const tableStatPrefix = statisticOptionsMap[statisticSelect][2]; + + const averageStr = curve.average; + const averageOptionsMap = matsCollections.average.findOne( + { name: "average" }, + { optionsMap: 1 } + ).optionsMap; + const average = averageOptionsMap[averageStr][0]; + + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + let kernelClause = ""; let probBinClause = ""; let radiusClause = ""; @@ -91,21 +115,18 @@ dataSeries = function (plotParams, plotFunction) { const { radius } = curve; radiusClause = `and m0.radius = ${radius}`; } - let validTimeClause = ""; - const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { - validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; - } - let forecastLength = curve["forecast-length"]; - const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; - const averageStr = curve.average; - const averageOptionsMap = matsCollections.average.findOne( - { name: "average" }, - { optionsMap: 1 } - ).optionsMap; - const average = averageOptionsMap[averageStr][0]; - const [statisticClause] = statisticOptionsMap[statisticSelect]; + + const regionStr = curve.region; + let region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do + + const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. @@ -120,54 +141,53 @@ dataSeries = function (plotParams, plotFunction) { let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select {{average}} as avtime, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{memberClause}} " + - "{{neighborhoodClause}} " + - "{{thresholdClause}} " + - "{{kernelClause}} " + - "{{probBinClause}} " + - "{{radiusClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{average}}", average); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{memberClause}}", memberClause); - statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{kernelClause}}", kernelClause); - statement = statement.replace("{{probBinClause}}", probBinClause); - statement = statement.replace("{{radiusClause}}", radiusClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - // math is done on forecastLength later on -- set all analyses to 0 - if (forecastLength === "-99") { - forecastLength = "0"; - } - let queryResult; const startMoment = moment(); let finishMoment; try { + statement = + "select {{average}} as avtime, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{memberClause}} " + + "{{neighborhoodClause}} " + + "{{thresholdClause}} " + + "{{kernelClause}} " + + "{{probBinClause}} " + + "{{radiusClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{average}}", average); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{memberClause}}", memberClause); + statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{kernelClause}}", kernelClause); + statement = statement.replace("{{probBinClause}}", probBinClause); + statement = statement.replace("{{radiusClause}}", radiusClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + + // math is done on forecastLength later on -- set all analyses to 0 + if (forecastLength === "-99") { + forecastLength = "0"; + } + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBTimeSeries( - sumPool, + sumPool, // eslint-disable-line no-undef statement, model, forecastLength, @@ -179,7 +199,9 @@ dataSeries = function (plotParams, plotFunction) { appParams, false ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -195,6 +217,7 @@ dataSeries = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/ensemble/server/dataFunctions/data_threshold.js b/apps/ensemble/server/dataFunctions/data_threshold.js index e99c2c94fd..fb270ad6bb 100644 --- a/apps/ensemble/server/dataFunctions/data_threshold.js +++ b/apps/ensemble/server/dataFunctions/data_threshold.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataThreshold = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,52 +24,69 @@ dataThreshold = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - let statType; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; + let statement = ""; + let error = ""; + const dataset = []; + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - const regionStr = curve.region; - let region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do + + const { members } = curve; + const memberClause = `and m0.mem = ${members}`; + + const neighborhoodSize = curve["neighborhood-size"]; + const neighborhoodClause = `and m0.nhd_size = ${neighborhoodSize}`; + + let validTimeClause = ""; + const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { + validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; + } + + const forecastLength = curve["forecast-length"]; + const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap[appParams.plotType]; + const [statisticClause] = statisticOptionsMap[statisticSelect]; const tableStatPrefix = statisticOptionsMap[statisticSelect][2]; - const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; - const { members } = curve; - const memberClause = `and m0.mem = ${members}`; - const neighborhoodSize = curve["neighborhood-size"]; - const neighborhoodClause = `and m0.nhd_size = ${neighborhoodSize}`; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + let kernelClause = ""; let probBinClause = ""; let radiusClause = ""; @@ -86,18 +104,18 @@ dataThreshold = function (plotParams, plotFunction) { const { radius } = curve; radiusClause = `and m0.radius = ${radius}`; } - let validTimeClause = ""; - const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { - validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; - } - const forecastLength = curve["forecast-length"]; - const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; - const [statisticClause] = statisticOptionsMap[statisticSelect]; + + const regionStr = curve.region; + let region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do + + const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. @@ -112,52 +130,52 @@ dataThreshold = function (plotParams, plotFunction) { let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.trsh as thresh, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{memberClause}} " + - "{{neighborhoodClause}} " + - "{{kernelClause}} " + - "{{probBinClause}} " + - "{{radiusClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by thresh " + - "order by thresh" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{memberClause}}", memberClause); - statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); - statement = statement.replace("{{kernelClause}}", kernelClause); - statement = statement.replace("{{probBinClause}}", probBinClause); - statement = statement.replace("{{radiusClause}}", radiusClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - let queryResult; const startMoment = moment(); let finishMoment; try { + statement = + "select m0.trsh as thresh, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{memberClause}} " + + "{{neighborhoodClause}} " + + "{{kernelClause}} " + + "{{probBinClause}} " + + "{{radiusClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by thresh " + + "order by thresh" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{memberClause}}", memberClause); + statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); + statement = statement.replace("{{kernelClause}}", kernelClause); + statement = statement.replace("{{probBinClause}}", probBinClause); + statement = statement.replace("{{radiusClause}}", radiusClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -173,6 +191,7 @@ dataThreshold = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/ensemble/server/dataFunctions/data_validtime.js b/apps/ensemble/server/dataFunctions/data_validtime.js index 48f91a1eaf..a8ad24039b 100644 --- a/apps/ensemble/server/dataFunctions/data_validtime.js +++ b/apps/ensemble/server/dataFunctions/data_validtime.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataValidTime = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,54 +24,66 @@ dataValidTime = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - let statType; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; + let statement = ""; + let error = ""; + const dataset = []; + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - const regionStr = curve.region; - let region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap[appParams.plotType]; - const tableStatPrefix = statisticOptionsMap[statisticSelect][2]; - const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; + const { threshold } = curve; const thresholdClause = `and m0.trsh = ${threshold}`; + const { members } = curve; const memberClause = `and m0.mem = ${members}`; + const neighborhoodSize = curve["neighborhood-size"]; const neighborhoodClause = `and m0.nhd_size = ${neighborhoodSize}`; + + const forecastLength = curve["forecast-length"]; + const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap[appParams.plotType]; + const [statisticClause] = statisticOptionsMap[statisticSelect]; + const tableStatPrefix = statisticOptionsMap[statisticSelect][2]; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + let kernelClause = ""; let probBinClause = ""; let radiusClause = ""; @@ -88,13 +101,17 @@ dataValidTime = function (plotParams, plotFunction) { const { radius } = curve; radiusClause = `and m0.radius = ${radius}`; } - const forecastLength = curve["forecast-length"]; - const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; - const [statisticClause] = statisticOptionsMap[statisticSelect]; + + const regionStr = curve.region; + let region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + region = region === "Full" ? "Full_domain" : region; // this db doesn't handle the full domain the way the others do + + const queryTableClause = `from ${databaseRef}.${model}_${tableStatPrefix}_${region} as m0`; // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. @@ -109,52 +126,52 @@ dataValidTime = function (plotParams, plotFunction) { let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select floor(m0.time%(24*3600)/3600) as hr_of_day, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{memberClause}} " + - "{{neighborhoodClause}} " + - "{{thresholdClause}} " + - "{{kernelClause}} " + - "{{probBinClause}} " + - "{{radiusClause}} " + - "{{forecastLengthClause}} " + - "group by hr_of_day " + - "order by hr_of_day" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{memberClause}}", memberClause); - statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{kernelClause}}", kernelClause); - statement = statement.replace("{{probBinClause}}", probBinClause); - statement = statement.replace("{{radiusClause}}", radiusClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - let queryResult; const startMoment = moment(); let finishMoment; try { + statement = + "select floor(m0.time%(24*3600)/3600) as hr_of_day, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{memberClause}} " + + "{{neighborhoodClause}} " + + "{{thresholdClause}} " + + "{{kernelClause}} " + + "{{probBinClause}} " + + "{{radiusClause}} " + + "{{forecastLengthClause}} " + + "group by hr_of_day " + + "order by hr_of_day" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{memberClause}}", memberClause); + statement = statement.replace("{{neighborhoodClause}}", neighborhoodClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{kernelClause}}", kernelClause); + statement = statement.replace("{{probBinClause}}", probBinClause); + statement = statement.replace("{{radiusClause}}", radiusClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -170,6 +187,7 @@ dataValidTime = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/ensemble/server/main.js b/apps/ensemble/server/main.js index 1f2ffa9b8a..7d797642ba 100644 --- a/apps/ensemble/server/main.js +++ b/apps/ensemble/server/main.js @@ -5,6 +5,7 @@ import { Meteor } from "meteor/meteor"; import { mysql } from "meteor/pcel:mysql"; import { moment } from "meteor/momentjs:moment"; +import { _ } from "meteor/underscore"; import { matsMethods, matsTypes, @@ -328,42 +329,36 @@ const doCurveParams = function () { const thresholdsModelOptionsMap = {}; const kernelModelOptionsMap = {}; const radiusModelOptionsMap = {}; - const masterRegionValuesMap = {}; + const allRegionValuesMap = {}; let allKernels = []; try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - metadataPool, + metadataPool, // eslint-disable-line no-undef "select short_name,description from region_descriptions;" ); - let masterRegDescription; - let masterShortName; for (let j = 0; j < rows.length; j += 1) { - masterRegDescription = rows[j].description.trim(); - masterShortName = rows[j].short_name.trim(); - masterRegionValuesMap[masterShortName] = masterRegDescription; + allRegionValuesMap[rows[j].short_name.trim()] = rows[j].description.trim(); } } catch (err) { throw new Error(err.message); } - let rows; - let didx; - try { - for (didx = 0; didx < variables.length; didx += 1) { - modelOptionsMap[variables[didx]] = {}; - modelDateRangeMap[variables[didx]] = {}; - forecastLengthOptionsMap[variables[didx]] = {}; - memberModelOptionsMap[variables[didx]] = {}; - neighborhoodModelOptionsMap[variables[didx]] = {}; - thresholdsModelOptionsMap[variables[didx]] = {}; - kernelModelOptionsMap[variables[didx]] = {}; - radiusModelOptionsMap[variables[didx]] = {}; - regionModelOptionsMap[variables[didx]] = {}; + for (let didx = 0; didx < variables.length; didx += 1) { + const variable = variables[didx]; + modelOptionsMap[variable] = {}; + modelDateRangeMap[variable] = {}; + forecastLengthOptionsMap[variable] = {}; + memberModelOptionsMap[variable] = {}; + neighborhoodModelOptionsMap[variable] = {}; + thresholdsModelOptionsMap[variable] = {}; + kernelModelOptionsMap[variable] = {}; + radiusModelOptionsMap[variable] = {}; + regionModelOptionsMap[variable] = {}; - rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( + sumPool, // eslint-disable-line no-undef `select model,regions,display_text,fcst_lens,mems,nhd_sizes,trshs,kernels,radii,mindate,maxdate from ${ variableDBNames[variables[didx]] }.regions_per_model_mats_all_categories order by display_category, display_order;` @@ -371,7 +366,7 @@ const doCurveParams = function () { for (let i = 0; i < rows.length; i += 1) { const modelValue = rows[i].model.trim(); const model = rows[i].display_text.trim(); - modelOptionsMap[variables[didx]][model] = [modelValue]; + modelOptionsMap[variable][model] = [modelValue]; const rowMinDate = moment .utc(rows[i].mindate * 1000) @@ -379,82 +374,73 @@ const doCurveParams = function () { const rowMaxDate = moment .utc(rows[i].maxdate * 1000) .format("MM/DD/YYYY HH:mm"); - modelDateRangeMap[variables[didx]][model] = { + modelDateRangeMap[variable][model] = { minDate: rowMinDate, maxDate: rowMaxDate, }; const forecastLengths = rows[i].fcst_lens; - const forecastLengthArr = forecastLengths + forecastLengthOptionsMap[variable][model] = forecastLengths .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (let j = 0; j < forecastLengthArr.length; j += 1) { - forecastLengthArr[j] = forecastLengthArr[j].replace(/'|\[|\]/g, ""); - } - forecastLengthOptionsMap[variables[didx]][model] = forecastLengthArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (fhr) { + return fhr.replace(/'|\[|\]/g, ""); + }); const { mems } = rows[i]; - const memArr = mems + memberModelOptionsMap[variable][model] = mems .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (let j = 0; j < memArr.length; j += 1) { - memArr[j] = memArr[j].replace(/'|\[|\]/g, ""); - } - memberModelOptionsMap[variables[didx]][model] = memArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (mem) { + return mem.replace(/'|\[|\]/g, ""); + }); const nhdSizes = rows[i].nhd_sizes; - const nhdSizeArr = nhdSizes + neighborhoodModelOptionsMap[variable][model] = nhdSizes .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (let j = 0; j < nhdSizeArr.length; j += 1) { - nhdSizeArr[j] = nhdSizeArr[j].replace(/'|\[|\]/g, ""); - } - neighborhoodModelOptionsMap[variables[didx]][model] = nhdSizeArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (nhdSize) { + return nhdSize.replace(/'|\[|\]/g, ""); + }); const thresholds = rows[i].trshs; - const thresholdsArr = thresholds + thresholdsModelOptionsMap[variable][model] = thresholds .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (let j = 0; j < thresholdsArr.length; j += 1) { - thresholdsArr[j] = thresholdsArr[j].replace(/'|\[|\]/g, ""); - } - thresholdsModelOptionsMap[variables[didx]][model] = thresholdsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (threshold) { + return threshold.replace(/'|\[|\]/g, ""); + }); const { kernels } = rows[i]; - const kernelArr = kernels + kernelModelOptionsMap[variable][model] = kernels .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (let j = 0; j < kernelArr.length; j += 1) { - kernelArr[j] = kernelArr[j].replace(/'|\[|\]/g, ""); - } - kernelModelOptionsMap[variables[didx]][model] = kernelArr; - allKernels = _.union(allKernels, kernelArr); + .map(Function.prototype.call, String.prototype.trim) + .map(function (kernel) { + return kernel.replace(/'|\[|\]/g, ""); + }); + allKernels = _.union(allKernels, kernelModelOptionsMap[variable][model]); const { radii } = rows[i]; - const radiusArr = radii + radiusModelOptionsMap[variable][model] = radii .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (let j = 0; j < radiusArr.length; j += 1) { - radiusArr[j] = radiusArr[j].replace(/'|\[|\]/g, ""); - } - radiusModelOptionsMap[variables[didx]][model] = radiusArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (radius) { + return radius.replace(/'|\[|\]/g, ""); + }); const { regions } = rows[i]; - const regionsArrRaw = regions + regionModelOptionsMap[variable][model] = regions .split(",") - .map(Function.prototype.call, String.prototype.trim); - const regionsArr = []; - let dummyRegion; - for (let j = 0; j < regionsArrRaw.length; j += 1) { - dummyRegion = regionsArrRaw[j].replace(/'|\[|\]/g, ""); - regionsArr.push(masterRegionValuesMap[dummyRegion]); - } - regionModelOptionsMap[variables[didx]][model] = regionsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (region) { + return allRegionValuesMap[region.replace(/'|\[|\]/g, "")]; + }); } } } catch (err) { throw new Error(err.message); } + if (matsCollections.label.findOne({ name: "label" }) === undefined) { matsCollections.label.insert({ name: "label", @@ -559,7 +545,7 @@ const doCurveParams = function () { regionModelOptionsMap[variables[0]][ Object.keys(regionModelOptionsMap[variables[0]])[0] ], - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -577,7 +563,7 @@ const doCurveParams = function () { const currentParam = matsCollections.region.findOne({ name: "region" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, regionModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterRegionValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allRegionValuesMap) ) { // have to reload region data matsCollections.region.update( @@ -585,7 +571,7 @@ const doCurveParams = function () { { $set: { optionsMap: regionModelOptionsMap, - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, options: regionModelOptionsMap[variables[0]][ Object.keys(regionModelOptionsMap[variables[0]])[0] @@ -1103,7 +1089,7 @@ const doCurveParams = function () { const probBinOptionsMap = { 0: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"], }; - for (didx = 0; didx < allKernels.length; didx += 1) { + for (let didx = 0; didx < allKernels.length; didx += 1) { probBinOptionsMap[allKernels[didx]] = [ "0", "10", @@ -1672,6 +1658,7 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { + // eslint-disable-next-line no-console console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); @@ -1716,6 +1703,7 @@ Meteor.startup(function () { ); if (cbConnection) { // global cbScorecardSettingsPool + // eslint-disable-next-line no-undef cbScorecardSettingsPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1742,6 +1730,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (metadataSettings) { + // eslint-disable-next-line no-undef metadataPool = mysql.createPool(metadataSettings); allPools.push({ pool: "metadataPool", role: matsTypes.DatabaseRoles.META_DATA }); } @@ -1762,6 +1751,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (sumSettings) { + // eslint-disable-next-line no-undef sumPool = mysql.createPool(sumSettings); allPools.push({ pool: "sumPool", role: matsTypes.DatabaseRoles.SUMS_DATA }); } @@ -1790,6 +1780,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doPlotGraph, doCurveParams, diff --git a/apps/landuse/.eslintrc.json b/apps/landuse/.eslintrc.json index b6823a8810..79d49c5bb6 100644 --- a/apps/landuse/.eslintrc.json +++ b/apps/landuse/.eslintrc.json @@ -28,22 +28,6 @@ "space-before-function-paren": "off", // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", - "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-plusplus": "warn", - "vars-on-top": "warn", - "no-var": "warn", - "block-scoped-var": "warn", - "no-loop-func": "warn", - "no-unused-vars": "warn", - "no-param-reassign": "warn", - "camelcase": "warn", - "no-redeclare": "warn", - "no-shadow": "warn" + "prefer-arrow-callback": "off" } } diff --git a/apps/landuse/client/main.js b/apps/landuse/client/main.js index a87407a1f4..ecd922b6a2 100644 --- a/apps/landuse/client/main.js +++ b/apps/landuse/client/main.js @@ -2,6 +2,7 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; import "@fortawesome/fontawesome-free"; import "@fortawesome/fontawesome-free/css/all.css"; diff --git a/apps/landuse/server/dataFunctions/data_contour.js b/apps/landuse/server/dataFunctions/data_contour.js index 78bf636a46..0cbc825076 100644 --- a/apps/landuse/server/dataFunctions/data_contour.js +++ b/apps/landuse/server/dataFunctions/data_contour.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContour = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,60 +23,77 @@ dataContour = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; - const totalProcessingStart = moment(); + + const curves = JSON.parse(JSON.stringify(plotParams.curves)); + if (curves.length > 1) { + throw new Error("INFO: There must only be one added curve."); + } + + const axisMap = Object.create(null); + + let statement = ""; + let error = ""; + const dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - const curves = JSON.parse(JSON.stringify(plotParams.curves)); - if (curves.length > 1) { - throw new Error("INFO: There must only be one added curve."); - } - const dataset = []; - const axisMap = Object.create(null); - // initialize variables specific to the curve + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - const vgtypStr = curve.vgtyp; - const vgtyp = Object.keys( - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap - ).find( - (key) => - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr - ); - const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; - const queryTableClause = `from ${model} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.hour IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + + `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -86,108 +104,118 @@ dataContour = function (plotParams, plotFunction) { dateString = "m0.valid_day+3600*m0.hour"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + - `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; - const statType = statisticOptionsMap[statisticSelect]; + + const vgtypStr = curve.vgtyp; + const vgtyp = Object.keys( + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap + ).find( + (key) => + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr + ); + const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; + + const queryTableClause = `from ${model} as m0`; + + // For contours, this functions as the colorbar label. const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); + const statType = statisticOptionsMap[statisticSelect]; const varUnits = statVarUnitMap[statisticSelect][variableStr]; - - // For contours, this functions as the colorbar label. curve.unitKey = varUnits; let d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{vgtypClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; - - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{vgtypClause}}", vgtypClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - `${statisticSelect}_${variableStr}` - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - if (error.includes("Unknown column")) { - throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for the model/vgtyp [${model} and ${vgtyp}].` - ); + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{vgtypClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; + + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{vgtypClause}}", vgtypClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + `${statisticSelect}_${variableStr}` + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); + } + + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { + // this is NOT an error just a no data condition + dataFoundForCurve = false; } else { - throw new Error(error); + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + if (error.includes("Unknown column")) { + throw new Error( + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for the model/vgtyp [${model} and ${vgtyp}].` + ); + } else { + throw new Error(error); + } } } - } - if (!dataFoundForCurve) { - // we found no data for any curves so don't bother proceeding - throw new Error("INFO: No valid data for any curves."); + if (!dataFoundForCurve) { + // we found no data for any curves so don't bother proceeding + throw new Error("INFO: No valid data for any curves."); + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); } - const postQueryStartMoment = moment(); - // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined diff --git a/apps/landuse/server/dataFunctions/data_contour_diff.js b/apps/landuse/server/dataFunctions/data_contour_diff.js index e899f00307..2b553c6773 100644 --- a/apps/landuse/server/dataFunctions/data_contour_diff.js +++ b/apps/landuse/server/dataFunctions/data_contour_diff.js @@ -14,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContourDiff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -24,64 +25,84 @@ dataContourDiff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; let dataNotFoundForAnyCurve = false; + + let curves = JSON.parse(JSON.stringify(plotParams.curves)); + const curvesLength = curves.length; + if (curvesLength !== 2) { + throw new Error("INFO: There must be two added curves."); + } + + const axisMap = Object.create(null); const showSignificance = plotParams.significance !== "none"; - const totalProcessingStart = moment(); + + let statType; + let statisticSelect; + let variableStr; + + let statement = ""; + let error = ""; + let dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - let curves = JSON.parse(JSON.stringify(plotParams.curves)); - const curvesLength = curves.length; - if (curvesLength !== 2) { - throw new Error("INFO: There must be two added curves."); - } - let dataset = []; - const axisMap = Object.create(null); - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var vgtypStr = curve.vgtyp; - const vgtyp = Object.keys( - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap - ).find( - (key) => - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr - ); - const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; - const queryTableClause = `from ${model} as m0`; - var variableStr = curve.variable; + + variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.hour IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + + `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -92,104 +113,110 @@ dataContourDiff = function (plotParams, plotFunction) { dateString = "m0.valid_day+3600*m0.hour"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - var statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + - `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; + + const vgtypStr = curve.vgtyp; + const vgtyp = Object.keys( + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap + ).find( + (key) => + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr + ); + const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; + const queryTableClause = `from ${model} as m0`; + + // For contours, this functions as the colorbar label. const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); + statType = statisticOptionsMap[statisticSelect]; const varUnits = statVarUnitMap[statisticSelect][variableStr]; - - // For contours, this functions as the colorbar label. curves[curveIndex].unitKey = varUnits; - var d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{vgtypClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; + let d; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{vgtypClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{vgtypClause}}", vgtypClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{vgtypClause}}", vgtypClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; - var queryResult; - const startMoment = moment(); - var finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - `${statisticSelect}_${variableStr}` - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - if (error.includes("Unknown column")) { - throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for the model/vgtyp [${model} and ${vgtyp}].` - ); - } else { - throw new Error(error); + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + `${statisticSelect}_${variableStr}` + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); + } + + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + if (error.includes("Unknown column")) { + throw new Error( + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for the model/vgtyp [${model} and ${vgtyp}].` + ); + } else { + throw new Error(error); + } } + dataNotFoundForAnyCurve = true; } - dataNotFoundForAnyCurve = true; + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); } - const postQueryStartMoment = moment(); - // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined @@ -238,8 +265,9 @@ dataContourDiff = function (plotParams, plotFunction) { statType === "ctc", statType === "scalar" ); - plotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); - curves = plotParams.curves; + const newPlotParams = plotParams; + newPlotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); + curves = newPlotParams.curves; dataset[0].name = matsPlotUtils.getCurveText( matsTypes.PlotTypes.contourDiff, curves[0] @@ -255,7 +283,7 @@ dataContourDiff = function (plotParams, plotFunction) { const result = matsDataProcessUtils.processDataContour( dataset, curveInfoParams, - plotParams, + newPlotParams, bookkeepingParams ); plotFunction(result); diff --git a/apps/landuse/server/dataFunctions/data_dailymodelcycle.js b/apps/landuse/server/dataFunctions/data_dailymodelcycle.js index e039e21394..5785c82180 100644 --- a/apps/landuse/server/dataFunctions/data_dailymodelcycle.js +++ b/apps/landuse/server/dataFunctions/data_dailymodelcycle.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDailyModelCycle = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,47 +24,48 @@ dataDailyModelCycle = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var vgtypStr = curve.vgtyp; - const vgtyp = Object.keys( - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap - ).find( - (key) => - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr - ); - const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; - const queryTableClause = `from ${model} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + if (curve["utc-cycle-start"].length !== 1) { throw new Error( "INFO: Please select exactly one UTC Cycle Init Hour for this plot type." @@ -72,8 +74,9 @@ dataDailyModelCycle = function (plotParams, plotFunction) { const utcCycleStart = Number(curve["utc-cycle-start"][0]); utcCycleStarts[curveIndex] = utcCycleStart; const utcCycleStartClause = `and floor((m0.valid_day+3600*m0.hour - m0.fcst_len*3600)%(24*3600)/3600) IN(${utcCycleStart})`; + const forecastLengthClause = "and m0.fcst_len < 24"; - const dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -82,59 +85,72 @@ dataDailyModelCycle = function (plotParams, plotFunction) { const statisticClause = `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; - const { statVarUnitMap } = matsCollections.variable.findOne( - { name: "variable" }, - { statVarUnitMap: 1 } + + const dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; + + const vgtypStr = curve.vgtyp; + const vgtyp = Object.keys( + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap + ).find( + (key) => + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr ); - const varUnits = statVarUnitMap[statisticSelect][variableStr]; + const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; + + const queryTableClause = `from ${model} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - const axisKey = varUnits; + const { statVarUnitMap } = matsCollections.variable.findOne( + { name: "variable" }, + { statVarUnitMap: 1 } + ); + statType = statisticOptionsMap[statisticSelect]; + const axisKey = statVarUnitMap[statisticSelect][variableStr]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.valid_day+3600*m0.hour as avtime, " + - "count(distinct m0.valid_day+3600*m0.hour) as N_times, " + - "min(m0.valid_day+3600*m0.hour) as min_secs, " + - "max(m0.valid_day+3600*m0.hour) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{utcCycleStartClause}} " + - "{{forecastLengthClause}} " + - "{{vgtypClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{vgtypClause}}", vgtypClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.valid_day+3600*m0.hour as avtime, " + + "count(distinct m0.valid_day+3600*m0.hour) as N_times, " + + "min(m0.valid_day+3600*m0.hour) as min_secs, " + + "max(m0.valid_day+3600*m0.hour) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{utcCycleStartClause}} " + + "{{forecastLengthClause}} " + + "{{vgtypClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{vgtypClause}}", vgtypClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, `${statisticSelect}_${variableStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -150,6 +166,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -170,7 +187,6 @@ dataDailyModelCycle = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -195,6 +211,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/landuse/server/dataFunctions/data_dieoff.js b/apps/landuse/server/dataFunctions/data_dieoff.js index d136f5ef91..44bcb46045 100644 --- a/apps/landuse/server/dataFunctions/data_dieoff.js +++ b/apps/landuse/server/dataFunctions/data_dieoff.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDieoff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,58 +24,82 @@ dataDieoff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var vgtypStr = curve.vgtyp; - const vgtyp = Object.keys( - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap - ).find( - (key) => - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr - ); - const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; - const queryTableClause = `from ${model} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; - var validTimes; + let validTimeClause = ""; - var utcCycleStart; + let validTimes; + let utcCycleStartClause = ""; + let utcCycleStart; + const forecastLengthStr = curve["dieoff-type"]; const forecastLengthOptionsMap = matsCollections["dieoff-type"].findOne( { name: "dieoff-type" }, { optionsMap: 1 } ).optionsMap; const forecastLength = forecastLengthOptionsMap[forecastLengthStr][0]; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + + `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; - var dateClause; + let dateClause; + + const vgtypStr = curve.vgtyp; + const vgtyp = Object.keys( + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap + ).find( + (key) => + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr + ); + const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; + + const queryTableClause = `from ${model} as m0`; + if (forecastLength === matsTypes.ForecastTypes.dieoff) { validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { @@ -91,67 +116,60 @@ dataDieoff = function (plotParams, plotFunction) { } else { dateClause = `and m0.valid_day+3600*m0.hour-m0.fcst_len*3600 = ${fromSecs}`; } - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + - `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; + + // axisKey is used to determine which axis a curve should use. + // This axisKeySet object is used like a set and if a curve has the same + // units (axisKey) it will use the same axis. + // The axis number is assigned to the axisKeySet value, which is the axisKey. const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); + statType = statisticOptionsMap[statisticSelect]; const varUnits = statVarUnitMap[statisticSelect][variableStr]; - // axisKey is used to determine which axis a curve should use. - // This axisKeySet object is used like a set and if a curve has the same - // units (axisKey) it will use the same axis. - // The axis number is assigned to the axisKeySet value, which is the axisKey. const axisKey = varUnits; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.fcst_len as fcst_lead, " + - "count(distinct m0.valid_day+3600*m0.hour) as N_times, " + - "min(m0.valid_day+3600*m0.hour) as min_secs, " + - "max(m0.valid_day+3600*m0.hour) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{utcCycleStartClause}} " + - "{{vgtypClause}} " + - "group by fcst_lead " + - "order by fcst_lead" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{vgtypClause}}", vgtypClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.fcst_len as fcst_lead, " + + "count(distinct m0.valid_day+3600*m0.hour) as N_times, " + + "min(m0.valid_day+3600*m0.hour) as min_secs, " + + "max(m0.valid_day+3600*m0.hour) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{utcCycleStartClause}} " + + "{{vgtypClause}} " + + "group by fcst_lead " + + "order by fcst_lead" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{vgtypClause}}", vgtypClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, `${statisticSelect}_${variableStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -167,6 +185,7 @@ dataDieoff = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -187,7 +206,6 @@ dataDieoff = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -212,6 +230,7 @@ dataDieoff = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/landuse/server/dataFunctions/data_histogram.js b/apps/landuse/server/dataFunctions/data_histogram.js index 0e4760ae0f..c935fc7959 100644 --- a/apps/landuse/server/dataFunctions/data_histogram.js +++ b/apps/landuse/server/dataFunctions/data_histogram.js @@ -11,6 +11,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataHistogram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -21,58 +22,56 @@ dataHistogram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const alreadyMatched = false; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries const dataFoundForCurve = []; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const alreadyMatched = false; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; + + const axisMap = Object.create(null); + let statType; + let varUnits; + + let statement = ""; + let error = ""; const dataset = []; const allReturnedSubStats = []; const allReturnedSubSecs = []; - const axisMap = Object.create(null); // process user bin customizations const binParams = matsDataUtils.setHistogramParameters(plotParams); const { yAxisFormat } = binParams; const { binNum } = binParams; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; dataFoundForCurve[curveIndex] = true; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var vgtypStr = curve.vgtyp; - const vgtyp = Object.keys( - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap - ).find( - (key) => - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr - ); - const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; - const queryTableClause = `from ${model} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.hour IN(${validTimes})`; } + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -81,16 +80,33 @@ dataHistogram = function (plotParams, plotFunction) { const statisticClause = `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; - const { statVarUnitMap } = matsCollections.variable.findOne( - { name: "variable" }, - { statVarUnitMap: 1 } + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; + + const vgtypStr = curve.vgtyp; + const vgtyp = Object.keys( + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap + ).find( + (key) => + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr ); - var varUnits = statVarUnitMap[statisticSelect][variableStr]; + const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; + + const queryTableClause = `from ${model} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + const { statVarUnitMap } = matsCollections.variable.findOne( + { name: "variable" }, + { statVarUnitMap: 1 } + ); + statType = statisticOptionsMap[statisticSelect]; + varUnits = statVarUnitMap[statisticSelect][variableStr]; let axisKey = yAxisFormat; if (yAxisFormat === "Relative frequency") { axisKey += " (x100)"; @@ -98,46 +114,46 @@ dataHistogram = function (plotParams, plotFunction) { curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options curves[curveIndex].binNum = binNum; // stash the binNum to use it later for bar chart options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.valid_day+3600*m0.hour as avtime, " + - "count(distinct m0.valid_day+3600*m0.hour) as N_times, " + - "min(m0.valid_day+3600*m0.hour) as min_secs, " + - "max(m0.valid_day+3600*m0.hour) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{vgtypClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{vgtypClause}}", vgtypClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.valid_day+3600*m0.hour as avtime, " + + "count(distinct m0.valid_day+3600*m0.hour) as N_times, " + + "min(m0.valid_day+3600*m0.hour) as min_secs, " + + "max(m0.valid_day+3600*m0.hour) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{vgtypClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{vgtypClause}}", vgtypClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, `${statisticSelect}_${variableStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -155,6 +171,7 @@ dataHistogram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/landuse/server/dataFunctions/data_series.js b/apps/landuse/server/dataFunctions/data_series.js index fd9e2cc47f..af291dab86 100644 --- a/apps/landuse/server/dataFunctions/data_series.js +++ b/apps/landuse/server/dataFunctions/data_series.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSeries = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,41 +24,41 @@ dataSeries = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var vgtypStr = curve.vgtyp; - const vgtyp = Object.keys( - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap - ).find( - (key) => - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr - ); - const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; - const queryTableClause = `from ${model} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, @@ -69,15 +70,10 @@ dataSeries = function (plotParams, plotFunction) { if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.hour IN(${validTimes})`; } + let forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; - const averageStr = curve.average; - const averageOptionsMap = matsCollections.average.findOne( - { name: "average" }, - { optionsMap: 1 } - ).optionsMap; - const average = averageOptionsMap[averageStr][0]; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -86,60 +82,79 @@ dataSeries = function (plotParams, plotFunction) { const statisticClause = `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; - const { statVarUnitMap } = matsCollections.variable.findOne( - { name: "variable" }, - { statVarUnitMap: 1 } + + const averageStr = curve.average; + const averageOptionsMap = matsCollections.average.findOne( + { name: "average" }, + { optionsMap: 1 } + ).optionsMap; + const average = averageOptionsMap[averageStr][0]; + + const dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; + + const vgtypStr = curve.vgtyp; + const vgtyp = Object.keys( + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap + ).find( + (key) => + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr ); - const varUnits = statVarUnitMap[statisticSelect][variableStr]; + const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; + + const queryTableClause = `from ${model} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + const { statVarUnitMap } = matsCollections.variable.findOne( + { name: "variable" }, + { statVarUnitMap: 1 } + ); + statType = statisticOptionsMap[statisticSelect]; + const varUnits = statVarUnitMap[statisticSelect][variableStr]; const axisKey = varUnits; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select {{average}} as avtime, " + - "count(distinct m0.valid_day+3600*m0.hour) as N_times, " + - "min(m0.valid_day+3600*m0.hour) as min_secs, " + - "max(m0.valid_day+3600*m0.hour) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{vgtypClause}} " + - "group by avtime " + - "order by avtime" + - ";"; + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select {{average}} as avtime, " + + "count(distinct m0.valid_day+3600*m0.hour) as N_times, " + + "min(m0.valid_day+3600*m0.hour) as min_secs, " + + "max(m0.valid_day+3600*m0.hour) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{vgtypClause}} " + + "group by avtime " + + "order by avtime" + + ";"; - statement = statement.replace("{{average}}", average); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{vgtypClause}}", vgtypClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; + statement = statement.replace("{{average}}", average); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{vgtypClause}}", vgtypClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; - // math is done on forecastLength later on -- set all analyses to 0 - if (forecastLength === "-99") { - forecastLength = "0"; - } + // math is done on forecastLength later on -- set all analyses to 0 + if (forecastLength === "-99") { + forecastLength = "0"; + } - var queryResult; - const startMoment = moment(); - var finishMoment; - try { // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBTimeSeries( - sumPool, + sumPool, // eslint-disable-line no-undef statement, model, forecastLength, @@ -151,7 +166,9 @@ dataSeries = function (plotParams, plotFunction) { appParams, false ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -167,6 +184,7 @@ dataSeries = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -187,7 +205,6 @@ dataSeries = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -212,6 +229,7 @@ dataSeries = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/landuse/server/dataFunctions/data_simple_scatter.js b/apps/landuse/server/dataFunctions/data_simple_scatter.js index 866874b6fa..a1054b0875 100644 --- a/apps/landuse/server/dataFunctions/data_simple_scatter.js +++ b/apps/landuse/server/dataFunctions/data_simple_scatter.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSimpleScatter = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,14 +23,15 @@ dataSimpleScatter = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; + const axisXMap = Object.create(null); const axisYMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; @@ -37,26 +39,27 @@ dataSimpleScatter = function (plotParams, plotFunction) { let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statType; + let varUnitsX; + let varUnitsY; + + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const binParam = curve["bin-parameter"]; const binClause = matsCollections["bin-parameter"].findOne({ name: "bin-parameter", }).optionsMap[binParam]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var vgtypStr = curve.vgtyp; - const vgtyp = Object.keys( - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap - ).find( - (key) => - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr - ); - const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; - const queryTableClause = `from ${model} as m0`; + const variableXStr = curve["x-variable"]; const variableYStr = curve["y-variable"]; const variableOptionsMap = matsCollections.variable.findOne( @@ -65,19 +68,16 @@ dataSimpleScatter = function (plotParams, plotFunction) { ).optionsMap; const variableX = variableOptionsMap[variableXStr]; const variableY = variableOptionsMap[variableYStr]; + let validTimeClause = ""; - let forecastLengthClause = ""; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let dateString = ""; - let dateClause = ""; if (binParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.hour IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (binParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; if (forecastLength === undefined) { @@ -87,12 +87,7 @@ dataSimpleScatter = function (plotParams, plotFunction) { } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } - if (binParam === "Init Date" && binParam !== "Valid Date") { - dateString = "m0.valid_day+3600*m0.hour-m0.fcst_len*3600"; - } else { - dateString = "m0.valid_day+3600*m0.hour"; - } - dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; + const statisticXSelect = curve["x-statistic"]; const statisticYSelect = curve["y-statistic"]; const statisticOptionsMap = matsCollections.statistic.findOne( @@ -103,57 +98,81 @@ dataSimpleScatter = function (plotParams, plotFunction) { `sum(${variableX[0]}) as square_diff_sumX, sum(${variableX[1]}) as N_sumX, sum(${variableX[2]}) as obs_model_diff_sumX, sum(${variableX[3]}) as model_sumX, sum(${variableX[4]}) as obs_sumX, sum(${variableX[5]}) as abs_sumX, ` + `sum(${variableY[0]}) as square_diff_sumY, sum(${variableY[1]}) as N_sumY, sum(${variableY[2]}) as obs_model_diff_sumY, sum(${variableY[3]}) as model_sumY, sum(${variableY[4]}) as obs_sumY, sum(${variableY[5]}) as abs_sumY, ` + `group_concat(m0.valid_day+3600*m0.hour, ';', ${variableX[0]}, ';', ${variableX[1]}, ';', ${variableX[2]}, ';', ${variableX[3]}, ';', ${variableX[4]}, ';', ${variableX[5]}, ';', ${variableY[0]}, ';', ${variableY[1]}, ';', ${variableY[2]}, ';', ${variableY[3]}, ';', ${variableY[4]}, ';', ${variableY[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variableX[0]}) as N0`; - var statType = statisticOptionsMap[statisticXSelect]; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + let dateString = ""; + let dateClause = ""; + if (binParam === "Init Date" && binParam !== "Valid Date") { + dateString = "m0.valid_day+3600*m0.hour-m0.fcst_len*3600"; + } else { + dateString = "m0.valid_day+3600*m0.hour"; + } + dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; + + const vgtypStr = curve.vgtyp; + const vgtyp = Object.keys( + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap + ).find( + (key) => + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr + ); + const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; + + const queryTableClause = `from ${model} as m0`; + const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); - const varUnitsX = statVarUnitMap[statisticXSelect][variableXStr]; - const varUnitsY = statVarUnitMap[statisticYSelect][variableYStr]; + statType = statisticOptionsMap[statisticXSelect]; + varUnitsX = statVarUnitMap[statisticXSelect][variableXStr]; + varUnitsY = statVarUnitMap[statisticYSelect][variableYStr]; - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{binClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{vgtypClause}} " + - "group by binVal " + - "order by binVal" + - ";"; - - statement = statement.replace("{{binClause}}", binClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{vgtypClause}}", vgtypClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "{{binClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{vgtypClause}} " + + "group by binVal " + + "order by binVal" + + ";"; + + statement = statement.replace("{{binClause}}", binClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{vgtypClause}}", vgtypClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSimpleScatter( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, `${statisticXSelect}_${variableXStr}`, `${statisticYSelect}_${variableYStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -169,6 +188,7 @@ dataSimpleScatter = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -189,7 +209,6 @@ dataSimpleScatter = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -197,7 +216,7 @@ dataSimpleScatter = function (plotParams, plotFunction) { ymax = ymax > d.ymax ? ymax : d.ymax; } } else { - // this is a difference curve -- not supported for ROC plots + // this is a difference curve -- not supported for scatter plots throw new Error( "INFO: Difference curves are not supported for performance diagrams, as they do not feature consistent x or y values across all curves." ); @@ -205,6 +224,7 @@ dataSimpleScatter = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/landuse/server/dataFunctions/data_validtime.js b/apps/landuse/server/dataFunctions/data_validtime.js index 63309bef5b..3cdf1eaa96 100644 --- a/apps/landuse/server/dataFunctions/data_validtime.js +++ b/apps/landuse/server/dataFunctions/data_validtime.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataValidTime = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,50 +24,47 @@ dataValidTime = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var vgtypStr = curve.vgtyp; - const vgtyp = Object.keys( - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap - ).find( - (key) => - matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr - ); - const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; - const queryTableClause = `from ${model} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -75,57 +73,74 @@ dataValidTime = function (plotParams, plotFunction) { const statisticClause = `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; - const { statVarUnitMap } = matsCollections.variable.findOne( - { name: "variable" }, - { statVarUnitMap: 1 } + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; + + const vgtypStr = curve.vgtyp; + const vgtyp = Object.keys( + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap + ).find( + (key) => + matsCollections.vgtyp.findOne({ name: "vgtyp" }).valuesMap[key] === vgtypStr ); - const varUnits = statVarUnitMap[statisticSelect][variableStr]; + const vgtypClause = `and m0.vgtyp IN(${vgtyp})`; + + const queryTableClause = `from ${model} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + const { statVarUnitMap } = matsCollections.variable.findOne( + { name: "variable" }, + { statVarUnitMap: 1 } + ); + statType = statisticOptionsMap[statisticSelect]; + const varUnits = statVarUnitMap[statisticSelect][variableStr]; const axisKey = varUnits; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.hour as hr_of_day, " + - "count(distinct m0.valid_day+3600*m0.hour) as N_times, " + - "min(m0.valid_day+3600*m0.hour) as min_secs, " + - "max(m0.valid_day+3600*m0.hour) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{forecastLengthClause}} " + - "{{vgtypClause}} " + - "group by hr_of_day " + - "order by hr_of_day" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{vgtypClause}}", vgtypClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.hour as hr_of_day, " + + "count(distinct m0.valid_day+3600*m0.hour) as N_times, " + + "min(m0.valid_day+3600*m0.hour) as min_secs, " + + "max(m0.valid_day+3600*m0.hour) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{forecastLengthClause}} " + + "{{vgtypClause}} " + + "group by hr_of_day " + + "order by hr_of_day" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{vgtypClause}}", vgtypClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, `${statisticSelect}_${variableStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -141,6 +156,7 @@ dataValidTime = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -161,7 +177,6 @@ dataValidTime = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -186,6 +201,7 @@ dataValidTime = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/landuse/server/main.js b/apps/landuse/server/main.js index 1794482693..da195324ed 100644 --- a/apps/landuse/server/main.js +++ b/apps/landuse/server/main.js @@ -4,7 +4,9 @@ import { Meteor } from "meteor/meteor"; import { mysql } from "meteor/pcel:mysql"; +import { moment } from "meteor/momentjs:moment"; import { + matsMethods, matsTypes, matsCollections, matsDataUtils, @@ -319,71 +321,64 @@ const doCurveParams = function () { const params = matsCollections.CurveParamsInfo.find({ curve_params: { $exists: true }, }).fetch()[0].curve_params; - for (let cp = 0; cp < params.length; cp++) { + for (let cp = 0; cp < params.length; cp += 1) { matsCollections[params[cp]].remove({}); } } + const modelOptionsMap = {}; let modelDateRangeMap = {}; const forecastLengthOptionsMap = {}; const vgtypsModelOptionsMap = {}; - const masterVgtypValuesMap = {}; + const allVgtypValuesMap = {}; try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select vgtyp,description from vgtyp_descriptions;" ); - let masterDescription; - let masterVgtyp; - for (var j = 0; j < rows.length; j++) { - masterDescription = rows[j].description.trim(); - masterVgtyp = rows[j].vgtyp.trim(); - masterVgtypValuesMap[masterVgtyp] = masterDescription; + for (let j = 0; j < rows.length; j += 1) { + allVgtypValuesMap[rows[j].vgtyp.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select model,display_text,fcst_lens,vgtyp,mindate,maxdate from regions_per_model_mats_all_categories order by display_category, display_order;" ); - for (let i = 0; i < rows.length; i++) { - const model_value = rows[i].model.trim(); + for (let i = 0; i < rows.length; i += 1) { + const modelValue = rows[i].model.trim(); const model = rows[i].display_text.trim(); - modelOptionsMap[model] = [model_value]; + modelOptionsMap[model] = [modelValue]; const rowMinDate = moment.utc(rows[i].mindate * 1000).format("MM/DD/YYYY HH:mm"); const rowMaxDate = moment.utc(rows[i].maxdate * 1000).format("MM/DD/YYYY HH:mm"); - modelDateRangeMap[model] = { minDate: rowMinDate, maxDate: rowMaxDate }; + modelDateRangeMap[model] = { + minDate: rowMinDate, + maxDate: rowMaxDate, + }; const forecastLengths = rows[i].fcst_lens; - const forecastLengthArr = forecastLengths + forecastLengthOptionsMap[model] = forecastLengths .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < forecastLengthArr.length; j++) { - forecastLengthArr[j] = forecastLengthArr[j].replace(/'|\[|\]/g, ""); - } - forecastLengthOptionsMap[model] = forecastLengthArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (fhr) { + return fhr.replace(/'|\[|\]/g, ""); + }); const vgtyps = rows[i].vgtyp; - const vgtypsArrRaw = vgtyps + vgtypsModelOptionsMap[model] = vgtyps .split(",") - .map(Function.prototype.call, String.prototype.trim); - const vgtypsArr = []; - var dummyVgtyp; - for (var j = 0; j < vgtypsArrRaw.length; j++) { - dummyVgtyp = vgtypsArrRaw[j].replace(/'|\[|\]/g, ""); - if (dummyVgtyp !== "0") { - vgtypsArr.push(masterVgtypValuesMap[dummyVgtyp]); - } - } - vgtypsModelOptionsMap[model] = vgtypsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (vgtyp) { + return allVgtypValuesMap[vgtyp.replace(/'|\[|\]/g, "")]; + }); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } if (matsCollections.label.findOne({ name: "label" }) === undefined) { @@ -421,7 +416,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["data-source"].findOne({ name: "data-source" }); + const currentParam = matsCollections["data-source"].findOne({ + name: "data-source", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, modelOptionsMap) || !matsDataUtils.areObjectsEqual(currentParam.dates, modelDateRangeMap) @@ -447,7 +444,7 @@ const doCurveParams = function () { type: matsTypes.InputTypes.select, optionsMap: vgtypsModelOptionsMap, options: vgtypsModelOptionsMap[Object.keys(vgtypsModelOptionsMap)[0]], - valuesMap: masterVgtypValuesMap, + valuesMap: allVgtypValuesMap, superiorNames: ["data-source"], controlButtonCovered: true, unique: false, @@ -460,10 +457,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.vgtyp.findOne({ name: "vgtyp" }); + const currentParam = matsCollections.region.findOne({ name: "vgtyp" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, vgtypsModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterVgtypValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allVgtypValuesMap) ) { // have to reload vgtyp data matsCollections.vgtyp.update( @@ -471,7 +468,7 @@ const doCurveParams = function () { { $set: { optionsMap: vgtypsModelOptionsMap, - valuesMap: masterVgtypValuesMap, + valuesMap: allVgtypValuesMap, options: vgtypsModelOptionsMap[Object.keys(vgtypsModelOptionsMap)[0]], default: vgtypsModelOptionsMap[Object.keys(vgtypsModelOptionsMap)[0]][0], }, @@ -480,7 +477,7 @@ const doCurveParams = function () { } } - const optionsMap = { + const statOptionsMap = { RMSE: "scalar", "Bias (Model - Obs)": "scalar", @@ -500,11 +497,11 @@ const doCurveParams = function () { matsCollections.statistic.insert({ name: "statistic", type: matsTypes.InputTypes.select, - optionsMap, - options: Object.keys(optionsMap), + optionsMap: statOptionsMap, + options: Object.keys(statOptionsMap), controlButtonCovered: true, unique: false, - default: Object.keys(optionsMap)[0], + default: Object.keys(statOptionsMap)[0], controlButtonVisibility: "block", displayOrder: 1, displayPriority: 1, @@ -516,11 +513,11 @@ const doCurveParams = function () { matsCollections["x-statistic"].insert({ name: "x-statistic", type: matsTypes.InputTypes.select, - optionsMap, - options: Object.keys(optionsMap), + optionsMap: statOptionsMap, + options: Object.keys(statOptionsMap), controlButtonCovered: true, unique: false, - default: Object.keys(optionsMap)[0], + default: Object.keys(statOptionsMap)[0], controlButtonVisibility: "block", displayOrder: 3, displayPriority: 1, @@ -532,11 +529,11 @@ const doCurveParams = function () { matsCollections["y-statistic"].insert({ name: "y-statistic", type: matsTypes.InputTypes.select, - optionsMap, - options: Object.keys(optionsMap), + optionsMap: statOptionsMap, + options: Object.keys(statOptionsMap), controlButtonCovered: true, unique: false, - default: Object.keys(optionsMap)[0], + default: Object.keys(statOptionsMap)[0], controlButtonVisibility: "block", displayOrder: 1, displayPriority: 1, @@ -704,7 +701,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["forecast-length"].findOne({ + const currentParam = matsCollections["forecast-length"].findOne({ name: "forecast-length", }); if ( @@ -998,7 +995,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["curve-dates"].findOne({ name: "curve-dates" }); + const currentParam = matsCollections["curve-dates"].findOne({ + name: "curve-dates", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.startDate, minDate) || !matsDataUtils.areObjectsEqual(currentParam.stopDate, maxDate) || @@ -1303,7 +1302,8 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { - console.log( + // eslint-disable-next-line no-console + console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); matsCollections.Databases.drop(); @@ -1320,7 +1320,7 @@ Meteor.startup(function () { databases = Meteor.settings.private.databases; } if (databases !== null && databases !== undefined && Array.isArray(databases)) { - for (let di = 0; di < databases.length; di++) { + for (let di = 0; di < databases.length; di += 1) { matsCollections.Databases.insert(databases[di]); } } @@ -1347,6 +1347,7 @@ Meteor.startup(function () { ); if (cbConnection) { // global cbScorecardSettingsPool + // eslint-disable-next-line no-undef cbScorecardSettingsPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1373,6 +1374,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (sumSettings) { + // eslint-disable-next-line no-undef sumPool = mysql.createPool(sumSettings); allPools.push({ pool: "sumPool", role: matsTypes.DatabaseRoles.SUMS_DATA }); } @@ -1389,7 +1391,7 @@ Meteor.startup(function () { appType: matsTypes.AppTypes.mats, }); } catch (error) { - console.log(error.message); + throw new Error(error.message); } }); @@ -1397,6 +1399,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doPlotGraph, doCurveParams, diff --git a/apps/precipAccum/.eslintrc.json b/apps/precipAccum/.eslintrc.json index 8b795b7df7..79d49c5bb6 100644 --- a/apps/precipAccum/.eslintrc.json +++ b/apps/precipAccum/.eslintrc.json @@ -28,22 +28,6 @@ "space-before-function-paren": "off", // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", - "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-plusplus": "warn", - "vars-on-top": "warn", - "no-var": "warn", - "block-scoped-var": "warn", - "no-loop-func": "warn", - "no-unused-vars": "warn", - "prefer-destructuring": "warn", - "no-param-reassign": "warn", - "camelcase": "warn", - "no-redeclare": "warn" + "prefer-arrow-callback": "off" } } diff --git a/apps/precipAccum/client/main.js b/apps/precipAccum/client/main.js index a87407a1f4..ecd922b6a2 100644 --- a/apps/precipAccum/client/main.js +++ b/apps/precipAccum/client/main.js @@ -2,6 +2,7 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; import "@fortawesome/fontawesome-free"; import "@fortawesome/fontawesome-free/css/all.css"; diff --git a/apps/precipAccum/server/dataFunctions/data_contour.js b/apps/precipAccum/server/dataFunctions/data_contour.js index d47b9547f6..c95df32b35 100644 --- a/apps/precipAccum/server/dataFunctions/data_contour.js +++ b/apps/precipAccum/server/dataFunctions/data_contour.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContour = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,56 +23,53 @@ dataContour = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; - const totalProcessingStart = moment(); + + const curves = JSON.parse(JSON.stringify(plotParams.curves)); + if (curves.length > 1) { + throw new Error("INFO: There must only be one added curve."); + } + + const axisMap = Object.create(null); + + let statement = ""; + let error = ""; + const dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - const curves = JSON.parse(JSON.stringify(plotParams.curves)); - if (curves.length > 1) { - throw new Error("INFO: There must only be one added curve."); - } - const dataset = []; - const axisMap = Object.create(null); - // initialize variables specific to the curve + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }).optionsMap[ variable ]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - const regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === - scaleStr - ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; + let thresholdClause = ""; - const forecastLength = 0; // precip apps have no forecast length, but the query and matching algorithms still need it passed in. - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { const thresholdStr = curve.threshold; + if (thresholdStr === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -82,6 +80,17 @@ dataContour = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold * 0.01}`; } + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === + scaleStr + ); + + let forecastTypeClause; const forecastTypeStr = curve["forecast-type"]; const forecastType = Object.keys( matsCollections["forecast-type"].findOne({ name: "forecast-type" }).valuesMap[ @@ -93,13 +102,12 @@ dataContour = function (plotParams, plotFunction) { variable ][key] === forecastTypeStr ); - let forecastTypeClause; if (databaseRef === "precip") { forecastTypeClause = `and m0.num_fcsts = ${forecastType}`; } else { forecastTypeClause = `and m0.accum_len = ${forecastType}`; } - dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -107,85 +115,104 @@ dataContour = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // For contours, this functions as the colorbar label. const statType = statisticOptionsMap[statisticSelect][0]; - curve.unitKey = statisticOptionsMap[statisticSelect][1]; + [, curve.unitKey] = statisticOptionsMap[statisticSelect]; let d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{forecastTypeClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; - - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{forecastTypeClause}}", forecastTypeClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{forecastTypeClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; + + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastTypeClause}}", forecastTypeClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - } - if (!dataFoundForCurve) { - // we found no data for any curves so don't bother proceeding - throw new Error("INFO: No valid data for any curves."); - } + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { + // this is NOT an error just a no data condition + dataFoundForCurve = false; + } else { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + } - const postQueryStartMoment = moment(); + if (!dataFoundForCurve) { + // we found no data for any curves so don't bother proceeding + throw new Error("INFO: No valid data for any curves."); + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined diff --git a/apps/precipAccum/server/dataFunctions/data_contour_diff.js b/apps/precipAccum/server/dataFunctions/data_contour_diff.js index 4732fc299c..ce2b0e3a75 100644 --- a/apps/precipAccum/server/dataFunctions/data_contour_diff.js +++ b/apps/precipAccum/server/dataFunctions/data_contour_diff.js @@ -14,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContourDiff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -24,59 +25,58 @@ dataContourDiff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; let dataNotFoundForAnyCurve = false; + + let curves = JSON.parse(JSON.stringify(plotParams.curves)); + const curvesLength = curves.length; + if (curvesLength !== 2) { + throw new Error("INFO: There must be two added curves."); + } + + const axisMap = Object.create(null); const showSignificance = plotParams.significance !== "none"; - const totalProcessingStart = moment(); + + let statType; + let statisticSelect; + + let statement = ""; + let error = ""; + let dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - let curves = JSON.parse(JSON.stringify(plotParams.curves)); - const curvesLength = curves.length; - if (curvesLength !== 2) { - throw new Error("INFO: There must be two added curves."); - } - let dataset = []; - const axisMap = Object.create(null); - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === - scaleStr - ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; + let thresholdClause = ""; - const forecastLength = 0; // precip apps have no forecast length, but the query and matching algorithms still need it passed in. - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { - var thresholdStr = curve.threshold; + const thresholdStr = curve.threshold; + if (thresholdStr === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -87,7 +87,18 @@ dataContourDiff = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold * 0.01}`; } - var forecastTypeStr = curve["forecast-type"]; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === + scaleStr + ); + + let forecastTypeClause; + const forecastTypeStr = curve["forecast-type"]; const forecastType = Object.keys( matsCollections["forecast-type"].findOne({ name: "forecast-type" }).valuesMap[ variable @@ -98,95 +109,110 @@ dataContourDiff = function (plotParams, plotFunction) { variable ][key] === forecastTypeStr ); - var forecastTypeClause; if (databaseRef === "precip") { forecastTypeClause = `and m0.num_fcsts = ${forecastType}`; } else { forecastTypeClause = `and m0.accum_len = ${forecastType}`; } - dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; - var statisticSelect = curve.statistic; + + statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // For contours, this functions as the colorbar label. - var statType = statisticOptionsMap[statisticSelect][0]; - curves[curveIndex].unitKey = statisticOptionsMap[statisticSelect][1]; + [statType] = statisticOptionsMap[statisticSelect]; + [, curve.unitKey] = statisticOptionsMap[statisticSelect]; - var d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{forecastTypeClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; + let d; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{forecastTypeClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{forecastTypeClause}}", forecastTypeClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastTypeClause}}", forecastTypeClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; - var queryResult; - const startMoment = moment(); - var finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - dataNotFoundForAnyCurve = true; - } - const postQueryStartMoment = moment(); + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + dataNotFoundForAnyCurve = true; + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined @@ -235,8 +261,9 @@ dataContourDiff = function (plotParams, plotFunction) { statType === "ctc", statType === "scalar" ); - plotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); - curves = plotParams.curves; + const newPlotParams = plotParams; + newPlotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); + curves = newPlotParams.curves; dataset[0].name = matsPlotUtils.getCurveText( matsTypes.PlotTypes.contourDiff, curves[0] @@ -252,7 +279,7 @@ dataContourDiff = function (plotParams, plotFunction) { const result = matsDataProcessUtils.processDataContour( dataset, curveInfoParams, - plotParams, + newPlotParams, bookkeepingParams ); plotFunction(result); diff --git a/apps/precipAccum/server/dataFunctions/data_histogram.js b/apps/precipAccum/server/dataFunctions/data_histogram.js index 63f76437a5..f3f9273559 100644 --- a/apps/precipAccum/server/dataFunctions/data_histogram.js +++ b/apps/precipAccum/server/dataFunctions/data_histogram.js @@ -11,6 +11,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataHistogram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -21,52 +22,45 @@ dataHistogram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const alreadyMatched = false; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries const dataFoundForCurve = []; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const alreadyMatched = false; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; + + const axisMap = Object.create(null); + let statType; + let varUnits; + + let statement = ""; + let error = ""; const dataset = []; const allReturnedSubStats = []; const allReturnedSubSecs = []; - const axisMap = Object.create(null); // process user bin customizations const binParams = matsDataUtils.setHistogramParameters(plotParams); const { yAxisFormat } = binParams; const { binNum } = binParams; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; dataFoundForCurve[curveIndex] = true; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === - scaleStr - ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -76,8 +70,18 @@ dataHistogram = function (plotParams, plotFunction) { ] === thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold * 0.01}`; - const forecastLength = 0; // precip apps have no forecast length, but the query and matching algorithms still need it passed in. - var forecastTypeStr = curve["forecast-type"]; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === + scaleStr + ); + + let forecastTypeClause; + const forecastTypeStr = curve["forecast-type"]; const forecastType = Object.keys( matsCollections["forecast-type"].findOne({ name: "forecast-type" }).valuesMap[ variable @@ -88,16 +92,12 @@ dataHistogram = function (plotParams, plotFunction) { variable ][key] === forecastTypeStr ); - var forecastTypeClause; if (databaseRef === "precip") { forecastTypeClause = `and m0.num_fcsts = ${forecastType}`; } else { forecastTypeClause = `and m0.accum_len = ${forecastType}`; } - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -105,12 +105,27 @@ dataHistogram = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; - var varUnits = statisticOptionsMap[statisticSelect][1]; + [statType] = statisticOptionsMap[statisticSelect]; + [, varUnits] = statisticOptionsMap[statisticSelect]; let axisKey = yAxisFormat; if (yAxisFormat === "Relative frequency") { axisKey += " (x100)"; @@ -118,44 +133,44 @@ dataHistogram = function (plotParams, plotFunction) { curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options curves[curveIndex].binNum = binNum; // stash the binNum to use it later for bar chart options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.time as avtime, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{forecastTypeClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{forecastTypeClause}}", forecastTypeClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.time as avtime, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{forecastTypeClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastTypeClause}}", forecastTypeClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -173,6 +188,7 @@ dataHistogram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/precipAccum/server/dataFunctions/data_perfDiagram.js b/apps/precipAccum/server/dataFunctions/data_perfDiagram.js index 057d743bea..4530b50dc3 100644 --- a/apps/precipAccum/server/dataFunctions/data_perfDiagram.js +++ b/apps/precipAccum/server/dataFunctions/data_perfDiagram.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataPerformanceDiagram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,57 +23,47 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statType; + + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const binParam = curve["bin-parameter"]; const binClause = matsCollections["bin-parameter"].findOne({ name: "bin-parameter", }).optionsMap[binParam]; - var { variable } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === - scaleStr - ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; + let thresholdClause = ""; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let dateClause = ""; if (binParam !== "Threshold") { - var thresholdStr = curve.threshold; + const thresholdStr = curve.threshold; if (thresholdStr === undefined) { throw new Error( `INFO: ${label}'s threshold is undefined. Please assign it a value.` @@ -88,7 +79,18 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold * 0.01}`; } - var forecastTypeStr = curve["forecast-type"]; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === + scaleStr + ); + + let forecastTypeClause; + const forecastTypeStr = curve["forecast-type"]; const forecastType = Object.keys( matsCollections["forecast-type"].findOne({ name: "forecast-type" }).valuesMap[ variable @@ -99,60 +101,74 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { variable ][key] === forecastTypeStr ); - var forecastTypeClause; if (databaseRef === "precip") { forecastTypeClause = `and m0.num_fcsts = ${forecastType}`; } else { forecastTypeClause = `and m0.accum_len = ${forecastType}`; } - dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + const statisticSelect = "PerformanceDiagram"; - var statType = "ctc"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // variable + statistic (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + statType = "ctc"; curves[curveIndex].axisKey = statisticSelect; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{binClause}} " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "((sum(m0.yy)+0.00)/sum(m0.yy+m0.yn)) as pod, ((sum(m0.ny)+0.00)/sum(m0.ny+m0.yy)) as far, " + - "sum(m0.yy+m0.yn) as oy_all, sum(m0.ny+m0.nn) as on_all, group_concat(m0.time, ';', m0.yy, ';', " + - "m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0 " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{forecastTypeClause}} " + - "group by binVal " + - "order by binVal" + - ";"; - - statement = statement.replace("{{binClause}}", binClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{forecastTypeClause}}", forecastTypeClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "{{binClause}} " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "((sum(m0.yy)+0.00)/sum(m0.yy+m0.yn)) as pod, ((sum(m0.ny)+0.00)/sum(m0.ny+m0.yy)) as far, " + + "sum(m0.yy+m0.yn) as oy_all, sum(m0.ny+m0.nn) as on_all, group_concat(m0.time, ';', m0.yy, ';', " + + "m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0 " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{forecastTypeClause}} " + + "group by binVal " + + "order by binVal" + + ";"; + + statement = statement.replace("{{binClause}}", binClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastTypeClause}}", forecastTypeClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBPerformanceDiagram( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -168,6 +184,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -189,7 +206,6 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -197,7 +213,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { ymax = ymax > d.ymax ? ymax : d.ymax; } } else { - // this is a difference curve -- not supported for ROC plots + // this is a difference curve -- not supported for performance diagrams throw new Error( "INFO: Difference curves are not supported for performance diagrams, as they do not feature consistent x or y values across all curves." ); @@ -205,6 +221,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipAccum/server/dataFunctions/data_series.js b/apps/precipAccum/server/dataFunctions/data_series.js index a62f0a21d7..ecf33d1aad 100644 --- a/apps/precipAccum/server/dataFunctions/data_series.js +++ b/apps/precipAccum/server/dataFunctions/data_series.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSeries = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,52 +24,46 @@ dataSeries = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === - scaleStr - ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -78,8 +73,19 @@ dataSeries = function (plotParams, plotFunction) { ] === thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold * 0.01}`; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === + scaleStr + ); + const forecastLength = 0; // precip apps have no forecast length, but the query and matching algorithms still need it passed in. - var forecastTypeStr = curve["forecast-type"]; + let forecastTypeClause; + const forecastTypeStr = curve["forecast-type"]; const forecastType = Object.keys( matsCollections["forecast-type"].findOne({ name: "forecast-type" }).valuesMap[ variable @@ -90,19 +96,12 @@ dataSeries = function (plotParams, plotFunction) { variable ][key] === forecastTypeStr ); - var forecastTypeClause; if (databaseRef === "precip") { forecastTypeClause = `and m0.num_fcsts = ${forecastType}`; } else { forecastTypeClause = `and m0.accum_len = ${forecastType}`; } - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; - const averageStr = curve.average; - const averageOptionsMap = matsCollections.average.findOne( - { name: "average" }, - { optionsMap: 1 } - ).optionsMap; - const average = averageOptionsMap[averageStr][0]; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -110,11 +109,30 @@ dataSeries = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const averageStr = curve.average; + const averageOptionsMap = matsCollections.average.findOne( + { name: "average" }, + { optionsMap: 1 } + ).optionsMap; + const average = averageOptionsMap[averageStr][0]; + + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -122,40 +140,38 @@ dataSeries = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select {{average}} as avtime, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{forecastTypeClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{average}}", average); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{forecastTypeClause}}", forecastTypeClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select {{average}} as avtime, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{forecastTypeClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{average}}", average); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastTypeClause}}", forecastTypeClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBTimeSeries( - sumPool, + sumPool, // eslint-disable-line no-undef statement, model, forecastLength, @@ -167,7 +183,9 @@ dataSeries = function (plotParams, plotFunction) { appParams, false ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -183,6 +201,7 @@ dataSeries = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -197,7 +216,6 @@ dataSeries = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -222,6 +240,7 @@ dataSeries = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipAccum/server/dataFunctions/data_threshold.js b/apps/precipAccum/server/dataFunctions/data_threshold.js index a3042c78ef..a0a75f16bf 100644 --- a/apps/precipAccum/server/dataFunctions/data_threshold.js +++ b/apps/precipAccum/server/dataFunctions/data_threshold.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataThreshold = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,51 +24,51 @@ dataThreshold = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( + const scaleStr = curve.scale; + const scale = Object.keys( matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] ).find( (key) => matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === scaleStr ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; - const thresholdClause = ""; - const forecastLength = 0; // precip apps have no forecast length, but the query and matching algorithms still need it passed in. - var forecastTypeStr = curve["forecast-type"]; + + let forecastTypeClause; + const forecastTypeStr = curve["forecast-type"]; const forecastType = Object.keys( matsCollections["forecast-type"].findOne({ name: "forecast-type" }).valuesMap[ variable @@ -78,16 +79,12 @@ dataThreshold = function (plotParams, plotFunction) { variable ][key] === forecastTypeStr ); - var forecastTypeClause; if (databaseRef === "precip") { forecastTypeClause = `and m0.num_fcsts = ${forecastType}`; } else { forecastTypeClause = `and m0.accum_len = ${forecastType}`; } - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -95,11 +92,26 @@ dataThreshold = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -107,44 +119,42 @@ dataThreshold = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.trsh as thresh, " + // produces thresholds in in - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{forecastTypeClause}} " + - "group by thresh " + - "order by thresh" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{forecastTypeClause}}", forecastTypeClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.trsh as thresh, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{forecastTypeClause}} " + + "group by thresh " + + "order by thresh" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{forecastTypeClause}}", forecastTypeClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -160,6 +170,7 @@ dataThreshold = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -174,7 +185,6 @@ dataThreshold = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -199,6 +209,7 @@ dataThreshold = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipAccum/server/main.js b/apps/precipAccum/server/main.js index 170439a8f9..06fe12be3d 100644 --- a/apps/precipAccum/server/main.js +++ b/apps/precipAccum/server/main.js @@ -4,7 +4,9 @@ import { Meteor } from "meteor/meteor"; import { mysql } from "meteor/pcel:mysql"; +import { moment } from "meteor/momentjs:moment"; import { + matsMethods, matsTypes, matsCollections, matsDataUtils, @@ -319,7 +321,7 @@ const doCurveParams = function () { const params = matsCollections.CurveParamsInfo.find({ curve_params: { $exists: true }, }).fetch()[0].curve_params; - for (let cp = 0; cp < params.length; cp++) { + for (let cp = 0; cp < params.length; cp += 1) { matsCollections[params[cp]].remove({}); } } @@ -330,113 +332,95 @@ const doCurveParams = function () { const thresholdsModelOptionsMap = {}; const scaleModelOptionsMap = {}; const fcstTypeModelOptionsMap = {}; - const masterRegionValuesMap = {}; - const masterThresholdValuesMap = {}; - const masterScaleValuesMap = {}; - const masterFcstTypeValuesMap = {}; + const allRegionValuesMap = {}; + const allThresholdValuesMap = {}; + const allScaleValuesMap = {}; + const allFcstTypeValuesMap = {}; try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - metadataPool, + metadataPool, // eslint-disable-line no-undef "select short_name,description from region_descriptions;" ); - let masterRegDescription; - let masterShortName; - for (var j = 0; j < rows.length; j++) { - masterRegDescription = rows[j].description.trim(); - masterShortName = rows[j].short_name.trim(); - masterRegionValuesMap[masterShortName] = masterRegDescription; + for (let j = 0; j < rows.length; j += 1) { + allRegionValuesMap[rows[j].short_name.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } - let rows; - let didx; - try { - for (didx = 0; didx < variables.length; didx++) { - masterThresholdValuesMap[variables[didx]] = {}; - rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + for (let didx = 0; didx < variables.length; didx += 1) { + allThresholdValuesMap[variables[didx]] = {}; + const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( + sumPool, // eslint-disable-line no-undef `select trsh,description from ${ variableDBNames[variables[didx]] }.threshold_descriptions;` ); - var masterDescription; - var masterTrsh; - for (var j = 0; j < rows.length; j++) { - masterDescription = rows[j].description.trim(); - masterTrsh = rows[j].trsh.trim(); - masterThresholdValuesMap[variables[didx]][masterTrsh] = masterDescription; + for (let j = 0; j < rows.length; j += 1) { + allThresholdValuesMap[variables[didx]][rows[j].trsh.trim()] = + rows[j].description.trim(); } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { - for (didx = 0; didx < variables.length; didx++) { - masterScaleValuesMap[variables[didx]] = {}; - rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + for (let didx = 0; didx < variables.length; didx += 1) { + allScaleValuesMap[variables[didx]] = {}; + const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( + sumPool, // eslint-disable-line no-undef `select scle,description from ${ variableDBNames[variables[didx]] }.scale_descriptions;` ); - var masterDescription; - var masterScale; - for (var j = 0; j < rows.length; j++) { - masterDescription = rows[j].description.trim(); - masterScale = rows[j].scle.trim(); - masterScaleValuesMap[variables[didx]][masterScale] = masterDescription; + for (let j = 0; j < rows.length; j += 1) { + allScaleValuesMap[variables[didx]][rows[j].scle.trim()] = + rows[j].description.trim(); } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { - for (didx = 0; didx < variables.length; didx++) { - masterFcstTypeValuesMap[variables[didx]] = {}; - rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + for (let didx = 0; didx < variables.length; didx += 1) { + allFcstTypeValuesMap[variables[didx]] = {}; + const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( + sumPool, // eslint-disable-line no-undef `select fcst_type,description from ${ variableDBNames[variables[didx]] }.fcst_type_descriptions;` ); - var masterFcstTypeDescription; - var masterFcstType; - for (var j = 0; j < rows.length; j++) { - masterFcstTypeDescription = rows[j].description.trim(); - masterFcstType = rows[j].fcst_type.trim(); - masterFcstTypeValuesMap[variables[didx]][masterFcstType] = - masterFcstTypeDescription; + for (let j = 0; j < rows.length; j += 1) { + allFcstTypeValuesMap[variables[didx]][rows[j].fcst_type.trim()] = + rows[j].description.trim(); } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { - for (didx = 0; didx < variables.length; didx++) { - modelOptionsMap[variables[didx]] = {}; - modelDateRangeMap[variables[didx]] = {}; - thresholdsModelOptionsMap[variables[didx]] = {}; - scaleModelOptionsMap[variables[didx]] = {}; - fcstTypeModelOptionsMap[variables[didx]] = {}; - regionModelOptionsMap[variables[didx]] = {}; - - rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, - `select model,regions,display_text,fcst_types,trsh,scle,mindate,maxdate from ${ - variableDBNames[variables[didx]] - }.regions_per_model_mats_all_categories order by display_category, display_order;` + for (let didx = 0; didx < variables.length; didx += 1) { + const variable = variables[didx]; + modelOptionsMap[variable] = {}; + modelDateRangeMap[variable] = {}; + thresholdsModelOptionsMap[variable] = {}; + scaleModelOptionsMap[variable] = {}; + fcstTypeModelOptionsMap[variable] = {}; + regionModelOptionsMap[variable] = {}; + + const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( + sumPool, // eslint-disable-line no-undef + `select model,regions,display_text,fcst_types,trsh,scle,mindate,maxdate from ${variableDBNames[variable]}.regions_per_model_mats_all_categories order by display_category, display_order;` ); - for (let i = 0; i < rows.length; i++) { - const model_value = rows[i].model.trim(); + for (let i = 0; i < rows.length; i += 1) { + const modelValue = rows[i].model.trim(); const model = rows[i].display_text.trim(); - modelOptionsMap[variables[didx]][model] = [model_value]; + modelOptionsMap[variable][model] = [modelValue]; const rowMinDate = moment .utc(rows[i].mindate * 1000) @@ -444,62 +428,46 @@ const doCurveParams = function () { const rowMaxDate = moment .utc(rows[i].maxdate * 1000) .format("MM/DD/YYYY HH:mm"); - modelDateRangeMap[variables[didx]][model] = { + modelDateRangeMap[variable][model] = { minDate: rowMinDate, maxDate: rowMaxDate, }; const fcstTypes = rows[i].fcst_types; - const fcstTypesArrRaw = fcstTypes + fcstTypeModelOptionsMap[variable][model] = fcstTypes .split(",") - .map(Function.prototype.call, String.prototype.trim); - const fcstTypesArr = []; - var dummyfcstType; - for (var j = 0; j < fcstTypesArrRaw.length; j++) { - dummyfcstType = fcstTypesArrRaw[j].replace(/'|\[|\]/g, ""); - fcstTypesArr.push(masterFcstTypeValuesMap[variables[didx]][dummyfcstType]); - } - fcstTypeModelOptionsMap[variables[didx]][model] = fcstTypesArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (fcstType) { + return allFcstTypeValuesMap[variable][fcstType.replace(/'|\[|\]/g, "")]; + }); const thresholds = rows[i].trsh; - const thresholdsArrRaw = thresholds + thresholdsModelOptionsMap[variable][model] = thresholds .split(",") - .map(Function.prototype.call, String.prototype.trim); - const thresholdsArr = []; - var dummyThresh; - for (var j = 0; j < thresholdsArrRaw.length; j++) { - dummyThresh = thresholdsArrRaw[j].replace(/'|\[|\]/g, ""); - thresholdsArr.push(masterThresholdValuesMap[variables[didx]][dummyThresh]); - } - thresholdsModelOptionsMap[variables[didx]][model] = thresholdsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (threshold) { + return allThresholdValuesMap[variable][threshold.replace(/'|\[|\]/g, "")]; + }); const scales = rows[i].scle; - const scalesArrRaw = scales + scaleModelOptionsMap[variable][model] = scales .split(",") - .map(Function.prototype.call, String.prototype.trim); - const scalesArr = []; - var dummyScale; - for (var j = 0; j < scalesArrRaw.length; j++) { - dummyScale = scalesArrRaw[j].replace(/'|\[|\]/g, ""); - scalesArr.push(masterScaleValuesMap[variables[didx]][dummyScale]); - } - scaleModelOptionsMap[variables[didx]][model] = scalesArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (scale) { + return allScaleValuesMap[variable][scale.replace(/'|\[|\]/g, "")]; + }); const { regions } = rows[i]; - const regionsArrRaw = regions + regionModelOptionsMap[variable][model] = regions .split(",") - .map(Function.prototype.call, String.prototype.trim); - const regionsArr = []; - var dummyRegion; - for (var j = 0; j < regionsArrRaw.length; j++) { - dummyRegion = regionsArrRaw[j].replace(/'|\[|\]/g, ""); - regionsArr.push(masterRegionValuesMap[dummyRegion]); - } - regionModelOptionsMap[variables[didx]][model] = regionsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (region) { + return allRegionValuesMap[region.replace(/'|\[|\]/g, "")]; + }); } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } if (matsCollections.label.findOne({ name: "label" }) === undefined) { @@ -537,7 +505,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.variable.findOne({ name: "variable" }); + const currentParam = matsCollections.variable.findOne({ name: "variable" }); if (!matsDataUtils.areObjectsEqual(currentParam.dates, modelDateRangeMap)) { // have to reload variable data matsCollections.variable.update( @@ -576,7 +544,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["data-source"].findOne({ name: "data-source" }); + const currentParam = matsCollections["data-source"].findOne({ + name: "data-source", + }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, modelOptionsMap)) { // have to reload model data matsCollections["data-source"].update( @@ -601,7 +571,7 @@ const doCurveParams = function () { regionModelOptionsMap[variables[0]][ Object.keys(regionModelOptionsMap[variables[0]])[0] ], - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -616,10 +586,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.region.findOne({ name: "region" }); + const currentParam = matsCollections.region.findOne({ name: "region" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, regionModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterRegionValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allRegionValuesMap) ) { // have to reload region data matsCollections.region.update( @@ -627,7 +597,7 @@ const doCurveParams = function () { { $set: { optionsMap: regionModelOptionsMap, - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, options: regionModelOptionsMap[variables[0]][ Object.keys(regionModelOptionsMap[variables[0]])[0] @@ -708,7 +678,7 @@ const doCurveParams = function () { thresholdsModelOptionsMap[variables[0]][ Object.keys(thresholdsModelOptionsMap[variables[0]])[0] ], - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -723,13 +693,13 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.threshold.findOne({ name: "threshold" }); + const currentParam = matsCollections.threshold.findOne({ name: "threshold" }); if ( !matsDataUtils.areObjectsEqual( currentParam.optionsMap, thresholdsModelOptionsMap ) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterThresholdValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allThresholdValuesMap) ) { // have to reload threshold data matsCollections.threshold.update( @@ -737,7 +707,7 @@ const doCurveParams = function () { { $set: { optionsMap: thresholdsModelOptionsMap, - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, options: thresholdsModelOptionsMap[variables[0]][ Object.keys(thresholdsModelOptionsMap[variables[0]])[0] @@ -761,7 +731,7 @@ const doCurveParams = function () { scaleModelOptionsMap[variables[0]][ Object.keys(scaleModelOptionsMap[variables[0]])[0] ], - valuesMap: masterScaleValuesMap, + valuesMap: allScaleValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -776,10 +746,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.scale.findOne({ name: "scale" }); + const currentParam = matsCollections.scale.findOne({ name: "scale" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, scaleModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterScaleValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allScaleValuesMap) ) { // have to reload scale data matsCollections.scale.update( @@ -787,7 +757,7 @@ const doCurveParams = function () { { $set: { optionsMap: scaleModelOptionsMap, - valuesMap: masterScaleValuesMap, + valuesMap: allScaleValuesMap, options: scaleModelOptionsMap[variables[0]][ Object.keys(scaleModelOptionsMap[variables[0]])[0] @@ -813,7 +783,7 @@ const doCurveParams = function () { fcstTypeModelOptionsMap[variables[0]][ Object.keys(fcstTypeModelOptionsMap[variables[0]])[0] ], - valuesMap: masterFcstTypeValuesMap, + valuesMap: allFcstTypeValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -829,7 +799,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["forecast-type"].findOne({ + const currentParam = matsCollections["forecast-type"].findOne({ name: "forecast-type", }); if ( @@ -837,7 +807,7 @@ const doCurveParams = function () { currentParam.optionsMap, fcstTypeModelOptionsMap ) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterFcstTypeValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allFcstTypeValuesMap) ) { // have to reload forecast type data matsCollections["forecast-type"].update( @@ -845,7 +815,7 @@ const doCurveParams = function () { { $set: { optionsMap: fcstTypeModelOptionsMap, - valuesMap: masterFcstTypeValuesMap, + valuesMap: allFcstTypeValuesMap, options: fcstTypeModelOptionsMap[variables[0]][ Object.keys(fcstTypeModelOptionsMap[variables[0]])[0] @@ -938,18 +908,14 @@ const doCurveParams = function () { } // determine date defaults for dates and curveDates - const defaultDb = matsCollections.variable.findOne( - { name: "variable" }, + const defaultDataSource = matsCollections["data-source"].findOne( + { name: "data-source" }, { default: 1 } ).default; modelDateRangeMap = matsCollections.variable.findOne( { name: "variable" }, { dates: 1 } ).dates; - const defaultDataSource = matsCollections["data-source"].findOne( - { name: "data-source" }, - { default: 1 } - ).default; minDate = modelDateRangeMap[variables[0]][defaultDataSource].minDate; maxDate = modelDateRangeMap[variables[0]][defaultDataSource].maxDate; @@ -990,7 +956,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["curve-dates"].findOne({ name: "curve-dates" }); + const currentParam = matsCollections["curve-dates"].findOne({ + name: "curve-dates", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.startDate, minDate) || !matsDataUtils.areObjectsEqual(currentParam.stopDate, maxDate) || @@ -1244,7 +1212,8 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { - console.log( + // eslint-disable-next-line no-console + console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); matsCollections.Databases.drop(); @@ -1261,7 +1230,7 @@ Meteor.startup(function () { databases = Meteor.settings.private.databases; } if (databases !== null && databases !== undefined && Array.isArray(databases)) { - for (let di = 0; di < databases.length; di++) { + for (let di = 0; di < databases.length; di += 1) { matsCollections.Databases.insert(databases[di]); } } @@ -1288,6 +1257,7 @@ Meteor.startup(function () { ); if (cbConnection) { // global cbScorecardSettingsPool + // eslint-disable-next-line no-undef cbScorecardSettingsPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1314,6 +1284,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (metadataSettings) { + // eslint-disable-next-line no-undef metadataPool = mysql.createPool(metadataSettings); allPools.push({ pool: "metadataPool", role: matsTypes.DatabaseRoles.META_DATA }); } @@ -1334,6 +1305,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (sumSettings) { + // eslint-disable-next-line no-undef sumPool = mysql.createPool(sumSettings); allPools.push({ pool: "sumPool", role: matsTypes.DatabaseRoles.SUMS_DATA }); } @@ -1342,7 +1314,7 @@ Meteor.startup(function () { const mdr = new matsTypes.MetaDataDBRecord("metadataPool", "mats_common", [ "region_descriptions", ]); - for (let didx = 0; didx < variables.length; didx++) { + for (let didx = 0; didx < variables.length; didx += 1) { mdr.addRecord("sumPool", variableDBNames[variables[didx]], [ "threshold_descriptions", "scale_descriptions", @@ -1357,7 +1329,7 @@ Meteor.startup(function () { appType: matsTypes.AppTypes.mats, }); } catch (error) { - console.log(error.message); + throw new Error(error.message); } }); @@ -1365,6 +1337,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doPlotGraph, doCurveParams, diff --git a/apps/precipGauge/.eslintrc.json b/apps/precipGauge/.eslintrc.json index 8b795b7df7..79d49c5bb6 100644 --- a/apps/precipGauge/.eslintrc.json +++ b/apps/precipGauge/.eslintrc.json @@ -28,22 +28,6 @@ "space-before-function-paren": "off", // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", - "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-plusplus": "warn", - "vars-on-top": "warn", - "no-var": "warn", - "block-scoped-var": "warn", - "no-loop-func": "warn", - "no-unused-vars": "warn", - "prefer-destructuring": "warn", - "no-param-reassign": "warn", - "camelcase": "warn", - "no-redeclare": "warn" + "prefer-arrow-callback": "off" } } diff --git a/apps/precipGauge/client/main.js b/apps/precipGauge/client/main.js index a87407a1f4..ecd922b6a2 100644 --- a/apps/precipGauge/client/main.js +++ b/apps/precipGauge/client/main.js @@ -2,6 +2,7 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; import "@fortawesome/fontawesome-free"; import "@fortawesome/fontawesome-free/css/all.css"; diff --git a/apps/precipGauge/server/dataFunctions/data_contour.js b/apps/precipGauge/server/dataFunctions/data_contour.js index e4cd3b9a06..eb4ea3d4c5 100644 --- a/apps/precipGauge/server/dataFunctions/data_contour.js +++ b/apps/precipGauge/server/dataFunctions/data_contour.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContour = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,51 +23,48 @@ dataContour = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; - const totalProcessingStart = moment(); + + const curves = JSON.parse(JSON.stringify(plotParams.curves)); + if (curves.length > 1) { + throw new Error("INFO: There must only be one added curve."); + } + + const axisMap = Object.create(null); + + let statement = ""; + let error = ""; + const dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - const curves = JSON.parse(JSON.stringify(plotParams.curves)); - if (curves.length > 1) { - throw new Error("INFO: There must only be one added curve."); - } - const dataset = []; - const axisMap = Object.create(null); - // initialize variables specific to the curve + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - const regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${region}${sourceStr} as m0`; + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { const thresholdStr = curve.threshold; + if (thresholdStr === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -76,16 +74,38 @@ dataContour = function (plotParams, plotFunction) { ); thresholdClause = `and m0.thresh = ${threshold}`; } + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.valid_time%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -96,95 +116,105 @@ dataContour = function (plotParams, plotFunction) { dateString = "m0.valid_time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${region}${source} as m0`; + // For contours, this functions as the colorbar label. const statType = statisticOptionsMap[statisticSelect][0]; - curve.unitKey = statisticOptionsMap[statisticSelect][1]; + [, curve.unitKey] = statisticOptionsMap[statisticSelect]; let d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; - - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; + + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - } - if (!dataFoundForCurve) { - // we found no data for any curves so don't bother proceeding - throw new Error("INFO: No valid data for any curves."); - } + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { + // this is NOT an error just a no data condition + dataFoundForCurve = false; + } else { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + } - const postQueryStartMoment = moment(); + if (!dataFoundForCurve) { + // we found no data for any curves so don't bother proceeding + throw new Error("INFO: No valid data for any curves."); + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined diff --git a/apps/precipGauge/server/dataFunctions/data_contour_diff.js b/apps/precipGauge/server/dataFunctions/data_contour_diff.js index 5460fe0f7f..44d4306424 100644 --- a/apps/precipGauge/server/dataFunctions/data_contour_diff.js +++ b/apps/precipGauge/server/dataFunctions/data_contour_diff.js @@ -14,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContourDiff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -24,55 +25,54 @@ dataContourDiff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; let dataNotFoundForAnyCurve = false; + + let curves = JSON.parse(JSON.stringify(plotParams.curves)); + const curvesLength = curves.length; + if (curvesLength !== 2) { + throw new Error("INFO: There must be two added curves."); + } + + const axisMap = Object.create(null); const showSignificance = plotParams.significance !== "none"; - const totalProcessingStart = moment(); + + let statType; + let statisticSelect; + + let statement = ""; + let error = ""; + let dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - let curves = JSON.parse(JSON.stringify(plotParams.curves)); - const curvesLength = curves.length; - if (curvesLength !== 2) { - throw new Error("INFO: There must be two added curves."); - } - let dataset = []; - const axisMap = Object.create(null); - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${region}${sourceStr} as m0`; + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { - var thresholdStr = curve.threshold; + const thresholdStr = curve.threshold; + if (thresholdStr === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -82,16 +82,38 @@ dataContourDiff = function (plotParams, plotFunction) { ); thresholdClause = `and m0.thresh = ${threshold}`; } + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.valid_time%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + + statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -102,91 +124,98 @@ dataContourDiff = function (plotParams, plotFunction) { dateString = "m0.valid_time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - var statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${region}${source} as m0`; + // For contours, this functions as the colorbar label. - var statType = statisticOptionsMap[statisticSelect][0]; - curves[curveIndex].unitKey = statisticOptionsMap[statisticSelect][1]; + [statType] = statisticOptionsMap[statisticSelect]; + [, curve.unitKey] = statisticOptionsMap[statisticSelect]; - var d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; + let d; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; - var queryResult; - const startMoment = moment(); - var finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - dataNotFoundForAnyCurve = true; - } - const postQueryStartMoment = moment(); + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + dataNotFoundForAnyCurve = true; + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined @@ -235,8 +264,9 @@ dataContourDiff = function (plotParams, plotFunction) { statType === "ctc", statType === "scalar" ); - plotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); - curves = plotParams.curves; + const newPlotParams = plotParams; + newPlotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); + curves = newPlotParams.curves; dataset[0].name = matsPlotUtils.getCurveText( matsTypes.PlotTypes.contourDiff, curves[0] @@ -252,7 +282,7 @@ dataContourDiff = function (plotParams, plotFunction) { const result = matsDataProcessUtils.processDataContour( dataset, curveInfoParams, - plotParams, + newPlotParams, bookkeepingParams ); plotFunction(result); diff --git a/apps/precipGauge/server/dataFunctions/data_dailymodelcycle.js b/apps/precipGauge/server/dataFunctions/data_dailymodelcycle.js index e0f4721495..314b566fb5 100644 --- a/apps/precipGauge/server/dataFunctions/data_dailymodelcycle.js +++ b/apps/precipGauge/server/dataFunctions/data_dailymodelcycle.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDailyModelCycle = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,46 +24,42 @@ dataDailyModelCycle = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${region}${sourceStr} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -71,6 +68,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { thresholdStr ); const thresholdClause = `and m0.thresh = ${threshold}`; + if (curve["utc-cycle-start"].length !== 1) { throw new Error( "INFO: Please select exactly one UTC Cycle Init Hour for this plot type." @@ -79,20 +77,35 @@ dataDailyModelCycle = function (plotParams, plotFunction) { const utcCycleStart = Number(curve["utc-cycle-start"][0]); utcCycleStarts[curveIndex] = utcCycleStart; const utcCycleStartClause = `and floor((m0.valid_time - m0.fcst_len*3600)%(24*3600)/3600) IN(${utcCycleStart})`; + const forecastLengthClause = "and m0.fcst_len < 24"; - const dateClause = `and m0.valid_time >= ${fromSecs} and m0.valid_time <= ${toSecs}`; + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap; const statisticClause = - "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + + const dateClause = `and m0.valid_time >= ${fromSecs} and m0.valid_time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${region}${source} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -100,46 +113,46 @@ dataDailyModelCycle = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.valid_time as avtime, " + - "count(distinct m0.valid_time) as N_times, " + - "min(m0.valid_time) as min_secs, " + - "max(m0.valid_time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{utcCycleStartClause}} " + - "{{thresholdClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.valid_time as avtime, " + + "count(distinct m0.valid_time) as N_times, " + + "min(m0.valid_time) as min_secs, " + + "max(m0.valid_time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{utcCycleStartClause}} " + + "{{thresholdClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -155,6 +168,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -169,7 +183,6 @@ dataDailyModelCycle = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -194,6 +207,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipGauge/server/dataFunctions/data_dieoff.js b/apps/precipGauge/server/dataFunctions/data_dieoff.js index 3684c6bcd2..20334fdb0a 100644 --- a/apps/precipGauge/server/dataFunctions/data_dieoff.js +++ b/apps/precipGauge/server/dataFunctions/data_dieoff.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDieoff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,43 +24,38 @@ dataDieoff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${region}${sourceStr} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -68,21 +64,44 @@ dataDieoff = function (plotParams, plotFunction) { thresholdStr ); const thresholdClause = `and m0.thresh = ${threshold}`; - var validTimes; + let validTimeClause = ""; - var utcCycleStart; + let validTimes; + let utcCycleStartClause = ""; + let utcCycleStart; + const forecastLengthStr = curve["dieoff-type"]; const forecastLengthOptionsMap = matsCollections["dieoff-type"].findOne( { name: "dieoff-type" }, { optionsMap: 1 } ).optionsMap; const forecastLength = forecastLengthOptionsMap[forecastLengthStr][0]; - const forecastLengthClause = ""; + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; - var dateClause; + let dateClause; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${region}${source} as m0`; + if (forecastLength === matsTypes.ForecastTypes.dieoff) { validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { @@ -99,18 +118,12 @@ dataDieoff = function (plotParams, plotFunction) { } else { dateClause = `and m0.valid_time-m0.fcst_len*3600 = ${fromSecs}`; } - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -118,48 +131,46 @@ dataDieoff = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.fcst_len as fcst_lead, " + - "count(distinct m0.valid_time) as N_times, " + - "min(m0.valid_time) as min_secs, " + - "max(m0.valid_time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{utcCycleStartClause}} " + - "group by fcst_lead " + - "order by fcst_lead" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.fcst_len as fcst_lead, " + + "count(distinct m0.valid_time) as N_times, " + + "min(m0.valid_time) as min_secs, " + + "max(m0.valid_time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{utcCycleStartClause}} " + + "group by fcst_lead " + + "order by fcst_lead" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -175,6 +186,7 @@ dataDieoff = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -189,7 +201,6 @@ dataDieoff = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -214,6 +225,7 @@ dataDieoff = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipGauge/server/dataFunctions/data_histogram.js b/apps/precipGauge/server/dataFunctions/data_histogram.js index 08e43cd2e6..aeef9ba343 100644 --- a/apps/precipGauge/server/dataFunctions/data_histogram.js +++ b/apps/precipGauge/server/dataFunctions/data_histogram.js @@ -11,6 +11,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataHistogram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -21,46 +22,42 @@ dataHistogram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const alreadyMatched = false; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries const dataFoundForCurve = []; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const alreadyMatched = false; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; + + const axisMap = Object.create(null); + let statType; + let varUnits; + + let statement = ""; + let error = ""; const dataset = []; const allReturnedSubStats = []; const allReturnedSubSecs = []; - const axisMap = Object.create(null); // process user bin customizations const binParams = matsDataUtils.setHistogramParameters(plotParams); const { yAxisFormat } = binParams; const { binNum } = binParams; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; dataFoundForCurve[curveIndex] = true; const { label } = curve; + const { diffFrom } = curve; + const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${region}${sourceStr} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -69,30 +66,46 @@ dataHistogram = function (plotParams, plotFunction) { thresholdStr ); const thresholdClause = `and m0.thresh = ${threshold}`; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.valid_time)%(24*3600)/3600) IN(${validTimes})`; } + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.valid_time >= ${fromSecs} and m0.valid_time <= ${toSecs}`; + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap; const statisticClause = - "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.valid_time >= ${fromSecs} and m0.valid_time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${region}${source} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; - var varUnits = statisticOptionsMap[statisticSelect][1]; + [statType] = statisticOptionsMap[statisticSelect]; + [, varUnits] = statisticOptionsMap[statisticSelect]; let axisKey = yAxisFormat; if (yAxisFormat === "Relative frequency") { axisKey += " (x100)"; @@ -100,46 +113,46 @@ dataHistogram = function (plotParams, plotFunction) { curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options curves[curveIndex].binNum = binNum; // stash the binNum to use it later for bar chart options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.valid_time as avtime, " + - "count(distinct m0.valid_time) as N_times, " + - "min(m0.valid_time) as min_secs, " + - "max(m0.valid_time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.valid_time as avtime, " + + "count(distinct m0.valid_time) as N_times, " + + "min(m0.valid_time) as min_secs, " + + "max(m0.valid_time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -157,6 +170,7 @@ dataHistogram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/precipGauge/server/dataFunctions/data_perfDiagram.js b/apps/precipGauge/server/dataFunctions/data_perfDiagram.js index 12c3fb5d20..a068e2685d 100644 --- a/apps/precipGauge/server/dataFunctions/data_perfDiagram.js +++ b/apps/precipGauge/server/dataFunctions/data_perfDiagram.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataPerformanceDiagram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,54 +23,43 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statType; + + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const binParam = curve["bin-parameter"]; const binClause = matsCollections["bin-parameter"].findOne({ name: "bin-parameter", }).optionsMap[binParam]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${region}${sourceStr} as m0`; + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let dateString = ""; - let dateClause = ""; if (binParam !== "Threshold") { - var thresholdStr = curve.threshold; + const thresholdStr = curve.threshold; if (thresholdStr === undefined) { throw new Error( `INFO: ${label}'s threshold is undefined. Please assign it a value.` @@ -84,12 +74,16 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { ); thresholdClause = `and m0.thresh = ${threshold}`; } + + let validTimeClause = ""; if (binParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.valid_time%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (binParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; if (forecastLength === undefined) { @@ -99,62 +93,81 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + + const statisticSelect = "PerformanceDiagram"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + let dateString = ""; + let dateClause = ""; if (binParam === "Init Date") { dateString = "m0.valid_time-m0.fcst_len*3600"; } else { dateString = "m0.valid_time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const statisticSelect = "PerformanceDiagram"; - var statType = "ctc"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${region}${source} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // variable + statistic (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + statType = "ctc"; curves[curveIndex].axisKey = statisticSelect; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{binClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "((sum(m0.yy)+0.00)/sum(m0.yy+m0.ny)) as pod, ((sum(m0.yn)+0.00)/sum(m0.yn+m0.yy)) as far, " + - "sum(m0.yy+m0.ny) as oy_all, sum(m0.yn+m0.nn) as on_all, group_concat(m0.valid_time, ';', m0.yy, ';', " + - "m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0 " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by binVal " + - "order by binVal" + - ";"; - - statement = statement.replace("{{binClause}}", binClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "{{binClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "((sum(m0.yy)+0.00)/sum(m0.yy+m0.ny)) as pod, ((sum(m0.yn)+0.00)/sum(m0.yn+m0.yy)) as far, " + + "sum(m0.yy+m0.ny) as oy_all, sum(m0.yn+m0.nn) as on_all, group_concat(m0.valid_time, ';', m0.yy, ';', " + + "m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0 " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by binVal " + + "order by binVal" + + ";"; + + statement = statement.replace("{{binClause}}", binClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBPerformanceDiagram( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -170,6 +183,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -177,21 +191,13 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { } else { // this is an error returned by the mysql database error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - if (error.includes("ER_NO_SUCH_TABLE")) { - throw new Error( - `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${model}]. ` + - `Choose a different scale to continue using this region.` - ); - } else { - throw new Error(error); - } + throw new Error(error); } } else { dataFoundForAnyCurve = true; } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -199,7 +205,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { ymax = ymax > d.ymax ? ymax : d.ymax; } } else { - // this is a difference curve -- not supported for ROC plots + // this is a difference curve -- not supported for performance diagrams throw new Error( "INFO: Difference curves are not supported for performance diagrams, as they do not feature consistent x or y values across all curves." ); @@ -207,6 +213,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipGauge/server/dataFunctions/data_series.js b/apps/precipGauge/server/dataFunctions/data_series.js index 7a929ba26b..79d9686be7 100644 --- a/apps/precipGauge/server/dataFunctions/data_series.js +++ b/apps/precipGauge/server/dataFunctions/data_series.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSeries = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,46 +24,42 @@ dataSeries = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${region}${sourceStr} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -71,20 +68,18 @@ dataSeries = function (plotParams, plotFunction) { thresholdStr ); const thresholdClause = `and m0.thresh = ${threshold}`; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.valid_time)%(24*3600)/3600) IN(${validTimes})`; } + let forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateClause = `and m0.valid_time >= ${fromSecs} and m0.valid_time <= ${toSecs}`; - const averageStr = curve.average; - const averageOptionsMap = matsCollections.average.findOne( - { name: "average" }, - { optionsMap: 1 } - ).optionsMap; - const average = averageOptionsMap[averageStr][0]; + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -92,11 +87,30 @@ dataSeries = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + + const averageStr = curve.average; + const averageOptionsMap = matsCollections.average.findOne( + { name: "average" }, + { optionsMap: 1 } + ).optionsMap; + const average = averageOptionsMap[averageStr][0]; + + const dateClause = `and m0.valid_time >= ${fromSecs} and m0.valid_time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${region}${source} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -104,47 +118,45 @@ dataSeries = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select {{average}} as avtime, " + - "count(distinct m0.valid_time) as N_times, " + - "min(m0.valid_time) as min_secs, " + - "max(m0.valid_time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select {{average}} as avtime, " + + "count(distinct m0.valid_time) as N_times, " + + "min(m0.valid_time) as min_secs, " + + "max(m0.valid_time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; - statement = statement.replace("{{average}}", average); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; + statement = statement.replace("{{average}}", average); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; - // math is done on forecastLength later on -- set all analyses to 0 - if (forecastLength === "-99") { - forecastLength = "0"; - } + // math is done on forecastLength later on -- set all analyses to 0 + if (forecastLength === "-99") { + forecastLength = "0"; + } - var queryResult; - const startMoment = moment(); - var finishMoment; - try { // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBTimeSeries( - sumPool, + sumPool, // eslint-disable-line no-undef statement, model, forecastLength, @@ -156,7 +168,9 @@ dataSeries = function (plotParams, plotFunction) { appParams, false ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -172,6 +186,7 @@ dataSeries = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -186,7 +201,6 @@ dataSeries = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -211,6 +225,7 @@ dataSeries = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipGauge/server/dataFunctions/data_threshold.js b/apps/precipGauge/server/dataFunctions/data_threshold.js index 1e8b3b36da..af77af8e6a 100644 --- a/apps/precipGauge/server/dataFunctions/data_threshold.js +++ b/apps/precipGauge/server/dataFunctions/data_threshold.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataThreshold = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,66 +24,75 @@ dataThreshold = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${region}${sourceStr} as m0`; - const thresholdClause = ""; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.valid_time)%(24*3600)/3600) IN(${validTimes})`; } + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.valid_time >= ${fromSecs} and m0.valid_time <= ${toSecs}`; + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap; const statisticClause = - "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.valid_time >= ${fromSecs} and m0.valid_time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${region}${source} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -90,46 +100,44 @@ dataThreshold = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.thresh/100 as thresh, " + // produces thresholds in inches - "count(distinct m0.valid_time) as N_times, " + - "min(m0.valid_time) as min_secs, " + - "max(m0.valid_time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by thresh " + - "order by thresh" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.thresh/100 as thresh, " + // produces thresholds in inches + "count(distinct m0.valid_time) as N_times, " + + "min(m0.valid_time) as min_secs, " + + "max(m0.valid_time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by thresh " + + "order by thresh" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -145,6 +153,7 @@ dataThreshold = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -159,7 +168,6 @@ dataThreshold = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -184,6 +192,7 @@ dataThreshold = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipGauge/server/dataFunctions/data_validtime.js b/apps/precipGauge/server/dataFunctions/data_validtime.js index 6df0ed4397..764138ced9 100644 --- a/apps/precipGauge/server/dataFunctions/data_validtime.js +++ b/apps/precipGauge/server/dataFunctions/data_validtime.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataValidTime = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,43 +24,38 @@ dataValidTime = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${region}${sourceStr} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -68,24 +64,39 @@ dataValidTime = function (plotParams, plotFunction) { thresholdStr ); const thresholdClause = `and m0.thresh = ${threshold}`; + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.valid_time >= ${fromSecs} and m0.valid_time <= ${toSecs}`; + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap; const statisticClause = - "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + "sum(m0.yy) as hit, sum(m0.yn) as fa, sum(m0.ny) as miss, sum(m0.nn) as cn, group_concat(m0.valid_time, ';', m0.yy, ';', m0.yn, ';', m0.ny, ';', m0.nn order by m0.valid_time) as sub_data, count(m0.yy) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.valid_time >= ${fromSecs} and m0.valid_time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${region}${source} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -93,44 +104,44 @@ dataValidTime = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select floor(m0.valid_time%(24*3600)/3600) as hr_of_day, " + - "count(distinct m0.valid_time) as N_times, " + - "min(m0.valid_time) as min_secs, " + - "max(m0.valid_time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{forecastLengthClause}} " + - "group by hr_of_day " + - "order by hr_of_day" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select floor(m0.valid_time%(24*3600)/3600) as hr_of_day, " + + "count(distinct m0.valid_time) as N_times, " + + "min(m0.valid_time) as min_secs, " + + "max(m0.valid_time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{forecastLengthClause}} " + + "group by hr_of_day " + + "order by hr_of_day" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -146,6 +157,7 @@ dataValidTime = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -160,7 +172,6 @@ dataValidTime = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -185,6 +196,7 @@ dataValidTime = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipGauge/server/main.js b/apps/precipGauge/server/main.js index f5bcd67650..f3b0947bc6 100644 --- a/apps/precipGauge/server/main.js +++ b/apps/precipGauge/server/main.js @@ -4,7 +4,9 @@ import { Meteor } from "meteor/meteor"; import { mysql } from "meteor/pcel:mysql"; +import { moment } from "meteor/momentjs:moment"; import { + matsMethods, matsTypes, matsCollections, matsDataUtils, @@ -320,109 +322,95 @@ const doCurveParams = function () { const params = matsCollections.CurveParamsInfo.find({ curve_params: { $exists: true }, }).fetch()[0].curve_params; - for (let cp = 0; cp < params.length; cp++) { + for (let cp = 0; cp < params.length; cp += 1) { matsCollections[params[cp]].remove({}); } } + const modelOptionsMap = {}; let modelDateRangeMap = {}; const regionModelOptionsMap = {}; const forecastLengthOptionsMap = {}; const thresholdsModelOptionsMap = {}; const sourceOptionsMap = {}; - const masterRegionValuesMap = {}; - const masterThresholdValuesMap = {}; + const allRegionValuesMap = {}; + const allThresholdValuesMap = {}; try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - metadataPool, + metadataPool, // eslint-disable-line no-undef "select short_name,description from region_descriptions;" ); - let masterRegDescription; - let masterShortName; - for (var j = 0; j < rows.length; j++) { - masterRegDescription = rows[j].description.trim(); - masterShortName = rows[j].short_name.trim(); - masterRegionValuesMap[masterShortName] = masterRegDescription; + for (let j = 0; j < rows.length; j += 1) { + allRegionValuesMap[rows[j].short_name.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - modelPool, + modelPool, // eslint-disable-line no-undef "select trsh,description from threshold_descriptions;" ); - let masterDescription; - let masterTrsh; - for (var j = 0; j < rows.length; j++) { - masterDescription = rows[j].description.trim(); - masterTrsh = rows[j].trsh.trim(); - masterThresholdValuesMap[masterTrsh] = masterDescription; + for (let j = 0; j < rows.length; j += 1) { + allThresholdValuesMap[rows[j].trsh.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select model,regions,sources,display_text,fcst_lens,trsh,mindate,maxdate from regions_per_model_mats_all_categories order by display_category, display_order;" ); - for (let i = 0; i < rows.length; i++) { - const model_value = rows[i].model.trim(); + for (let i = 0; i < rows.length; i += 1) { + const modelValue = rows[i].model.trim(); const model = rows[i].display_text.trim(); - modelOptionsMap[model] = [model_value]; + modelOptionsMap[model] = [modelValue]; const rowMinDate = moment.utc(rows[i].mindate * 1000).format("MM/DD/YYYY HH:mm"); const rowMaxDate = moment.utc(rows[i].maxdate * 1000).format("MM/DD/YYYY HH:mm"); - modelDateRangeMap[model] = { minDate: rowMinDate, maxDate: rowMaxDate }; + modelDateRangeMap[model] = { + minDate: rowMinDate, + maxDate: rowMaxDate, + }; const { sources } = rows[i]; - const sourceArr = sources + sourceOptionsMap[model] = sources .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < sourceArr.length; j++) { - sourceArr[j] = sourceArr[j].replace(/'|\[|\]/g, ""); - } - sourceOptionsMap[model] = sourceArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (source) { + return source.replace(/'|\[|\]/g, ""); + }); const forecastLengths = rows[i].fcst_lens; - const forecastLengthArr = forecastLengths + forecastLengthOptionsMap[model] = forecastLengths .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < forecastLengthArr.length; j++) { - forecastLengthArr[j] = forecastLengthArr[j].replace(/'|\[|\]/g, ""); - } - forecastLengthOptionsMap[model] = forecastLengthArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (fhr) { + return fhr.replace(/'|\[|\]/g, ""); + }); const thresholds = rows[i].trsh; - const thresholdsArrRaw = thresholds + thresholdsModelOptionsMap[model] = thresholds .split(",") - .map(Function.prototype.call, String.prototype.trim); - const thresholdsArr = []; - var dummyThresh; - for (var j = 0; j < thresholdsArrRaw.length; j++) { - dummyThresh = thresholdsArrRaw[j].replace(/'|\[|\]/g, ""); - thresholdsArr.push(masterThresholdValuesMap[dummyThresh]); - } - thresholdsModelOptionsMap[model] = thresholdsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (threshold) { + return allThresholdValuesMap[threshold.replace(/'|\[|\]/g, "")]; + }); const { regions } = rows[i]; - const regionsArrRaw = regions + regionModelOptionsMap[model] = regions .split(",") - .map(Function.prototype.call, String.prototype.trim); - const regionsArr = []; - var dummyRegion; - for (var j = 0; j < regionsArrRaw.length; j++) { - dummyRegion = regionsArrRaw[j].replace(/'|\[|\]/g, ""); - regionsArr.push(masterRegionValuesMap[dummyRegion]); - } - regionModelOptionsMap[model] = regionsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (region) { + return allRegionValuesMap[region.replace(/'|\[|\]/g, "")]; + }); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } if (matsCollections.label.findOne({ name: "label" }) === undefined) { @@ -467,7 +455,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["data-source"].findOne({ name: "data-source" }); + const currentParam = matsCollections["data-source"].findOne({ + name: "data-source", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, modelOptionsMap) || !matsDataUtils.areObjectsEqual(currentParam.dates, modelDateRangeMap) @@ -493,7 +483,7 @@ const doCurveParams = function () { type: matsTypes.InputTypes.select, optionsMap: regionModelOptionsMap, options: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]], - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, superiorNames: ["data-source"], controlButtonCovered: true, unique: false, @@ -505,10 +495,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.region.findOne({ name: "region" }); + const currentParam = matsCollections.region.findOne({ name: "region" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, regionModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterRegionValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allRegionValuesMap) ) { // have to reload region data matsCollections.region.update( @@ -516,7 +506,7 @@ const doCurveParams = function () { { $set: { optionsMap: regionModelOptionsMap, - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, options: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]], default: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]][0], }, @@ -588,7 +578,7 @@ const doCurveParams = function () { type: matsTypes.InputTypes.select, optionsMap: thresholdsModelOptionsMap, options: thresholdsModelOptionsMap[Object.keys(thresholdsModelOptionsMap)[0]], - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, superiorNames: ["data-source"], controlButtonCovered: true, unique: false, @@ -600,13 +590,13 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.threshold.findOne({ name: "threshold" }); + const currentParam = matsCollections.threshold.findOne({ name: "threshold" }); if ( !matsDataUtils.areObjectsEqual( currentParam.optionsMap, thresholdsModelOptionsMap ) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterThresholdValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allThresholdValuesMap) ) { // have to reload threshold data matsCollections.threshold.update( @@ -614,7 +604,7 @@ const doCurveParams = function () { { $set: { optionsMap: thresholdsModelOptionsMap, - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, options: thresholdsModelOptionsMap[Object.keys(thresholdsModelOptionsMap)[0]], default: @@ -642,7 +632,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.truth.findOne({ name: "truth" }); + const currentParam = matsCollections.truth.findOne({ name: "truth" }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, sourceOptionsMap)) { // have to reload truth data matsCollections.truth.update( @@ -680,7 +670,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["forecast-length"].findOne({ + const currentParam = matsCollections["forecast-length"].findOne({ name: "forecast-length", }); if ( @@ -962,7 +952,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["curve-dates"].findOne({ name: "curve-dates" }); + const currentParam = matsCollections["curve-dates"].findOne({ + name: "curve-dates", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.startDate, minDate) || !matsDataUtils.areObjectsEqual(currentParam.stopDate, maxDate) || @@ -1308,7 +1300,8 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { - console.log( + // eslint-disable-next-line no-console + console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); matsCollections.Databases.drop(); @@ -1325,7 +1318,7 @@ Meteor.startup(function () { databases = Meteor.settings.private.databases; } if (databases !== null && databases !== undefined && Array.isArray(databases)) { - for (let di = 0; di < databases.length; di++) { + for (let di = 0; di < databases.length; di += 1) { matsCollections.Databases.insert(databases[di]); } } @@ -1352,6 +1345,7 @@ Meteor.startup(function () { ); if (cbConnection) { // global cbScorecardSettingsPool + // eslint-disable-next-line no-undef cbScorecardSettingsPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1378,6 +1372,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (metadataSettings) { + // eslint-disable-next-line no-undef metadataPool = mysql.createPool(metadataSettings); allPools.push({ pool: "metadataPool", role: matsTypes.DatabaseRoles.META_DATA }); } @@ -1395,6 +1390,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (modelSettings) { + // eslint-disable-next-line no-undef modelPool = mysql.createPool(modelSettings); allPools.push({ pool: "modelPool", role: matsTypes.DatabaseRoles.MODEL_DATA }); } @@ -1415,6 +1411,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (sumSettings) { + // eslint-disable-next-line no-undef sumPool = mysql.createPool(sumSettings); allPools.push({ pool: "sumPool", role: matsTypes.DatabaseRoles.SUMS_DATA }); } @@ -1434,7 +1431,7 @@ Meteor.startup(function () { appType: matsTypes.AppTypes.mats, }); } catch (error) { - console.log(error.message); + throw new Error(error.message); } }); @@ -1442,6 +1439,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doPlotGraph, doCurveParams, diff --git a/apps/precipitation1hr/.eslintrc.json b/apps/precipitation1hr/.eslintrc.json index 8b795b7df7..79d49c5bb6 100644 --- a/apps/precipitation1hr/.eslintrc.json +++ b/apps/precipitation1hr/.eslintrc.json @@ -28,22 +28,6 @@ "space-before-function-paren": "off", // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", - "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-plusplus": "warn", - "vars-on-top": "warn", - "no-var": "warn", - "block-scoped-var": "warn", - "no-loop-func": "warn", - "no-unused-vars": "warn", - "prefer-destructuring": "warn", - "no-param-reassign": "warn", - "camelcase": "warn", - "no-redeclare": "warn" + "prefer-arrow-callback": "off" } } diff --git a/apps/precipitation1hr/client/main.js b/apps/precipitation1hr/client/main.js index a87407a1f4..ecd922b6a2 100644 --- a/apps/precipitation1hr/client/main.js +++ b/apps/precipitation1hr/client/main.js @@ -2,6 +2,7 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; import "@fortawesome/fontawesome-free"; import "@fortawesome/fontawesome-free/css/all.css"; diff --git a/apps/precipitation1hr/server/dataFunctions/data_contour.js b/apps/precipitation1hr/server/dataFunctions/data_contour.js index 7e0dd2f574..508c06e924 100644 --- a/apps/precipitation1hr/server/dataFunctions/data_contour.js +++ b/apps/precipitation1hr/server/dataFunctions/data_contour.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContour = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,58 +23,48 @@ dataContour = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; - const totalProcessingStart = moment(); + + const curves = JSON.parse(JSON.stringify(plotParams.curves)); + if (curves.length > 1) { + throw new Error("INFO: There must only be one added curve."); + } + + const axisMap = Object.create(null); + + let statement = ""; + let error = ""; + const dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - const curves = JSON.parse(JSON.stringify(plotParams.curves)); - if (curves.length > 1) { - throw new Error("INFO: There must only be one added curve."); - } - const dataset = []; - const axisMap = Object.create(null); - // initialize variables specific to the curve + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - const regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${grid_scale}${sourceStr}_${region} as m0`; + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { const thresholdStr = curve.threshold; + if (thresholdStr === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -83,16 +74,46 @@ dataContour = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold * 0.01}`; } + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.time%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + "sum(m0.hit) as hit, sum(m0.fa) as fa, sum(m0.miss) as miss, sum(m0.cn) as cn, group_concat(m0.time, ';', m0.hit, ';', m0.fa, ';', m0.miss, ';', m0.cn order by m0.time) as sub_data, count(m0.hit) as N0"; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -103,95 +124,105 @@ dataContour = function (plotParams, plotFunction) { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - "sum(m0.hit) as hit, sum(m0.fa) as fa, sum(m0.miss) as miss, sum(m0.cn) as cn, group_concat(m0.time, ';', m0.hit, ';', m0.fa, ';', m0.miss, ';', m0.cn order by m0.time) as sub_data, count(m0.hit) as N0"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${scale}${source}_${region} as m0`; + // For contours, this functions as the colorbar label. const statType = statisticOptionsMap[statisticSelect][0]; - curve.unitKey = statisticOptionsMap[statisticSelect][1]; + [, curve.unitKey] = statisticOptionsMap[statisticSelect]; let d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; - - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; + + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - } - if (!dataFoundForCurve) { - // we found no data for any curves so don't bother proceeding - throw new Error("INFO: No valid data for any curves."); - } + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { + // this is NOT an error just a no data condition + dataFoundForCurve = false; + } else { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + } - const postQueryStartMoment = moment(); + if (!dataFoundForCurve) { + // we found no data for any curves so don't bother proceeding + throw new Error("INFO: No valid data for any curves."); + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined diff --git a/apps/precipitation1hr/server/dataFunctions/data_contour_diff.js b/apps/precipitation1hr/server/dataFunctions/data_contour_diff.js index 3f14ae824e..2cb3a698f7 100644 --- a/apps/precipitation1hr/server/dataFunctions/data_contour_diff.js +++ b/apps/precipitation1hr/server/dataFunctions/data_contour_diff.js @@ -14,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContourDiff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -24,62 +25,54 @@ dataContourDiff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; let dataNotFoundForAnyCurve = false; + + let curves = JSON.parse(JSON.stringify(plotParams.curves)); + const curvesLength = curves.length; + if (curvesLength !== 2) { + throw new Error("INFO: There must be two added curves."); + } + + const axisMap = Object.create(null); const showSignificance = plotParams.significance !== "none"; - const totalProcessingStart = moment(); + + let statType; + let statisticSelect; + + let statement = ""; + let error = ""; + let dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - let curves = JSON.parse(JSON.stringify(plotParams.curves)); - const curvesLength = curves.length; - if (curvesLength !== 2) { - throw new Error("INFO: There must be two added curves."); - } - let dataset = []; - const axisMap = Object.create(null); - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${grid_scale}${sourceStr}_${region} as m0`; + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { - var thresholdStr = curve.threshold; + const thresholdStr = curve.threshold; + if (thresholdStr === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -89,16 +82,46 @@ dataContourDiff = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold * 0.01}`; } + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.time%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + + statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + "sum(m0.hit) as hit, sum(m0.fa) as fa, sum(m0.miss) as miss, sum(m0.cn) as cn, group_concat(m0.time, ';', m0.hit, ';', m0.fa, ';', m0.miss, ';', m0.cn order by m0.time) as sub_data, count(m0.hit) as N0"; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -109,91 +132,98 @@ dataContourDiff = function (plotParams, plotFunction) { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - var statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - "sum(m0.hit) as hit, sum(m0.fa) as fa, sum(m0.miss) as miss, sum(m0.cn) as cn, group_concat(m0.time, ';', m0.hit, ';', m0.fa, ';', m0.miss, ';', m0.cn order by m0.time) as sub_data, count(m0.hit) as N0"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${scale}${source}_${region} as m0`; + // For contours, this functions as the colorbar label. - var statType = statisticOptionsMap[statisticSelect][0]; - curves[curveIndex].unitKey = statisticOptionsMap[statisticSelect][1]; + [statType] = statisticOptionsMap[statisticSelect]; + [, curve.unitKey] = statisticOptionsMap[statisticSelect]; - var d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; + let d; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; - var queryResult; - const startMoment = moment(); - var finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - dataNotFoundForAnyCurve = true; - } - const postQueryStartMoment = moment(); + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + dataNotFoundForAnyCurve = true; + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined @@ -242,8 +272,9 @@ dataContourDiff = function (plotParams, plotFunction) { statType === "ctc", statType === "scalar" ); - plotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); - curves = plotParams.curves; + const newPlotParams = plotParams; + newPlotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); + curves = newPlotParams.curves; dataset[0].name = matsPlotUtils.getCurveText( matsTypes.PlotTypes.contourDiff, curves[0] @@ -259,7 +290,7 @@ dataContourDiff = function (plotParams, plotFunction) { const result = matsDataProcessUtils.processDataContour( dataset, curveInfoParams, - plotParams, + newPlotParams, bookkeepingParams ); plotFunction(result); diff --git a/apps/precipitation1hr/server/dataFunctions/data_dailymodelcycle.js b/apps/precipitation1hr/server/dataFunctions/data_dailymodelcycle.js index fe955e3613..de3aedda2f 100644 --- a/apps/precipitation1hr/server/dataFunctions/data_dailymodelcycle.js +++ b/apps/precipitation1hr/server/dataFunctions/data_dailymodelcycle.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDailyModelCycle = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,53 +24,42 @@ dataDailyModelCycle = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${grid_scale}${sourceStr}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -78,6 +68,15 @@ dataDailyModelCycle = function (plotParams, plotFunction) { thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold * 0.01}`; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + if (curve["utc-cycle-start"].length !== 1) { throw new Error( "INFO: Please select exactly one UTC Cycle Init Hour for this plot type." @@ -86,8 +85,11 @@ dataDailyModelCycle = function (plotParams, plotFunction) { const utcCycleStart = Number(curve["utc-cycle-start"][0]); utcCycleStarts[curveIndex] = utcCycleStart; const utcCycleStartClause = `and floor((m0.time - m0.fcst_len*3600)%(24*3600)/3600) IN(${utcCycleStart})`; + const forecastLengthClause = "and m0.fcst_len < 24"; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -95,11 +97,23 @@ dataDailyModelCycle = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.hit) as hit, sum(m0.fa) as fa, sum(m0.miss) as miss, sum(m0.cn) as cn, group_concat(m0.time, ';', m0.hit, ';', m0.fa, ';', m0.miss, ';', m0.cn order by m0.time) as sub_data, count(m0.hit) as N0"; + + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${scale}${source}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -107,46 +121,46 @@ dataDailyModelCycle = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.time as avtime, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{utcCycleStartClause}} " + - "{{thresholdClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.time as avtime, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{utcCycleStartClause}} " + + "{{thresholdClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -162,6 +176,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -176,7 +191,6 @@ dataDailyModelCycle = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -201,6 +215,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipitation1hr/server/dataFunctions/data_dieoff.js b/apps/precipitation1hr/server/dataFunctions/data_dieoff.js index b38a02403f..c6ea50ac19 100644 --- a/apps/precipitation1hr/server/dataFunctions/data_dieoff.js +++ b/apps/precipitation1hr/server/dataFunctions/data_dieoff.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDieoff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,50 +24,38 @@ dataDieoff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${grid_scale}${sourceStr}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -75,21 +64,52 @@ dataDieoff = function (plotParams, plotFunction) { thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold * 0.01}`; - var validTimes; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + let validTimeClause = ""; - var utcCycleStart; + let validTimes; + let utcCycleStartClause = ""; + let utcCycleStart; + const forecastLengthStr = curve["dieoff-type"]; const forecastLengthOptionsMap = matsCollections["dieoff-type"].findOne( { name: "dieoff-type" }, { optionsMap: 1 } ).optionsMap; const forecastLength = forecastLengthOptionsMap[forecastLengthStr][0]; - const forecastLengthClause = ""; + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + "sum(m0.hit) as hit, sum(m0.fa) as fa, sum(m0.miss) as miss, sum(m0.cn) as cn, group_concat(m0.time, ';', m0.hit, ';', m0.fa, ';', m0.miss, ';', m0.cn order by m0.time) as sub_data, count(m0.hit) as N0"; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; - var dateClause; + let dateClause; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${scale}${source}_${region} as m0`; + if (forecastLength === matsTypes.ForecastTypes.dieoff) { validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { @@ -106,18 +126,12 @@ dataDieoff = function (plotParams, plotFunction) { } else { dateClause = `and m0.time-m0.fcst_len*3600 = ${fromSecs}`; } - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - "sum(m0.hit) as hit, sum(m0.fa) as fa, sum(m0.miss) as miss, sum(m0.cn) as cn, group_concat(m0.time, ';', m0.hit, ';', m0.fa, ';', m0.miss, ';', m0.cn order by m0.time) as sub_data, count(m0.hit) as N0"; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -125,48 +139,46 @@ dataDieoff = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.fcst_len as fcst_lead, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{utcCycleStartClause}} " + - "group by fcst_lead " + - "order by fcst_lead" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.fcst_len as fcst_lead, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{utcCycleStartClause}} " + + "group by fcst_lead " + + "order by fcst_lead" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -182,6 +194,7 @@ dataDieoff = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -196,7 +209,6 @@ dataDieoff = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -221,6 +233,7 @@ dataDieoff = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipitation1hr/server/dataFunctions/data_histogram.js b/apps/precipitation1hr/server/dataFunctions/data_histogram.js index 3f1324a817..466a9c0f4f 100644 --- a/apps/precipitation1hr/server/dataFunctions/data_histogram.js +++ b/apps/precipitation1hr/server/dataFunctions/data_histogram.js @@ -11,6 +11,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataHistogram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -21,53 +22,42 @@ dataHistogram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const alreadyMatched = false; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries const dataFoundForCurve = []; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const alreadyMatched = false; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; + + const axisMap = Object.create(null); + let statType; + let varUnits; + + let statement = ""; + let error = ""; const dataset = []; const allReturnedSubStats = []; const allReturnedSubSecs = []; - const axisMap = Object.create(null); // process user bin customizations const binParams = matsDataUtils.setHistogramParameters(plotParams); const { yAxisFormat } = binParams; const { binNum } = binParams; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; dataFoundForCurve[curveIndex] = true; const { label } = curve; + const { diffFrom } = curve; + const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${grid_scale}${sourceStr}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -76,17 +66,26 @@ dataHistogram = function (plotParams, plotFunction) { thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold * 0.01}`; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; } + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -94,12 +93,26 @@ dataHistogram = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.hit) as hit, sum(m0.fa) as fa, sum(m0.miss) as miss, sum(m0.cn) as cn, group_concat(m0.time, ';', m0.hit, ';', m0.fa, ';', m0.miss, ';', m0.cn order by m0.time) as sub_data, count(m0.hit) as N0"; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${scale}${source}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; - var varUnits = statisticOptionsMap[statisticSelect][1]; + [statType] = statisticOptionsMap[statisticSelect]; + [, varUnits] = statisticOptionsMap[statisticSelect]; let axisKey = yAxisFormat; if (yAxisFormat === "Relative frequency") { axisKey += " (x100)"; @@ -107,46 +120,46 @@ dataHistogram = function (plotParams, plotFunction) { curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options curves[curveIndex].binNum = binNum; // stash the binNum to use it later for bar chart options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.time as avtime, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.time as avtime, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -164,6 +177,7 @@ dataHistogram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/precipitation1hr/server/dataFunctions/data_perfDiagram.js b/apps/precipitation1hr/server/dataFunctions/data_perfDiagram.js index ea562f906e..2d30b32150 100644 --- a/apps/precipitation1hr/server/dataFunctions/data_perfDiagram.js +++ b/apps/precipitation1hr/server/dataFunctions/data_perfDiagram.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataPerformanceDiagram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,61 +23,43 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statType; + + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const binParam = curve["bin-parameter"]; const binClause = matsCollections["bin-parameter"].findOne({ name: "bin-parameter", }).optionsMap[binParam]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${grid_scale}${sourceStr}_${region} as m0`; + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let dateString = ""; - let dateClause = ""; if (binParam !== "Threshold") { - var thresholdStr = curve.threshold; + const thresholdStr = curve.threshold; if (thresholdStr === undefined) { throw new Error( `INFO: ${label}'s threshold is undefined. Please assign it a value.` @@ -91,12 +74,24 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold * 0.01}`; } + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + + let validTimeClause = ""; if (binParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.time%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (binParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; if (forecastLength === undefined) { @@ -106,62 +101,81 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + + const statisticSelect = "PerformanceDiagram"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + let dateString = ""; + let dateClause = ""; if (binParam === "Init Date") { dateString = "m0.time-m0.fcst_len*3600"; } else { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const statisticSelect = "PerformanceDiagram"; - var statType = "ctc"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${scale}${source}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // variable + statistic (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + statType = "ctc"; curves[curveIndex].axisKey = statisticSelect; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{binClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "((sum(m0.hit)+0.00)/sum(m0.hit+m0.miss)) as pod, ((sum(m0.fa)+0.00)/sum(m0.fa+m0.hit)) as far, " + - "sum(m0.hit+m0.miss) as oy_all, sum(m0.fa+m0.cn) as on_all, group_concat(m0.time, ';', m0.hit, ';', " + - "m0.fa, ';', m0.miss, ';', m0.cn order by m0.time) as sub_data, count(m0.hit) as N0 " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by binVal " + - "order by binVal" + - ";"; - - statement = statement.replace("{{binClause}}", binClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "{{binClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "((sum(m0.hit)+0.00)/sum(m0.hit+m0.miss)) as pod, ((sum(m0.fa)+0.00)/sum(m0.fa+m0.hit)) as far, " + + "sum(m0.hit+m0.miss) as oy_all, sum(m0.fa+m0.cn) as on_all, group_concat(m0.time, ';', m0.hit, ';', " + + "m0.fa, ';', m0.miss, ';', m0.cn order by m0.time) as sub_data, count(m0.hit) as N0 " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by binVal " + + "order by binVal" + + ";"; + + statement = statement.replace("{{binClause}}", binClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBPerformanceDiagram( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -177,6 +191,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -198,7 +213,6 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -206,7 +220,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { ymax = ymax > d.ymax ? ymax : d.ymax; } } else { - // this is a difference curve -- not supported for ROC plots + // this is a difference curve -- not supported for performance diagrams throw new Error( "INFO: Difference curves are not supported for performance diagrams, as they do not feature consistent x or y values across all curves." ); @@ -214,6 +228,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipitation1hr/server/dataFunctions/data_series.js b/apps/precipitation1hr/server/dataFunctions/data_series.js index 427ba3c98f..698d87c047 100644 --- a/apps/precipitation1hr/server/dataFunctions/data_series.js +++ b/apps/precipitation1hr/server/dataFunctions/data_series.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSeries = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,53 +24,42 @@ dataSeries = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${grid_scale}${sourceStr}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -78,20 +68,26 @@ dataSeries = function (plotParams, plotFunction) { thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold * 0.01}`; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; } + let forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; - const averageStr = curve.average; - const averageOptionsMap = matsCollections.average.findOne( - { name: "average" }, - { optionsMap: 1 } - ).optionsMap; - const average = averageOptionsMap[averageStr][0]; + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -99,11 +95,30 @@ dataSeries = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.hit) as hit, sum(m0.fa) as fa, sum(m0.miss) as miss, sum(m0.cn) as cn, group_concat(m0.time, ';', m0.hit, ';', m0.fa, ';', m0.miss, ';', m0.cn order by m0.time) as sub_data, count(m0.hit) as N0"; + + const averageStr = curve.average; + const averageOptionsMap = matsCollections.average.findOne( + { name: "average" }, + { optionsMap: 1 } + ).optionsMap; + const average = averageOptionsMap[averageStr][0]; + + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${scale}${source}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -111,47 +126,45 @@ dataSeries = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select {{average}} as avtime, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select {{average}} as avtime, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; - statement = statement.replace("{{average}}", average); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; + statement = statement.replace("{{average}}", average); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; - // math is done on forecastLength later on -- set all analyses to 0 - if (forecastLength === "-99") { - forecastLength = "0"; - } + // math is done on forecastLength later on -- set all analyses to 0 + if (forecastLength === "-99") { + forecastLength = "0"; + } - var queryResult; - const startMoment = moment(); - var finishMoment; - try { // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBTimeSeries( - sumPool, + sumPool, // eslint-disable-line no-undef statement, model, forecastLength, @@ -163,7 +176,9 @@ dataSeries = function (plotParams, plotFunction) { appParams, false ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -179,6 +194,7 @@ dataSeries = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -193,7 +209,6 @@ dataSeries = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -218,6 +233,7 @@ dataSeries = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipitation1hr/server/dataFunctions/data_threshold.js b/apps/precipitation1hr/server/dataFunctions/data_threshold.js index 7769401475..a7f304c54f 100644 --- a/apps/precipitation1hr/server/dataFunctions/data_threshold.js +++ b/apps/precipitation1hr/server/dataFunctions/data_threshold.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataThreshold = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,61 +24,56 @@ dataThreshold = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( + + const scaleStr = curve.scale; + const scale = Object.keys( matsCollections.scale.findOne({ name: "scale" }).valuesMap ).find( (key) => matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${grid_scale}${sourceStr}_${region} as m0`; - const thresholdClause = ""; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; } + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -85,11 +81,26 @@ dataThreshold = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.hit) as hit, sum(m0.fa) as fa, sum(m0.miss) as miss, sum(m0.cn) as cn, group_concat(m0.time, ';', m0.hit, ';', m0.fa, ';', m0.miss, ';', m0.cn order by m0.time) as sub_data, count(m0.hit) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${scale}${source}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -97,46 +108,44 @@ dataThreshold = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.trsh as thresh, " + // produces thresholds in in - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by thresh " + - "order by thresh" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.trsh as thresh, " + // produces thresholds in in + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by thresh " + + "order by thresh" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -152,6 +161,7 @@ dataThreshold = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -173,7 +183,6 @@ dataThreshold = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -198,6 +207,7 @@ dataThreshold = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipitation1hr/server/dataFunctions/data_validtime.js b/apps/precipitation1hr/server/dataFunctions/data_validtime.js index eae8cb0b8d..aab14f122b 100644 --- a/apps/precipitation1hr/server/dataFunctions/data_validtime.js +++ b/apps/precipitation1hr/server/dataFunctions/data_validtime.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataValidTime = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,50 +24,38 @@ dataValidTime = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const source = curve.truth; - let sourceStr = ""; - if (source !== "All") { - sourceStr = `_${source}`; - } - const queryTableClause = `from ${model}_${grid_scale}${sourceStr}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap ).find( @@ -75,12 +64,20 @@ dataValidTime = function (plotParams, plotFunction) { thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold * 0.01}`; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const source = curve.truth === "All" ? "" : `_${curve.truth}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -88,11 +85,26 @@ dataValidTime = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.hit) as hit, sum(m0.fa) as fa, sum(m0.miss) as miss, sum(m0.cn) as cn, group_concat(m0.time, ';', m0.hit, ';', m0.fa, ';', m0.miss, ';', m0.cn order by m0.time) as sub_data, count(m0.hit) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_${scale}${source}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -100,44 +112,44 @@ dataValidTime = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select floor(m0.time%(24*3600)/3600) as hr_of_day, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{forecastLengthClause}} " + - "group by hr_of_day " + - "order by hr_of_day" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select floor(m0.time%(24*3600)/3600) as hr_of_day, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{forecastLengthClause}} " + + "group by hr_of_day " + + "order by hr_of_day" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -153,6 +165,7 @@ dataValidTime = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -167,7 +180,6 @@ dataValidTime = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -192,6 +204,7 @@ dataValidTime = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/precipitation1hr/server/main.js b/apps/precipitation1hr/server/main.js index cc3538dd26..29c9162f25 100644 --- a/apps/precipitation1hr/server/main.js +++ b/apps/precipitation1hr/server/main.js @@ -4,7 +4,9 @@ import { Meteor } from "meteor/meteor"; import { mysql } from "meteor/pcel:mysql"; +import { moment } from "meteor/momentjs:moment"; import { + matsMethods, matsTypes, matsCollections, matsDataUtils, @@ -319,10 +321,11 @@ const doCurveParams = function () { const params = matsCollections.CurveParamsInfo.find({ curve_params: { $exists: true }, }).fetch()[0].curve_params; - for (let cp = 0; cp < params.length; cp++) { + for (let cp = 0; cp < params.length; cp += 1) { matsCollections[params[cp]].remove({}); } } + const modelOptionsMap = {}; let modelDateRangeMap = {}; const regionModelOptionsMap = {}; @@ -330,128 +333,105 @@ const doCurveParams = function () { const thresholdsModelOptionsMap = {}; const scaleModelOptionsMap = {}; const sourceOptionsMap = {}; - const masterRegionValuesMap = {}; - const masterThresholdValuesMap = {}; - const masterScaleValuesMap = {}; + const allRegionValuesMap = {}; + const allThresholdValuesMap = {}; + const allScaleValuesMap = {}; try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - metadataPool, + metadataPool, // eslint-disable-line no-undef "select short_name,description from region_descriptions;" ); - let masterRegDescription; - let masterShortName; - for (var j = 0; j < rows.length; j++) { - masterRegDescription = rows[j].description.trim(); - masterShortName = rows[j].short_name.trim(); - masterRegionValuesMap[masterShortName] = masterRegDescription; + for (let j = 0; j < rows.length; j += 1) { + allRegionValuesMap[rows[j].short_name.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select trsh,description from threshold_descriptions;" ); - let masterDescription; - let masterTrsh; - for (var j = 0; j < rows.length; j++) { - masterDescription = rows[j].description.trim(); - masterTrsh = rows[j].trsh.trim(); - masterThresholdValuesMap[masterTrsh] = masterDescription; + for (let j = 0; j < rows.length; j += 1) { + allThresholdValuesMap[rows[j].trsh.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select scle,description from scale_descriptions;" ); - let masterScaleDescription; - let masterScale; - for (var j = 0; j < rows.length; j++) { - masterScaleDescription = rows[j].description.trim(); - masterScale = rows[j].scle.trim(); - masterScaleValuesMap[masterScale] = masterScaleDescription; + for (let j = 0; j < rows.length; j += 1) { + allScaleValuesMap[rows[j].scle.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select model,regions,sources,display_text,fcst_lens,thresh,scale,mindate,maxdate from regions_per_model_mats_all_categories order by display_category, display_order;" ); - for (let i = 0; i < rows.length; i++) { - const model_value = rows[i].model.trim(); + for (let i = 0; i < rows.length; i += 1) { + const modelValue = rows[i].model.trim(); const model = rows[i].display_text.trim(); - modelOptionsMap[model] = [model_value]; + modelOptionsMap[model] = [modelValue]; const rowMinDate = moment.utc(rows[i].mindate * 1000).format("MM/DD/YYYY HH:mm"); const rowMaxDate = moment.utc(rows[i].maxdate * 1000).format("MM/DD/YYYY HH:mm"); - modelDateRangeMap[model] = { minDate: rowMinDate, maxDate: rowMaxDate }; + modelDateRangeMap[model] = { + minDate: rowMinDate, + maxDate: rowMaxDate, + }; const { sources } = rows[i]; - const sourceArr = sources + sourceOptionsMap[model] = sources .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < sourceArr.length; j++) { - sourceArr[j] = sourceArr[j].replace(/'|\[|\]/g, ""); - } - sourceOptionsMap[model] = sourceArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (source) { + return source.replace(/'|\[|\]/g, ""); + }); const forecastLengths = rows[i].fcst_lens; - const forecastLengthArr = forecastLengths + forecastLengthOptionsMap[model] = forecastLengths .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < forecastLengthArr.length; j++) { - forecastLengthArr[j] = forecastLengthArr[j].replace(/'|\[|\]/g, ""); - } - forecastLengthOptionsMap[model] = forecastLengthArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (fhr) { + return fhr.replace(/'|\[|\]/g, ""); + }); const thresholds = rows[i].thresh; - const thresholdsArrRaw = thresholds + thresholdsModelOptionsMap[model] = thresholds .split(",") - .map(Function.prototype.call, String.prototype.trim); - const thresholdsArr = []; - var dummyThresh; - for (var j = 0; j < thresholdsArrRaw.length; j++) { - dummyThresh = thresholdsArrRaw[j].replace(/'|\[|\]/g, ""); - thresholdsArr.push(masterThresholdValuesMap[dummyThresh]); - } - thresholdsModelOptionsMap[model] = thresholdsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (threshold) { + return allThresholdValuesMap[threshold.replace(/'|\[|\]/g, "")]; + }); const scales = rows[i].scale; - const scalesArrRaw = scales + scaleModelOptionsMap[model] = scales .split(",") - .map(Function.prototype.call, String.prototype.trim); - const scalesArr = []; - var dummyScale; - for (var j = 0; j < scalesArrRaw.length; j++) { - dummyScale = scalesArrRaw[j].replace(/'|\[|\]/g, ""); - scalesArr.push(masterScaleValuesMap[dummyScale]); - } - scaleModelOptionsMap[model] = scalesArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (scale) { + return allScaleValuesMap[scale.replace(/'|\[|\]/g, "")]; + }); const { regions } = rows[i]; - const regionsArrRaw = regions + regionModelOptionsMap[model] = regions .split(",") - .map(Function.prototype.call, String.prototype.trim); - const regionsArr = []; - var dummyRegion; - for (var j = 0; j < regionsArrRaw.length; j++) { - dummyRegion = regionsArrRaw[j].replace(/'|\[|\]/g, ""); - regionsArr.push(masterRegionValuesMap[dummyRegion]); - } - regionModelOptionsMap[model] = regionsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (region) { + return allRegionValuesMap[region.replace(/'|\[|\]/g, "")]; + }); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } if (matsCollections.label.findOne({ name: "label" }) === undefined) { @@ -497,7 +477,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["data-source"].findOne({ name: "data-source" }); + const currentParam = matsCollections["data-source"].findOne({ + name: "data-source", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, modelOptionsMap) || !matsDataUtils.areObjectsEqual(currentParam.dates, modelDateRangeMap) @@ -523,7 +505,7 @@ const doCurveParams = function () { type: matsTypes.InputTypes.select, optionsMap: regionModelOptionsMap, options: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]], - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, superiorNames: ["data-source"], controlButtonCovered: true, unique: false, @@ -535,10 +517,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.region.findOne({ name: "region" }); + const currentParam = matsCollections.region.findOne({ name: "region" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, regionModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterRegionValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allRegionValuesMap) ) { // have to reload region data matsCollections.region.update( @@ -546,7 +528,7 @@ const doCurveParams = function () { { $set: { optionsMap: regionModelOptionsMap, - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, options: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]], default: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]][0], }, @@ -618,7 +600,7 @@ const doCurveParams = function () { type: matsTypes.InputTypes.select, optionsMap: thresholdsModelOptionsMap, options: thresholdsModelOptionsMap[Object.keys(thresholdsModelOptionsMap)[0]], - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, superiorNames: ["data-source"], controlButtonCovered: true, unique: false, @@ -630,13 +612,13 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.threshold.findOne({ name: "threshold" }); + const currentParam = matsCollections.threshold.findOne({ name: "threshold" }); if ( !matsDataUtils.areObjectsEqual( currentParam.optionsMap, thresholdsModelOptionsMap ) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterThresholdValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allThresholdValuesMap) ) { // have to reload threshold data matsCollections.threshold.update( @@ -644,7 +626,7 @@ const doCurveParams = function () { { $set: { optionsMap: thresholdsModelOptionsMap, - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, options: thresholdsModelOptionsMap[Object.keys(thresholdsModelOptionsMap)[0]], default: @@ -661,7 +643,7 @@ const doCurveParams = function () { type: matsTypes.InputTypes.select, optionsMap: scaleModelOptionsMap, options: scaleModelOptionsMap[Object.keys(scaleModelOptionsMap)[0]], - valuesMap: masterScaleValuesMap, + valuesMap: allScaleValuesMap, superiorNames: ["data-source"], controlButtonCovered: true, unique: false, @@ -673,10 +655,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.scale.findOne({ name: "scale" }); + const currentParam = matsCollections.scale.findOne({ name: "scale" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, scaleModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterScaleValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allScaleValuesMap) ) { // have to reload scale data matsCollections.scale.update( @@ -684,7 +666,7 @@ const doCurveParams = function () { { $set: { optionsMap: scaleModelOptionsMap, - valuesMap: masterScaleValuesMap, + valuesMap: allScaleValuesMap, options: scaleModelOptionsMap[Object.keys(scaleModelOptionsMap)[0]], default: scaleModelOptionsMap[Object.keys(scaleModelOptionsMap)[0]][0], }, @@ -710,7 +692,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.truth.findOne({ name: "truth" }); + const currentParam = matsCollections.truth.findOne({ name: "truth" }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, sourceOptionsMap)) { // have to reload truth data matsCollections.truth.update( @@ -748,7 +730,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["forecast-length"].findOne({ + const currentParam = matsCollections["forecast-length"].findOne({ name: "forecast-length", }); if ( @@ -1023,7 +1005,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["curve-dates"].findOne({ name: "curve-dates" }); + const currentParam = matsCollections["curve-dates"].findOne({ + name: "curve-dates", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.startDate, minDate) || !matsDataUtils.areObjectsEqual(currentParam.stopDate, maxDate) || @@ -1387,7 +1371,8 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { - console.log( + // eslint-disable-next-line no-console + console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); matsCollections.Databases.drop(); @@ -1404,7 +1389,7 @@ Meteor.startup(function () { databases = Meteor.settings.private.databases; } if (databases !== null && databases !== undefined && Array.isArray(databases)) { - for (let di = 0; di < databases.length; di++) { + for (let di = 0; di < databases.length; di += 1) { matsCollections.Databases.insert(databases[di]); } } @@ -1431,6 +1416,7 @@ Meteor.startup(function () { ); if (cbConnection) { // global cbScorecardSettingsPool + // eslint-disable-next-line no-undef cbScorecardSettingsPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1457,6 +1443,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (metadataSettings) { + // eslint-disable-next-line no-undef metadataPool = mysql.createPool(metadataSettings); allPools.push({ pool: "metadataPool", role: matsTypes.DatabaseRoles.META_DATA }); } @@ -1477,6 +1464,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (sumSettings) { + // eslint-disable-next-line no-undef sumPool = mysql.createPool(sumSettings); allPools.push({ pool: "sumPool", role: matsTypes.DatabaseRoles.SUMS_DATA }); } @@ -1497,7 +1485,7 @@ Meteor.startup(function () { appType: matsTypes.AppTypes.mats, }); } catch (error) { - console.log(error.message); + throw new Error(error.message); } }); @@ -1505,6 +1493,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doPlotGraph, doCurveParams, diff --git a/apps/ptype/.eslintrc.json b/apps/ptype/.eslintrc.json index 8b795b7df7..79d49c5bb6 100644 --- a/apps/ptype/.eslintrc.json +++ b/apps/ptype/.eslintrc.json @@ -28,22 +28,6 @@ "space-before-function-paren": "off", // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", - "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-plusplus": "warn", - "vars-on-top": "warn", - "no-var": "warn", - "block-scoped-var": "warn", - "no-loop-func": "warn", - "no-unused-vars": "warn", - "prefer-destructuring": "warn", - "no-param-reassign": "warn", - "camelcase": "warn", - "no-redeclare": "warn" + "prefer-arrow-callback": "off" } } diff --git a/apps/ptype/client/main.js b/apps/ptype/client/main.js index a87407a1f4..ecd922b6a2 100644 --- a/apps/ptype/client/main.js +++ b/apps/ptype/client/main.js @@ -2,6 +2,7 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; import "@fortawesome/fontawesome-free"; import "@fortawesome/fontawesome-free/css/all.css"; diff --git a/apps/ptype/server/dataFunctions/data_contour.js b/apps/ptype/server/dataFunctions/data_contour.js index bf0950d1fb..70c7edaf16 100644 --- a/apps/ptype/server/dataFunctions/data_contour.js +++ b/apps/ptype/server/dataFunctions/data_contour.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContour = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,70 +23,92 @@ dataContour = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; - const totalProcessingStart = moment(); + + const curves = JSON.parse(JSON.stringify(plotParams.curves)); + if (curves.length > 1) { + throw new Error("INFO: There must only be one added curve."); + } + + const axisMap = Object.create(null); + + let statement = ""; + let error = ""; + const dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - const curves = JSON.parse(JSON.stringify(plotParams.curves)); - if (curves.length > 1) { - throw new Error("INFO: There must only be one added curve."); - } - const dataset = []; - const axisMap = Object.create(null); - // initialize variables specific to the curve + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - const regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${model}_freq_${region} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; - let validTimeClause = ""; + let scaleClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; + const scaleStr = curve.scale; + if (xAxisParam !== "Grid scale" && yAxisParam !== "Grid scale") { + if (scaleStr === undefined) { + throw new Error( + `INFO: ${label}'s grid scale is undefined. Please assign it a value.` + ); + } + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + scaleClause = `and m0.scale = ${scale} `; + } + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.valid_secs%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = Number(curve["forecast-length"]) * 60; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } - if (xAxisParam !== "Grid scale" && yAxisParam !== "Grid scale") { - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - scaleClause = `and m0.scale = ${grid_scale} `; - } + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = statisticOptionsMap[statisticSelect][0]; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -96,102 +119,113 @@ dataContour = function (plotParams, plotFunction) { dateString = "m0.valid_secs"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = statisticOptionsMap[statisticSelect][0]; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_freq_${region} as m0`; + // For contours, this functions as the colorbar label. const statType = statisticOptionsMap[statisticSelect][1]; - curve.unitKey = statisticOptionsMap[statisticSelect][2]; + [, , curve.unitKey] = statisticOptionsMap[statisticSelect]; let d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{scaleClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; - - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - statement = statement.split("{{variable}}").join(variable); - dataRequests[label] = statement; - - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - if (error.includes("ER_NO_SUCH_TABLE")) { - throw new Error( - `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${dataSourceStr}]. ` + - `Choose a different scale to continue using this region.` - ); + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{scaleClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; + + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + statement = statement.split("{{variable}}").join(variable); + dataRequests[label] = statement; + + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); + } + + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { + // this is NOT an error just a no data condition + dataFoundForCurve = false; } else { - throw new Error(error); + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + if (error.includes("ER_NO_SUCH_TABLE")) { + throw new Error( + `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${model}]. ` + + `Choose a different scale to continue using this region.` + ); + } else { + throw new Error(error); + } } } - } - if (!dataFoundForCurve) { - // we found no data for any curves so don't bother proceeding - throw new Error("INFO: No valid data for any curves."); + if (!dataFoundForCurve) { + // we found no data for any curves so don't bother proceeding + throw new Error("INFO: No valid data for any curves."); + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); } - const postQueryStartMoment = moment(); - // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined diff --git a/apps/ptype/server/dataFunctions/data_contour_diff.js b/apps/ptype/server/dataFunctions/data_contour_diff.js index 2032dc5d99..df2e01a320 100644 --- a/apps/ptype/server/dataFunctions/data_contour_diff.js +++ b/apps/ptype/server/dataFunctions/data_contour_diff.js @@ -14,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContourDiff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -24,74 +25,98 @@ dataContourDiff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; let dataNotFoundForAnyCurve = false; - const showSignificance = false; - const totalProcessingStart = moment(); + + let curves = JSON.parse(JSON.stringify(plotParams.curves)); + const curvesLength = curves.length; + if (curvesLength !== 2) { + throw new Error("INFO: There must be two added curves."); + } + + const axisMap = Object.create(null); + const showSignificance = plotParams.significance !== "none"; + + let statType; + let statisticSelect; + + let statement = ""; + let error = ""; + let dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - let curves = JSON.parse(JSON.stringify(plotParams.curves)); - const curvesLength = curves.length; - if (curvesLength !== 2) { - throw new Error("INFO: There must be two added curves."); - } - let dataset = []; - const axisMap = Object.create(null); - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${model}_freq_${region} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; - let validTimeClause = ""; + let scaleClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; + const scaleStr = curve.scale; + if (xAxisParam !== "Grid scale" && yAxisParam !== "Grid scale") { + if (scaleStr === undefined) { + throw new Error( + `INFO: ${label}'s grid scale is undefined. Please assign it a value.` + ); + } + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + scaleClause = `and m0.scale = ${scale} `; + } + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.valid_secs%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = Number(curve["forecast-length"]) * 60; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } - if (xAxisParam !== "Grid scale" && yAxisParam !== "Grid scale") { - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - scaleClause = `and m0.scale = ${grid_scale} `; - } + + statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = statisticOptionsMap[statisticSelect][0]; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -102,98 +127,106 @@ dataContourDiff = function (plotParams, plotFunction) { dateString = "m0.valid_secs"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - var statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = statisticOptionsMap[statisticSelect][0]; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_freq_${region} as m0`; + // For contours, this functions as the colorbar label. - var statType = statisticOptionsMap[statisticSelect][1]; - curves[curveIndex].unitKey = statisticOptionsMap[statisticSelect][2]; + [, statType] = statisticOptionsMap[statisticSelect]; + [, , curve.unitKey] = statisticOptionsMap[statisticSelect]; - var d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{scaleClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; + let d; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{scaleClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - statement = statement.split("{{variable}}").join(variable); - dataRequests[label] = statement; + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + statement = statement.split("{{variable}}").join(variable); + dataRequests[label] = statement; - var queryResult; - const startMoment = moment(); - var finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - if (error.includes("ER_NO_SUCH_TABLE")) { - throw new Error( - `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${dataSourceStr}]. ` + - `Choose a different scale to continue using this region.` - ); - } else { - throw new Error(error); + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); + } + + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + if (error.includes("ER_NO_SUCH_TABLE")) { + throw new Error( + `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${model}]. ` + + `Choose a different scale to continue using this region.` + ); + } else { + throw new Error(error); + } } + dataNotFoundForAnyCurve = true; } - dataNotFoundForAnyCurve = true; + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); } - const postQueryStartMoment = moment(); - // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined @@ -239,14 +272,17 @@ dataContourDiff = function (plotParams, plotFunction) { showSignificance, undefined, statisticSelect, - statType === "ctc" + statType === "ctc", + statType === "scalar" ); - plotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); - curves = plotParams.curves; + const newPlotParams = plotParams; + newPlotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); + curves = newPlotParams.curves; dataset[0].name = matsPlotUtils.getCurveText( matsTypes.PlotTypes.contourDiff, curves[0] ); + dataset[1] = matsDataCurveOpsUtils.getContourSignificanceLayer(dataset); // process the data returned by the query const curveInfoParams = { curve: curves, statType, axisMap }; @@ -257,7 +293,7 @@ dataContourDiff = function (plotParams, plotFunction) { const result = matsDataProcessUtils.processDataContour( dataset, curveInfoParams, - plotParams, + newPlotParams, bookkeepingParams ); plotFunction(result); diff --git a/apps/ptype/server/dataFunctions/data_dailymodelcycle.js b/apps/ptype/server/dataFunctions/data_dailymodelcycle.js index adef91b43b..e0e8cb890d 100644 --- a/apps/ptype/server/dataFunctions/data_dailymodelcycle.js +++ b/apps/ptype/server/dataFunctions/data_dailymodelcycle.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDailyModelCycle = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,54 +24,57 @@ dataDailyModelCycle = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const scaleClause = `and m0.scale = ${grid_scale}`; - const queryTableClause = `from ${model}_freq_${region} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + const scaleClause = `and m0.scale = ${scale}`; + if (curve["utc-cycle-start"].length !== 1) { throw new Error( "INFO: Please select exactly one UTC Cycle Init Hour for this plot type." @@ -79,19 +83,32 @@ dataDailyModelCycle = function (plotParams, plotFunction) { const utcCycleStart = Number(curve["utc-cycle-start"][0]); utcCycleStarts[curveIndex] = utcCycleStart; const utcCycleStartClause = `and floor(((m0.valid_secs - m0.fcst_len*60))%(24*3600)/900)/4 IN(${utcCycleStart})`; + const forecastLengthClause = "and m0.fcst_len < 24 * 60"; - const dateClause = `and m0.valid_secs >= ${fromSecs} and m0.valid_secs <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap; const statisticClause = statisticOptionsMap[statisticSelect][0]; + + const dateClause = `and m0.valid_secs >= ${fromSecs} and m0.valid_secs <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_freq_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][1]; + [, statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][2]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][3]; @@ -99,47 +116,47 @@ dataDailyModelCycle = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.valid_secs as avtime, " + - "count(distinct m0.valid_secs) as N_times, " + - "min(m0.valid_secs) as min_secs, " + - "max(m0.valid_secs) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{utcCycleStartClause}} " + - "{{forecastLengthClause}} " + - "{{scaleClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{variable}}").join(variable); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.valid_secs as avtime, " + + "count(distinct m0.valid_secs) as N_times, " + + "min(m0.valid_secs) as min_secs, " + + "max(m0.valid_secs) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{utcCycleStartClause}} " + + "{{forecastLengthClause}} " + + "{{scaleClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{variable}}").join(variable); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -155,6 +172,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -164,7 +182,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; if (error.includes("ER_NO_SUCH_TABLE")) { throw new Error( - `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${dataSourceStr}]. ` + + `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${model}]. ` + `Choose a different scale to continue using this region.` ); } else { @@ -176,7 +194,6 @@ dataDailyModelCycle = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -201,6 +218,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ptype/server/dataFunctions/data_dieoff.js b/apps/ptype/server/dataFunctions/data_dieoff.js index 2bdf9b33d5..92a663f780 100644 --- a/apps/ptype/server/dataFunctions/data_dieoff.js +++ b/apps/ptype/server/dataFunctions/data_dieoff.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDieoff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,65 +24,87 @@ dataDieoff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const scaleClause = `and m0.scale = ${grid_scale}`; - const queryTableClause = `from ${model}_freq_${region} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; - var validTimes; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + const scaleClause = `and m0.scale = ${scale}`; + let validTimeClause = ""; - var utcCycleStart; + let validTimes; + let utcCycleStartClause = ""; + let utcCycleStart; + const forecastLengthStr = curve["dieoff-type"]; const forecastLengthOptionsMap = matsCollections["dieoff-type"].findOne( { name: "dieoff-type" }, { optionsMap: 1 } ).optionsMap; const forecastLength = forecastLengthOptionsMap[forecastLengthStr][0]; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = statisticOptionsMap[statisticSelect][0]; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; - var dateClause; + let dateClause; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_freq_${region} as m0`; + if (forecastLength === matsTypes.ForecastTypes.dieoff) { validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { @@ -98,17 +121,12 @@ dataDieoff = function (plotParams, plotFunction) { } else { dateClause = `and m0.valid_secs-m0.fcst_len*60 = ${fromSecs}`; } - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = statisticOptionsMap[statisticSelect][0]; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][1]; + [, statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][2]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][3]; @@ -116,47 +134,47 @@ dataDieoff = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.fcst_len/60 as fcst_lead, " + - "count(distinct m0.valid_secs) as N_times, " + - "min(m0.valid_secs) as min_secs, " + - "max(m0.valid_secs) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{utcCycleStartClause}} " + - "{{scaleClause}} " + - "group by fcst_lead " + - "order by fcst_lead" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{variable}}").join(variable); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.fcst_len/60 as fcst_lead, " + + "count(distinct m0.valid_secs) as N_times, " + + "min(m0.valid_secs) as min_secs, " + + "max(m0.valid_secs) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{utcCycleStartClause}} " + + "{{scaleClause}} " + + "group by fcst_lead " + + "order by fcst_lead" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{variable}}").join(variable); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -172,6 +190,7 @@ dataDieoff = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -181,7 +200,7 @@ dataDieoff = function (plotParams, plotFunction) { error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; if (error.includes("ER_NO_SUCH_TABLE")) { throw new Error( - `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${dataSourceStr}]. ` + + `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${model}]. ` + `Choose a different scale to continue using this region.` ); } else { @@ -193,7 +212,6 @@ dataDieoff = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -218,6 +236,7 @@ dataDieoff = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ptype/server/dataFunctions/data_histogram.js b/apps/ptype/server/dataFunctions/data_histogram.js index e3ffa28416..26433dab5e 100644 --- a/apps/ptype/server/dataFunctions/data_histogram.js +++ b/apps/ptype/server/dataFunctions/data_histogram.js @@ -11,6 +11,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataHistogram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -21,76 +22,90 @@ dataHistogram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const alreadyMatched = false; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries const dataFoundForCurve = []; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const alreadyMatched = false; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; + + const axisMap = Object.create(null); + let statType; + + let statement = ""; + let error = ""; const dataset = []; const allReturnedSubStats = []; const allReturnedSubSecs = []; - const axisMap = Object.create(null); // process user bin customizations const binParams = matsDataUtils.setHistogramParameters(plotParams); const { yAxisFormat } = binParams; const { binNum } = binParams; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; dataFoundForCurve[curveIndex] = true; const { label } = curve; + const { diffFrom } = curve; + const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const scaleClause = `and m0.scale = ${grid_scale}`; - const queryTableClause = `from ${model}_freq_${region} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + const scaleClause = `and m0.scale = ${scale}`; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and (m0.valid_secs)%(24*3600)/3600 IN(${validTimes})`; } + const forecastLength = Number(curve["forecast-length"]) * 60; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.valid_secs >= ${fromSecs} and m0.valid_secs <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap; const statisticClause = statisticOptionsMap[statisticSelect][0]; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.valid_secs >= ${fromSecs} and m0.valid_secs <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_freq_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][1]; + [, statType] = statisticOptionsMap[statisticSelect]; let axisKey = yAxisFormat; if (yAxisFormat === "Relative frequency") { axisKey += " (x100)"; @@ -98,47 +113,47 @@ dataHistogram = function (plotParams, plotFunction) { curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options curves[curveIndex].binNum = binNum; // stash the binNum to use it later for bar chart options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.valid_secs as avtime, " + - "count(distinct m0.valid_secs) as N_times, " + - "min(m0.valid_secs) as min_secs, " + - "max(m0.valid_secs) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{scaleClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{variable}}").join(variable); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.valid_secs as avtime, " + + "count(distinct m0.valid_secs) as N_times, " + + "min(m0.valid_secs) as min_secs, " + + "max(m0.valid_secs) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{scaleClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{variable}}").join(variable); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -156,6 +171,7 @@ dataHistogram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -165,7 +181,7 @@ dataHistogram = function (plotParams, plotFunction) { error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; if (error.includes("ER_NO_SUCH_TABLE")) { throw new Error( - `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${dataSourceStr}]. ` + + `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${model}]. ` + `Choose a different scale to continue using this region.` ); } else { diff --git a/apps/ptype/server/dataFunctions/data_series.js b/apps/ptype/server/dataFunctions/data_series.js index 35370a86bc..00cf621146 100644 --- a/apps/ptype/server/dataFunctions/data_series.js +++ b/apps/ptype/server/dataFunctions/data_series.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSeries = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,79 +24,96 @@ dataSeries = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const scaleClause = `and m0.scale = ${grid_scale}`; - const queryTableClause = `from ${model}_freq_${region} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + const scaleClause = `and m0.scale = ${scale}`; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and (m0.valid_secs)%(24*3600)/3600 IN(${validTimes})`; } + let forecastLength = Number(curve["forecast-length"]) * 60; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateClause = `and m0.valid_secs >= ${fromSecs} and m0.valid_secs <= ${toSecs}`; - const averageStr = curve.average; - const averageOptionsMap = matsCollections.average.findOne( - { name: "average" }, - { optionsMap: 1 } - ).optionsMap; - const average = averageOptionsMap[averageStr][0]; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap; const statisticClause = statisticOptionsMap[statisticSelect][0]; + + const averageStr = curve.average; + const averageOptionsMap = matsCollections.average.findOne( + { name: "average" }, + { optionsMap: 1 } + ).optionsMap; + const average = averageOptionsMap[averageStr][0]; + + const dateClause = `and m0.valid_secs >= ${fromSecs} and m0.valid_secs <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_freq_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][1]; + [, statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][2]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][3]; @@ -103,48 +121,46 @@ dataSeries = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select {{average}} as avtime, " + - "count(distinct m0.valid_secs) as N_times, " + - "min(m0.valid_secs) as min_secs, " + - "max(m0.valid_secs) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{scaleClause}} " + - "group by avtime " + - "order by avtime" + - ";"; + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select {{average}} as avtime, " + + "count(distinct m0.valid_secs) as N_times, " + + "min(m0.valid_secs) as min_secs, " + + "max(m0.valid_secs) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{scaleClause}} " + + "group by avtime " + + "order by avtime" + + ";"; - statement = statement.replace("{{average}}", average); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{variable}}").join(variable); - dataRequests[label] = statement; + statement = statement.replace("{{average}}", average); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{variable}}").join(variable); + dataRequests[label] = statement; - // math is done on forecastLength later on -- set all analyses to 0 - if (forecastLength === "-99") { - forecastLength = "0"; - } + // math is done on forecastLength later on -- set all analyses to 0 + if (forecastLength === "-99") { + forecastLength = "0"; + } - var queryResult; - const startMoment = moment(); - var finishMoment; - try { // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBTimeSeries( - sumPool, + sumPool, // eslint-disable-line no-undef statement, model, forecastLength, @@ -156,7 +172,9 @@ dataSeries = function (plotParams, plotFunction) { appParams, false ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -172,6 +190,7 @@ dataSeries = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -181,7 +200,7 @@ dataSeries = function (plotParams, plotFunction) { error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; if (error.includes("ER_NO_SUCH_TABLE")) { throw new Error( - `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${dataSourceStr}]. ` + + `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${model}]. ` + `Choose a different scale to continue using this region.` ); } else { @@ -193,7 +212,6 @@ dataSeries = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -218,6 +236,7 @@ dataSeries = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ptype/server/dataFunctions/data_validtime.js b/apps/ptype/server/dataFunctions/data_validtime.js index 7f3c96d483..78154000ee 100644 --- a/apps/ptype/server/dataFunctions/data_validtime.js +++ b/apps/ptype/server/dataFunctions/data_validtime.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataValidTime = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,68 +24,82 @@ dataValidTime = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const scaleClause = `and m0.scale = ${grid_scale}`; - const queryTableClause = `from ${model}_freq_${region} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + const scaleClause = `and m0.scale = ${scale}`; + const forecastLength = Number(curve["forecast-length"]) * 60; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.valid_secs >= ${fromSecs} and m0.valid_secs <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, { optionsMap: 1 } ).optionsMap; const statisticClause = statisticOptionsMap[statisticSelect][0]; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.valid_secs >= ${fromSecs} and m0.valid_secs <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${model}_freq_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][1]; + [, statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][2]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][3]; @@ -92,45 +107,45 @@ dataValidTime = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select floor((m0.valid_secs)%(24*3600)/900)/4 as hr_of_day, " + - "count(distinct m0.valid_secs) as N_times, " + - "min(m0.valid_secs) as min_secs, " + - "max(m0.valid_secs) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{forecastLengthClause}} " + - "{{scaleClause}} " + - "group by hr_of_day " + - "order by hr_of_day" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{variable}}").join(variable); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select floor((m0.valid_secs)%(24*3600)/900)/4 as hr_of_day, " + + "count(distinct m0.valid_secs) as N_times, " + + "min(m0.valid_secs) as min_secs, " + + "max(m0.valid_secs) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{forecastLengthClause}} " + + "{{scaleClause}} " + + "group by hr_of_day " + + "order by hr_of_day" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{variable}}").join(variable); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -146,6 +161,7 @@ dataValidTime = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -155,7 +171,7 @@ dataValidTime = function (plotParams, plotFunction) { error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; if (error.includes("ER_NO_SUCH_TABLE")) { throw new Error( - `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${dataSourceStr}]. ` + + `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${model}]. ` + `Choose a different scale to continue using this region.` ); } else { @@ -167,7 +183,6 @@ dataValidTime = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -192,6 +207,7 @@ dataValidTime = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/ptype/server/main.js b/apps/ptype/server/main.js index 8e1efa8af2..baf82d999b 100644 --- a/apps/ptype/server/main.js +++ b/apps/ptype/server/main.js @@ -4,7 +4,9 @@ import { Meteor } from "meteor/meteor"; import { mysql } from "meteor/pcel:mysql"; +import { moment } from "meteor/momentjs:moment"; import { + matsMethods, matsTypes, matsCollections, matsDataUtils, @@ -304,118 +306,99 @@ const doCurveParams = function () { const params = matsCollections.CurveParamsInfo.find({ curve_params: { $exists: true }, }).fetch()[0].curve_params; - for (let cp = 0; cp < params.length; cp++) { + for (let cp = 0; cp < params.length; cp += 1) { matsCollections[params[cp]].remove({}); } } + const modelOptionsMap = {}; let modelDateRangeMap = {}; const regionModelOptionsMap = {}; const forecastLengthOptionsMap = {}; const scaleModelOptionsMap = {}; - const masterRegionValuesMap = {}; - const masterScaleValuesMap = {}; - const masterVariableValuesMap = {}; + const allRegionValuesMap = {}; + const allScaleValuesMap = {}; + const allVariableValuesMap = {}; try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - metadataPool, + metadataPool, // eslint-disable-line no-undef "select short_name,description from region_descriptions;" ); - let masterRegDescription; - let masterShortName; - for (var j = 0; j < rows.length; j++) { - masterRegDescription = rows[j].description.trim(); - masterShortName = rows[j].short_name.trim(); - masterRegionValuesMap[masterShortName] = masterRegDescription; + for (let j = 0; j < rows.length; j += 1) { + allRegionValuesMap[rows[j].short_name.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, - "select scle,description from scale_descriptions;" + sumPool, // eslint-disable-line no-undef + "select ptype,description from ptype_descriptions;" ); - let masterDescription; - let masterScale; - for (var j = 0; j < rows.length; j++) { - masterDescription = rows[j].description.trim(); - masterScale = rows[j].scle.trim(); - masterScaleValuesMap[masterScale] = masterDescription; + for (let j = 0; j < rows.length; j += 1) { + allVariableValuesMap[rows[j].description.trim()] = rows[j].ptype.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, - "select ptype,description from ptype_descriptions;" + sumPool, // eslint-disable-line no-undef + "select scle,description from scale_descriptions;" ); - let masterVarDescription; - let masterVariable; - for (var j = 0; j < rows.length; j++) { - masterVarDescription = rows[j].description.trim(); - masterVariable = rows[j].ptype.trim(); - masterVariableValuesMap[masterVarDescription] = masterVariable; + for (let j = 0; j < rows.length; j += 1) { + allScaleValuesMap[rows[j].scle.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select model,regions,display_text,fcst_lens,scale,mindate,maxdate from regions_per_model_mats_all_categories order by display_category, display_order;" ); - for (let i = 0; i < rows.length; i++) { - const model_value = rows[i].model.trim(); + for (let i = 0; i < rows.length; i += 1) { + const modelValue = rows[i].model.trim(); const model = rows[i].display_text.trim(); - modelOptionsMap[model] = [model_value]; + modelOptionsMap[model] = [modelValue]; const rowMinDate = moment.utc(rows[i].mindate * 1000).format("MM/DD/YYYY HH:mm"); const rowMaxDate = moment.utc(rows[i].maxdate * 1000).format("MM/DD/YYYY HH:mm"); - modelDateRangeMap[model] = { minDate: rowMinDate, maxDate: rowMaxDate }; + modelDateRangeMap[model] = { + minDate: rowMinDate, + maxDate: rowMaxDate, + }; const forecastLengths = rows[i].fcst_lens; - const forecastLengthArr = forecastLengths + forecastLengthOptionsMap[model] = forecastLengths .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < forecastLengthArr.length; j++) { - forecastLengthArr[j] = ( - Number(forecastLengthArr[j].replace(/'|\[|\]/g, "")) / 60 - ).toString(); - } - forecastLengthOptionsMap[model] = forecastLengthArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (fhr) { + return Number(fhr.replace(/'|\[|\]/g, "")) / 60; + }); const scales = rows[i].scale; - const scalesArrRaw = scales + scaleModelOptionsMap[model] = scales .split(",") - .map(Function.prototype.call, String.prototype.trim); - const scalesArr = []; - var dummyScale; - for (var j = 0; j < scalesArrRaw.length; j++) { - dummyScale = scalesArrRaw[j].replace(/'|\[|\]/g, ""); - scalesArr.push(masterScaleValuesMap[dummyScale]); - } - scaleModelOptionsMap[model] = scalesArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (scale) { + return allScaleValuesMap[scale.replace(/'|\[|\]/g, "")]; + }); const { regions } = rows[i]; - const regionsArrRaw = regions + regionModelOptionsMap[model] = regions .split(",") - .map(Function.prototype.call, String.prototype.trim); - const regionsArr = []; - var dummyRegion; - for (var j = 0; j < regionsArrRaw.length; j++) { - dummyRegion = regionsArrRaw[j].replace(/'|\[|\]/g, ""); - regionsArr.push(masterRegionValuesMap[dummyRegion]); - } - regionModelOptionsMap[model] = regionsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (region) { + return allRegionValuesMap[region.replace(/'|\[|\]/g, "")]; + }); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } if (matsCollections.label.findOne({ name: "label" }) === undefined) { @@ -453,7 +436,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["data-source"].findOne({ name: "data-source" }); + const currentParam = matsCollections["data-source"].findOne({ + name: "data-source", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, modelOptionsMap) || !matsDataUtils.areObjectsEqual(currentParam.dates, modelDateRangeMap) @@ -479,7 +464,7 @@ const doCurveParams = function () { type: matsTypes.InputTypes.select, optionsMap: regionModelOptionsMap, options: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]], - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, superiorNames: ["data-source"], controlButtonCovered: true, unique: false, @@ -491,10 +476,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.region.findOne({ name: "region" }); + const currentParam = matsCollections.region.findOne({ name: "region" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, regionModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterRegionValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allRegionValuesMap) ) { // have to reload region data matsCollections.region.update( @@ -502,7 +487,7 @@ const doCurveParams = function () { { $set: { optionsMap: regionModelOptionsMap, - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, options: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]], default: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]][0], }, @@ -526,7 +511,6 @@ const doCurveParams = function () { null, ], }; - matsCollections.statistic.insert({ name: "statistic", type: matsTypes.InputTypes.select, @@ -546,11 +530,11 @@ const doCurveParams = function () { matsCollections.variable.insert({ name: "variable", type: matsTypes.InputTypes.select, - optionsMap: masterVariableValuesMap, - options: Object.keys(masterVariableValuesMap), + optionsMap: allVariableValuesMap, + options: Object.keys(allVariableValuesMap), controlButtonCovered: true, unique: false, - default: Object.keys(masterVariableValuesMap)[0], + default: Object.keys(allVariableValuesMap)[0], controlButtonVisibility: "block", displayOrder: 2, displayPriority: 1, @@ -558,18 +542,16 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.variable.findOne({ name: "variable" }); - if ( - !matsDataUtils.areObjectsEqual(currentParam.optionsMap, masterVariableValuesMap) - ) { + const currentParam = matsCollections.variable.findOne({ name: "variable" }); + if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, allVariableValuesMap)) { // have to reload variable data matsCollections.variable.update( { name: "variable" }, { $set: { - optionsMap: masterVariableValuesMap, - options: masterVariableValuesMap[Object.keys(masterVariableValuesMap)], - default: masterVariableValuesMap[Object.keys(masterVariableValuesMap)[0]], + optionsMap: allVariableValuesMap, + options: allVariableValuesMap[Object.keys(allVariableValuesMap)], + default: allVariableValuesMap[Object.keys(allVariableValuesMap)[0]], }, } ); @@ -582,7 +564,7 @@ const doCurveParams = function () { type: matsTypes.InputTypes.select, optionsMap: scaleModelOptionsMap, options: scaleModelOptionsMap[Object.keys(scaleModelOptionsMap)[0]], - valuesMap: masterScaleValuesMap, + valuesMap: allScaleValuesMap, superiorNames: ["data-source"], controlButtonCovered: true, unique: false, @@ -594,10 +576,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.scale.findOne({ name: "scale" }); + const currentParam = matsCollections.scale.findOne({ name: "scale" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, scaleModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterScaleValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allScaleValuesMap) ) { // have to reload scale data matsCollections.scale.update( @@ -605,7 +587,7 @@ const doCurveParams = function () { { $set: { optionsMap: scaleModelOptionsMap, - valuesMap: masterScaleValuesMap, + valuesMap: allScaleValuesMap, options: scaleModelOptionsMap[Object.keys(scaleModelOptionsMap)[0]], default: scaleModelOptionsMap[Object.keys(scaleModelOptionsMap)[0]][0], }, @@ -636,7 +618,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["forecast-length"].findOne({ + const currentParam = matsCollections["forecast-length"].findOne({ name: "forecast-length", }); if ( @@ -868,7 +850,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["curve-dates"].findOne({ name: "curve-dates" }); + const currentParam = matsCollections["curve-dates"].findOne({ + name: "curve-dates", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.startDate, minDate) || !matsDataUtils.areObjectsEqual(currentParam.stopDate, maxDate) || @@ -1153,7 +1137,8 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { - console.log( + // eslint-disable-next-line no-console + console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); matsCollections.Databases.drop(); @@ -1170,7 +1155,7 @@ Meteor.startup(function () { databases = Meteor.settings.private.databases; } if (databases !== null && databases !== undefined && Array.isArray(databases)) { - for (let di = 0; di < databases.length; di++) { + for (let di = 0; di < databases.length; di += 1) { matsCollections.Databases.insert(databases[di]); } } @@ -1197,6 +1182,7 @@ Meteor.startup(function () { ); if (cbConnection) { // global cbScorecardSettingsPool + // eslint-disable-next-line no-undef cbScorecardSettingsPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1223,6 +1209,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (metadataSettings) { + // eslint-disable-next-line no-undef metadataPool = mysql.createPool(metadataSettings); allPools.push({ pool: "metadataPool", role: matsTypes.DatabaseRoles.META_DATA }); } @@ -1243,6 +1230,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (sumSettings) { + // eslint-disable-next-line no-undef sumPool = mysql.createPool(sumSettings); allPools.push({ pool: "sumPool", role: matsTypes.DatabaseRoles.SUMS_DATA }); } @@ -1263,7 +1251,7 @@ Meteor.startup(function () { appType: matsTypes.AppTypes.mats, }); } catch (error) { - console.log(error.message); + throw new Error(error.message); } }); @@ -1271,6 +1259,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doPlotGraph, doCurveParams, diff --git a/apps/radar/.eslintrc.json b/apps/radar/.eslintrc.json index 8b795b7df7..79d49c5bb6 100644 --- a/apps/radar/.eslintrc.json +++ b/apps/radar/.eslintrc.json @@ -28,22 +28,6 @@ "space-before-function-paren": "off", // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", - "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-plusplus": "warn", - "vars-on-top": "warn", - "no-var": "warn", - "block-scoped-var": "warn", - "no-loop-func": "warn", - "no-unused-vars": "warn", - "prefer-destructuring": "warn", - "no-param-reassign": "warn", - "camelcase": "warn", - "no-redeclare": "warn" + "prefer-arrow-callback": "off" } } diff --git a/apps/radar/client/main.js b/apps/radar/client/main.js index a87407a1f4..ecd922b6a2 100644 --- a/apps/radar/client/main.js +++ b/apps/radar/client/main.js @@ -2,6 +2,7 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; import "@fortawesome/fontawesome-free"; import "@fortawesome/fontawesome-free/css/all.css"; diff --git a/apps/radar/server/dataFunctions/data_contour.js b/apps/radar/server/dataFunctions/data_contour.js index f37c909014..fbed93abc2 100644 --- a/apps/radar/server/dataFunctions/data_contour.js +++ b/apps/radar/server/dataFunctions/data_contour.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContour = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,58 +23,53 @@ dataContour = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; - const totalProcessingStart = moment(); + + const curves = JSON.parse(JSON.stringify(plotParams.curves)); + if (curves.length > 1) { + throw new Error("INFO: There must only be one added curve."); + } + + const axisMap = Object.create(null); + + let statement = ""; + let error = ""; + const dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - const curves = JSON.parse(JSON.stringify(plotParams.curves)); - if (curves.length > 1) { - throw new Error("INFO: There must only be one added curve."); - } - const dataset = []; - const axisMap = Object.create(null); - // initialize variables specific to the curve + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }).optionsMap[ variable ]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - const regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === - scaleStr - ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { const thresholdStr = curve.threshold; + if (thresholdStr === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -84,16 +80,45 @@ dataContour = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold / 10000}`; } + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === + scaleStr + ); + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.time%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -104,102 +129,112 @@ dataContour = function (plotParams, plotFunction) { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // For contours, this functions as the colorbar label. const statType = statisticOptionsMap[statisticSelect][0]; - curve.unitKey = statisticOptionsMap[statisticSelect][1]; + [, curve.unitKey] = statisticOptionsMap[statisticSelect]; let d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; - - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - if (error.includes("ER_NO_SUCH_TABLE")) { - throw new Error( - `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${model}]. ` + - `Choose a different scale to continue using this region.` - ); + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; + + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); + } + + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { + // this is NOT an error just a no data condition + dataFoundForCurve = false; } else { - throw new Error(error); + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + if (error.includes("ER_NO_SUCH_TABLE")) { + throw new Error( + `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${model}]. ` + + `Choose a different scale to continue using this region.` + ); + } else { + throw new Error(error); + } } } - } - if (!dataFoundForCurve) { - // we found no data for any curves so don't bother proceeding - throw new Error("INFO: No valid data for any curves."); + if (!dataFoundForCurve) { + // we found no data for any curves so don't bother proceeding + throw new Error("INFO: No valid data for any curves."); + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); } - const postQueryStartMoment = moment(); - // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined diff --git a/apps/radar/server/dataFunctions/data_contour_diff.js b/apps/radar/server/dataFunctions/data_contour_diff.js index f367787887..02d29f0126 100644 --- a/apps/radar/server/dataFunctions/data_contour_diff.js +++ b/apps/radar/server/dataFunctions/data_contour_diff.js @@ -14,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContourDiff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -24,61 +25,58 @@ dataContourDiff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; let dataNotFoundForAnyCurve = false; + + let curves = JSON.parse(JSON.stringify(plotParams.curves)); + const curvesLength = curves.length; + if (curvesLength !== 2) { + throw new Error("INFO: There must be two added curves."); + } + + const axisMap = Object.create(null); const showSignificance = plotParams.significance !== "none"; - const totalProcessingStart = moment(); + + let statType; + let statisticSelect; + + let statement = ""; + let error = ""; + let dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - let curves = JSON.parse(JSON.stringify(plotParams.curves)); - const curvesLength = curves.length; - if (curvesLength !== 2) { - throw new Error("INFO: There must be two added curves."); - } - let dataset = []; - const axisMap = Object.create(null); - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === - scaleStr - ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Threshold" && yAxisParam !== "Threshold") { - var thresholdStr = curve.threshold; + const thresholdStr = curve.threshold; + if (thresholdStr === undefined) { + throw new Error( + `INFO: ${label}'s threshold is undefined. Please assign it a value.` + ); + } const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -89,16 +87,45 @@ dataContourDiff = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold / 10000}`; } + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === + scaleStr + ); + + let validTimeClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.time%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -109,98 +136,105 @@ dataContourDiff = function (plotParams, plotFunction) { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - var statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // For contours, this functions as the colorbar label. - var statType = statisticOptionsMap[statisticSelect][0]; - curves[curveIndex].unitKey = statisticOptionsMap[statisticSelect][1]; + [statType] = statisticOptionsMap[statisticSelect]; + [, curve.unitKey] = statisticOptionsMap[statisticSelect]; - var d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; + let d; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; - var queryResult; - const startMoment = moment(); - var finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - statisticSelect - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - if (error.includes("ER_NO_SUCH_TABLE")) { - throw new Error( - `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${model}]. ` + - `Choose a different scale to continue using this region.` - ); - } else { - throw new Error(error); + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + statisticSelect + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); + } + + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + if (error.includes("ER_NO_SUCH_TABLE")) { + throw new Error( + `INFO: The region/scale combination [${regionStr} and ${scaleStr}] is not supported by the database for the model [${model}]. ` + + `Choose a different scale to continue using this region.` + ); + } else { + throw new Error(error); + } } + dataNotFoundForAnyCurve = true; } - dataNotFoundForAnyCurve = true; + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); } - const postQueryStartMoment = moment(); - // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined @@ -249,8 +283,9 @@ dataContourDiff = function (plotParams, plotFunction) { statType === "ctc", statType === "scalar" ); - plotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); - curves = plotParams.curves; + const newPlotParams = plotParams; + newPlotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); + curves = newPlotParams.curves; dataset[0].name = matsPlotUtils.getCurveText( matsTypes.PlotTypes.contourDiff, curves[0] @@ -266,7 +301,7 @@ dataContourDiff = function (plotParams, plotFunction) { const result = matsDataProcessUtils.processDataContour( dataset, curveInfoParams, - plotParams, + newPlotParams, bookkeepingParams ); plotFunction(result); diff --git a/apps/radar/server/dataFunctions/data_dailymodelcycle.js b/apps/radar/server/dataFunctions/data_dailymodelcycle.js index be4a3b51b7..5ca328ca9d 100644 --- a/apps/radar/server/dataFunctions/data_dailymodelcycle.js +++ b/apps/radar/server/dataFunctions/data_dailymodelcycle.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDailyModelCycle = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,52 +24,46 @@ dataDailyModelCycle = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === - scaleStr - ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -78,6 +73,16 @@ dataDailyModelCycle = function (plotParams, plotFunction) { ] === thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold / 10000}`; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === + scaleStr + ); + if (curve["utc-cycle-start"].length !== 1) { throw new Error( "INFO: Please select exactly one UTC Cycle Init Hour for this plot type." @@ -86,8 +91,9 @@ dataDailyModelCycle = function (plotParams, plotFunction) { const utcCycleStart = Number(curve["utc-cycle-start"][0]); utcCycleStarts[curveIndex] = utcCycleStart; const utcCycleStartClause = `and floor((m0.time - m0.fcst_len*3600)%(24*3600)/3600) IN(${utcCycleStart})`; + const forecastLengthClause = "and m0.fcst_len < 24"; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -95,11 +101,23 @@ dataDailyModelCycle = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -107,46 +125,46 @@ dataDailyModelCycle = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.time as avtime, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{utcCycleStartClause}} " + - "{{thresholdClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.time as avtime, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{utcCycleStartClause}} " + + "{{thresholdClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -162,6 +180,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -183,7 +202,6 @@ dataDailyModelCycle = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -208,6 +226,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/radar/server/dataFunctions/data_dieoff.js b/apps/radar/server/dataFunctions/data_dieoff.js index b9ef5bd281..08f8664c6f 100644 --- a/apps/radar/server/dataFunctions/data_dieoff.js +++ b/apps/radar/server/dataFunctions/data_dieoff.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDieoff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,49 +24,42 @@ dataDieoff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === - scaleStr - ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -75,21 +69,51 @@ dataDieoff = function (plotParams, plotFunction) { ] === thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold / 10000}`; - var validTimes; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === + scaleStr + ); + let validTimeClause = ""; - var utcCycleStart; + let validTimes; + let utcCycleStartClause = ""; + let utcCycleStart; + const forecastLengthStr = curve["dieoff-type"]; const forecastLengthOptionsMap = matsCollections["dieoff-type"].findOne( { name: "dieoff-type" }, { optionsMap: 1 } ).optionsMap; const forecastLength = forecastLengthOptionsMap[forecastLengthStr][0]; - const forecastLengthClause = ""; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; - var dateClause; + let dateClause; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + if (forecastLength === matsTypes.ForecastTypes.dieoff) { validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { @@ -106,18 +130,12 @@ dataDieoff = function (plotParams, plotFunction) { } else { dateClause = `and m0.time-m0.fcst_len*3600 = ${fromSecs}`; } - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -125,48 +143,46 @@ dataDieoff = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.fcst_len as fcst_lead, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{utcCycleStartClause}} " + - "group by fcst_lead " + - "order by fcst_lead" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.fcst_len as fcst_lead, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{utcCycleStartClause}} " + + "group by fcst_lead " + + "order by fcst_lead" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -182,6 +198,7 @@ dataDieoff = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -203,7 +220,6 @@ dataDieoff = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -228,6 +244,7 @@ dataDieoff = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/radar/server/dataFunctions/data_histogram.js b/apps/radar/server/dataFunctions/data_histogram.js index 1ea2f4bb97..00c9d5738d 100644 --- a/apps/radar/server/dataFunctions/data_histogram.js +++ b/apps/radar/server/dataFunctions/data_histogram.js @@ -11,6 +11,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataHistogram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -21,52 +22,45 @@ dataHistogram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const alreadyMatched = false; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries const dataFoundForCurve = []; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const alreadyMatched = false; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; + + const axisMap = Object.create(null); + let statType; + let varUnits; + + let statement = ""; + let error = ""; const dataset = []; const allReturnedSubStats = []; const allReturnedSubSecs = []; - const axisMap = Object.create(null); // process user bin customizations const binParams = matsDataUtils.setHistogramParameters(plotParams); const { yAxisFormat } = binParams; const { binNum } = binParams; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; dataFoundForCurve[curveIndex] = true; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === - scaleStr - ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -76,17 +70,25 @@ dataHistogram = function (plotParams, plotFunction) { ] === thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold / 10000}`; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === + scaleStr + ); + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; } + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -94,12 +96,27 @@ dataHistogram = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; - var varUnits = statisticOptionsMap[statisticSelect][1]; + [statType] = statisticOptionsMap[statisticSelect]; + [, varUnits] = statisticOptionsMap[statisticSelect]; let axisKey = yAxisFormat; if (yAxisFormat === "Relative frequency") { axisKey += " (x100)"; @@ -107,46 +124,46 @@ dataHistogram = function (plotParams, plotFunction) { curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options curves[curveIndex].binNum = binNum; // stash the binNum to use it later for bar chart options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.time as avtime, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.time as avtime, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -164,6 +181,7 @@ dataHistogram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/radar/server/dataFunctions/data_perfDiagram.js b/apps/radar/server/dataFunctions/data_perfDiagram.js index 037bc7324f..a3e1d281df 100644 --- a/apps/radar/server/dataFunctions/data_perfDiagram.js +++ b/apps/radar/server/dataFunctions/data_perfDiagram.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataPerformanceDiagram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,60 +23,47 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statType; + + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const binParam = curve["bin-parameter"]; const binClause = matsCollections["bin-parameter"].findOne({ name: "bin-parameter", }).optionsMap[binParam]; - var { variable } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === - scaleStr - ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; + let thresholdClause = ""; - let validTimeClause = ""; - let forecastLengthClause = ""; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let dateString = ""; - let dateClause = ""; if (binParam !== "Threshold") { - var thresholdStr = curve.threshold; + const thresholdStr = curve.threshold; if (thresholdStr === undefined) { throw new Error( `INFO: ${label}'s threshold is undefined. Please assign it a value.` @@ -91,12 +79,25 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { ); thresholdClause = `and m0.trsh = ${threshold / 10000}`; } + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === + scaleStr + ); + + let validTimeClause = ""; if (binParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.time%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (binParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; if (forecastLength === undefined) { @@ -106,62 +107,79 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const statisticSelect = "PerformanceDiagram"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + let dateString = ""; + let dateClause = ""; if (binParam === "Init Date") { dateString = "m0.time-m0.fcst_len*3600"; } else { dateString = "m0.time"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const statisticSelect = "PerformanceDiagram"; - var statType = "ctc"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // variable + statistic (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + statType = "ctc"; curves[curveIndex].axisKey = statisticSelect; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{binClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "((sum(m0.yy)+0.00)/sum(m0.yy+m0.yn)) as pod, ((sum(m0.ny)+0.00)/sum(m0.ny+m0.yy)) as far, " + - "sum(m0.yy+m0.yn) as oy_all, sum(m0.ny+m0.nn) as on_all, group_concat(m0.time, ';', m0.yy, ';', " + - "m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0 " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by binVal " + - "order by binVal" + - ";"; - - statement = statement.replace("{{binClause}}", binClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "{{binClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "((sum(m0.yy)+0.00)/sum(m0.yy+m0.yn)) as pod, ((sum(m0.ny)+0.00)/sum(m0.ny+m0.yy)) as far, " + + "sum(m0.yy+m0.yn) as oy_all, sum(m0.ny+m0.nn) as on_all, group_concat(m0.time, ';', m0.yy, ';', " + + "m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0 " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by binVal " + + "order by binVal" + + ";"; + + statement = statement.replace("{{binClause}}", binClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBPerformanceDiagram( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -177,6 +195,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -198,7 +217,6 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -206,7 +224,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { ymax = ymax > d.ymax ? ymax : d.ymax; } } else { - // this is a difference curve -- not supported for ROC plots + // this is a difference curve -- not supported for performance diagrams throw new Error( "INFO: Difference curves are not supported for performance diagrams, as they do not feature consistent x or y values across all curves." ); @@ -214,6 +232,7 @@ dataPerformanceDiagram = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/radar/server/dataFunctions/data_series.js b/apps/radar/server/dataFunctions/data_series.js index efe9d2e739..1711a7d44b 100644 --- a/apps/radar/server/dataFunctions/data_series.js +++ b/apps/radar/server/dataFunctions/data_series.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSeries = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,52 +24,46 @@ dataSeries = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === - scaleStr - ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -78,20 +73,25 @@ dataSeries = function (plotParams, plotFunction) { ] === thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold / 10000}`; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === + scaleStr + ); + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; } + let forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; - const averageStr = curve.average; - const averageOptionsMap = matsCollections.average.findOne( - { name: "average" }, - { optionsMap: 1 } - ).optionsMap; - const average = averageOptionsMap[averageStr][0]; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -99,11 +99,30 @@ dataSeries = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const averageStr = curve.average; + const averageOptionsMap = matsCollections.average.findOne( + { name: "average" }, + { optionsMap: 1 } + ).optionsMap; + const average = averageOptionsMap[averageStr][0]; + + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -111,47 +130,45 @@ dataSeries = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select {{average}} as avtime, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select {{average}} as avtime, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; - statement = statement.replace("{{average}}", average); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; + statement = statement.replace("{{average}}", average); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; - // math is done on forecastLength later on -- set all analyses to 0 - if (forecastLength === "-99") { - forecastLength = "0"; - } + // math is done on forecastLength later on -- set all analyses to 0 + if (forecastLength === "-99") { + forecastLength = "0"; + } - var queryResult; - const startMoment = moment(); - var finishMoment; - try { // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBTimeSeries( - sumPool, + sumPool, // eslint-disable-line no-undef statement, model, forecastLength, @@ -163,7 +180,9 @@ dataSeries = function (plotParams, plotFunction) { appParams, false ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -179,6 +198,7 @@ dataSeries = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -200,7 +220,6 @@ dataSeries = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -225,6 +244,7 @@ dataSeries = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/radar/server/dataFunctions/data_threshold.js b/apps/radar/server/dataFunctions/data_threshold.js index 7e5d769ea3..c3510c7677 100644 --- a/apps/radar/server/dataFunctions/data_threshold.js +++ b/apps/radar/server/dataFunctions/data_threshold.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataThreshold = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,59 +24,58 @@ dataThreshold = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( + const scaleStr = curve.scale; + const scale = Object.keys( matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] ).find( (key) => matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === scaleStr ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((m0.time)%(24*3600)/3600) IN(${validTimes})`; } + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -83,11 +83,26 @@ dataThreshold = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -95,44 +110,44 @@ dataThreshold = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.trsh as thresh, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by thresh " + - "order by thresh" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.trsh as thresh, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by thresh " + + "order by thresh" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -148,6 +163,7 @@ dataThreshold = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -169,7 +185,6 @@ dataThreshold = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -194,6 +209,7 @@ dataThreshold = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/radar/server/dataFunctions/data_validtime.js b/apps/radar/server/dataFunctions/data_validtime.js index 006c9be2e6..0ab81c84a5 100644 --- a/apps/radar/server/dataFunctions/data_validtime.js +++ b/apps/radar/server/dataFunctions/data_validtime.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataValidTime = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,49 +24,42 @@ dataValidTime = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; - var { variable } = curve; + const { diffFrom } = curve; + + const { variable } = curve; const databaseRef = matsCollections.variable.findOne({ name: "variable" }) .optionsMap[variable]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[variable][curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === - scaleStr - ); - const queryTableClause = `from ${databaseRef}.${model}_${grid_scale}_${region} as m0`; - var thresholdStr = curve.threshold; + + const thresholdStr = curve.threshold; const threshold = Object.keys( matsCollections.threshold.findOne({ name: "threshold" }).valuesMap[variable] ).find( @@ -75,12 +69,19 @@ dataValidTime = function (plotParams, plotFunction) { ] === thresholdStr ); const thresholdClause = `and m0.trsh = ${threshold / 10000}`; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable] + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[variable][key] === + scaleStr + ); + const forecastLength = curve["forecast-length"]; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -88,11 +89,26 @@ dataValidTime = function (plotParams, plotFunction) { ).optionsMap; const statisticClause = "sum(m0.yy) as hit, sum(m0.ny) as fa, sum(m0.yn) as miss, sum(m0.nn) as cn, group_concat(m0.time, ';', m0.yy, ';', m0.ny, ';', m0.yn, ';', m0.nn order by m0.time) as sub_data, count(m0.yy) as N0"; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and m0.time >= ${fromSecs} and m0.time <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + const queryTableClause = `from ${databaseRef}.${model}_${scale}_${region} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - var statType = statisticOptionsMap[statisticSelect][0]; + [statType] = statisticOptionsMap[statisticSelect]; const axisKey = statisticOptionsMap[statisticSelect][1]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options const idealVal = statisticOptionsMap[statisticSelect][2]; @@ -100,44 +116,44 @@ dataValidTime = function (plotParams, plotFunction) { idealValues.push(idealVal); } - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select floor(m0.time%(24*3600)/3600) as hr_of_day, " + - "count(distinct m0.time) as N_times, " + - "min(m0.time) as min_secs, " + - "max(m0.time) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{thresholdClause}} " + - "{{forecastLengthClause}} " + - "group by hr_of_day " + - "order by hr_of_day" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{thresholdClause}}", thresholdClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select floor(m0.time%(24*3600)/3600) as hr_of_day, " + + "count(distinct m0.time) as N_times, " + + "min(m0.time) as min_secs, " + + "max(m0.time) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{thresholdClause}} " + + "{{forecastLengthClause}} " + + "group by hr_of_day " + + "order by hr_of_day" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{thresholdClause}}", thresholdClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, statisticSelect ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -153,6 +169,7 @@ dataValidTime = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -174,7 +191,6 @@ dataValidTime = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -199,6 +215,7 @@ dataValidTime = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/radar/server/main.js b/apps/radar/server/main.js index 0a0de69cfb..830a2e1bae 100644 --- a/apps/radar/server/main.js +++ b/apps/radar/server/main.js @@ -4,7 +4,9 @@ import { Meteor } from "meteor/meteor"; import { mysql } from "meteor/pcel:mysql"; +import { moment } from "meteor/momentjs:moment"; import { + matsMethods, matsTypes, matsCollections, matsDataUtils, @@ -328,7 +330,7 @@ const doCurveParams = function () { const params = matsCollections.CurveParamsInfo.find({ curve_params: { $exists: true }, }).fetch()[0].curve_params; - for (let cp = 0; cp < params.length; cp++) { + for (let cp = 0; cp < params.length; cp += 1) { matsCollections[params[cp]].remove({}); } } @@ -339,92 +341,76 @@ const doCurveParams = function () { const forecastLengthOptionsMap = {}; const thresholdsModelOptionsMap = {}; const scaleModelOptionsMap = {}; - const masterRegionValuesMap = {}; - const masterThresholdValuesMap = {}; - const masterScaleValuesMap = {}; + const allRegionValuesMap = {}; + const allThresholdValuesMap = {}; + const allScaleValuesMap = {}; try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - metadataPool, + metadataPool, // eslint-disable-line no-undef "select short_name,description from region_descriptions;" ); - let masterRegDescription; - let masterShortName; - for (var j = 0; j < rows.length; j++) { - masterRegDescription = rows[j].description.trim(); - masterShortName = rows[j].short_name.trim(); - masterRegionValuesMap[masterShortName] = masterRegDescription; + for (let j = 0; j < rows.length; j += 1) { + allRegionValuesMap[rows[j].short_name.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } - let rows; - let didx; - try { - for (didx = 0; didx < variables.length; didx++) { - masterThresholdValuesMap[variables[didx]] = {}; - rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + for (let didx = 0; didx < variables.length; didx += 1) { + allThresholdValuesMap[variables[didx]] = {}; + const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( + sumPool, // eslint-disable-line no-undef `select trsh,description from ${ variableDBNames[variables[didx]] }.threshold_descriptions;` ); - var masterDescription; - var masterTrsh; - var trshTemp; - for (var j = 0; j < rows.length; j++) { - masterDescription = rows[j].description.trim(); - trshTemp = rows[j].trsh.trim(); - masterTrsh = trshTemp * 10000; - masterThresholdValuesMap[variables[didx]][masterTrsh] = masterDescription; + for (let j = 0; j < rows.length; j += 1) { + allThresholdValuesMap[variables[didx]][rows[j].trsh.trim() * 10000] = + rows[j].description.trim(); } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { - for (didx = 0; didx < variables.length; didx++) { - masterScaleValuesMap[variables[didx]] = {}; - rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + for (let didx = 0; didx < variables.length; didx += 1) { + allScaleValuesMap[variables[didx]] = {}; + const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( + sumPool, // eslint-disable-line no-undef `select scale,description from ${ variableDBNames[variables[didx]] }.scale_descriptions;` ); - var masterDescription; - var masterScale; - for (var j = 0; j < rows.length; j++) { - masterDescription = rows[j].description.trim(); - masterScale = rows[j].scale.trim(); - masterScaleValuesMap[variables[didx]][masterScale] = masterDescription; + for (let j = 0; j < rows.length; j += 1) { + allScaleValuesMap[variables[didx]][rows[j].scale.trim()] = + rows[j].description.trim(); } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { - for (didx = 0; didx < variables.length; didx++) { - modelOptionsMap[variables[didx]] = {}; - modelDateRangeMap[variables[didx]] = {}; - forecastLengthOptionsMap[variables[didx]] = {}; - thresholdsModelOptionsMap[variables[didx]] = {}; - scaleModelOptionsMap[variables[didx]] = {}; - regionModelOptionsMap[variables[didx]] = {}; - - rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, - `select model,regions,display_text,fcst_lens,trsh,scale,mindate,maxdate from ${ - variableDBNames[variables[didx]] - }.regions_per_model_mats_all_categories order by display_category, display_order;` + for (let didx = 0; didx < variables.length; didx += 1) { + const variable = variables[didx]; + modelOptionsMap[variable] = {}; + modelDateRangeMap[variable] = {}; + forecastLengthOptionsMap[variable] = {}; + thresholdsModelOptionsMap[variable] = {}; + scaleModelOptionsMap[variable] = {}; + regionModelOptionsMap[variable] = {}; + + const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( + sumPool, // eslint-disable-line no-undef + `select model,regions,display_text,fcst_lens,trsh,scale,mindate,maxdate from ${variableDBNames[variable]}.regions_per_model_mats_all_categories order by display_category, display_order;` ); - for (let i = 0; i < rows.length; i++) { - const model_value = rows[i].model.trim(); + for (let i = 0; i < rows.length; i += 1) { + const modelValue = rows[i].model.trim(); const model = rows[i].display_text.trim(); - modelOptionsMap[variables[didx]][model] = [model_value]; + modelOptionsMap[variable][model] = [modelValue]; const rowMinDate = moment .utc(rows[i].mindate * 1000) @@ -432,59 +418,48 @@ const doCurveParams = function () { const rowMaxDate = moment .utc(rows[i].maxdate * 1000) .format("MM/DD/YYYY HH:mm"); - modelDateRangeMap[variables[didx]][model] = { + modelDateRangeMap[variable][model] = { minDate: rowMinDate, maxDate: rowMaxDate, }; const forecastLengths = rows[i].fcst_lens; - const forecastLengthArr = forecastLengths + forecastLengthOptionsMap[variable][model] = forecastLengths .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < forecastLengthArr.length; j++) { - forecastLengthArr[j] = forecastLengthArr[j].replace(/'|\[|\]/g, ""); - } - forecastLengthOptionsMap[variables[didx]][model] = forecastLengthArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (fhr) { + return fhr.replace(/'|\[|\]/g, ""); + }); const thresholds = rows[i].trsh; - const thresholdsArrRaw = thresholds + thresholdsModelOptionsMap[variable][model] = thresholds .split(",") - .map(Function.prototype.call, String.prototype.trim); - const thresholdsArr = []; - var dummyThresh; - for (var j = 0; j < thresholdsArrRaw.length; j++) { - dummyThresh = thresholdsArrRaw[j].replace(/'|\[|\]/g, "") * 10000; - thresholdsArr.push(masterThresholdValuesMap[variables[didx]][dummyThresh]); - } - thresholdsModelOptionsMap[variables[didx]][model] = thresholdsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (threshold) { + return allThresholdValuesMap[ + variable + ][threshold.replace(/'|\[|\]/g, "") * 10000]; + }); const scales = rows[i].scale; - const scalesArrRaw = scales + scaleModelOptionsMap[variable][model] = scales .split(",") - .map(Function.prototype.call, String.prototype.trim); - const scalesArr = []; - var dummyScale; - for (var j = 0; j < scalesArrRaw.length; j++) { - dummyScale = scalesArrRaw[j].replace(/'|\[|\]/g, ""); - scalesArr.push(masterScaleValuesMap[variables[didx]][dummyScale]); - } - scaleModelOptionsMap[variables[didx]][model] = scalesArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (scale) { + return allScaleValuesMap[variable][scale.replace(/'|\[|\]/g, "")]; + }); const { regions } = rows[i]; - const regionsArrRaw = regions + regionModelOptionsMap[variable][model] = regions .split(",") - .map(Function.prototype.call, String.prototype.trim); - const regionsArr = []; - var dummyRegion; - for (var j = 0; j < regionsArrRaw.length; j++) { - dummyRegion = regionsArrRaw[j].replace(/'|\[|\]/g, ""); - regionsArr.push(masterRegionValuesMap[dummyRegion]); - } - regionModelOptionsMap[variables[didx]][model] = regionsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (region) { + return allRegionValuesMap[region.replace(/'|\[|\]/g, "")]; + }); } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } if (matsCollections.label.findOne({ name: "label" }) === undefined) { @@ -522,7 +497,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.variable.findOne({ name: "variable" }); + const currentParam = matsCollections.variable.findOne({ name: "variable" }); if (!matsDataUtils.areObjectsEqual(currentParam.dates, modelDateRangeMap)) { // have to reload variable data matsCollections.variable.update( @@ -561,7 +536,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["data-source"].findOne({ name: "data-source" }); + const currentParam = matsCollections["data-source"].findOne({ + name: "data-source", + }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, modelOptionsMap)) { // have to reload model data matsCollections["data-source"].update( @@ -586,7 +563,7 @@ const doCurveParams = function () { regionModelOptionsMap[variables[0]][ Object.keys(regionModelOptionsMap[variables[0]])[0] ], - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -601,10 +578,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.region.findOne({ name: "region" }); + const currentParam = matsCollections.region.findOne({ name: "region" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, regionModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterRegionValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allRegionValuesMap) ) { // have to reload region data matsCollections.region.update( @@ -612,7 +589,7 @@ const doCurveParams = function () { { $set: { optionsMap: regionModelOptionsMap, - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, options: regionModelOptionsMap[variables[0]][ Object.keys(regionModelOptionsMap[variables[0]])[0] @@ -693,7 +670,7 @@ const doCurveParams = function () { thresholdsModelOptionsMap[variables[0]][ Object.keys(thresholdsModelOptionsMap[variables[0]])[0] ], - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -708,13 +685,13 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.threshold.findOne({ name: "threshold" }); + const currentParam = matsCollections.threshold.findOne({ name: "threshold" }); if ( !matsDataUtils.areObjectsEqual( currentParam.optionsMap, thresholdsModelOptionsMap ) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterThresholdValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allThresholdValuesMap) ) { // have to reload threshold data matsCollections.threshold.update( @@ -722,7 +699,7 @@ const doCurveParams = function () { { $set: { optionsMap: thresholdsModelOptionsMap, - valuesMap: masterThresholdValuesMap, + valuesMap: allThresholdValuesMap, options: thresholdsModelOptionsMap[variables[0]][ Object.keys(thresholdsModelOptionsMap[variables[0]])[0] @@ -746,7 +723,7 @@ const doCurveParams = function () { scaleModelOptionsMap[variables[0]][ Object.keys(scaleModelOptionsMap[variables[0]])[0] ], - valuesMap: masterScaleValuesMap, + valuesMap: allScaleValuesMap, superiorNames: ["variable", "data-source"], controlButtonCovered: true, unique: false, @@ -761,10 +738,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.scale.findOne({ name: "scale" }); + const currentParam = matsCollections.scale.findOne({ name: "scale" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, scaleModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterScaleValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allScaleValuesMap) ) { // have to reload scale data matsCollections.scale.update( @@ -772,7 +749,7 @@ const doCurveParams = function () { { $set: { optionsMap: scaleModelOptionsMap, - valuesMap: masterScaleValuesMap, + valuesMap: allScaleValuesMap, options: scaleModelOptionsMap[variables[0]][ Object.keys(scaleModelOptionsMap[variables[0]])[0] @@ -812,7 +789,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["forecast-length"].findOne({ + const currentParam = matsCollections["forecast-length"].findOne({ name: "forecast-length", }); if ( @@ -1042,18 +1019,14 @@ const doCurveParams = function () { } // determine date defaults for dates and curveDates - const defaultDb = matsCollections.variable.findOne( - { name: "variable" }, + const defaultDataSource = matsCollections["data-source"].findOne( + { name: "data-source" }, { default: 1 } ).default; modelDateRangeMap = matsCollections.variable.findOne( { name: "variable" }, { dates: 1 } ).dates; - const defaultDataSource = matsCollections["data-source"].findOne( - { name: "data-source" }, - { default: 1 } - ).default; minDate = modelDateRangeMap[variables[0]][defaultDataSource].minDate; maxDate = modelDateRangeMap[variables[0]][defaultDataSource].maxDate; @@ -1094,7 +1067,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["curve-dates"].findOne({ name: "curve-dates" }); + const currentParam = matsCollections["curve-dates"].findOne({ + name: "curve-dates", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.startDate, minDate) || !matsDataUtils.areObjectsEqual(currentParam.stopDate, maxDate) || @@ -1458,7 +1433,8 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { - console.log( + // eslint-disable-next-line no-console + console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); matsCollections.Databases.drop(); @@ -1475,7 +1451,7 @@ Meteor.startup(function () { databases = Meteor.settings.private.databases; } if (databases !== null && databases !== undefined && Array.isArray(databases)) { - for (let di = 0; di < databases.length; di++) { + for (let di = 0; di < databases.length; di += 1) { matsCollections.Databases.insert(databases[di]); } } @@ -1502,6 +1478,7 @@ Meteor.startup(function () { ); if (cbConnection) { // global cbScorecardSettingsPool + // eslint-disable-next-line no-undef cbScorecardSettingsPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1528,6 +1505,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (metadataSettings) { + // eslint-disable-next-line no-undef metadataPool = mysql.createPool(metadataSettings); allPools.push({ pool: "metadataPool", role: matsTypes.DatabaseRoles.META_DATA }); } @@ -1548,6 +1526,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (sumSettings) { + // eslint-disable-next-line no-undef sumPool = mysql.createPool(sumSettings); allPools.push({ pool: "sumPool", role: matsTypes.DatabaseRoles.SUMS_DATA }); } @@ -1556,7 +1535,7 @@ Meteor.startup(function () { const mdr = new matsTypes.MetaDataDBRecord("metadataPool", "mats_common", [ "region_descriptions", ]); - for (let didx = 0; didx < variables.length; didx++) { + for (let didx = 0; didx < variables.length; didx += 1) { mdr.addRecord("sumPool", variableDBNames[variables[didx]], [ "threshold_descriptions", "scale_descriptions", @@ -1570,7 +1549,7 @@ Meteor.startup(function () { appType: matsTypes.AppTypes.mats, }); } catch (error) { - console.log(error.message); + throw new Error(error.message); } }); @@ -1578,6 +1557,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doPlotGraph, doCurveParams, diff --git a/apps/scorecard/.eslintrc.json b/apps/scorecard/.eslintrc.json index c248c52f7d..79d49c5bb6 100644 --- a/apps/scorecard/.eslintrc.json +++ b/apps/scorecard/.eslintrc.json @@ -28,28 +28,6 @@ "space-before-function-paren": "off", // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", - "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-plusplus": "warn", - "vars-on-top": "warn", - "no-var": "warn", - "block-scoped-var": "warn", - "no-loop-func": "warn", - "no-unused-vars": "warn", - "prefer-destructuring": "warn", - "no-param-reassign": "warn", - "camelcase": "warn", - "no-redeclare": "warn", - "no-shadow": "warn", - "no-useless-escape": "warn", - "global-require": "warn", - "default-case": "warn", - "no-unused-expressions": "warn", - "no-sequences": "warn" + "prefer-arrow-callback": "off" } } diff --git a/apps/scorecard/client/main.js b/apps/scorecard/client/main.js index a87407a1f4..ecd922b6a2 100644 --- a/apps/scorecard/client/main.js +++ b/apps/scorecard/client/main.js @@ -2,6 +2,7 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; import "@fortawesome/fontawesome-free"; import "@fortawesome/fontawesome-free/css/all.css"; diff --git a/apps/scorecard/server/dataFunctions/processScorecard.js b/apps/scorecard/server/dataFunctions/processScorecard.js index dc3c2f3a1c..84ae4e140a 100644 --- a/apps/scorecard/server/dataFunctions/processScorecard.js +++ b/apps/scorecard/server/dataFunctions/processScorecard.js @@ -5,8 +5,8 @@ import { matsTypes, matsParamUtils, matsCollections } from "meteor/randyp:mats-c /** A function to sanitize JSON keys by replacing the "." character with "__DOT__" */ const sanitizeKeys = function (str) { - str = str.replace(/\./g, "__DOT__"); - return str; + const newStr = str.replace(/\./g, "__DOT__"); + return newStr; }; const dealWithUATables = function ( @@ -182,10 +182,13 @@ processScorecard = function (plotParams, plotFunction) { // get the union of the fcst-length arrays of all the curves const fcstLengthsSet = new Set(); plotParams.curves.forEach(function (curve) { - if (!curve["forecast-length"]) curve["forecast-length"] = ["0"]; - curve["forecast-length"].forEach(function (fcl) { - fcstLengthsSet.add(fcl); - }); + if (!curve["forecast-length"]) { + fcstLengthsSet.add("0"); + } else { + curve["forecast-length"].forEach(function (fcl) { + fcstLengthsSet.add(fcl); + }); + } }); const fcstLengths = Array.from(fcstLengthsSet); @@ -383,6 +386,10 @@ processScorecard = function (plotParams, plotFunction) { // create the empty object for this block const { label } = curve; + + // duplicate the curve so we're not modifying a function parameter, which the linter doesn't like + const scorecardCurve = curve; + scorecardDocument.results.blocks[label] = {}; scorecardDocument.queryMap.blocks[label] = {}; // add the top level elements. @@ -395,76 +402,73 @@ processScorecard = function (plotParams, plotFunction) { return !notIncludedParams.includes(paramName); }); - scorecardDocument.results.blocks[curve.label].blockTitle = { + scorecardDocument.results.blocks[label].blockTitle = { label, - dataSource: curve["data-source"], - controlDataSource: curve["control-data-source"], + dataSource: scorecardCurve["data-source"], + controlDataSource: scorecardCurve["control-data-source"], }; const appName = Meteor.settings.public.app; const appUrl = `${Meteor.settings.public.home}/${appName}`; - scorecardDocument.results.blocks[curve.label].blockApplication = appUrl; - scorecardDocument.results.blocks[curve.label].blockParameters = blockParameters; - scorecardDocument.results.blocks[curve.label].regions = regions; - scorecardDocument.results.blocks[curve.label].fcstlens = fcstLengths; - scorecardDocument.results.blocks[curve.label].data = {}; - scorecardDocument.queryMap.blocks[curve.label].data = {}; - curve.threshold = - curve.threshold === undefined ? ["threshold_NA"] : curve.threshold; - curve.level = curve.level === undefined ? ["level_NA"] : curve.level; + scorecardDocument.results.blocks[label].blockApplication = appUrl; + scorecardDocument.results.blocks[label].blockParameters = blockParameters; + scorecardDocument.results.blocks[label].regions = regions; + scorecardDocument.results.blocks[label].fcstlens = fcstLengths; + scorecardDocument.results.blocks[label].data = {}; + scorecardDocument.queryMap.blocks[label].data = {}; + scorecardCurve.threshold = + scorecardCurve.threshold === undefined + ? ["threshold_NA"] + : scorecardCurve.threshold; + scorecardCurve.level = + scorecardCurve.level === undefined ? ["level_NA"] : scorecardCurve.level; regions.forEach(function (regionText) { const region = sanitizeKeys(regionText); - if (scorecardDocument.results.blocks[curve.label].data[region] === undefined) { - scorecardDocument.results.blocks[curve.label].data[region] = {}; - scorecardDocument.queryMap.blocks[curve.label].data[region] = {}; + if (scorecardDocument.results.blocks[label].data[region] === undefined) { + scorecardDocument.results.blocks[label].data[region] = {}; + scorecardDocument.queryMap.blocks[label].data[region] = {}; } - curve.statistic.forEach(function (statText) { + scorecardCurve.statistic.forEach(function (statText) { const stat = sanitizeKeys(statText); - if ( - scorecardDocument.results.blocks[curve.label].data[region][stat] === undefined - ) { - scorecardDocument.results.blocks[curve.label].data[region][stat] = {}; - scorecardDocument.queryMap.blocks[curve.label].data[region][stat] = {}; + if (scorecardDocument.results.blocks[label].data[region][stat] === undefined) { + scorecardDocument.results.blocks[label].data[region][stat] = {}; + scorecardDocument.queryMap.blocks[label].data[region][stat] = {}; } - curve.variable.forEach(function (variableText) { + scorecardCurve.variable.forEach(function (variableText) { const variable = sanitizeKeys(variableText); if ( - scorecardDocument.results.blocks[curve.label].data[region][stat][ - variable - ] === undefined + scorecardDocument.results.blocks[label].data[region][stat][variable] === + undefined ) { - scorecardDocument.results.blocks[curve.label].data[region][stat][variable] = - {}; - scorecardDocument.queryMap.blocks[curve.label].data[region][stat][ - variable - ] = {}; + scorecardDocument.results.blocks[label].data[region][stat][variable] = {}; + scorecardDocument.queryMap.blocks[label].data[region][stat][variable] = {}; } - curve.threshold.forEach(function (thresholdText) { + scorecardCurve.threshold.forEach(function (thresholdText) { const threshold = sanitizeKeys(thresholdText); if ( - scorecardDocument.results.blocks[curve.label].data[region][stat][ - variable - ][threshold] === undefined + scorecardDocument.results.blocks[label].data[region][stat][variable][ + threshold + ] === undefined ) { - scorecardDocument.results.blocks[curve.label].data[region][stat][ - variable - ][threshold] = {}; - scorecardDocument.queryMap.blocks[curve.label].data[region][stat][ - variable - ][threshold] = {}; + scorecardDocument.results.blocks[label].data[region][stat][variable][ + threshold + ] = {}; + scorecardDocument.queryMap.blocks[label].data[region][stat][variable][ + threshold + ] = {}; } - curve.level.forEach(function (levelText) { + scorecardCurve.level.forEach(function (levelText) { const level = sanitizeKeys(levelText); if ( - scorecardDocument.results.blocks[curve.label].data[region][stat][ - variable - ][threshold][level] === undefined + scorecardDocument.results.blocks[label].data[region][stat][variable][ + threshold + ][level] === undefined ) { - scorecardDocument.results.blocks[curve.label].data[region][stat][ - variable - ][threshold][level] = {}; - scorecardDocument.queryMap.blocks[curve.label].data[region][stat][ - variable - ][threshold][level] = {}; + scorecardDocument.results.blocks[label].data[region][stat][variable][ + threshold + ][level] = {}; + scorecardDocument.queryMap.blocks[label].data[region][stat][variable][ + threshold + ][level] = {}; } fcstLengths.forEach(function (fcstlenText) { const fcstlen = sanitizeKeys(fcstlenText); @@ -527,7 +531,7 @@ processScorecard = function (plotParams, plotFunction) { // populate variable in query template -- partial sums if (localQueryTemplate.includes("{{variable0}}")) { const variableArray = variableMap[variableText]; - for (let vidx = 0; vidx < variableArray.length; vidx++) { + for (let vidx = 0; vidx < variableArray.length; vidx += 1) { const replaceString = `{{variable${vidx.toString()}}}`; const regex = new RegExp(replaceString, "g"); localQueryTemplate = localQueryTemplate.replace( @@ -545,7 +549,7 @@ processScorecard = function (plotParams, plotFunction) { if (application === "upperair") { // the upper air tables are unfortuately really inconsistent and need some handling localQueryTemplate = dealWithUATables( - curve, + scorecardCurve, regionValue, databaseValue, localQueryTemplate @@ -558,22 +562,23 @@ processScorecard = function (plotParams, plotFunction) { } // populate experimental model in query template - const experimentalModelValue = modelMap[curve["data-source"]]; + const experimentalModelValue = modelMap[scorecardCurve["data-source"]]; const experimentalQueryTemplate = localQueryTemplate.replace( /\{\{model\}\}/g, experimentalModelValue ); // populate control model in query template - const controlModelValue = modelMap[curve["control-data-source"]]; + const controlModelValue = + modelMap[scorecardCurve["control-data-source"]]; const controlQueryTemplate = localQueryTemplate.replace( /\{\{model\}\}/g, controlModelValue ); - scorecardDocument.queryMap.blocks[curve.label].data[region][stat][ - variable - ][threshold][level][fcstlen] = { + scorecardDocument.queryMap.blocks[label].data[region][stat][variable][ + threshold + ][level][fcstlen] = { experimentalQueryTemplate, controlQueryTemplate, }; @@ -583,18 +588,16 @@ processScorecard = function (plotParams, plotFunction) { const sval = -9999; if ( - scorecardDocument.results.blocks[curve.label].fcstlens.includes( - fcstlen - ) + scorecardDocument.results.blocks[label].fcstlens.includes(fcstlen) ) { - scorecardDocument.results.blocks[curve.label].data[region][stat][ - variable - ][threshold][level][fcstlen] = sval; + scorecardDocument.results.blocks[label].data[region][stat][variable][ + threshold + ][level][fcstlen] = sval; } else { // mark this undefined - scorecardDocument.results.blocks[curve.label].data[region][stat][ - variable - ][threshold][level][fcstlen] = "undefined"; + scorecardDocument.results.blocks[label].data[region][stat][variable][ + threshold + ][level][fcstlen] = "undefined"; } }); }); @@ -640,10 +643,11 @@ processScorecard = function (plotParams, plotFunction) { // to be in the public section of the scorecard settings file // NOTE: For now we do not have scheduled jobs. When we do we will need to change this. const notifyDataProcessorURL = `${Meteor.settings.public.vxdataProcessorUrl}`; - const sDocument = `\{"docid": "${id}"\}`; + const sDocument = `{"docid": "${id}"}`; HTTP.post( notifyDataProcessorURL, { content: `${sDocument}` }, + // eslint-disable-next-line no-unused-vars function (error, response) { if (error) { console.log(error); diff --git a/apps/scorecard/server/main.js b/apps/scorecard/server/main.js index 77b1da5ffa..faed40c55d 100644 --- a/apps/scorecard/server/main.js +++ b/apps/scorecard/server/main.js @@ -3,10 +3,13 @@ */ import { Meteor } from "meteor/meteor"; +import { moment } from "meteor/momentjs:moment"; import { + matsMethods, matsTypes, matsCollections, matsDataUtils, + matsParamUtils, matsCouchbaseUtils, } from "meteor/randyp:mats-common"; @@ -619,7 +622,7 @@ const doCurveParams = function () { const params = matsCollections.CurveParamsInfo.find({ curve_params: { $exists: true }, }).fetch()[0].curve_params; - for (let cp = 0; cp < params.length; cp++) { + for (let cp = 0; cp < params.length; cp += 1) { matsCollections[params[cp]].remove({}); } } @@ -657,8 +660,8 @@ const doCurveParams = function () { let currentURL; let queryURL; let expectedApps = []; - for (let aidx = 0; aidx < appsToScore.length; aidx++) { - currentApp = Object.keys(appsToScore[aidx])[0]; + for (let aidx = 0; aidx < appsToScore.length; aidx += 1) { + [currentApp] = Object.keys(appsToScore[aidx]); currentURL = appsToScore[aidx][currentApp]; // clean up URL if users left a trailing slash or didn't include https:// @@ -677,7 +680,7 @@ const doCurveParams = function () { ); // store the URL that was used to get each of these apps - for (let eaidx = 0; eaidx < expectedApps.length; eaidx++) { + for (let eaidx = 0; eaidx < expectedApps.length; eaidx += 1) { const thisApp = expectedApps[eaidx]; applicationSourceMap[thisApp] = currentApp; } @@ -910,10 +913,10 @@ const doCurveParams = function () { // remove excluded stats from the scorecard // we need to do this after the main metadata loop // is finished or we don't have the full list of apps - for (let aidx = 0; aidx < applicationOptions.length; aidx++) { + for (let aidx = 0; aidx < applicationOptions.length; aidx += 1) { // loop through all the applications found inside the app list in the settings const thisApp = applicationOptions[aidx]; - for (let sidx = statisticOptionsMap[thisApp].length - 1; sidx >= 0; sidx--) { + for (let sidx = statisticOptionsMap[thisApp].length - 1; sidx >= 0; sidx -= 1) { // loop backwards through the statistics for this app const thisStat = statisticOptionsMap[thisApp][sidx]; if (matsDataUtils.excludeStatFromScorecard(thisStat)) { @@ -924,7 +927,7 @@ const doCurveParams = function () { } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } if (matsCollections.label.findOne({ name: "label" }) === undefined) { @@ -979,7 +982,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.application.findOne({ name: "application" }); + const currentParam = matsCollections.application.findOne({ name: "application" }); if (!matsDataUtils.areObjectsEqual(currentParam.dates, dateOptionsMap)) { // have to reload application data matsCollections.application.update( @@ -1023,7 +1026,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["data-source"].findOne({ name: "data-source" }); + const currentParam = matsCollections["data-source"].findOne({ + name: "data-source", + }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, modelOptionsMap)) { // have to reload model data matsCollections["data-source"].update( @@ -1069,7 +1074,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["control-data-source"].findOne({ + const currentParam = matsCollections["control-data-source"].findOne({ name: "control-data-source", }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, modelOptionsMap)) { @@ -1115,7 +1120,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.region.findOne({ name: "region" }); + const currentParam = matsCollections.region.findOne({ name: "region" }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, regionOptionsMap)) { // have to reload region data matsCollections.region.update( @@ -1159,7 +1164,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.statistic.findOne({ name: "statistic" }); + const currentParam = matsCollections.statistic.findOne({ name: "statistic" }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, statisticOptionsMap)) { // have to reload statistic data matsCollections.statistic.update( @@ -1196,7 +1201,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.variable.findOne({ name: "variable" }); + const currentParam = matsCollections.variable.findOne({ name: "variable" }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, variableOptionsMap)) { // have to reload variable data matsCollections.variable.update( @@ -1244,7 +1249,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.threshold.findOne({ name: "threshold" }); + const currentParam = matsCollections.threshold.findOne({ name: "threshold" }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, thresholdOptionsMap)) { // have to reload threshold data matsCollections.threshold.update( @@ -1297,7 +1302,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.scale.findOne({ name: "scale" }); + const currentParam = matsCollections.scale.findOne({ name: "scale" }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, scaleOptionsMap)) { // have to reload scale data matsCollections.scale.update( @@ -1350,7 +1355,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.truth.findOne({ name: "truth" }); + const currentParam = matsCollections.truth.findOne({ name: "truth" }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, truthOptionsMap)) { // have to reload truth data matsCollections.truth.update( @@ -1403,7 +1408,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["forecast-length"].findOne({ + const currentParam = matsCollections["forecast-length"].findOne({ name: "forecast-length", }); if ( @@ -1459,7 +1464,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["forecast-type"].findOne({ + const currentParam = matsCollections["forecast-type"].findOne({ name: "forecast-type", }); if ( @@ -1511,7 +1516,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["valid-time"].findOne({ name: "valid-time" }); + const currentParam = matsCollections["valid-time"].findOne({ name: "valid-time" }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, validTimeOptionsMap)) { // have to reload valid time data matsCollections["valid-time"].update( @@ -1556,7 +1561,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.level.findOne({ name: "level" }); + const currentParam = matsCollections.level.findOne({ name: "level" }); if (!matsDataUtils.areObjectsEqual(currentParam.optionsMap, levelOptionsMap)) { // have to reload level data matsCollections.level.update( @@ -1674,7 +1679,8 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { - console.log( + // eslint-disable-next-line no-console + console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); matsCollections.Databases.drop(); @@ -1691,7 +1697,7 @@ Meteor.startup(function () { databases = Meteor.settings.private.databases; } if (databases !== null && databases !== undefined && Array.isArray(databases)) { - for (let di = 0; di < databases.length; di++) { + for (let di = 0; di < databases.length; di += 1) { matsCollections.Databases.insert(databases[di]); } } @@ -1723,6 +1729,7 @@ Meteor.startup(function () { cbConnections.forEach(function (cbConnection) { if (cbConnection.collection === "METAR") { // global cbPool + // eslint-disable-next-line no-undef cbPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1735,6 +1742,7 @@ Meteor.startup(function () { } if (cbConnection.collection === "SCORECARD") { // global cbScorecardPool + // eslint-disable-next-line no-undef cbScorecardPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1750,6 +1758,7 @@ Meteor.startup(function () { } if (cbConnection.collection === "SCORECARD_SETTINGS") { // global cbScorecardSettingsPool + // eslint-disable-next-line no-undef cbScorecardSettingsPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1776,7 +1785,7 @@ Meteor.startup(function () { dbType: matsTypes.DbTypes.couchbase, }); } catch (error) { - console.log(error.message); + throw new Error(error.message); } }); @@ -1784,6 +1793,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doCurveParams, doPlotGraph, diff --git a/apps/surface/.eslintrc.json b/apps/surface/.eslintrc.json index e5e813ea94..79d49c5bb6 100644 --- a/apps/surface/.eslintrc.json +++ b/apps/surface/.eslintrc.json @@ -28,23 +28,6 @@ "space-before-function-paren": "off", // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", - "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-plusplus": "warn", - "vars-on-top": "warn", - "no-var": "warn", - "block-scoped-var": "warn", - "no-loop-func": "warn", - "no-unused-vars": "warn", - "prefer-destructuring": "warn", - "no-param-reassign": "warn", - "camelcase": "warn", - "no-redeclare": "warn", - "no-shadow": "warn" + "prefer-arrow-callback": "off" } } diff --git a/apps/surface/client/main.js b/apps/surface/client/main.js index a87407a1f4..ecd922b6a2 100644 --- a/apps/surface/client/main.js +++ b/apps/surface/client/main.js @@ -2,6 +2,7 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; import "@fortawesome/fontawesome-free"; import "@fortawesome/fontawesome-free/css/all.css"; diff --git a/apps/surface/server/dataFunctions/data_contour.js b/apps/surface/server/dataFunctions/data_contour.js index 52046e6ff7..dcfe1260bd 100644 --- a/apps/surface/server/dataFunctions/data_contour.js +++ b/apps/surface/server/dataFunctions/data_contour.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContour = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,68 +23,87 @@ dataContour = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; - const totalProcessingStart = moment(); + + const curves = JSON.parse(JSON.stringify(plotParams.curves)); + if (curves.length > 1) { + throw new Error("INFO: There must only be one added curve."); + } + + const axisMap = Object.create(null); + + let statement = ""; + let error = ""; + const dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - const curves = JSON.parse(JSON.stringify(plotParams.curves)); - if (curves.length > 1) { - throw new Error("INFO: There must only be one added curve."); - } - const dataset = []; - const axisMap = Object.create(null); - // initialize variables specific to the curve + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - const metarStringStr = curve.truth; - const metarString = Object.keys( - matsCollections.truth.findOne({ name: "truth" }).valuesMap - ).find( - (key) => - matsCollections.truth.findOne({ name: "truth" }).valuesMap[key] === metarStringStr - ); - const regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${model}_${metarString}_${region} as m0`; - // contours are only for predefined regions--no station plots + const regionType = "Predefined region"; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[regionType][variableStr]; + + const metarStringStr = curve.truth; + const metarString = Object.keys( + matsCollections.truth.findOne({ name: "truth" }).valuesMap + ).find( + (key) => + matsCollections.truth.findOne({ name: "truth" }).valuesMap[key] === metarStringStr + ); + let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.hour IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + + `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -94,106 +114,115 @@ dataContour = function (plotParams, plotFunction) { dateString = "m0.valid_day+3600*m0.hour"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + - `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; - const statType = statisticOptionsMap[statisticSelect]; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + + const queryTableClause = `from ${model}_${metarString}_${region} as m0`; + + // For contours, this functions as the colorbar label. const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); + const statType = statisticOptionsMap[statisticSelect]; const varUnits = statVarUnitMap[statisticSelect][variableStr]; - - // For contours, this functions as the colorbar label. curve.unitKey = varUnits; let d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; - - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - `${statisticSelect}_${variableStr}` - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - if (error.includes("Unknown column")) { - throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for the model/region [${model} and ${region}].` - ); + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; + + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + `${statisticSelect}_${variableStr}` + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); + } + + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { + // this is NOT an error just a no data condition + dataFoundForCurve = false; } else { - throw new Error(error); + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + if (error.includes("Unknown column")) { + throw new Error( + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for this model and region].` + ); + } else { + throw new Error(error); + } } } - } - if (!dataFoundForCurve) { - // we found no data for any curves so don't bother proceeding - throw new Error("INFO: No valid data for any curves."); + if (!dataFoundForCurve) { + // we found no data for any curves so don't bother proceeding + throw new Error("INFO: No valid data for any curves."); + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); } - const postQueryStartMoment = moment(); - // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined diff --git a/apps/surface/server/dataFunctions/data_contour_diff.js b/apps/surface/server/dataFunctions/data_contour_diff.js index faa62f66ff..957a9924ca 100644 --- a/apps/surface/server/dataFunctions/data_contour_diff.js +++ b/apps/surface/server/dataFunctions/data_contour_diff.js @@ -14,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContourDiff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -24,36 +25,57 @@ dataContourDiff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; let dataNotFoundForAnyCurve = false; + + let curves = JSON.parse(JSON.stringify(plotParams.curves)); + const curvesLength = curves.length; + if (curvesLength !== 2) { + throw new Error("INFO: There must be two added curves."); + } + + const axisMap = Object.create(null); const showSignificance = plotParams.significance !== "none"; - const totalProcessingStart = moment(); + + let statType; + let statisticSelect; + let variableStr; + + let statement = ""; + let error = ""; + let dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - let curves = JSON.parse(JSON.stringify(plotParams.curves)); - const curvesLength = curves.length; - if (curvesLength !== 2) { - throw new Error("INFO: There must be two added curves."); - } - let dataset = []; - const axisMap = Object.create(null); - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var metarStringStr = curve.truth; + + const regionType = "Predefined region"; + + variableStr = curve.variable; + const variableOptionsMap = matsCollections.variable.findOne( + { name: "variable" }, + { optionsMap: 1 } + ).optionsMap; + const variable = variableOptionsMap[regionType][variableStr]; + + const metarStringStr = curve.truth; const metarString = Object.keys( matsCollections.truth.findOne({ name: "truth" }).valuesMap ).find( @@ -61,36 +83,37 @@ dataContourDiff = function (plotParams, plotFunction) { matsCollections.truth.findOne({ name: "truth" }).valuesMap[key] === metarStringStr ); - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${model}_${metarString}_${region} as m0`; - // contours are only for predefined regions--no station plots - const regionType = "Predefined region"; - var variableStr = curve.variable; - const variableOptionsMap = matsCollections.variable.findOne( - { name: "variable" }, - { optionsMap: 1 } - ).optionsMap; - const variable = variableOptionsMap[regionType][variableStr]; + let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.hour IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + + `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; + + let dateString = ""; + let dateClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -101,102 +124,108 @@ dataContourDiff = function (plotParams, plotFunction) { dateString = "m0.valid_day+3600*m0.hour"; } dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - var statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - `sum(${variable[0]}) as square_diff_sum, sum(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + - `group_concat(m0.valid_day+3600*m0.hour, ';', ${variable[0]}, ';', ${variable[1]}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + + const queryTableClause = `from ${model}_${metarString}_${region} as m0`; + + // For contours, this functions as the colorbar label. const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); + statType = statisticOptionsMap[statisticSelect]; const varUnits = statVarUnitMap[statisticSelect][variableStr]; - - // For contours, this functions as the colorbar label. curves[curveIndex].unitKey = varUnits; - var d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; + let d; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; - var queryResult; - const startMoment = moment(); - var finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - `${statisticSelect}_${variableStr}` - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - if (error.includes("Unknown column")) { - throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for the model/region [${model} and ${region}].` - ); - } else { - throw new Error(error); + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + `${statisticSelect}_${variableStr}` + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); + } + + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + if (error.includes("Unknown column")) { + throw new Error( + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for this model and region].` + ); + } else { + throw new Error(error); + } } + dataNotFoundForAnyCurve = true; } - dataNotFoundForAnyCurve = true; + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); } - const postQueryStartMoment = moment(); - // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined @@ -245,8 +274,9 @@ dataContourDiff = function (plotParams, plotFunction) { statType === "ctc", statType === "scalar" ); - plotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); - curves = plotParams.curves; + const newPlotParams = plotParams; + newPlotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); + curves = newPlotParams.curves; dataset[0].name = matsPlotUtils.getCurveText( matsTypes.PlotTypes.contourDiff, curves[0] @@ -262,7 +292,7 @@ dataContourDiff = function (plotParams, plotFunction) { const result = matsDataProcessUtils.processDataContour( dataset, curveInfoParams, - plotParams, + newPlotParams, bookkeepingParams ); plotFunction(result); diff --git a/apps/surface/server/dataFunctions/data_dailymodelcycle.js b/apps/surface/server/dataFunctions/data_dailymodelcycle.js index 9bb3b5aa3a..c28e0d9868 100644 --- a/apps/surface/server/dataFunctions/data_dailymodelcycle.js +++ b/apps/surface/server/dataFunctions/data_dailymodelcycle.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDailyModelCycle = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,54 +24,78 @@ dataDailyModelCycle = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; + let queryTableClause = ""; const regionType = curve["region-type"]; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[regionType][variableStr]; + let utcCycleStartClause = ""; + const forecastLength = curve["forecast-length"]; const forecastLengthClause = "and m0.fcst_len < 24"; - var timeVar; - var dateClause; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + + let timeVar; + let dateClause; let siteDateClause = ""; let siteMatchClause = ""; let sitesClause = ""; - var NAggregate; - var NClause; - var queryPool; + let NAggregate; + let NClause; + let queryPool; + if (regionType === "Predefined region") { timeVar = "m0.valid_day+3600*m0.hour"; - var metarStringStr = curve.truth; + dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; + NAggregate = "sum"; + [, NClause] = variable; + + const metarStringStr = curve.truth; const metarString = Object.keys( matsCollections.truth.findOne({ name: "truth" }).valuesMap ).find( @@ -78,8 +103,9 @@ dataDailyModelCycle = function (plotParams, plotFunction) { matsCollections.truth.findOne({ name: "truth" }).valuesMap[key] === metarStringStr ); - var regionStr = curve.region; - var region = Object.keys( + + const regionStr = curve.region; + const region = Object.keys( matsCollections.region.findOne({ name: "region" }).valuesMap ).find( (key) => @@ -87,13 +113,16 @@ dataDailyModelCycle = function (plotParams, plotFunction) { regionStr ); queryTableClause = `from ${model}_${metarString}_${region} as m0`; - dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; - NAggregate = "sum"; - NClause = variable[1]; - queryPool = sumPool; + queryPool = sumPool; // eslint-disable-line no-undef } else { timeVar = "m0.time"; - var modelTable; + dateClause = `and m0.time >= ${fromSecs} - 900 and m0.time <= ${toSecs} + 900`; + siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; + siteMatchClause = "and m0.sta_id = o.sta_id and m0.time = o.time"; + NAggregate = "count"; + NClause = "1"; + + let modelTable; if (forecastLength === 1) { modelTable = `${model}qp1f`; } else { @@ -108,28 +137,20 @@ dataDailyModelCycle = function (plotParams, plotFunction) { { optionsMap: 1 } ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - var thisSite; - var thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.sta_id in('${querySites.join("','")}')`; } else { throw new Error( "INFO: Please add sites in order to get a single/multi station plot." ); } - dateClause = `and m0.time >= ${fromSecs} - 900 and m0.time <= ${toSecs} + 900`; - siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; - siteMatchClause = "and m0.sta_id = o.sta_id and m0.time = o.time"; - NAggregate = "count"; - NClause = "1"; - queryPool = sitePool; + queryPool = sitePool; // eslint-disable-line no-undef } + if (curve["utc-cycle-start"].length !== 1) { throw new Error( "INFO: Please select exactly one UTC Cycle Init Hour for this plot type." @@ -142,72 +163,68 @@ dataDailyModelCycle = function (plotParams, plotFunction) { } else { utcCycleStartClause = `and floor(((${timeVar}+1800)-m0.fcst_len*3600)%(24*3600)/3600) IN(${utcCycleStart})`; // adjust by 1800 seconds to center obs at the top of the hour } - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; + const statisticClause = `sum(${variable[0]}) as square_diff_sum, ${NAggregate}(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(${timeVar}, ';', ${variable[0]}, ';', ${NClause}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by ${timeVar}) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; + // axisKey is used to determine which axis a curve should use. + // This axisKeySet object is used like a set and if a curve has the same + // units (axisKey) it will use the same axis. + // The axis number is assigned to the axisKeySet value, which is the axisKey. const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); + statType = statisticOptionsMap[statisticSelect]; const varUnits = statVarUnitMap[statisticSelect][variableStr]; - // axisKey is used to determine which axis a curve should use. - // This axisKeySet object is used like a set and if a curve has the same - // units (axisKey) it will use the same axis. - // The axis number is assigned to the axisKeySet value, which is the axisKey. const axisKey = varUnits; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select ceil(3600*floor(({{timeVar}}+1800)/3600)) as avtime, " + - "count(distinct ceil(3600*floor(({{timeVar}}+1800)/3600))) as N_times, " + - "min(ceil(3600*floor(({{timeVar}}+1800)/3600))) as min_secs, " + - "max(ceil(3600*floor(({{timeVar}}+1800)/3600))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{utcCycleStartClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - statement = statement.split("{{timeVar}}").join(timeVar); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select ceil(3600*floor(({{timeVar}}+1800)/3600)) as avtime, " + + "count(distinct ceil(3600*floor(({{timeVar}}+1800)/3600))) as N_times, " + + "min(ceil(3600*floor(({{timeVar}}+1800)/3600))) as min_secs, " + + "max(ceil(3600*floor(({{timeVar}}+1800)/3600))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{utcCycleStartClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + statement = statement.split("{{timeVar}}").join(timeVar); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - queryPool, + queryPool, // eslint-disable-line no-undef statement, appParams, `${statisticSelect}_${variableStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -223,6 +240,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -232,7 +250,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; if (error.includes("Unknown column")) { throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for the model/region [${model} and ${region}].` + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for this model and region].` ); } else { throw new Error(error); @@ -243,7 +261,6 @@ dataDailyModelCycle = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -268,6 +285,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/surface/server/dataFunctions/data_dieoff.js b/apps/surface/server/dataFunctions/data_dieoff.js index fc82e69855..d64e6ddb29 100644 --- a/apps/surface/server/dataFunctions/data_dieoff.js +++ b/apps/surface/server/dataFunctions/data_dieoff.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDieoff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,57 +24,80 @@ dataDieoff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; + let queryTableClause = ""; const regionType = curve["region-type"]; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[regionType][variableStr]; + const forecastLengthStr = curve["dieoff-type"]; const forecastLengthOptionsMap = matsCollections["dieoff-type"].findOne( { name: "dieoff-type" }, { optionsMap: 1 } ).optionsMap; const forecastLength = forecastLengthOptionsMap[forecastLengthStr][0]; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; - var timeVar; - var dateClause; + + let timeVar; + let dateClause; let siteDateClause = ""; let siteMatchClause = ""; let sitesClause = ""; - var NAggregate; - var NClause; - var queryPool; + let NAggregate; + let NClause; + let queryPool; + if (regionType === "Predefined region") { timeVar = "m0.valid_day+3600*m0.hour"; - var metarStringStr = curve.truth; + dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; + NAggregate = "sum"; + [, NClause] = variable; + + const metarStringStr = curve.truth; const metarString = Object.keys( matsCollections.truth.findOne({ name: "truth" }).valuesMap ).find( @@ -81,8 +105,9 @@ dataDieoff = function (plotParams, plotFunction) { matsCollections.truth.findOne({ name: "truth" }).valuesMap[key] === metarStringStr ); - var regionStr = curve.region; - var region = Object.keys( + + const regionStr = curve.region; + const region = Object.keys( matsCollections.region.findOne({ name: "region" }).valuesMap ).find( (key) => @@ -90,12 +115,15 @@ dataDieoff = function (plotParams, plotFunction) { regionStr ); queryTableClause = `from ${model}_${metarString}_${region} as m0`; - dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; - NAggregate = "sum"; - NClause = variable[1]; - queryPool = sumPool; + queryPool = sumPool; // eslint-disable-line no-undef } else { timeVar = "m0.time"; + dateClause = `and m0.time >= ${fromSecs} - 900 and m0.time <= ${toSecs} + 900`; + siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; + siteMatchClause = "and m0.sta_id = o.sta_id and m0.time = o.time"; + NAggregate = "count"; + NClause = "1"; + const modelTable = model.includes("ret_") || model.includes("Ret_") ? `${model}p` : `${model}qp`; const obsTable = @@ -106,32 +134,26 @@ dataDieoff = function (plotParams, plotFunction) { { optionsMap: 1 } ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - var thisSite; - var thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.sta_id in('${querySites.join("','")}')`; } else { throw new Error( "INFO: Please add sites in order to get a single/multi station plot." ); } - dateClause = `and m0.time >= ${fromSecs} - 900 and m0.time <= ${toSecs} + 900`; - siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; - siteMatchClause = "and m0.sta_id = o.sta_id and m0.time = o.time"; - NAggregate = "count"; - NClause = "1"; - queryPool = sitePool; + queryPool = sitePool; // eslint-disable-line no-undef } - var validTimes; + + let validTimes; let validTimeClause = ""; - var utcCycleStart; + + let utcCycleStart; let utcCycleStartClause = ""; + if (forecastLength === matsTypes.ForecastTypes.dieoff) { validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { @@ -151,72 +173,69 @@ dataDieoff = function (plotParams, plotFunction) { } else { dateClause = `and ${timeVar}-m0.fcst_len*3600 = ${fromSecs}`; } - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; + const statisticClause = `sum(${variable[0]}) as square_diff_sum, ${NAggregate}(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(${timeVar}, ';', ${variable[0]}, ';', ${NClause}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by ${timeVar}) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; + + // axisKey is used to determine which axis a curve should use. + // This axisKeySet object is used like a set and if a curve has the same + // units (axisKey) it will use the same axis. + // The axis number is assigned to the axisKeySet value, which is the axisKey. const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); + statType = statisticOptionsMap[statisticSelect]; const varUnits = statVarUnitMap[statisticSelect][variableStr]; - // axisKey is used to determine which axis a curve should use. - // This axisKeySet object is used like a set and if a curve has the same - // units (axisKey) it will use the same axis. - // The axis number is assigned to the axisKeySet value, which is the axisKey. const axisKey = varUnits; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.fcst_len as fcst_lead, " + - "count(distinct ceil(3600*floor(({{timeVar}}+1800)/3600))) as N_times, " + - "min(ceil(3600*floor(({{timeVar}}+1800)/3600))) as min_secs, " + - "max(ceil(3600*floor(({{timeVar}}+1800)/3600))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{validTimeClause}} " + - "{{utcCycleStartClause}} " + - "group by fcst_lead " + - "order by fcst_lead" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - statement = statement.split("{{timeVar}}").join(timeVar); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.fcst_len as fcst_lead, " + + "count(distinct ceil(3600*floor(({{timeVar}}+1800)/3600))) as N_times, " + + "min(ceil(3600*floor(({{timeVar}}+1800)/3600))) as min_secs, " + + "max(ceil(3600*floor(({{timeVar}}+1800)/3600))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{validTimeClause}} " + + "{{utcCycleStartClause}} " + + "group by fcst_lead " + + "order by fcst_lead" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + statement = statement.split("{{timeVar}}").join(timeVar); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - queryPool, + queryPool, // eslint-disable-line no-undef statement, appParams, `${statisticSelect}_${variableStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -232,6 +251,7 @@ dataDieoff = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -241,7 +261,7 @@ dataDieoff = function (plotParams, plotFunction) { error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; if (error.includes("Unknown column")) { throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for the model/region [${model} and ${region}].` + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for this model and region].` ); } else { throw new Error(error); @@ -252,7 +272,6 @@ dataDieoff = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -277,6 +296,7 @@ dataDieoff = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/surface/server/dataFunctions/data_histogram.js b/apps/surface/server/dataFunctions/data_histogram.js index 4d6298a4a7..1d674000cc 100644 --- a/apps/surface/server/dataFunctions/data_histogram.js +++ b/apps/surface/server/dataFunctions/data_histogram.js @@ -11,6 +11,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataHistogram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -21,57 +22,82 @@ dataHistogram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const alreadyMatched = false; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries const dataFoundForCurve = []; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const alreadyMatched = false; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; + + const axisMap = Object.create(null); + let statType; + let varUnits; + + let statement = ""; + let error = ""; const dataset = []; const allReturnedSubStats = []; const allReturnedSubSecs = []; - const axisMap = Object.create(null); // process user bin customizations const binParams = matsDataUtils.setHistogramParameters(plotParams); const { yAxisFormat } = binParams; const { binNum } = binParams; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; dataFoundForCurve[curveIndex] = true; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; + let queryTableClause = ""; const regionType = curve["region-type"]; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[regionType][variableStr]; + let validTimeClause = ""; + const forecastLength = curve["forecast-length"]; let forecastLengthClause = ""; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; - var timeVar; - var dateClause; + + let timeVar; + let dateClause; let siteDateClause = ""; let siteMatchClause = ""; let sitesClause = ""; - var NAggregate; - var NClause; - var queryPool; + let NAggregate; + let NClause; + let queryPool; + if (regionType === "Predefined region") { timeVar = "m0.valid_day+3600*m0.hour"; - var metarStringStr = curve.truth; + forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; + dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; + NAggregate = "sum"; + [, NClause] = variable; + + const metarStringStr = curve.truth; const metarString = Object.keys( matsCollections.truth.findOne({ name: "truth" }).valuesMap ).find( @@ -79,8 +105,9 @@ dataHistogram = function (plotParams, plotFunction) { matsCollections.truth.findOne({ name: "truth" }).valuesMap[key] === metarStringStr ); - var regionStr = curve.region; - var region = Object.keys( + + const regionStr = curve.region; + const region = Object.keys( matsCollections.region.findOne({ name: "region" }).valuesMap ).find( (key) => @@ -88,14 +115,16 @@ dataHistogram = function (plotParams, plotFunction) { regionStr ); queryTableClause = `from ${model}_${metarString}_${region} as m0`; - forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; - NAggregate = "sum"; - NClause = variable[1]; - queryPool = sumPool; + queryPool = sumPool; // eslint-disable-line no-undef } else { timeVar = "m0.time"; - var modelTable; + dateClause = `and m0.time >= ${fromSecs} - 900 and m0.time <= ${toSecs} + 900`; + siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; + siteMatchClause = "and m0.sta_id = o.sta_id and m0.time = o.time"; + NAggregate = "count"; + NClause = "1"; + + let modelTable; if (forecastLength === 1) { modelTable = `${model}qp1f`; forecastLengthClause = ""; @@ -112,50 +141,38 @@ dataHistogram = function (plotParams, plotFunction) { { optionsMap: 1 } ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - var thisSite; - var thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.sta_id in('${querySites.join("','")}')`; } else { throw new Error( "INFO: Please add sites in order to get a single/multi station plot." ); } - dateClause = `and m0.time >= ${fromSecs} - 900 and m0.time <= ${toSecs} + 900`; - siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; - siteMatchClause = "and m0.sta_id = o.sta_id and m0.time = o.time"; - NAggregate = "count"; - NClause = "1"; - queryPool = sitePool; + queryPool = sitePool; // eslint-disable-line no-undef } + const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((${timeVar}+1800)%(24*3600)/3600) IN(${validTimes})`; // adjust by 1800 seconds to center obs at the top of the hour } - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; + const statisticClause = `sum(${variable[0]}) as square_diff_sum, ${NAggregate}(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(${timeVar}, ';', ${variable[0]}, ';', ${NClause}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by ${timeVar}) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; - const { statVarUnitMap } = matsCollections.variable.findOne( - { name: "variable" }, - { statVarUnitMap: 1 } - ); - var varUnits = statVarUnitMap[statisticSelect][variableStr]; // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + const { statVarUnitMap } = matsCollections.variable.findOne( + { name: "variable" }, + { statVarUnitMap: 1 } + ); + statType = statisticOptionsMap[statisticSelect]; + varUnits = statVarUnitMap[statisticSelect][variableStr]; let axisKey = yAxisFormat; if (yAxisFormat === "Relative frequency") { axisKey += " (x100)"; @@ -163,51 +180,51 @@ dataHistogram = function (plotParams, plotFunction) { curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options curves[curveIndex].binNum = binNum; // stash the binNum to use it later for bar chart options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select ceil(3600*floor(({{timeVar}}+1800)/3600)) as avtime, " + - "count(distinct ceil(3600*floor(({{timeVar}}+1800)/3600))) as N_times, " + - "min(ceil(3600*floor(({{timeVar}}+1800)/3600))) as min_secs, " + - "max(ceil(3600*floor(({{timeVar}}+1800)/3600))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - statement = statement.split("{{timeVar}}").join(timeVar); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select ceil(3600*floor(({{timeVar}}+1800)/3600)) as avtime, " + + "count(distinct ceil(3600*floor(({{timeVar}}+1800)/3600))) as N_times, " + + "min(ceil(3600*floor(({{timeVar}}+1800)/3600))) as min_secs, " + + "max(ceil(3600*floor(({{timeVar}}+1800)/3600))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + statement = statement.split("{{timeVar}}").join(timeVar); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - queryPool, + queryPool, // eslint-disable-line no-undef statement, appParams, `${statisticSelect}_${variableStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -225,6 +242,7 @@ dataHistogram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -234,7 +252,7 @@ dataHistogram = function (plotParams, plotFunction) { error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; if (error.includes("Unknown column")) { throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for the model/region [${model} and ${region}].` + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for this model and region].` ); } else { throw new Error(error); diff --git a/apps/surface/server/dataFunctions/data_map.js b/apps/surface/server/dataFunctions/data_map.js index 8c5ade2115..e1c17713e1 100644 --- a/apps/surface/server/dataFunctions/data_map.js +++ b/apps/surface/server/dataFunctions/data_map.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataMap = function (plotParams, plotFunction) { const appParams = { plotType: matsTypes.PlotTypes.map, @@ -21,38 +22,52 @@ dataMap = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; + const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const dataRequests = {}; // used to store data queries + const curves = JSON.parse(JSON.stringify(plotParams.curves)); if (curves.length > 1) { throw new Error("INFO: There must only be one added curve."); } + + let statement = ""; + let error = ""; const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - // map plots are only for stations--no predefined regions + const regionType = "Select stations"; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[regionType][variableStr]; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { - validTimeClause = `and floor((m0.time+1800)%(24*3600)/3600) IN(${validTimes})`; // adjust by 1800 seconds to center obs at the top of the hour + validTimeClause = `and floor((m0.time+1800)%(24*3600)/3600) IN(${validTimes})`; } + const forecastLength = curve["forecast-length"]; let forecastLengthClause = ""; + + const statisticSelect = curve.statistic; + let sitesClause = ""; + let modelTable; if (forecastLength === 1) { modelTable = `${model}qp1f`; @@ -70,15 +85,11 @@ dataMap = function (plotParams, plotFunction) { { optionsMap: 1 } ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - let thisSite; - let thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.sta_id in('${querySites.join("','")}')`; } else { throw new Error( @@ -88,103 +99,114 @@ dataMap = function (plotParams, plotFunction) { const dateClause = `and m0.time >= ${fromSecs} - 900 and m0.time <= ${toSecs} + 900`; const siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; const siteMatchClause = "and m0.sta_id = o.sta_id and m0.time = o.time"; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; + const statisticClause = `sum(${variable[0]}) as square_diff_sum, count(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(m0.time, ';', ${variable[0]}, ';', 1, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.time) as sub_data, count(${variable[0]}) as N0`; - const statType = statisticOptionsMap[statisticSelect]; + const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); const varUnits = statVarUnitMap[statisticSelect][variableStr]; - let statement = - "select m0.sta_id as sta_id, " + - "count(distinct ceil(3600*floor((m0.time+1800)/3600))) as N_times, " + - "min(ceil(3600*floor((m0.time+1800)/3600))) as min_secs, " + - "max(ceil(3600*floor((m0.time+1800)/3600))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by sta_id " + - "order by sta_id" + - ";"; + let d; + let dLowest; + let dLow; + let dModerate; + let dHigh; + let dHighest; + let valueLimits; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select m0.sta_id as sta_id, " + + "count(distinct ceil(3600*floor((m0.time+1800)/3600))) as N_times, " + + "min(ceil(3600*floor((m0.time+1800)/3600))) as min_secs, " + + "max(ceil(3600*floor((m0.time+1800)/3600))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by sta_id " + + "order by sta_id" + + ";"; - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - dataRequests[label] = statement; + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + dataRequests[label] = statement; - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBMapScalar( - sitePool, - statement, - model, - statisticSelect, - variableStr, - varUnits, - siteMap, - appParams - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.length, - }; - // get the data back from the query - var d = queryResult.data; - var dLowest = queryResult.dataLowest; - var dLow = queryResult.dataLow; - var dModerate = queryResult.dataModerate; - var dHigh = queryResult.dataHigh; - var dHighest = queryResult.dataHighest; - var { valueLimits } = queryResult; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - if (error.includes("Unknown column")) { - throw new Error( - `INFO: The variable [${variableStr}] is not supported by the database for the model/sites [${model} and ${sitesList}].` - ); - } else { - throw new Error(error); + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBMapScalar( + sitePool, // eslint-disable-line no-undef + statement, + model, + statisticSelect, + variableStr, + varUnits, + siteMap, + appParams + ); + + finishMoment = moment(); + dataRequests[label] = "Station plot -- no one query."; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.length, + }; + // get the data back from the query + d = queryResult.data; + dLowest = queryResult.dataLowest; + dLow = queryResult.dataLow; + dModerate = queryResult.dataModerate; + dHigh = queryResult.dataHigh; + dHighest = queryResult.dataHighest; + valueLimits = queryResult.valueLimits; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); + } + + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + if (error.includes("Unknown column")) { + throw new Error( + `INFO: The variable [${variableStr}] is not supported by the database for the model/sites [${model} and ${sitesList}].` + ); + } else { + throw new Error(error); + } } } + } else { + // this is a difference curve -- not supported for maps + throw new Error( + "INFO: Difference curves are not supported for maps, as there is only one curve." + ); } + const postQueryStartMoment = moment(); let cOptions = matsDataCurveOpsUtils.generateMapCurveOptions( curve, d, @@ -252,6 +274,15 @@ dataMap = function (plotParams, plotFunction) { ); // generate highest text layer dataset.push(cOptions); + const postQueryFinishMoment = moment(); + dataRequests[`post data retrieval (query) process time - ${label}`] = { + begin: postQueryStartMoment.format(), + finish: postQueryFinishMoment.format(), + duration: `${moment + .duration(postQueryFinishMoment.diff(postQueryStartMoment)) + .asSeconds()} seconds`, + }; + const resultOptions = matsDataPlotOpsUtils.generateMapPlotOptions(false); const totalProcessingFinish = moment(); dataRequests["total retrieval and processing time for curve set"] = { diff --git a/apps/surface/server/dataFunctions/data_series.js b/apps/surface/server/dataFunctions/data_series.js index 1fc2ea65be..b9c9d3d06f 100644 --- a/apps/surface/server/dataFunctions/data_series.js +++ b/apps/surface/server/dataFunctions/data_series.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSeries = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,54 +24,85 @@ dataSeries = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; + let queryTableClause = ""; const regionType = curve["region-type"]; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[regionType][variableStr]; + let validTimeClause = ""; let forecastLength = curve["forecast-length"]; let forecastLengthClause = ""; - var timeVar; - var dateClause; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + + const averageStr = curve.average; + const averageOptionsMap = matsCollections.average.findOne( + { name: "average" }, + { optionsMap: 1 } + ).optionsMap; + const average = averageOptionsMap[averageStr][0]; + + let timeVar; + let dateClause; let siteDateClause = ""; let siteMatchClause = ""; let sitesClause = ""; - var NAggregate; - var NClause; - var queryPool; + let NAggregate; + let NClause; + let queryPool; + if (regionType === "Predefined region") { timeVar = "m0.valid_day+3600*m0.hour"; - var metarStringStr = curve.truth; + forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; + dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; + NAggregate = "sum"; + [, NClause] = variable; + + const metarStringStr = curve.truth; const metarString = Object.keys( matsCollections.truth.findOne({ name: "truth" }).valuesMap ).find( @@ -78,8 +110,9 @@ dataSeries = function (plotParams, plotFunction) { matsCollections.truth.findOne({ name: "truth" }).valuesMap[key] === metarStringStr ); - var regionStr = curve.region; - var region = Object.keys( + + const regionStr = curve.region; + const region = Object.keys( matsCollections.region.findOne({ name: "region" }).valuesMap ).find( (key) => @@ -87,14 +120,16 @@ dataSeries = function (plotParams, plotFunction) { regionStr ); queryTableClause = `from ${model}_${metarString}_${region} as m0`; - forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; - NAggregate = "sum"; - NClause = variable[1]; - queryPool = sumPool; + queryPool = sumPool; // eslint-disable-line no-undef } else { timeVar = "m0.time"; - var modelTable; + dateClause = `and m0.time >= ${fromSecs} - 900 and m0.time <= ${toSecs} + 900`; + siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; + siteMatchClause = "and m0.sta_id = o.sta_id and m0.time = o.time"; + NAggregate = "count"; + NClause = "1"; + + let modelTable; if (forecastLength === 1) { modelTable = `${model}qp1f`; forecastLengthClause = ""; @@ -111,105 +146,86 @@ dataSeries = function (plotParams, plotFunction) { { optionsMap: 1 } ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - var thisSite; - var thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.sta_id in('${querySites.join("','")}')`; } else { throw new Error( "INFO: Please add sites in order to get a single/multi station plot." ); } - dateClause = `and m0.time >= ${fromSecs} - 900 and m0.time <= ${toSecs} + 900`; - siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; - siteMatchClause = "and m0.sta_id = o.sta_id and m0.time = o.time"; - NAggregate = "count"; - NClause = "1"; - queryPool = sitePool; + queryPool = sitePool; // eslint-disable-line no-undef } + const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and floor((${timeVar}+1800)%(24*3600)/3600) IN(${validTimes})`; // adjust by 1800 seconds to center obs at the top of the hour } - const averageStr = curve.average; - const averageOptionsMap = matsCollections.average.findOne( - { name: "average" }, - { optionsMap: 1 } - ).optionsMap; - const average = averageOptionsMap[averageStr][0]; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; + const statisticClause = `sum(${variable[0]}) as square_diff_sum, ${NAggregate}(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(${timeVar}, ';', ${variable[0]}, ';', ${NClause}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by ${timeVar}) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; + + // axisKey is used to determine which axis a curve should use. + // This axisKeySet object is used like a set and if a curve has the same + // units (axisKey) it will use the same axis. + // The axis number is assigned to the axisKeySet value, which is the axisKey. const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); + statType = statisticOptionsMap[statisticSelect]; const varUnits = statVarUnitMap[statisticSelect][variableStr]; - // axisKey is used to determine which axis a curve should use. - // This axisKeySet object is used like a set and if a curve has the same - // units (axisKey) it will use the same axis. - // The axis number is assigned to the axisKeySet value, which is the axisKey. const axisKey = varUnits; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select {{average}} as avtime, " + - "count(distinct ceil(3600*floor(({{timeVar}}+1800)/3600))) as N_times, " + - "min(ceil(3600*floor(({{timeVar}}+1800)/3600))) as min_secs, " + - "max(ceil(3600*floor(({{timeVar}}+1800)/3600))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{average}}", average); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - statement = statement.split("{{timeVar}}").join(timeVar); - dataRequests[label] = statement; - - // math is done on forecastLength later on -- set all analyses to 0 - if (forecastLength === "-99") { - forecastLength = "0"; - } - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select {{average}} as avtime, " + + "count(distinct ceil(3600*floor(({{timeVar}}+1800)/3600))) as N_times, " + + "min(ceil(3600*floor(({{timeVar}}+1800)/3600))) as min_secs, " + + "max(ceil(3600*floor(({{timeVar}}+1800)/3600))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{average}}", average); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + statement = statement.split("{{timeVar}}").join(timeVar); + dataRequests[label] = statement; + + // math is done on forecastLength later on -- set all analyses to 0 + if (forecastLength === "-99") { + forecastLength = "0"; + } + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBTimeSeries( - queryPool, + queryPool, // eslint-disable-line no-undef statement, model, forecastLength, @@ -221,7 +237,9 @@ dataSeries = function (plotParams, plotFunction) { appParams, false ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -237,6 +255,7 @@ dataSeries = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -246,7 +265,7 @@ dataSeries = function (plotParams, plotFunction) { error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; if (error.includes("Unknown column")) { throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for the model/region [${model} and ${region}].` + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for this model and region].` ); } else { throw new Error(error); @@ -257,7 +276,6 @@ dataSeries = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -282,6 +300,7 @@ dataSeries = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/surface/server/dataFunctions/data_simple_scatter.js b/apps/surface/server/dataFunctions/data_simple_scatter.js index bba6d2e206..34a7d37525 100644 --- a/apps/surface/server/dataFunctions/data_simple_scatter.js +++ b/apps/surface/server/dataFunctions/data_simple_scatter.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSimpleScatter = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,14 +23,15 @@ dataSimpleScatter = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; + const axisXMap = Object.create(null); const axisYMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; @@ -37,35 +39,29 @@ dataSimpleScatter = function (plotParams, plotFunction) { let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statType; + let varUnitsX; + let varUnitsY; + + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const binParam = curve["bin-parameter"]; const binClause = matsCollections["bin-parameter"].findOne({ name: "bin-parameter", }).optionsMap[binParam]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var metarStringStr = curve.truth; - const metarString = Object.keys( - matsCollections.truth.findOne({ name: "truth" }).valuesMap - ).find( - (key) => - matsCollections.truth.findOne({ name: "truth" }).valuesMap[key] === - metarStringStr - ); - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - const queryTableClause = `from ${model}_${metarString}_${region} as m0`; - // scatterplots are only for predefined regions--no station plots + const regionType = "Predefined region"; + const variableXStr = curve["x-variable"]; const variableYStr = curve["y-variable"]; const variableOptionsMap = matsCollections.variable.findOne( @@ -74,19 +70,25 @@ dataSimpleScatter = function (plotParams, plotFunction) { ).optionsMap; const variableX = variableOptionsMap[regionType][variableXStr]; const variableY = variableOptionsMap[regionType][variableYStr]; + + const metarStringStr = curve.truth; + const metarString = Object.keys( + matsCollections.truth.findOne({ name: "truth" }).valuesMap + ).find( + (key) => + matsCollections.truth.findOne({ name: "truth" }).valuesMap[key] === + metarStringStr + ); + let validTimeClause = ""; - let forecastLengthClause = ""; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let dateString = ""; - let dateClause = ""; if (binParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and m0.hour IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (binParam !== "Fcst lead time") { const forecastLength = curve["forecast-length"]; if (forecastLength === undefined) { @@ -96,12 +98,7 @@ dataSimpleScatter = function (plotParams, plotFunction) { } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } - if (binParam === "Init Date" && binParam !== "Valid Date") { - dateString = "m0.valid_day+3600*m0.hour-m0.fcst_len*3600"; - } else { - dateString = "m0.valid_day+3600*m0.hour"; - } - dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; + const statisticXSelect = curve["x-statistic"]; const statisticYSelect = curve["y-statistic"]; const statisticOptionsMap = matsCollections.statistic.findOne( @@ -112,55 +109,78 @@ dataSimpleScatter = function (plotParams, plotFunction) { `sum(${variableX[0]}) as square_diff_sumX, sum(${variableX[1]}) as N_sumX, sum(${variableX[2]}) as obs_model_diff_sumX, sum(${variableX[3]}) as model_sumX, sum(${variableX[4]}) as obs_sumX, sum(${variableX[5]}) as abs_sumX, ` + `sum(${variableY[0]}) as square_diff_sumY, sum(${variableY[1]}) as N_sumY, sum(${variableY[2]}) as obs_model_diff_sumY, sum(${variableY[3]}) as model_sumY, sum(${variableY[4]}) as obs_sumY, sum(${variableY[5]}) as abs_sumY, ` + `group_concat(m0.valid_day+3600*m0.hour, ';', ${variableX[0]}, ';', ${variableX[1]}, ';', ${variableX[2]}, ';', ${variableX[3]}, ';', ${variableX[4]}, ';', ${variableX[5]}, ';', ${variableY[0]}, ';', ${variableY[1]}, ';', ${variableY[2]}, ';', ${variableY[3]}, ';', ${variableY[4]}, ';', ${variableY[5]} order by m0.valid_day+3600*m0.hour) as sub_data, count(${variableX[0]}) as N0`; - var statType = statisticOptionsMap[statisticXSelect]; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + let dateString = ""; + let dateClause = ""; + if (binParam === "Init Date" && binParam !== "Valid Date") { + dateString = "m0.valid_day+3600*m0.hour-m0.fcst_len*3600"; + } else { + dateString = "m0.valid_day+3600*m0.hour"; + } + dateClause = `and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + + const queryTableClause = `from ${model}_${metarString}_${region} as m0`; + const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); - const varUnitsX = statVarUnitMap[statisticXSelect][variableXStr]; - const varUnitsY = statVarUnitMap[statisticYSelect][variableYStr]; + statType = statisticOptionsMap[statisticXSelect]; + varUnitsX = statVarUnitMap[statisticXSelect][variableXStr]; + varUnitsY = statVarUnitMap[statisticYSelect][variableYStr]; - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{binClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "group by binVal " + - "order by binVal" + - ";"; - - statement = statement.replace("{{binClause}}", binClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "{{binClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "group by binVal " + + "order by binVal" + + ";"; + + statement = statement.replace("{{binClause}}", binClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSimpleScatter( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, `${statisticXSelect}_${variableXStr}`, `${statisticYSelect}_${variableYStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -176,6 +196,7 @@ dataSimpleScatter = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -185,7 +206,7 @@ dataSimpleScatter = function (plotParams, plotFunction) { error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; if (error.includes("Unknown column")) { throw new Error( - `INFO: The statistic/variable combination [${statisticXSelect}/${variableXStr} and ${statisticYSelect}/${variableYStr}] is not supported by the database for the model/region [${model} and ${region}].` + `INFO: The statistic/variable combination [${statisticXSelect}/${variableXStr} and ${statisticYSelect}/${variableYStr}] is not supported by the database for this model and region].` ); } else { throw new Error(error); @@ -196,7 +217,6 @@ dataSimpleScatter = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -204,7 +224,7 @@ dataSimpleScatter = function (plotParams, plotFunction) { ymax = ymax > d.ymax ? ymax : d.ymax; } } else { - // this is a difference curve -- not supported for ROC plots + // this is a difference curve -- not supported for scatter plots throw new Error( "INFO: Difference curves are not supported for performance diagrams, as they do not feature consistent x or y values across all curves." ); @@ -212,6 +232,7 @@ dataSimpleScatter = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/surface/server/dataFunctions/data_simple_scatter_scratch.js b/apps/surface/server/dataFunctions/data_simple_scatter_scratch.js deleted file mode 100644 index f83318468d..0000000000 --- a/apps/surface/server/dataFunctions/data_simple_scatter_scratch.js +++ /dev/null @@ -1,282 +0,0 @@ -// /* -// * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. -// */ -// -// import {matsCollections} from 'meteor/randyp:mats-common'; -// import {matsTypes} from 'meteor/randyp:mats-common'; -// import {matsDataUtils} from 'meteor/randyp:mats-common'; -// import {matsDataQueryUtils} from 'meteor/randyp:mats-common'; -// import {matsDataCurveOpsUtils} from 'meteor/randyp:mats-common'; -// import {matsDataProcessUtils} from 'meteor/randyp:mats-common'; -// import {moment} from 'meteor/momentjs:moment'; -// -// dataSimpleScatter = function (plotParams, plotFunction) { -// // initialize variables common to all curves -// const appParams = { -// "plotType": matsTypes.PlotTypes.simpleScatter, -// "matching": plotParams['plotAction'] === matsTypes.PlotActions.matched, -// "completeness": plotParams['completeness'], -// "outliers": plotParams['outliers'], -// "hideGaps": plotParams['noGapsCheck'], -// "hasLevels": false -// }; -// var dataRequests = {}; // used to store data queries -// var dataFoundForCurve = true; -// var dataFoundForAnyCurve = false; -// var totalProcessingStart = moment(); -// var error = ""; -// var curves = JSON.parse(JSON.stringify(plotParams.curves)); -// var curvesLength = curves.length; -// var dataset = []; -// var axisXMap = Object.create(null); -// var axisYMap = Object.create(null); -// var xmax = -1 * Number.MAX_VALUE; -// var ymax = -1 * Number.MAX_VALUE; -// var xmin = Number.MAX_VALUE; -// var ymin = Number.MAX_VALUE; -// -// for (var curveIndex = 0; curveIndex < curvesLength; curveIndex++) { -// // initialize variables specific to each curve -// var curve = curves[curveIndex]; -// var diffFrom = curve.diffFrom; -// var label = curve['label']; -// var regionType = curve['region-type']; -// var binParam = curve['bin-parameter']; -// var binClause = matsCollections['bin-parameter'].findOne({name: 'bin-parameter'}).optionsMap[regionType][binParam]; -// var model = matsCollections['data-source'].findOne({name: 'data-source'}).optionsMap[curve['data-source']][0]; -// var queryTableClause = ""; -// var variableXStr = curve['x-variable']; -// var variableYStr = curve['y-variable']; -// var variableOptionsMap = matsCollections['variable'].findOne({name: 'variable'}, {optionsMap: 1})['optionsMap']; -// var variableX = variableOptionsMap[regionType][variableXStr]; -// var variableY = variableOptionsMap[regionType][variableYStr]; -// var validTimeClause = ""; -// var forecastLength = curve['forecast-length']; -// var forecastLengthClause = ""; -// var dateRange = matsDataUtils.getDateRange(curve['curve-dates']); -// var fromSecs = dateRange.fromSeconds; -// var toSecs = dateRange.toSeconds; -// var timeVar; -// var dateString = ""; -// var dateClause = ""; -// var siteDateClause = ""; -// var siteMatchClause = ""; -// var sitesClause = ""; -// var NAggregate; -// var NClauseX; -// var NClauseY; -// var queryPool; -// if (regionType === 'Predefined region') { -// timeVar = "m0.valid_day+3600*m0.hour"; -// var metarStringStr = curve['truth']; -// var metarString = Object.keys(matsCollections['truth'].findOne({name: 'truth'}).valuesMap).find(key => matsCollections['truth'].findOne({name: 'truth'}).valuesMap[key] === metarStringStr); -// var regionStr = curve['region']; -// var region = Object.keys(matsCollections['region'].findOne({name: 'region'}).valuesMap).find(key => matsCollections['region'].findOne({name: 'region'}).valuesMap[key] === regionStr); -// queryTableClause = "from " + model + "_" + metarString + "_" + region + " as m0"; -// if (binParam !== 'Fcst lead time') { -// if (forecastLength === undefined) { -// throw new Error("INFO: " + label + "'s forecast lead time is undefined. Please assign it a value."); -// } -// forecastLengthClause = "and m0.fcst_len = " + forecastLength; -// } -// if ((binParam === 'Init Date') && (binParam !== 'Valid Date')) { -// dateString = "m0.valid_day+3600*m0.hour-m0.fcst_len*3600"; -// } else { -// dateString = "m0.valid_day+3600*m0.hour"; -// } -// dateClause = "and " + dateString + " >= " + fromSecs + " and " + dateString + " <= " + toSecs; -// NAggregate = 'sum'; -// NClauseX = variableX[1]; -// NClauseY = variableY[1]; -// queryPool = sumPool; -// } else { -// timeVar = "m0.time"; -// var modelTable; -// if (binParam !== 'Fcst lead time') { -// if (forecastLength === undefined) { -// throw new Error("INFO: " + label + "'s forecast lead time is undefined. Please assign it a value."); -// } -// if (forecastLength === 1) { -// modelTable = model + "qp1f"; -// forecastLengthClause = ""; -// } else { -// modelTable = (model.includes('ret_') || model.includes('Ret_')) ? model + "p" : model + "qp"; -// forecastLengthClause = "and m0.fcst_len = " + forecastLength + " " -// } -// } else { -// modelTable = (model.includes('ret_') || model.includes('Ret_')) ? model + "p" : model + "qp"; -// } -// var obsTable = (model.includes('ret_') || model.includes('Ret_')) ? 'obs_retro' : 'obs'; -// queryTableClause = "from " + obsTable + " as o, " + modelTable + " as m0 "; -// var siteMap = matsCollections.StationMap.findOne({name: 'stations'}, {optionsMap: 1})['optionsMap']; -// var sitesList = curve['sites'] === undefined ? [] : curve['sites']; -// var querySites = []; -// if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { -// var thisSite; -// var thisSiteObj; -// for (var sidx = 0; sidx < sitesList.length; sidx++) { -// thisSite = sitesList[sidx]; -// thisSiteObj = siteMap.find(obj => { -// return obj.origName === thisSite; -// }); -// querySites.push(thisSiteObj.options.id); -// } -// sitesClause = " and m0.sta_id in('" + querySites.join("','") + "')"; -// } else { -// throw new Error("INFO: Please add sites in order to get a single/multi station plot."); -// } -// if ((binParam === 'Init Date') && (binParam !== 'Valid Date')) { -// dateString = "m0.time-m0.fcst_len*3600"; -// } else { -// dateString = "m0.time"; -// } -// dateClause = "and " + dateString + " >= " + fromSecs + " - 900 and " + dateString + " <= " + toSecs + " + 900"; -// siteDateClause = "and o.time >= " + fromSecs + " - 900 and o.time <= " + toSecs + " + 900"; -// siteMatchClause = "and m0.sta_id = o.sta_id and m0.time = o.time"; -// NAggregate = 'count'; -// NClauseX = '1'; -// NClauseY = '1'; -// queryPool = sitePool; -// } -// if (binParam !== 'Valid UTC hour') { -// var validTimes = curve['valid-time'] === undefined ? [] : curve['valid-time']; -// if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { -// validTimeClause = "and floor((" + timeVar + "+1800)%(24*3600)/3600) IN(" + validTimes + ")"; // adjust by 1800 seconds to center obs at the top of the hour -// } -// } -// var statisticXSelect = curve['x-statistic']; -// var statisticYSelect = curve['y-statistic']; -// var statisticOptionsMap = matsCollections['statistic'].findOne({name: 'statistic'}, {optionsMap: 1})['optionsMap']; -// var statisticClause = "sum(" + variableX[0] + ") as square_diff_sumX, " + NAggregate + "(" + variableX[1] + ") as N_sumX, sum(" + variableX[2] + ") as obs_model_diff_sumX, sum(" + variableX[3] + ") as model_sumX, sum(" + variableX[4] + ") as obs_sumX, sum(" + variableX[5] + ") as abs_sumX, " + -// "sum(" + variableY[0] + ") as square_diff_sumY, " + NAggregate + "(" + variableY[1] + ") as N_sumY, sum(" + variableY[2] + ") as obs_model_diff_sumY, sum(" + variableY[3] + ") as model_sumY, sum(" + variableY[4] + ") as obs_sumY, sum(" + variableY[5] + ") as abs_sumY, " + -// "group_concat(" + timeVar + ", ';', " + variableX[0] + ", ';', " + NClauseX + ", ';', " + variableX[2] + ", ';', " + variableX[3] + ", ';', " + variableX[4] + ", ';', " + variableX[5] + ", ';', " + -// variableY[0] + ", ';', " + NClauseY + ", ';', " + variableY[2] + ", ';', " + variableY[3] + ", ';', " + variableY[4] + ", ';', " + variableY[5] + " order by " + timeVar + ") as sub_data, count(" + variableX[0] + ") as N0"; -// var statType = statisticOptionsMap[statisticXSelect]; -// var statVarUnitMap = matsCollections['variable'].findOne({name: 'variable'}, {statVarUnitMap: 1})['statVarUnitMap']; -// var varUnitsX = statVarUnitMap[statisticXSelect][variableXStr]; -// var varUnitsY = statVarUnitMap[statisticYSelect][variableYStr]; -// -// var d; -// if (!diffFrom) { -// // this is a database driven curve, not a difference curve -// // prepare the query from the above parameters -// var statement = "{{binClause}} " + -// "count(distinct ceil(3600*floor(({{timeVar}}+1800)/3600))) as N_times, " + -// "min(ceil(3600*floor(({{timeVar}}+1800)/3600))) as min_secs, " + -// "max(ceil(3600*floor(({{timeVar}}+1800)/3600))) as max_secs, " + -// "{{statisticClause}} " + -// "{{queryTableClause}} " + -// "where 1=1 " + -// "{{siteMatchClause}} " + -// "{{sitesClause}} " + -// "{{dateClause}} " + -// "{{siteDateClause}} " + -// "{{validTimeClause}} " + -// "{{forecastLengthClause}} " + -// "group by binVal " + -// "order by binVal" + -// ";"; -// -// statement = statement.replace('{{binClause}}', binClause); -// statement = statement.replace('{{statisticClause}}', statisticClause); -// statement = statement.replace('{{queryTableClause}}', queryTableClause); -// statement = statement.replace('{{siteMatchClause}}', siteMatchClause); -// statement = statement.replace('{{sitesClause}}', sitesClause); -// statement = statement.replace('{{validTimeClause}}', validTimeClause); -// statement = statement.replace('{{forecastLengthClause}}', forecastLengthClause); -// statement = statement.replace('{{dateClause}}', dateClause); -// statement = statement.replace('{{siteDateClause}}', siteDateClause); -// statement = statement.split('{{timeVar}}').join(timeVar); -// dataRequests[label] = statement; -// -// var queryResult; -// var startMoment = moment(); -// var finishMoment; -// try { -// // send the query statement to the query function -// queryResult = matsDataQueryUtils.queryDBSimpleScatter(queryPool, statement, appParams, statisticXSelect + "_" + variableXStr, statisticYSelect + "_" + variableYStr); -// finishMoment = moment(); -// dataRequests["data retrieval (query) time - " + label] = { -// begin: startMoment.format(), -// finish: finishMoment.format(), -// duration: moment.duration(finishMoment.diff(startMoment)).asSeconds() + " seconds", -// recordCount: queryResult.data.x.length -// }; -// // get the data back from the query -// d = queryResult.data; -// } catch (e) { -// // this is an error produced by a bug in the query function, not an error returned by the mysql database -// e.message = "Error in queryDB: " + e.message + " for statement: " + statement; -// throw new Error(e.message); -// } -// if (queryResult.error !== undefined && queryResult.error !== "") { -// if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { -// // this is NOT an error just a no data condition -// dataFoundForCurve = false; -// } else { -// // this is an error returned by the mysql database -// error += "Error from verification query:
" + queryResult.error + "
query:
" + statement + "
"; -// if (error.includes('Unknown column')) { -// throw new Error("INFO: The statistic/variable combination [" + statisticXSelect + "/" + variableXStr + " and " + statisticYSelect + "/" + variableYStr + "] is not supported by the database for the model/region [" + model + " and " + region + "]."); -// } else { -// throw new Error(error); -// } -// } -// } else { -// dataFoundForAnyCurve = true; -// } -// -// // set axis limits based on returned data -// var postQueryStartMoment = moment(); -// if (dataFoundForCurve) { -// xmin = xmin < d.xmin ? xmin : d.xmin; -// xmax = xmax > d.xmax ? xmax : d.xmax; -// ymin = ymin < d.ymin ? ymin : d.ymin; -// ymax = ymax > d.ymax ? ymax : d.ymax; -// } -// } else { -// // this is a difference curve -- not supported for ROC plots -// throw new Error("INFO: Difference curves are not supported for performance diagrams, as they do not feature consistent x or y values across all curves."); -// } -// -// // set curve annotation to be the curve mean -- may be recalculated later -// // also pass previously calculated axis stats to curve options -// const mean = d.sum / d.x.length; -// const annotation = mean === undefined ? label + "- mean = NoData" : label + "- mean = " + mean.toPrecision(4); -// curve['annotation'] = annotation; -// curve['xmin'] = d.xmin; -// curve['xmax'] = d.xmax; -// curve['ymin'] = d.ymin; -// curve['ymax'] = d.ymax; -// curve['axisXKey'] = varUnitsX; -// curve['axisYKey'] = varUnitsY; -// curve['binParam'] = binParam; -// const cOptions = matsDataCurveOpsUtils.generateScatterCurveOptions(curve, curveIndex, axisXMap, axisYMap, d, appParams); // generate plot with data, curve annotation, axis labels, etc. -// dataset.push(cOptions); -// var postQueryFinishMoment = moment(); -// dataRequests["post data retrieval (query) process time - " + label] = { -// begin: postQueryStartMoment.format(), -// finish: postQueryFinishMoment.format(), -// duration: moment.duration(postQueryFinishMoment.diff(postQueryStartMoment)).asSeconds() + ' seconds' -// }; -// } // end for curves -// -// if (!dataFoundForAnyCurve) { -// // we found no data for any curves so don't bother proceeding -// throw new Error("INFO: No valid data for any curves."); -// } -// -// // process the data returned by the query -// const curveInfoParams = { -// "curves": curves, -// "curvesLength": curvesLength, -// "statType": statType, -// "axisXMap": axisXMap, -// "axisYMap": axisYMap, -// "xmax": xmax, -// "xmin": xmin -// }; -// const bookkeepingParams = {"dataRequests": dataRequests, "totalProcessingStart": totalProcessingStart}; -// var result = matsDataProcessUtils.processDataSimpleScatter(dataset, appParams, curveInfoParams, plotParams, bookkeepingParams); -// plotFunction(result); -// }; diff --git a/apps/surface/server/dataFunctions/data_validtime.js b/apps/surface/server/dataFunctions/data_validtime.js index 6016b1fbd5..a08ae0d493 100644 --- a/apps/surface/server/dataFunctions/data_validtime.js +++ b/apps/surface/server/dataFunctions/data_validtime.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataValidTime = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,55 +24,80 @@ dataValidTime = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; + let queryTableClause = ""; const regionType = curve["region-type"]; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[regionType][variableStr]; - var validTimeVar; + + let validTimeVar; + const forecastLength = curve["forecast-length"]; let forecastLengthClause = ""; + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; - var timeVar; - var dateClause; + + let timeVar; + let dateClause; let siteDateClause = ""; let siteMatchClause = ""; let sitesClause = ""; - var NAggregate; - var NClause; - var queryPool; + let NAggregate; + let NClause; + let queryPool; + if (regionType === "Predefined region") { timeVar = "m0.valid_day+3600*m0.hour"; validTimeVar = "m0.hour"; - var metarStringStr = curve.truth; + forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; + dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; + NAggregate = "sum"; + [, NClause] = variable; + + const metarStringStr = curve.truth; const metarString = Object.keys( matsCollections.truth.findOne({ name: "truth" }).valuesMap ).find( @@ -79,8 +105,9 @@ dataValidTime = function (plotParams, plotFunction) { matsCollections.truth.findOne({ name: "truth" }).valuesMap[key] === metarStringStr ); - var regionStr = curve.region; - var region = Object.keys( + + const regionStr = curve.region; + const region = Object.keys( matsCollections.region.findOne({ name: "region" }).valuesMap ).find( (key) => @@ -88,15 +115,17 @@ dataValidTime = function (plotParams, plotFunction) { regionStr ); queryTableClause = `from ${model}_${metarString}_${region} as m0`; - forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - dateClause = `and m0.valid_day+3600*m0.hour >= ${fromSecs} and m0.valid_day+3600*m0.hour <= ${toSecs}`; - NAggregate = "sum"; - NClause = variable[1]; - queryPool = sumPool; + queryPool = sumPool; // eslint-disable-line no-undef } else { timeVar = "m0.time"; validTimeVar = `floor((${timeVar}+1800)%(24*3600)/3600)`; // adjust by 1800 seconds to center obs at the top of the hour - var modelTable; + dateClause = `and m0.time >= ${fromSecs} - 900 and m0.time <= ${toSecs} + 900`; + siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; + siteMatchClause = "and m0.sta_id = o.sta_id and m0.time = o.time"; + NAggregate = "count"; + NClause = "1"; + + let modelTable; if (forecastLength === 1) { modelTable = `${model}qp1f`; forecastLengthClause = ""; @@ -113,93 +142,81 @@ dataValidTime = function (plotParams, plotFunction) { { optionsMap: 1 } ).optionsMap; const sitesList = curve.sites === undefined ? [] : curve.sites; - const querySites = []; + let querySites = []; if (sitesList.length > 0 && sitesList !== matsTypes.InputTypes.unused) { - var thisSite; - var thisSiteObj; - for (let sidx = 0; sidx < sitesList.length; sidx++) { - thisSite = sitesList[sidx]; - thisSiteObj = siteMap.find((obj) => obj.origName === thisSite); - querySites.push(thisSiteObj.options.id); - } + querySites = sitesList.map(function (site) { + return siteMap.find((obj) => obj.origName === site).options.id; + }); sitesClause = ` and m0.sta_id in('${querySites.join("','")}')`; } else { throw new Error( "INFO: Please add sites in order to get a single/multi station plot." ); } - dateClause = `and m0.time >= ${fromSecs} - 900 and m0.time <= ${toSecs} + 900`; - siteDateClause = `and o.time >= ${fromSecs} - 900 and o.time <= ${toSecs} + 900`; - siteMatchClause = "and m0.sta_id = o.sta_id and m0.time = o.time"; - NAggregate = "count"; - NClause = "1"; - queryPool = sitePool; + queryPool = sitePool; // eslint-disable-line no-undef } - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; + const statisticClause = `sum(${variable[0]}) as square_diff_sum, ${NAggregate}(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(${timeVar}, ';', ${variable[0]}, ';', ${NClause}, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by ${timeVar}) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; + + // axisKey is used to determine which axis a curve should use. + // This axisKeySet object is used like a set and if a curve has the same + // units (axisKey) it will use the same axis. + // The axis number is assigned to the axisKeySet value, which is the axisKey. const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); + statType = statisticOptionsMap[statisticSelect]; const varUnits = statVarUnitMap[statisticSelect][variableStr]; - // axisKey is used to determine which axis a curve should use. - // This axisKeySet object is used like a set and if a curve has the same - // units (axisKey) it will use the same axis. - // The axis number is assigned to the axisKeySet value, which is the axisKey. const axisKey = varUnits; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select {{validTimeVar}} as hr_of_day, " + - "count(distinct ceil(3600*floor(({{timeVar}}+1800)/3600))) as N_times, " + - "min(ceil(3600*floor(({{timeVar}}+1800)/3600))) as min_secs, " + - "max(ceil(3600*floor(({{timeVar}}+1800)/3600))) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{siteMatchClause}} " + - "{{sitesClause}} " + - "{{dateClause}} " + - "{{siteDateClause}} " + - "{{forecastLengthClause}} " + - "group by hr_of_day " + - "order by hr_of_day" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{siteMatchClause}}", siteMatchClause); - statement = statement.replace("{{sitesClause}}", sitesClause); - statement = statement.replace("{{validTimeVar}}", validTimeVar); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.replace("{{siteDateClause}}", siteDateClause); - statement = statement.split("{{timeVar}}").join(timeVar); - dataRequests[label] = statement; - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select {{validTimeVar}} as hr_of_day, " + + "count(distinct ceil(3600*floor(({{timeVar}}+1800)/3600))) as N_times, " + + "min(ceil(3600*floor(({{timeVar}}+1800)/3600))) as min_secs, " + + "max(ceil(3600*floor(({{timeVar}}+1800)/3600))) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{siteMatchClause}} " + + "{{sitesClause}} " + + "{{dateClause}} " + + "{{siteDateClause}} " + + "{{forecastLengthClause}} " + + "group by hr_of_day " + + "order by hr_of_day" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{siteMatchClause}}", siteMatchClause); + statement = statement.replace("{{sitesClause}}", sitesClause); + statement = statement.replace("{{validTimeVar}}", validTimeVar); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.replace("{{siteDateClause}}", siteDateClause); + statement = statement.split("{{timeVar}}").join(timeVar); + dataRequests[label] = statement; + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - queryPool, + queryPool, // eslint-disable-line no-undef statement, appParams, `${statisticSelect}_${variableStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -215,6 +232,7 @@ dataValidTime = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -224,7 +242,7 @@ dataValidTime = function (plotParams, plotFunction) { error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; if (error.includes("Unknown column")) { throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for the model/region [${model} and ${region}].` + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is not supported by the database for this model and region].` ); } else { throw new Error(error); @@ -235,7 +253,6 @@ dataValidTime = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -260,6 +277,7 @@ dataValidTime = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/surface/server/main.js b/apps/surface/server/main.js index ab05927b1f..1655857e5e 100644 --- a/apps/surface/server/main.js +++ b/apps/surface/server/main.js @@ -4,7 +4,9 @@ import { Meteor } from "meteor/meteor"; import { mysql } from "meteor/pcel:mysql"; +import { moment } from "meteor/momentjs:moment"; import { + matsMethods, matsTypes, matsCollections, matsDataUtils, @@ -319,140 +321,132 @@ const doCurveParams = function () { const params = matsCollections.CurveParamsInfo.find({ curve_params: { $exists: true }, }).fetch()[0].curve_params; - for (let cp = 0; cp < params.length; cp++) { + for (let cp = 0; cp < params.length; cp += 1) { matsCollections[params[cp]].remove({}); } } + const modelOptionsMap = {}; const forecastLengthOptionsMap = {}; const regionModelOptionsMap = {}; const siteOptionsMap = {}; const sitesLocationMap = []; - const masterRegionValuesMap = {}; - const masterMETARValuesMap = {}; + const allRegionValuesMap = {}; + const allMETARValuesMap = {}; let modelDateRangeMap = {}; const metarModelOptionsMap = {}; try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - metadataPool, + metadataPool, // eslint-disable-line no-undef "select short_name,description from region_descriptions;" ); - let masterRegDescription; - let masterShortName; - for (var j = 0; j < rows.length; j++) { - masterRegDescription = rows[j].description.trim(); - masterShortName = rows[j].short_name.trim(); - masterRegionValuesMap[masterShortName] = masterRegDescription; + for (let j = 0; j < rows.length; j += 1) { + allRegionValuesMap[rows[j].short_name.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select metar_string,description from metar_string_descriptions;" ); - let masterMETARDescription; - let masterMETARString; - for (var j = 0; j < rows.length; j++) { - masterMETARDescription = rows[j].description.trim(); - masterMETARString = rows[j].metar_string.trim(); - masterMETARValuesMap[masterMETARString] = masterMETARDescription; + for (let j = 0; j < rows.length; j += 1) { + allMETARValuesMap[rows[j].metar_string.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select model,metar_string,regions,display_text,fcst_lens,mindate,maxdate from regions_per_model_mats_all_categories order by display_category, display_order;" ); - for (var i = 0; i < rows.length; i++) { - const model_value = rows[i].model.trim(); + for (let i = 0; i < rows.length; i += 1) { + const modelValue = rows[i].model.trim(); const model = rows[i].display_text.trim(); - modelOptionsMap[model] = [model_value]; + modelOptionsMap[model] = [modelValue]; const rowMinDate = moment.utc(rows[i].mindate * 1000).format("MM/DD/YYYY HH:mm"); const rowMaxDate = moment.utc(rows[i].maxdate * 1000).format("MM/DD/YYYY HH:mm"); - modelDateRangeMap[model] = { minDate: rowMinDate, maxDate: rowMaxDate }; + modelDateRangeMap[model] = { + minDate: rowMinDate, + maxDate: rowMaxDate, + }; - const metarStrings = rows[i].metar_string; - const metarStringsArr = metarStrings + const metars = rows[i].metar_string; + metarModelOptionsMap[model] = metars .split(",") - .map(Function.prototype.call, String.prototype.trim); - const metarArr = []; - var dummyMETAR; - for (var j = 0; j < metarStringsArr.length; j++) { - dummyMETAR = metarStringsArr[j].replace(/'|\[|\]/g, ""); - metarArr.push(masterMETARValuesMap[dummyMETAR]); - } - metarModelOptionsMap[model] = metarArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (metar) { + return allMETARValuesMap[metar.replace(/'|\[|\]/g, "")]; + }); const forecastLengths = rows[i].fcst_lens; - const forecastLengthArr = forecastLengths + forecastLengthOptionsMap[model] = forecastLengths .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < forecastLengthArr.length; j++) { - forecastLengthArr[j] = forecastLengthArr[j].replace(/'|\[|\]/g, ""); - } - forecastLengthOptionsMap[model] = forecastLengthArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (fhr) { + return fhr.replace(/'|\[|\]/g, ""); + }); const { regions } = rows[i]; - const regionsArrRaw = regions + regionModelOptionsMap[model] = regions .split(",") - .map(Function.prototype.call, String.prototype.trim); - const regionsArr = []; - var dummyRegion; - for (var j = 0; j < regionsArrRaw.length; j++) { - dummyRegion = regionsArrRaw[j].replace(/'|\[|\]/g, ""); - regionsArr.push(masterRegionValuesMap[dummyRegion]); - } - regionModelOptionsMap[model] = regionsArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (region) { + return allRegionValuesMap[region.replace(/'|\[|\]/g, "")]; + }); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { matsCollections.SiteMap.remove({}); const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sitePool, - "select madis_id,name,lat,lon,elev,description from metars_mats_global where lat > -16380 and lat < 16380 and lon > -32760 and lon < 32760 order by name;" + sumPool, // eslint-disable-line no-undef + "select madis_id,name,lat,lon,elev,description from madis3.metars_mats_global where lat > -16380 and lat < 16380 and lon > -32760 and lon < 32760 order by name;" ); - for (var i = 0; i < rows.length; i++) { - const site_name = rows[i].name; - const site_description = rows[i].description; - const site_id = rows[i].madis_id; - const site_lat = rows[i].lat / 182; - const site_lon = rows[i].lon / 182; - const site_elev = rows[i].elev; - siteOptionsMap[site_name] = [site_id]; - - const point = [site_lat, site_lon]; - const obj = { - name: site_name, - origName: site_name, - point, - elevation: site_elev, - options: { - title: site_description, - color: "red", - size: 5, - network: "METAR", - peerOption: site_name, - id: site_id, - highLightColor: "blue", - }, - }; - sitesLocationMap.push(obj); - - matsCollections.SiteMap.insert({ siteName: site_name, siteId: site_id }); + for (let i = 0; i < rows.length; i += 1) { + const siteName = rows[i].name === undefined ? "unknown" : rows[i].name; + const siteDescription = + rows[i].description === undefined ? "unknown" : rows[i].description; + const siteId = rows[i].madis_id; + const siteLat = rows[i].lat === undefined ? -90 : rows[i].lat / 182; + const siteLon = rows[i].lon === undefined ? 0 : rows[i].lon / 182; + const siteElev = rows[i].elev === undefined ? 0 : rows[i].elev; + + // There's one station right at the south pole that the map doesn't know how to render at all, so exclude it. + // Also exclude stations with missing data + if (siteLat < 90 && siteLat > -90) { + siteOptionsMap[siteName] = [siteId]; + + const point = [siteLat, siteLon]; + const obj = { + name: siteName, + origName: siteName, + point, + elevation: siteElev, + options: { + title: siteDescription, + color: "red", + size: 5, + network: "METAR", + peerOption: siteName, + id: siteId, + highLightColor: "blue", + }, + }; + sitesLocationMap.push(obj); + matsCollections.SiteMap.insert({ siteName, siteId }); + } } } catch (err) { - console.log(err.message); + throw new Error(err.message); } matsCollections.StationMap.remove({}); @@ -517,7 +511,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["data-source"].findOne({ name: "data-source" }); + const currentParam = matsCollections["data-source"].findOne({ + name: "data-source", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, modelOptionsMap) || !matsDataUtils.areObjectsEqual(currentParam.dates, modelDateRangeMap) @@ -543,7 +539,7 @@ const doCurveParams = function () { type: matsTypes.InputTypes.select, optionsMap: regionModelOptionsMap, options: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]], - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, superiorNames: ["data-source"], controlButtonCovered: true, unique: false, @@ -555,10 +551,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.region.findOne({ name: "region" }); + const currentParam = matsCollections.region.findOne({ name: "region" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, regionModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterRegionValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allRegionValuesMap) ) { // have to reload region data matsCollections.region.update( @@ -566,7 +562,7 @@ const doCurveParams = function () { { $set: { optionsMap: regionModelOptionsMap, - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, options: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]], default: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]][0], }, @@ -575,7 +571,7 @@ const doCurveParams = function () { } } - const optionsMap = { + const statOptionsMap = { RMSE: "scalar", "Bias (Model - Obs)": "scalar", @@ -595,11 +591,11 @@ const doCurveParams = function () { matsCollections.statistic.insert({ name: "statistic", type: matsTypes.InputTypes.select, - optionsMap, - options: Object.keys(optionsMap), + optionsMap: statOptionsMap, + options: Object.keys(statOptionsMap), controlButtonCovered: true, unique: false, - default: Object.keys(optionsMap)[0], + default: Object.keys(statOptionsMap)[0], controlButtonVisibility: "block", displayOrder: 2, displayPriority: 1, @@ -611,11 +607,11 @@ const doCurveParams = function () { matsCollections["x-statistic"].insert({ name: "x-statistic", type: matsTypes.InputTypes.select, - optionsMap, - options: Object.keys(optionsMap), + optionsMap: statOptionsMap, + options: Object.keys(statOptionsMap), controlButtonCovered: true, unique: false, - default: Object.keys(optionsMap)[0], + default: Object.keys(statOptionsMap)[0], controlButtonVisibility: "block", displayOrder: 4, displayPriority: 1, @@ -627,11 +623,11 @@ const doCurveParams = function () { matsCollections["y-statistic"].insert({ name: "y-statistic", type: matsTypes.InputTypes.select, - optionsMap, - options: Object.keys(optionsMap), + optionsMap: statOptionsMap, + options: Object.keys(statOptionsMap), controlButtonCovered: true, unique: false, - default: Object.keys(optionsMap)[0], + default: Object.keys(statOptionsMap)[0], controlButtonVisibility: "block", displayOrder: 4, displayPriority: 1, @@ -818,7 +814,7 @@ const doCurveParams = function () { type: matsTypes.InputTypes.select, optionsMap: metarModelOptionsMap, options: metarModelOptionsMap[Object.keys(metarModelOptionsMap)[0]], - valuesMap: masterMETARValuesMap, + valuesMap: allMETARValuesMap, superiorNames: ["data-source"], controlButtonCovered: true, unique: false, @@ -830,10 +826,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.truth.findOne({ name: "truth" }); + const currentParam = matsCollections.truth.findOne({ name: "truth" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, metarModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterMETARValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allMETARValuesMap) ) { // have to reload truth data matsCollections.truth.update( @@ -841,7 +837,7 @@ const doCurveParams = function () { { $set: { optionsMap: metarModelOptionsMap, - valuesMap: masterMETARValuesMap, + valuesMap: allMETARValuesMap, options: metarModelOptionsMap[Object.keys(metarModelOptionsMap)[0]], }, } @@ -871,7 +867,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["forecast-length"].findOne({ + const currentParam = matsCollections["forecast-length"].findOne({ name: "forecast-length", }); if ( @@ -1188,7 +1184,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["curve-dates"].findOne({ name: "curve-dates" }); + const currentParam = matsCollections["curve-dates"].findOne({ + name: "curve-dates", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.startDate, minDate) || !matsDataUtils.areObjectsEqual(currentParam.stopDate, maxDate) || @@ -1558,7 +1556,8 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { - console.log( + // eslint-disable-next-line no-console + console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); matsCollections.Databases.drop(); @@ -1575,7 +1574,7 @@ Meteor.startup(function () { databases = Meteor.settings.private.databases; } if (databases !== null && databases !== undefined && Array.isArray(databases)) { - for (let di = 0; di < databases.length; di++) { + for (let di = 0; di < databases.length; di += 1) { matsCollections.Databases.insert(databases[di]); } } @@ -1602,6 +1601,7 @@ Meteor.startup(function () { ); if (cbConnection) { // global cbScorecardSettingsPool + // eslint-disable-next-line no-undef cbScorecardSettingsPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1628,6 +1628,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (metadataSettings) { + // eslint-disable-next-line no-undef metadataPool = mysql.createPool(metadataSettings); allPools.push({ pool: "metadataPool", role: matsTypes.DatabaseRoles.META_DATA }); } @@ -1648,6 +1649,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (sumSettings) { + // eslint-disable-next-line no-undef sumPool = mysql.createPool(sumSettings); allPools.push({ pool: "sumPool", role: matsTypes.DatabaseRoles.SUMS_DATA }); } @@ -1668,6 +1670,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (siteSettings) { + // eslint-disable-next-line no-undef sitePool = mysql.createPool(siteSettings); allPools.push({ pool: "sitePool", role: matsTypes.DatabaseRoles.SITE_DATA }); } @@ -1684,7 +1687,7 @@ Meteor.startup(function () { appType: matsTypes.AppTypes.mats, }); } catch (error) { - console.log(error.message); + throw new Error(error.message); } }); @@ -1692,6 +1695,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doPlotGraph, doCurveParams, diff --git a/apps/surfrad/.eslintrc.json b/apps/surfrad/.eslintrc.json index b6823a8810..79d49c5bb6 100644 --- a/apps/surfrad/.eslintrc.json +++ b/apps/surfrad/.eslintrc.json @@ -28,22 +28,6 @@ "space-before-function-paren": "off", // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", - "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities - "no-undef": "warn", - "no-plusplus": "warn", - "vars-on-top": "warn", - "no-var": "warn", - "block-scoped-var": "warn", - "no-loop-func": "warn", - "no-unused-vars": "warn", - "no-param-reassign": "warn", - "camelcase": "warn", - "no-redeclare": "warn", - "no-shadow": "warn" + "prefer-arrow-callback": "off" } } diff --git a/apps/surfrad/client/main.js b/apps/surfrad/client/main.js index a87407a1f4..ecd922b6a2 100644 --- a/apps/surfrad/client/main.js +++ b/apps/surfrad/client/main.js @@ -2,6 +2,7 @@ * Copyright (c) 2021 Colorado State University and Regents of the University of Colorado. All rights reserved. */ +// eslint-disable-next-line no-unused-vars import { matsTypes, matsCollections, methods } from "meteor/randyp:mats-common"; import "@fortawesome/fontawesome-free"; import "@fortawesome/fontawesome-free/css/all.css"; diff --git a/apps/surfrad/server/dataFunctions/data_contour.js b/apps/surfrad/server/dataFunctions/data_contour.js index ab03418aa2..e8a5839d46 100644 --- a/apps/surfrad/server/dataFunctions/data_contour.js +++ b/apps/surfrad/server/dataFunctions/data_contour.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContour = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,78 +23,95 @@ dataContour = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; - const totalProcessingStart = moment(); + + const curves = JSON.parse(JSON.stringify(plotParams.curves)); + if (curves.length > 1) { + throw new Error("INFO: There must only be one added curve."); + } + + const axisMap = Object.create(null); + + let statement = ""; + let error = ""; + const dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - const curves = JSON.parse(JSON.stringify(plotParams.curves)); - if (curves.length > 1) { - throw new Error("INFO: There must only be one added curve."); - } - const dataset = []; - const axisMap = Object.create(null); - // initialize variables specific to the curve + // initialize variables specific to this curve const curve = curves[0]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - const regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - let regionClause; - if (region === "all_stat") { - regionClause = ""; - } else if (region === "all_surf") { - regionClause = "and m0.id in(1,2,3,4,5,6,7) "; - } else if (region === "all_sol") { - regionClause = "and m0.id in(8,9,10,11,12,13,14) "; - } else { - regionClause = `and m0.id in(${region}) `; - } - const scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const scaleClause = `and m0.scale = ${grid_scale}`; - const queryTableClause = `from surfrad as o, ${model} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + + let scaleClause = ""; + const scaleStr = curve.scale; + if (xAxisParam !== "Grid scale" && yAxisParam !== "Grid scale") { + if (scaleStr === undefined) { + throw new Error( + `INFO: ${label}'s grid scale is undefined. Please assign it a value.` + ); + } + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + scaleClause = `and m0.scale = ${scale} `; + } + let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; - let matchClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and (m0.secs)%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = Number(curve["forecast-length"]) * 60; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + const statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + `sum(${variable[0]}) as square_diff_sum, count(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + + `group_concat(m0.secs, ';', ${variable[0]}, ';', 1, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.secs) as sub_data, count(${variable[0]}) as N0`; + + let dateString = ""; + let dateClause = ""; + let matchClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -103,119 +121,137 @@ dataContour = function (plotParams, plotFunction) { } else { dateString = "m0.secs"; } - dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs}`; - dateClause = `${dateClause} and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; + dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs} and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; matchClause = "and m0.id = o.id and m0.secs = o.secs"; - const statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - `sum(${variable[0]}) as square_diff_sum, count(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + - `group_concat(m0.secs, ';', ${variable[0]}, ';', 1, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.secs) as sub_data, count(${variable[0]}) as N0`; - const statType = statisticOptionsMap[statisticSelect]; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + let regionClause; + if (region === "all_stat") { + regionClause = ""; + } else if (region === "all_surf") { + regionClause = "and m0.id in(1,2,3,4,5,6,7) "; + } else if (region === "all_sol") { + regionClause = "and m0.id in(8,9,10,11,12,13,14) "; + } else { + regionClause = `and m0.id in(${region}) `; + } + + const queryTableClause = `from surfrad as o, ${model} as m0`; + + // For contours, this functions as the colorbar label. const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); + const statType = statisticOptionsMap[statisticSelect]; const varUnits = statVarUnitMap[statisticSelect][variableStr]; - - // For contours, this functions as the colorbar label. curve.unitKey = varUnits; let d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{matchClause}} " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{scaleClause}} " + - "{{regionClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{matchClause}} " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{scaleClause}} " + + "{{regionClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{regionClause}}", regionClause); - statement = statement.replace("{{matchClause}}", matchClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{regionClause}}", regionClause); + statement = statement.replace("{{matchClause}}", matchClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; - if ( - model !== "HRRR" && - variableStr !== "dswrf" && - statisticSelect !== "Obs average" - ) { - throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is only available for the HRRR data-source.` - ); - } + if ( + model !== "HRRR" && + variableStr !== "dswrf" && + statisticSelect !== "Obs average" + ) { + throw new Error( + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is only available for the HRRR data-source.` + ); + } - let queryResult; - const startMoment = moment(); - let finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - `${statisticSelect}_${variableStr}` - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + `${statisticSelect}_${variableStr}` + ); + + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - } - if (!dataFoundForCurve) { - // we found no data for any curves so don't bother proceeding - throw new Error("INFO: No valid data for any curves."); - } + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { + // this is NOT an error just a no data condition + dataFoundForCurve = false; + } else { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + } - const postQueryStartMoment = moment(); + if (!dataFoundForCurve) { + // we found no data for any curves so don't bother proceeding + throw new Error("INFO: No valid data for any curves."); + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined diff --git a/apps/surfrad/server/dataFunctions/data_contour_diff.js b/apps/surfrad/server/dataFunctions/data_contour_diff.js index c72228140b..ae6f0c2be7 100644 --- a/apps/surfrad/server/dataFunctions/data_contour_diff.js +++ b/apps/surfrad/server/dataFunctions/data_contour_diff.js @@ -14,6 +14,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataContourDiff = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -24,82 +25,102 @@ dataContourDiff = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries - let dataFoundForCurve = true; let dataNotFoundForAnyCurve = false; + + let curves = JSON.parse(JSON.stringify(plotParams.curves)); + const curvesLength = curves.length; + if (curvesLength !== 2) { + throw new Error("INFO: There must be two added curves."); + } + + const axisMap = Object.create(null); const showSignificance = plotParams.significance !== "none"; - const totalProcessingStart = moment(); + + let statType; + let statisticSelect; + let variableStr; + + let statement = ""; + let error = ""; + let dataset = []; + const dateRange = matsDataUtils.getDateRange(plotParams.dates); const fromSecs = dateRange.fromSeconds; const toSecs = dateRange.toSeconds; + const xAxisParam = plotParams["x-axis-parameter"]; const yAxisParam = plotParams["y-axis-parameter"]; const xValClause = matsCollections.PlotParams.findOne({ name: "x-axis-parameter" }) .optionsMap[xAxisParam]; const yValClause = matsCollections.PlotParams.findOne({ name: "y-axis-parameter" }) .optionsMap[yAxisParam]; - let error = ""; - let curves = JSON.parse(JSON.stringify(plotParams.curves)); - const curvesLength = curves.length; - if (curvesLength !== 2) { - throw new Error("INFO: There must be two added curves."); - } - let dataset = []; - const axisMap = Object.create(null); - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var regionClause; - if (region === "all_stat") { - regionClause = ""; - } else if (region === "all_surf") { - regionClause = "and m0.id in(1,2,3,4,5,6,7) "; - } else if (region === "all_sol") { - regionClause = "and m0.id in(8,9,10,11,12,13,14) "; - } else { - regionClause = `and m0.id in(${region}) `; - } - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const scaleClause = `and m0.scale = ${grid_scale}`; - const queryTableClause = `from surfrad as o, ${model} as m0`; - var variableStr = curve.variable; + + variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + + let scaleClause = ""; + const scaleStr = curve.scale; + if (xAxisParam !== "Grid scale" && yAxisParam !== "Grid scale") { + if (scaleStr === undefined) { + throw new Error( + `INFO: ${label}'s grid scale is undefined. Please assign it a value.` + ); + } + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + scaleClause = `and m0.scale = ${scale} `; + } + let validTimeClause = ""; - let forecastLengthClause = ""; - let dateString = ""; - let dateClause = ""; - let matchClause = ""; if (xAxisParam !== "Valid UTC hour" && yAxisParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and (m0.secs)%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (xAxisParam !== "Fcst lead time" && yAxisParam !== "Fcst lead time") { const forecastLength = Number(curve["forecast-length"]) * 60; + if (forecastLength === undefined) { + throw new Error( + `INFO: ${label}'s forecast lead time is undefined. Please assign it a value.` + ); + } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } + + statisticSelect = curve.statistic; + const statisticOptionsMap = matsCollections.statistic.findOne( + { name: "statistic" }, + { optionsMap: 1 } + ).optionsMap; + const statisticClause = + `sum(${variable[0]}) as square_diff_sum, count(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + + `group_concat(m0.secs, ';', ${variable[0]}, ';', 1, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.secs) as sub_data, count(${variable[0]}) as N0`; + + let dateString = ""; + let dateClause = ""; + let matchClause = ""; if ( (xAxisParam === "Init Date" || yAxisParam === "Init Date") && xAxisParam !== "Valid Date" && @@ -109,115 +130,130 @@ dataContourDiff = function (plotParams, plotFunction) { } else { dateString = "m0.secs"; } - dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs}`; - dateClause = `${dateClause} and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; + dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs} and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; matchClause = "and m0.id = o.id and m0.secs = o.secs"; - var statisticSelect = curve.statistic; - const statisticOptionsMap = matsCollections.statistic.findOne( - { name: "statistic" }, - { optionsMap: 1 } - ).optionsMap; - const statisticClause = - `sum(${variable[0]}) as square_diff_sum, count(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + - `group_concat(m0.secs, ';', ${variable[0]}, ';', 1, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.secs) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + let regionClause; + if (region === "all_stat") { + regionClause = ""; + } else if (region === "all_surf") { + regionClause = "and m0.id in(1,2,3,4,5,6,7) "; + } else if (region === "all_sol") { + regionClause = "and m0.id in(8,9,10,11,12,13,14) "; + } else { + regionClause = `and m0.id in(${region}) `; + } + + const queryTableClause = `from surfrad as o, ${model} as m0`; + + // For contours, this functions as the colorbar label. const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); + statType = statisticOptionsMap[statisticSelect]; const varUnits = statVarUnitMap[statisticSelect][variableStr]; + curves[curveIndex].unitKey = varUnits; - // For contours, this functions as the colorbar label. - curve.unitKey = varUnits; + let d; + if (!diffFrom) { + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{xValClause}} " + + "{{yValClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{matchClause}} " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{scaleClause}} " + + "{{regionClause}} " + + "group by xVal,yVal " + + "order by xVal,yVal" + + ";"; - var d; - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{xValClause}} " + - "{{yValClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{matchClause}} " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{scaleClause}} " + - "{{regionClause}} " + - "group by xVal,yVal " + - "order by xVal,yVal" + - ";"; + statement = statement.replace("{{xValClause}}", xValClause); + statement = statement.replace("{{yValClause}}", yValClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{regionClause}}", regionClause); + statement = statement.replace("{{matchClause}}", matchClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; - statement = statement.replace("{{xValClause}}", xValClause); - statement = statement.replace("{{yValClause}}", yValClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{regionClause}}", regionClause); - statement = statement.replace("{{matchClause}}", matchClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; + if ( + model !== "HRRR" && + variableStr !== "dswrf" && + statisticSelect !== "Obs average" + ) { + throw new Error( + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is only available for the HRRR data-source.` + ); + } - if ( - model !== "HRRR" && - variableStr !== "dswrf" && - statisticSelect !== "Obs average" - ) { - throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is only available for the HRRR data-source.` - ); - } + // send the query statement to the query function + queryResult = matsDataQueryUtils.queryDBContour( + sumPool, // eslint-disable-line no-undef + statement, + appParams, + `${statisticSelect}_${variableStr}` + ); - var queryResult; - const startMoment = moment(); - var finishMoment; - try { - // send the query statement to the query function - queryResult = matsDataQueryUtils.queryDBContour( - sumPool, - statement, - appParams, - `${statisticSelect}_${variableStr}` - ); - finishMoment = moment(); - dataRequests[`data retrieval (query) time - ${label}`] = { - begin: startMoment.format(), - finish: finishMoment.format(), - duration: `${moment - .duration(finishMoment.diff(startMoment)) - .asSeconds()} seconds`, - recordCount: queryResult.data.xTextOutput.length, - }; - // get the data back from the query - d = queryResult.data; - } catch (e) { - // this is an error produced by a bug in the query function, not an error returned by the mysql database - e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; - throw new Error(e.message); - } - if (queryResult.error !== undefined && queryResult.error !== "") { - if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { - // this is NOT an error just a no data condition - dataFoundForCurve = false; - } else { - // this is an error returned by the mysql database - error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; - throw new Error(error); + finishMoment = moment(); + dataRequests[label] = statement; + dataRequests[`data retrieval (query) time - ${label}`] = { + begin: startMoment.format(), + finish: finishMoment.format(), + duration: `${moment + .duration(finishMoment.diff(startMoment)) + .asSeconds()} seconds`, + recordCount: queryResult.data.xTextOutput.length, + }; + // get the data back from the query + d = queryResult.data; + } catch (e) { + // this is an error produced by a bug in the query function, not an error returned by the mysql database + e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; + throw new Error(e.message); } - dataNotFoundForAnyCurve = true; - } - const postQueryStartMoment = moment(); + if (queryResult.error !== undefined && queryResult.error !== "") { + if (queryResult.error !== matsTypes.Messages.NO_DATA_FOUND) { + // this is an error returned by the mysql database + error += `Error from verification query:
${queryResult.error}
query:
${statement}
`; + throw new Error(error); + } + dataNotFoundForAnyCurve = true; + } + } else { + // this is a difference curve -- not supported for contours + throw new Error( + "INFO: Difference curves are not supported for contours, as there is only one curve." + ); + } // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const { mean } = d.glob_stats; const annotation = mean === undefined @@ -266,8 +302,9 @@ dataContourDiff = function (plotParams, plotFunction) { statType === "ctc", statType === "scalar" ); - plotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); - curves = plotParams.curves; + const newPlotParams = plotParams; + newPlotParams.curves = matsDataUtils.getDiffContourCurveParams(plotParams.curves); + curves = newPlotParams.curves; dataset[0].name = matsPlotUtils.getCurveText( matsTypes.PlotTypes.contourDiff, curves[0] @@ -283,7 +320,7 @@ dataContourDiff = function (plotParams, plotFunction) { const result = matsDataProcessUtils.processDataContour( dataset, curveInfoParams, - plotParams, + newPlotParams, bookkeepingParams ); plotFunction(result); diff --git a/apps/surfrad/server/dataFunctions/data_dailymodelcycle.js b/apps/surfrad/server/dataFunctions/data_dailymodelcycle.js index 11b354ff91..abac793260 100644 --- a/apps/surfrad/server/dataFunctions/data_dailymodelcycle.js +++ b/apps/surfrad/server/dataFunctions/data_dailymodelcycle.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataDailyModelCycle = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,71 +24,63 @@ dataDailyModelCycle = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var regionClause; - if (region === "all_stat") { - regionClause = ""; - } else if (region === "all_surf") { - regionClause = "and m0.id in(1,2,3,4,5,6,7) "; - } else if (region === "all_sol") { - regionClause = "and m0.id in(8,9,10,11,12,13,14) "; - } else { - regionClause = `and m0.id in(${region}) `; - } - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const scaleClause = `and m0.scale = ${grid_scale}`; - const queryTableClause = `from surfrad as o, ${model} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; - const utcCycleStart = Number(curve["utc-cycle-start"]); + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + const scaleClause = `and m0.scale = ${scale}`; + + const utcCycleStart = Number(curve["utc-cycle-start"][0]); utcCycleStarts[curveIndex] = utcCycleStart; const utcCycleStartClause = `and floor(((m0.secs - m0.fcst_len*60))%(24*3600)/900)/4 IN(${utcCycleStart})`; + const forecastLengthClause = "and m0.fcst_len < 24 * 60"; - let dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs}`; - dateClause = `${dateClause} and m0.secs >= ${fromSecs} and m0.secs <= ${toSecs}`; - const matchClause = "and m0.id = o.id and m0.secs = o.secs"; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -96,73 +89,96 @@ dataDailyModelCycle = function (plotParams, plotFunction) { const statisticClause = `sum(${variable[0]}) as square_diff_sum, count(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(m0.secs, ';', ${variable[0]}, ';', 1, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.secs) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; - const { statVarUnitMap } = matsCollections.variable.findOne( - { name: "variable" }, - { statVarUnitMap: 1 } + + const dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs} and m0.secs >= ${fromSecs} and m0.secs <= ${toSecs}`; + const matchClause = "and m0.id = o.id and m0.secs = o.secs"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr ); - const varUnits = statVarUnitMap[statisticSelect][variableStr]; + let regionClause; + if (region === "all_stat") { + regionClause = ""; + } else if (region === "all_surf") { + regionClause = "and m0.id in(1,2,3,4,5,6,7) "; + } else if (region === "all_sol") { + regionClause = "and m0.id in(8,9,10,11,12,13,14) "; + } else { + regionClause = `and m0.id in(${region}) `; + } + + const queryTableClause = `from surfrad as o, ${model} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - const axisKey = varUnits; + const { statVarUnitMap } = matsCollections.variable.findOne( + { name: "variable" }, + { statVarUnitMap: 1 } + ); + statType = statisticOptionsMap[statisticSelect]; + const axisKey = statVarUnitMap[statisticSelect][variableStr]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.secs as avtime, " + - "count(distinct m0.secs) as N_times, " + - "min(m0.secs) as min_secs, " + - "max(m0.secs) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{matchClause}} " + - "{{dateClause}} " + - "{{utcCycleStartClause}} " + - "{{forecastLengthClause}} " + - "{{scaleClause}} " + - "{{regionClause}} " + - "group by avtime " + - "order by avtime" + - ";"; + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select m0.secs as avtime, " + + "count(distinct m0.secs) as N_times, " + + "min(m0.secs) as min_secs, " + + "max(m0.secs) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{matchClause}} " + + "{{dateClause}} " + + "{{utcCycleStartClause}} " + + "{{forecastLengthClause}} " + + "{{scaleClause}} " + + "{{regionClause}} " + + "group by avtime " + + "order by avtime" + + ";"; - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{regionClause}}", regionClause); - statement = statement.replace("{{matchClause}}", matchClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{utcCycleStartClause}}", utcCycleStartClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{regionClause}}", regionClause); + statement = statement.replace("{{matchClause}}", matchClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; - if ( - model !== "HRRR" && - variableStr !== "dswrf" && - statisticSelect !== "Obs average" - ) { - throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is only available for the HRRR data-source.` - ); - } + if ( + model !== "HRRR" && + variableStr !== "dswrf" && + statisticSelect !== "Obs average" + ) { + throw new Error( + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is only available for the HRRR data-source.` + ); + } - var queryResult; - const startMoment = moment(); - var finishMoment; - try { // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, `${statisticSelect}_${variableStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -178,6 +194,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -192,7 +209,6 @@ dataDailyModelCycle = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -217,6 +233,7 @@ dataDailyModelCycle = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/surfrad/server/dataFunctions/data_histogram.js b/apps/surfrad/server/dataFunctions/data_histogram.js index d349041731..333884b0fe 100644 --- a/apps/surfrad/server/dataFunctions/data_histogram.js +++ b/apps/surfrad/server/dataFunctions/data_histogram.js @@ -11,6 +11,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataHistogram = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -21,77 +22,65 @@ dataHistogram = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; - const alreadyMatched = false; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries const dataFoundForCurve = []; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const alreadyMatched = false; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; + + const axisMap = Object.create(null); + let statType; + let varUnits; + + let statement = ""; + let error = ""; const dataset = []; const allReturnedSubStats = []; const allReturnedSubSecs = []; - const axisMap = Object.create(null); // process user bin customizations const binParams = matsDataUtils.setHistogramParameters(plotParams); const { yAxisFormat } = binParams; const { binNum } = binParams; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; dataFoundForCurve[curveIndex] = true; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var regionClause; - if (region === "all_stat") { - regionClause = ""; - } else if (region === "all_surf") { - regionClause = "and m0.id in(1,2,3,4,5,6,7) "; - } else if (region === "all_sol") { - regionClause = "and m0.id in(8,9,10,11,12,13,14) "; - } else { - regionClause = `and m0.id in(${region}) `; - } - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const scaleClause = `and m0.scale = ${grid_scale}`; - const queryTableClause = `from surfrad as o, ${model} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + const scaleClause = `and m0.scale = ${scale}`; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and (m0.secs)%(24*3600)/3600 IN(${validTimes})`; } + const forecastLength = Number(curve["forecast-length"]) * 60; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs}`; - dateClause = `${dateClause} and m0.secs >= ${fromSecs} and m0.secs <= ${toSecs}`; - const matchClause = "and m0.id = o.id and m0.secs = o.secs"; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -100,16 +89,43 @@ dataHistogram = function (plotParams, plotFunction) { const statisticClause = `sum(${variable[0]}) as square_diff_sum, count(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(m0.secs, ';', ${variable[0]}, ';', 1, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.secs) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; - const { statVarUnitMap } = matsCollections.variable.findOne( - { name: "variable" }, - { statVarUnitMap: 1 } + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs} and m0.secs >= ${fromSecs} and m0.secs <= ${toSecs}`; + const matchClause = "and m0.id = o.id and m0.secs = o.secs"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr ); - var varUnits = statVarUnitMap[statisticSelect][variableStr]; + let regionClause; + if (region === "all_stat") { + regionClause = ""; + } else if (region === "all_surf") { + regionClause = "and m0.id in(1,2,3,4,5,6,7) "; + } else if (region === "all_sol") { + regionClause = "and m0.id in(8,9,10,11,12,13,14) "; + } else { + regionClause = `and m0.id in(${region}) `; + } + + const queryTableClause = `from surfrad as o, ${model} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. + const { statVarUnitMap } = matsCollections.variable.findOne( + { name: "variable" }, + { statVarUnitMap: 1 } + ); + statType = statisticOptionsMap[statisticSelect]; + varUnits = statVarUnitMap[statisticSelect][variableStr]; let axisKey = yAxisFormat; if (yAxisFormat === "Relative frequency") { axisKey += " (x100)"; @@ -117,60 +133,60 @@ dataHistogram = function (plotParams, plotFunction) { curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options curves[curveIndex].binNum = binNum; // stash the binNum to use it later for bar chart options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select m0.secs as avtime, " + - "count(distinct m0.secs) as N_times, " + - "min(m0.secs) as min_secs, " + - "max(m0.secs) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{matchClause}} " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{scaleClause}} " + - "{{regionClause}} " + - "group by avtime " + - "order by avtime" + - ";"; - - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{regionClause}}", regionClause); - statement = statement.replace("{{matchClause}}", matchClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; - - if ( - model !== "HRRR" && - variableStr !== "dswrf" && - statisticSelect !== "Obs average" - ) { - throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is only available for the HRRR data-source.` - ); - } - - var queryResult; + let queryResult; const startMoment = moment(); - var finishMoment; + let finishMoment; try { + statement = + "select m0.secs as avtime, " + + "count(distinct m0.secs) as N_times, " + + "min(m0.secs) as min_secs, " + + "max(m0.secs) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{matchClause}} " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{scaleClause}} " + + "{{regionClause}} " + + "group by avtime " + + "order by avtime" + + ";"; + + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{regionClause}}", regionClause); + statement = statement.replace("{{matchClause}}", matchClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; + + if ( + model !== "HRRR" && + variableStr !== "dswrf" && + statisticSelect !== "Obs average" + ) { + throw new Error( + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is only available for the HRRR data-source.` + ); + } + // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, `${statisticSelect}_${variableStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -188,6 +204,7 @@ dataHistogram = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition diff --git a/apps/surfrad/server/dataFunctions/data_series.js b/apps/surfrad/server/dataFunctions/data_series.js index 27676e430a..cfa67b09c8 100644 --- a/apps/surfrad/server/dataFunctions/data_series.js +++ b/apps/surfrad/server/dataFunctions/data_series.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSeries = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,80 +24,66 @@ dataSeries = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - const dateRange = matsDataUtils.getDateRange(plotParams.dates); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + const dateRange = matsDataUtils.getDateRange(plotParams.dates); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var regionClause; - if (region === "all_stat") { - regionClause = ""; - } else if (region === "all_surf") { - regionClause = "and m0.id in(1,2,3,4,5,6,7) "; - } else if (region === "all_sol") { - regionClause = "and m0.id in(8,9,10,11,12,13,14) "; - } else { - regionClause = `and m0.id in(${region}) `; - } - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const scaleClause = `and m0.scale = ${grid_scale}`; - const queryTableClause = `from surfrad as o, ${model} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + const scaleClause = `and m0.scale = ${scale}`; + let validTimeClause = ""; const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and (m0.secs)%(24*3600)/3600 IN(${validTimes})`; } + let forecastLength = Number(curve["forecast-length"]) * 60; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - let dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs}`; - dateClause = `${dateClause} and m0.secs >= ${fromSecs} and m0.secs <= ${toSecs}`; - const matchClause = "and m0.id = o.id and m0.secs = o.secs"; - const averageStr = curve.average; - const averageOptionsMap = matsCollections.average.findOne( - { name: "average" }, - { optionsMap: 1 } - ).optionsMap; - const average = averageOptionsMap[averageStr][0]; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -105,74 +92,102 @@ dataSeries = function (plotParams, plotFunction) { const statisticClause = `sum(${variable[0]}) as square_diff_sum, count(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(m0.secs, ';', ${variable[0]}, ';', 1, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.secs) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; - const { statVarUnitMap } = matsCollections.variable.findOne( - { name: "variable" }, - { statVarUnitMap: 1 } + + const averageStr = curve.average; + const averageOptionsMap = matsCollections.average.findOne( + { name: "average" }, + { optionsMap: 1 } + ).optionsMap; + const average = averageOptionsMap[averageStr][0]; + + const dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs} and m0.secs >= ${fromSecs} and m0.secs <= ${toSecs}`; + const matchClause = "and m0.id = o.id and m0.secs = o.secs"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr ); - const varUnits = statVarUnitMap[statisticSelect][variableStr]; + let regionClause; + if (region === "all_stat") { + regionClause = ""; + } else if (region === "all_surf") { + regionClause = "and m0.id in(1,2,3,4,5,6,7) "; + } else if (region === "all_sol") { + regionClause = "and m0.id in(8,9,10,11,12,13,14) "; + } else { + regionClause = `and m0.id in(${region}) `; + } + + const queryTableClause = `from surfrad as o, ${model} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - const axisKey = varUnits; + const { statVarUnitMap } = matsCollections.variable.findOne( + { name: "variable" }, + { statVarUnitMap: 1 } + ); + statType = statisticOptionsMap[statisticSelect]; + const axisKey = statVarUnitMap[statisticSelect][variableStr]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select {{average}} as avtime, " + - "count(distinct m0.secs) as N_times, " + - "min(m0.secs) as min_secs, " + - "max(m0.secs) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{matchClause}} " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{scaleClause}} " + - "{{regionClause}} " + - "group by avtime " + - "order by avtime" + - ";"; + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select {{average}} as avtime, " + + "count(distinct m0.secs) as N_times, " + + "min(m0.secs) as min_secs, " + + "max(m0.secs) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{matchClause}} " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{scaleClause}} " + + "{{regionClause}} " + + "group by avtime " + + "order by avtime" + + ";"; - statement = statement.replace("{{average}}", average); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{regionClause}}", regionClause); - statement = statement.replace("{{matchClause}}", matchClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; + statement = statement.replace("{{average}}", average); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{regionClause}}", regionClause); + statement = statement.replace("{{matchClause}}", matchClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; - if ( - model !== "HRRR" && - variableStr !== "dswrf" && - statisticSelect !== "Obs average" - ) { - throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is only available for the HRRR data-source.` - ); - } + if ( + model !== "HRRR" && + variableStr !== "dswrf" && + statisticSelect !== "Obs average" + ) { + throw new Error( + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is only available for the HRRR data-source.` + ); + } - // math is done on forecastLength later on -- set all analyses to 0 - if (forecastLength === "-99") { - forecastLength = "0"; - } + // math is done on forecastLength later on -- set all analyses to 0 + if (forecastLength === "-99") { + forecastLength = "0"; + } - var queryResult; - const startMoment = moment(); - var finishMoment; - try { // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBTimeSeries( - sumPool, + sumPool, // eslint-disable-line no-undef statement, model, forecastLength, @@ -184,7 +199,9 @@ dataSeries = function (plotParams, plotFunction) { appParams, false ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -200,6 +217,7 @@ dataSeries = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -214,7 +232,6 @@ dataSeries = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -239,6 +256,7 @@ dataSeries = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/surfrad/server/dataFunctions/data_simple_scatter.js b/apps/surfrad/server/dataFunctions/data_simple_scatter.js index 6e5c1134e6..4193a83304 100644 --- a/apps/surfrad/server/dataFunctions/data_simple_scatter.js +++ b/apps/surfrad/server/dataFunctions/data_simple_scatter.js @@ -12,6 +12,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataSimpleScatter = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -22,14 +23,15 @@ dataSimpleScatter = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; + const axisXMap = Object.create(null); const axisYMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; @@ -37,43 +39,27 @@ dataSimpleScatter = function (plotParams, plotFunction) { let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statType; + let varUnitsX; + let varUnitsY; + + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; + const binParam = curve["bin-parameter"]; const binClause = matsCollections["bin-parameter"].findOne({ name: "bin-parameter", }).optionsMap[binParam]; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var regionClause; - if (region === "all_stat") { - regionClause = ""; - } else if (region === "all_surf") { - regionClause = "and m0.id in(1,2,3,4,5,6,7) "; - } else if (region === "all_sol") { - regionClause = "and m0.id in(8,9,10,11,12,13,14) "; - } else { - regionClause = `and m0.id in(${region}) `; - } - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const scaleClause = `and m0.scale = ${grid_scale}`; - const queryTableClause = `from surfrad as o, ${model} as m0`; + const variableXStr = curve["x-variable"]; const variableYStr = curve["y-variable"]; const variableOptionsMap = matsCollections.variable.findOne( @@ -82,20 +68,25 @@ dataSimpleScatter = function (plotParams, plotFunction) { ).optionsMap; const variableX = variableOptionsMap[variableXStr]; const variableY = variableOptionsMap[variableYStr]; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + const scaleClause = `and m0.scale = ${scale}`; + let validTimeClause = ""; - let forecastLengthClause = ""; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let dateString = ""; - let dateClause = ""; - let matchClause = ""; if (binParam !== "Valid UTC hour") { const validTimes = curve["valid-time"] === undefined ? [] : curve["valid-time"]; - if (validTimes.length > 0 && validTimes !== matsTypes.InputTypes.unused) { + if (validTimes.length !== 0 && validTimes !== matsTypes.InputTypes.unused) { validTimeClause = `and (m0.secs)%(24*3600)/3600 IN(${validTimes})`; } } + + let forecastLengthClause = ""; if (binParam !== "Fcst lead time") { const forecastLength = Number(curve["forecast-length"]) * 60; if (forecastLength === undefined) { @@ -105,14 +96,7 @@ dataSimpleScatter = function (plotParams, plotFunction) { } forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; } - if (binParam === "Init Date" && binParam !== "Valid Date") { - dateString = "m0.secs-m0.fcst_len*60"; - } else { - dateString = "m0.secs"; - } - dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs}`; - dateClause = `${dateClause} and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; - matchClause = "and m0.id = o.id and m0.secs = o.secs"; + const statisticXSelect = curve["x-statistic"]; const statisticYSelect = curve["y-statistic"]; const statisticOptionsMap = matsCollections.statistic.findOne( @@ -123,79 +107,114 @@ dataSimpleScatter = function (plotParams, plotFunction) { `sum(${variableX[0]}) as square_diff_sumX, count(${variableX[1]}) as N_sumX, sum(${variableX[2]}) as obs_model_diff_sumX, sum(${variableX[3]}) as model_sumX, sum(${variableX[4]}) as obs_sumX, sum(${variableX[5]}) as abs_sumX, ` + `sum(${variableY[0]}) as square_diff_sumY, count(${variableY[1]}) as N_sumY, sum(${variableY[2]}) as obs_model_diff_sumY, sum(${variableY[3]}) as model_sumY, sum(${variableY[4]}) as obs_sumY, sum(${variableY[5]}) as abs_sumY, ` + `group_concat(m0.secs, ';', ${variableX[0]}, ';', 1, ';', ${variableX[2]}, ';', ${variableX[3]}, ';', ${variableX[4]}, ';', ${variableX[5]}, ';', ${variableY[0]}, ';', 1, ';', ${variableY[2]}, ';', ${variableY[3]}, ';', ${variableY[4]}, ';', ${variableY[5]} order by m0.secs) as sub_data, count(${variableX[0]}) as N0`; - var statType = statisticOptionsMap[statisticXSelect]; + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + let dateString = ""; + let dateClause = ""; + let matchClause = ""; + if (binParam === "Init Date" && binParam !== "Valid Date") { + dateString = "m0.secs-m0.fcst_len*60"; + } else { + dateString = "m0.secs"; + } + dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs} and ${dateString} >= ${fromSecs} and ${dateString} <= ${toSecs}`; + matchClause = "and m0.id = o.id and m0.secs = o.secs"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr + ); + let regionClause; + if (region === "all_stat") { + regionClause = ""; + } else if (region === "all_surf") { + regionClause = "and m0.id in(1,2,3,4,5,6,7) "; + } else if (region === "all_sol") { + regionClause = "and m0.id in(8,9,10,11,12,13,14) "; + } else { + regionClause = `and m0.id in(${region}) `; + } + + const queryTableClause = `from surfrad as o, ${model} as m0`; + const { statVarUnitMap } = matsCollections.variable.findOne( { name: "variable" }, { statVarUnitMap: 1 } ); - const varUnitsX = statVarUnitMap[statisticXSelect][variableXStr]; - const varUnitsY = statVarUnitMap[statisticYSelect][variableYStr]; + statType = statisticOptionsMap[statisticXSelect]; + varUnitsX = statVarUnitMap[statisticXSelect][variableXStr]; + varUnitsY = statVarUnitMap[statisticYSelect][variableYStr]; - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "{{binClause}} " + - "count(distinct {{dateString}}) as N_times, " + - "min({{dateString}}) as min_secs, " + - "max({{dateString}}) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{matchClause}} " + - "{{dateClause}} " + - "{{validTimeClause}} " + - "{{forecastLengthClause}} " + - "{{scaleClause}} " + - "{{regionClause}} " + - "group by binVal " + - "order by binVal" + - ";"; + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "{{binClause}} " + + "count(distinct {{dateString}}) as N_times, " + + "min({{dateString}}) as min_secs, " + + "max({{dateString}}) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{matchClause}} " + + "{{dateClause}} " + + "{{validTimeClause}} " + + "{{forecastLengthClause}} " + + "{{scaleClause}} " + + "{{regionClause}} " + + "group by binVal " + + "order by binVal" + + ";"; - statement = statement.replace("{{binClause}}", binClause); - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{validTimeClause}}", validTimeClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{regionClause}}", regionClause); - statement = statement.replace("{{matchClause}}", matchClause); - statement = statement.replace("{{dateClause}}", dateClause); - statement = statement.split("{{dateString}}").join(dateString); - dataRequests[label] = statement; + statement = statement.replace("{{binClause}}", binClause); + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{validTimeClause}}", validTimeClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{regionClause}}", regionClause); + statement = statement.replace("{{matchClause}}", matchClause); + statement = statement.replace("{{dateClause}}", dateClause); + statement = statement.split("{{dateString}}").join(dateString); + dataRequests[label] = statement; - if ( - model !== "HRRR" && - variableXStr !== "dswrf" && - statisticXSelect !== "Obs average" - ) { - throw new Error( - `INFO: The statistic/variable combination [${statisticXSelect} and ${variableXStr}] is only available for the HRRR data-source.` - ); - } else if ( - model !== "HRRR" && - variableYStr !== "dswrf" && - statisticYSelect !== "Obs average" - ) { - throw new Error( - `INFO: The statistic/variable combination [${statisticYSelect} and ${variableYStr}] is only available for the HRRR data-source.` - ); - } + if ( + model !== "HRRR" && + variableXStr !== "dswrf" && + statisticXSelect !== "Obs average" + ) { + throw new Error( + `INFO: The statistic/variable combination [${statisticXSelect} and ${variableXStr}] is only available for the HRRR data-source.` + ); + } else if ( + model !== "HRRR" && + variableYStr !== "dswrf" && + statisticYSelect !== "Obs average" + ) { + throw new Error( + `INFO: The statistic/variable combination [${statisticYSelect} and ${variableYStr}] is only available for the HRRR data-source.` + ); + } - var queryResult; - const startMoment = moment(); - var finishMoment; - try { // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSimpleScatter( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, `${statisticXSelect}_${variableXStr}`, `${statisticYSelect}_${variableYStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -211,6 +230,7 @@ dataSimpleScatter = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -225,7 +245,6 @@ dataSimpleScatter = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -233,7 +252,7 @@ dataSimpleScatter = function (plotParams, plotFunction) { ymax = ymax > d.ymax ? ymax : d.ymax; } } else { - // this is a difference curve -- not supported for ROC plots + // this is a difference curve -- not supported for scatter plots throw new Error( "INFO: Difference curves are not supported for performance diagrams, as they do not feature consistent x or y values across all curves." ); @@ -241,6 +260,7 @@ dataSimpleScatter = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/surfrad/server/dataFunctions/data_validtime.js b/apps/surfrad/server/dataFunctions/data_validtime.js index bc0c4cff22..d2eaf27344 100644 --- a/apps/surfrad/server/dataFunctions/data_validtime.js +++ b/apps/surfrad/server/dataFunctions/data_validtime.js @@ -13,6 +13,7 @@ import { } from "meteor/randyp:mats-common"; import { moment } from "meteor/momentjs:moment"; +// eslint-disable-next-line no-undef dataValidTime = function (plotParams, plotFunction) { // initialize variables common to all curves const appParams = { @@ -23,69 +24,56 @@ dataValidTime = function (plotParams, plotFunction) { hideGaps: plotParams.noGapsCheck, hasLevels: false, }; + + const totalProcessingStart = moment(); const dataRequests = {}; // used to store data queries let dataFoundForCurve = true; let dataFoundForAnyCurve = false; - const totalProcessingStart = moment(); - let error = ""; + const curves = JSON.parse(JSON.stringify(plotParams.curves)); const curvesLength = curves.length; - const dataset = []; - const utcCycleStarts = []; + const axisMap = Object.create(null); let xmax = -1 * Number.MAX_VALUE; let ymax = -1 * Number.MAX_VALUE; let xmin = Number.MAX_VALUE; let ymin = Number.MAX_VALUE; + + let statType; + const utcCycleStarts = []; const idealValues = []; - for (let curveIndex = 0; curveIndex < curvesLength; curveIndex++) { + let statement = ""; + let error = ""; + const dataset = []; + + for (let curveIndex = 0; curveIndex < curvesLength; curveIndex += 1) { // initialize variables specific to each curve const curve = curves[curveIndex]; - const { diffFrom } = curve; const { label } = curve; + const { diffFrom } = curve; const model = matsCollections["data-source"].findOne({ name: "data-source" }) .optionsMap[curve["data-source"]][0]; - var regionStr = curve.region; - const region = Object.keys( - matsCollections.region.findOne({ name: "region" }).valuesMap - ).find( - (key) => - matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr - ); - var regionClause; - if (region === "all_stat") { - regionClause = ""; - } else if (region === "all_surf") { - regionClause = "and m0.id in(1,2,3,4,5,6,7) "; - } else if (region === "all_sol") { - regionClause = "and m0.id in(8,9,10,11,12,13,14) "; - } else { - regionClause = `and m0.id in(${region}) `; - } - var scaleStr = curve.scale; - const grid_scale = Object.keys( - matsCollections.scale.findOne({ name: "scale" }).valuesMap - ).find( - (key) => - matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr - ); - const scaleClause = `and m0.scale = ${grid_scale}`; - const queryTableClause = `from surfrad as o, ${model} as m0`; + const variableStr = curve.variable; const variableOptionsMap = matsCollections.variable.findOne( { name: "variable" }, { optionsMap: 1 } ).optionsMap; const variable = variableOptionsMap[variableStr]; + + const scaleStr = curve.scale; + const scale = Object.keys( + matsCollections.scale.findOne({ name: "scale" }).valuesMap + ).find( + (key) => + matsCollections.scale.findOne({ name: "scale" }).valuesMap[key] === scaleStr + ); + const scaleClause = `and m0.scale = ${scale}`; + const forecastLength = Number(curve["forecast-length"]) * 60; const forecastLengthClause = `and m0.fcst_len = ${forecastLength}`; - const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); - const fromSecs = dateRange.fromSeconds; - const toSecs = dateRange.toSeconds; - let dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs}`; - dateClause = `${dateClause} and m0.secs >= ${fromSecs} and m0.secs <= ${toSecs}`; - const matchClause = "and m0.id = o.id and m0.secs = o.secs"; + const statisticSelect = curve.statistic; const statisticOptionsMap = matsCollections.statistic.findOne( { name: "statistic" }, @@ -94,71 +82,97 @@ dataValidTime = function (plotParams, plotFunction) { const statisticClause = `sum(${variable[0]}) as square_diff_sum, count(${variable[1]}) as N_sum, sum(${variable[2]}) as obs_model_diff_sum, sum(${variable[3]}) as model_sum, sum(${variable[4]}) as obs_sum, sum(${variable[5]}) as abs_sum, ` + `group_concat(m0.secs, ';', ${variable[0]}, ';', 1, ';', ${variable[2]}, ';', ${variable[3]}, ';', ${variable[4]}, ';', ${variable[5]} order by m0.secs) as sub_data, count(${variable[0]}) as N0`; - var statType = statisticOptionsMap[statisticSelect]; - const { statVarUnitMap } = matsCollections.variable.findOne( - { name: "variable" }, - { statVarUnitMap: 1 } + + const dateRange = matsDataUtils.getDateRange(curve["curve-dates"]); + const fromSecs = dateRange.fromSeconds; + const toSecs = dateRange.toSeconds; + const dateClause = `and o.secs >= ${fromSecs} and o.secs <= ${toSecs} and m0.secs >= ${fromSecs} and m0.secs <= ${toSecs}`; + const matchClause = "and m0.id = o.id and m0.secs = o.secs"; + + const regionStr = curve.region; + const region = Object.keys( + matsCollections.region.findOne({ name: "region" }).valuesMap + ).find( + (key) => + matsCollections.region.findOne({ name: "region" }).valuesMap[key] === regionStr ); - const varUnits = statVarUnitMap[statisticSelect][variableStr]; + let regionClause; + if (region === "all_stat") { + regionClause = ""; + } else if (region === "all_surf") { + regionClause = "and m0.id in(1,2,3,4,5,6,7) "; + } else if (region === "all_sol") { + regionClause = "and m0.id in(8,9,10,11,12,13,14) "; + } else { + regionClause = `and m0.id in(${region}) `; + } + + const queryTableClause = `from surfrad as o, ${model} as m0`; + // axisKey is used to determine which axis a curve should use. // This axisKeySet object is used like a set and if a curve has the same // units (axisKey) it will use the same axis. // The axis number is assigned to the axisKeySet value, which is the axisKey. - const axisKey = varUnits; + const { statVarUnitMap } = matsCollections.variable.findOne( + { name: "variable" }, + { statVarUnitMap: 1 } + ); + statType = statisticOptionsMap[statisticSelect]; + const axisKey = statVarUnitMap[statisticSelect][variableStr]; curves[curveIndex].axisKey = axisKey; // stash the axisKey to use it later for axis options - var d; + let d; if (!diffFrom) { - // this is a database driven curve, not a difference curve - // prepare the query from the above parameters - let statement = - "select floor((m0.secs)%(24*3600)/900)/4 as hr_of_day, " + - "count(distinct m0.secs) as N_times, " + - "min(m0.secs) as min_secs, " + - "max(m0.secs) as max_secs, " + - "{{statisticClause}} " + - "{{queryTableClause}} " + - "where 1=1 " + - "{{matchClause}} " + - "{{dateClause}} " + - "{{forecastLengthClause}} " + - "{{scaleClause}} " + - "{{regionClause}} " + - "group by hr_of_day " + - "order by hr_of_day" + - ";"; + let queryResult; + const startMoment = moment(); + let finishMoment; + try { + statement = + "select floor((m0.secs)%(24*3600)/900)/4 as hr_of_day, " + + "count(distinct m0.secs) as N_times, " + + "min(m0.secs) as min_secs, " + + "max(m0.secs) as max_secs, " + + "{{statisticClause}} " + + "{{queryTableClause}} " + + "where 1=1 " + + "{{matchClause}} " + + "{{dateClause}} " + + "{{forecastLengthClause}} " + + "{{scaleClause}} " + + "{{regionClause}} " + + "group by hr_of_day " + + "order by hr_of_day" + + ";"; - statement = statement.replace("{{statisticClause}}", statisticClause); - statement = statement.replace("{{queryTableClause}}", queryTableClause); - statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); - statement = statement.replace("{{scaleClause}}", scaleClause); - statement = statement.replace("{{regionClause}}", regionClause); - statement = statement.replace("{{matchClause}}", matchClause); - statement = statement.replace("{{dateClause}}", dateClause); - dataRequests[label] = statement; + statement = statement.replace("{{statisticClause}}", statisticClause); + statement = statement.replace("{{queryTableClause}}", queryTableClause); + statement = statement.replace("{{forecastLengthClause}}", forecastLengthClause); + statement = statement.replace("{{scaleClause}}", scaleClause); + statement = statement.replace("{{regionClause}}", regionClause); + statement = statement.replace("{{matchClause}}", matchClause); + statement = statement.replace("{{dateClause}}", dateClause); + dataRequests[label] = statement; - if ( - model !== "HRRR" && - variableStr !== "dswrf" && - statisticSelect !== "Obs average" - ) { - throw new Error( - `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is only available for the HRRR data-source.` - ); - } + if ( + model !== "HRRR" && + variableStr !== "dswrf" && + statisticSelect !== "Obs average" + ) { + throw new Error( + `INFO: The statistic/variable combination [${statisticSelect} and ${variableStr}] is only available for the HRRR data-source.` + ); + } - var queryResult; - const startMoment = moment(); - var finishMoment; - try { // send the query statement to the query function queryResult = matsDataQueryUtils.queryDBSpecialtyCurve( - sumPool, + sumPool, // eslint-disable-line no-undef statement, appParams, `${statisticSelect}_${variableStr}` ); + finishMoment = moment(); + dataRequests[label] = statement; dataRequests[`data retrieval (query) time - ${label}`] = { begin: startMoment.format(), finish: finishMoment.format(), @@ -174,6 +188,7 @@ dataValidTime = function (plotParams, plotFunction) { e.message = `Error in queryDB: ${e.message} for statement: ${statement}`; throw new Error(e.message); } + if (queryResult.error !== undefined && queryResult.error !== "") { if (queryResult.error === matsTypes.Messages.NO_DATA_FOUND) { // this is NOT an error just a no data condition @@ -188,7 +203,6 @@ dataValidTime = function (plotParams, plotFunction) { } // set axis limits based on returned data - var postQueryStartMoment = moment(); if (dataFoundForCurve) { xmin = xmin < d.xmin ? xmin : d.xmin; xmax = xmax > d.xmax ? xmax : d.xmax; @@ -213,6 +227,7 @@ dataValidTime = function (plotParams, plotFunction) { // set curve annotation to be the curve mean -- may be recalculated later // also pass previously calculated axis stats to curve options + const postQueryStartMoment = moment(); const mean = d.sum / d.x.length; const annotation = mean === undefined diff --git a/apps/surfrad/server/main.js b/apps/surfrad/server/main.js index b35b513cc4..6d020e2238 100644 --- a/apps/surfrad/server/main.js +++ b/apps/surfrad/server/main.js @@ -4,7 +4,9 @@ import { Meteor } from "meteor/meteor"; import { mysql } from "meteor/pcel:mysql"; +import { moment } from "meteor/momentjs:moment"; import { + matsMethods, matsTypes, matsCollections, matsDataUtils, @@ -317,104 +319,93 @@ const doCurveParams = function () { const params = matsCollections.CurveParamsInfo.find({ curve_params: { $exists: true }, }).fetch()[0].curve_params; - for (let cp = 0; cp < params.length; cp++) { + for (let cp = 0; cp < params.length; cp += 1) { matsCollections[params[cp]].remove({}); } } + const modelOptionsMap = {}; let modelDateRangeMap = {}; const regionModelOptionsMap = {}; const forecastLengthOptionsMap = {}; const scaleModelOptionsMap = {}; - const masterRegionValuesMap = {}; - const masterScaleValuesMap = {}; + const allRegionValuesMap = {}; + const allScaleValuesMap = {}; try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select station,description from station_descriptions;" ); - let masterRegDescription; - let masterShortName; - for (var j = 0; j < rows.length; j++) { - masterRegDescription = rows[j].description.trim(); - masterShortName = rows[j].station.trim(); - masterRegionValuesMap[masterShortName] = masterRegDescription; + for (let j = 0; j < rows.length; j += 1) { + allRegionValuesMap[rows[j].station.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select scle,description from scale_descriptions;" ); - let masterDescription; - let masterScale; - for (var j = 0; j < rows.length; j++) { - masterDescription = rows[j].description.trim(); - masterScale = rows[j].scle.trim(); - masterScaleValuesMap[masterScale] = masterDescription; + for (let j = 0; j < rows.length; j += 1) { + allScaleValuesMap[rows[j].scle.trim()] = rows[j].description.trim(); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } try { const rows = matsDataQueryUtils.simplePoolQueryWrapSynchronous( - sumPool, + sumPool, // eslint-disable-line no-undef "select model,regions,display_text,fcst_lens,scle,mindate,maxdate from regions_per_model_mats_all_categories order by display_category, display_order;" ); - for (let i = 0; i < rows.length; i++) { - const model_value = rows[i].model.trim(); + for (let i = 0; i < rows.length; i += 1) { + const modelValue = rows[i].model.trim(); const model = rows[i].display_text.trim(); - modelOptionsMap[model] = [model_value]; + modelOptionsMap[model] = [modelValue]; const rowMinDate = moment.utc(rows[i].mindate * 1000).format("MM/DD/YYYY HH:mm"); const rowMaxDate = moment.utc(rows[i].maxdate * 1000).format("MM/DD/YYYY HH:mm"); - modelDateRangeMap[model] = { minDate: rowMinDate, maxDate: rowMaxDate }; + modelDateRangeMap[model] = { + minDate: rowMinDate, + maxDate: rowMaxDate, + }; const forecastLengths = rows[i].fcst_lens; - const forecastLengthArr = forecastLengths + forecastLengthOptionsMap[model] = forecastLengths .split(",") - .map(Function.prototype.call, String.prototype.trim); - for (var j = 0; j < forecastLengthArr.length; j++) { - forecastLengthArr[j] = ( - Number(forecastLengthArr[j].replace(/'|\[|\]/g, "")) / 60 - ).toString(); - } - forecastLengthOptionsMap[model] = forecastLengthArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (fhr) { + return Number(fhr.replace(/'|\[|\]/g, "")) / 60; + }); const scales = rows[i].scle; - const scalesArrRaw = scales + scaleModelOptionsMap[model] = scales .split(",") - .map(Function.prototype.call, String.prototype.trim); - const scalesArr = []; - var dummyScale; - for (var j = 0; j < scalesArrRaw.length; j++) { - dummyScale = scalesArrRaw[j].replace(/'|\[|\]/g, ""); - scalesArr.push(masterScaleValuesMap[dummyScale]); - } - scaleModelOptionsMap[model] = scalesArr; + .map(Function.prototype.call, String.prototype.trim) + .map(function (scale) { + return allScaleValuesMap[scale.replace(/'|\[|\]/g, "")]; + }); const { regions } = rows[i]; - const regionsArrRaw = regions - .split(",") - .map(Function.prototype.call, String.prototype.trim); - const regionsArr = []; - regionsArr.push(masterRegionValuesMap.all_stat); - regionsArr.push(masterRegionValuesMap.all_surf); - regionsArr.push(masterRegionValuesMap.all_sol); - var dummyRegion; - for (var j = 0; j < regionsArrRaw.length; j++) { - dummyRegion = regionsArrRaw[j].replace(/'|\[|\]/g, ""); - regionsArr.push(masterRegionValuesMap[dummyRegion]); - } - regionModelOptionsMap[model] = regionsArr; + regionModelOptionsMap[model] = [ + allRegionValuesMap.all_stat, + allRegionValuesMap.all_surf, + allRegionValuesMap.all_sol, + ]; + regionModelOptionsMap[model] = regionModelOptionsMap[model].concat( + regions + .split(",") + .map(Function.prototype.call, String.prototype.trim) + .map(function (region) { + return allRegionValuesMap[region.replace(/'|\[|\]/g, "")]; + }) + ); } } catch (err) { - console.log(err.message); + throw new Error(err.message); } if (matsCollections.label.findOne({ name: "label" }) === undefined) { @@ -452,7 +443,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["data-source"].findOne({ name: "data-source" }); + const currentParam = matsCollections["data-source"].findOne({ + name: "data-source", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, modelOptionsMap) || !matsDataUtils.areObjectsEqual(currentParam.dates, modelDateRangeMap) @@ -478,7 +471,7 @@ const doCurveParams = function () { type: matsTypes.InputTypes.select, optionsMap: regionModelOptionsMap, options: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]], - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, superiorNames: ["data-source"], controlButtonCovered: true, controlButtonText: "site", @@ -491,10 +484,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.region.findOne({ name: "region" }); + const currentParam = matsCollections.region.findOne({ name: "region" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, regionModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterRegionValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allRegionValuesMap) ) { // have to reload region data matsCollections.region.update( @@ -502,7 +495,7 @@ const doCurveParams = function () { { $set: { optionsMap: regionModelOptionsMap, - valuesMap: masterRegionValuesMap, + valuesMap: allRegionValuesMap, options: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]], default: regionModelOptionsMap[Object.keys(regionModelOptionsMap)[0]][0], }, @@ -511,7 +504,7 @@ const doCurveParams = function () { } } - const optionsMap = { + const statOptionsMap = { RMSE: "scalar", "Bias (Model - Obs)": "scalar", @@ -531,11 +524,11 @@ const doCurveParams = function () { matsCollections.statistic.insert({ name: "statistic", type: matsTypes.InputTypes.select, - optionsMap, - options: Object.keys(optionsMap), + optionsMap: statOptionsMap, + options: Object.keys(statOptionsMap), controlButtonCovered: true, unique: false, - default: Object.keys(optionsMap)[0], + default: Object.keys(statOptionsMap)[0], controlButtonVisibility: "block", displayOrder: 1, displayPriority: 1, @@ -547,11 +540,11 @@ const doCurveParams = function () { matsCollections["x-statistic"].insert({ name: "x-statistic", type: matsTypes.InputTypes.select, - optionsMap, - options: Object.keys(optionsMap), + optionsMap: statOptionsMap, + options: Object.keys(statOptionsMap), controlButtonCovered: true, unique: false, - default: Object.keys(optionsMap)[0], + default: Object.keys(statOptionsMap)[0], controlButtonVisibility: "block", displayOrder: 3, displayPriority: 1, @@ -563,11 +556,11 @@ const doCurveParams = function () { matsCollections["y-statistic"].insert({ name: "y-statistic", type: matsTypes.InputTypes.select, - optionsMap, - options: Object.keys(optionsMap), + optionsMap: statOptionsMap, + options: Object.keys(statOptionsMap), controlButtonCovered: true, unique: false, - default: Object.keys(optionsMap)[0], + default: Object.keys(statOptionsMap)[0], controlButtonVisibility: "block", displayOrder: 1, displayPriority: 1, @@ -733,7 +726,7 @@ const doCurveParams = function () { type: matsTypes.InputTypes.select, optionsMap: scaleModelOptionsMap, options: scaleModelOptionsMap[Object.keys(scaleModelOptionsMap)[0]], - valuesMap: masterScaleValuesMap, + valuesMap: allScaleValuesMap, superiorNames: ["data-source"], controlButtonCovered: true, unique: false, @@ -745,10 +738,10 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections.scale.findOne({ name: "scale" }); + const currentParam = matsCollections.scale.findOne({ name: "scale" }); if ( !matsDataUtils.areObjectsEqual(currentParam.optionsMap, scaleModelOptionsMap) || - !matsDataUtils.areObjectsEqual(currentParam.valuesMap, masterScaleValuesMap) + !matsDataUtils.areObjectsEqual(currentParam.valuesMap, allScaleValuesMap) ) { // have to reload scale data matsCollections.scale.update( @@ -756,7 +749,7 @@ const doCurveParams = function () { { $set: { optionsMap: scaleModelOptionsMap, - valuesMap: masterScaleValuesMap, + valuesMap: allScaleValuesMap, options: scaleModelOptionsMap[Object.keys(scaleModelOptionsMap)[0]], default: scaleModelOptionsMap[Object.keys(scaleModelOptionsMap)[0]][1], }, @@ -787,7 +780,7 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["forecast-length"].findOne({ + const currentParam = matsCollections["forecast-length"].findOne({ name: "forecast-length", }); if ( @@ -1006,7 +999,9 @@ const doCurveParams = function () { }); } else { // it is defined but check for necessary update - var currentParam = matsCollections["curve-dates"].findOne({ name: "curve-dates" }); + const currentParam = matsCollections["curve-dates"].findOne({ + name: "curve-dates", + }); if ( !matsDataUtils.areObjectsEqual(currentParam.startDate, minDate) || !matsDataUtils.areObjectsEqual(currentParam.stopDate, maxDate) || @@ -1293,7 +1288,8 @@ const doPlotGraph = function () { Meteor.startup(function () { matsCollections.Databases.remove({}); if (matsCollections.Databases.find({}).count() < 0) { - console.log( + // eslint-disable-next-line no-console + console.warn( "main startup: corrupted Databases collection: dropping Databases collection" ); matsCollections.Databases.drop(); @@ -1310,7 +1306,7 @@ Meteor.startup(function () { databases = Meteor.settings.private.databases; } if (databases !== null && databases !== undefined && Array.isArray(databases)) { - for (let di = 0; di < databases.length; di++) { + for (let di = 0; di < databases.length; di += 1) { matsCollections.Databases.insert(databases[di]); } } @@ -1337,6 +1333,7 @@ Meteor.startup(function () { ); if (cbConnection) { // global cbScorecardSettingsPool + // eslint-disable-next-line no-undef cbScorecardSettingsPool = new matsCouchbaseUtils.CBUtilities( cbConnection.host, cbConnection.bucket, @@ -1363,6 +1360,7 @@ Meteor.startup(function () { ); // the pool is intended to be global if (sumSettings) { + // eslint-disable-next-line no-undef sumPool = mysql.createPool(sumSettings); allPools.push({ pool: "sumPool", role: matsTypes.DatabaseRoles.SUMS_DATA }); } @@ -1380,7 +1378,7 @@ Meteor.startup(function () { appType: matsTypes.AppTypes.mats, }); } catch (error) { - console.log(error.message); + throw new Error(error.message); } }); @@ -1388,6 +1386,7 @@ Meteor.startup(function () { // These are application specific mongo data - like curve params // The appSpecificResetRoutines object is a special name, // as is doCurveParams. The refreshMetaData mechanism depends on them being named that way. +// eslint-disable-next-line no-undef appSpecificResetRoutines = [ doPlotGraph, doCurveParams, diff --git a/apps/upperair/.eslintrc.json b/apps/upperair/.eslintrc.json index 243e6f82c6..80b2c78142 100644 --- a/apps/upperair/.eslintrc.json +++ b/apps/upperair/.eslintrc.json @@ -29,11 +29,6 @@ // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications "func-names": "off", "prefer-arrow-callback": "off", - - // Vx Team modifications - Warn on rules that would require refactoring to implement. - // We want to be able to turn these back into "error"'s at some point. However, for - // our first pass, we'll only consider the checks that ESLint can auto-fix as errors. - // https://eslint.org/docs/latest/use/configure/rules#rule-severities "no-undef": "warn", "no-plusplus": "warn", "vars-on-top": "warn", diff --git a/tests/package-lock.json b/tests/package-lock.json index 2285841cd5..dbc9e4ec8c 100644 --- a/tests/package-lock.json +++ b/tests/package-lock.json @@ -25,7 +25,7 @@ "@wdio/spec-reporter": "^7.16.14", "@wdio/static-server-service": "^7.16.14", "@wdio/sync": "^7.16.14", - "chromedriver": "^116.0.0", + "chromedriver": "^118.0.0", "eslint": "^8.9.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.25.4", @@ -79,12 +79,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" @@ -130,9 +131,9 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -188,28 +189,43 @@ } }, "node_modules/@babel/eslint-parser/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.10.tgz", - "integrity": "sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.17.10", - "@jridgewell/gen-mapping": "^0.1.0", + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", @@ -254,9 +270,9 @@ } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -319,22 +335,19 @@ } }, "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, "engines": { "node": ">=6.9.0" } @@ -352,25 +365,25 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", - "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.16.7", - "@babel/types": "^7.17.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -495,21 +508,30 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -554,13 +576,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", - "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -568,9 +590,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.10.tgz", - "integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1662,9 +1684,9 @@ } }, "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -1718,33 +1740,33 @@ } }, "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.10.tgz", - "integrity": "sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.10", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.17.9", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.10", - "@babel/types": "^7.17.10", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1753,82 +1775,98 @@ } }, "node_modules/@babel/types": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.10.tgz", - "integrity": "sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, + "optional": true, "engines": { - "node": ">= 12" + "node": ">=0.1.90" } }, "node_modules/@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { "node": ">=12" } }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@cucumber/ci-environment": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-9.0.4.tgz", - "integrity": "sha512-da6H/wtVerhGUP4OCWTOmbNd4+gC1FhAcLzYgn6O68HgQbMwkmV3M8AwtbQWZkfF+Ph7z0M/UQYYdNIDu5V5MA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-9.1.0.tgz", + "integrity": "sha512-jdnF6APXP3GawMue8kdMxhu6TBhyRUO4KDRxTowf06NtclLjIw2Ybpo9IcIOMvE8kHukvJyM00uxWX+CfS7JgQ==", "dev": true }, "node_modules/@cucumber/cucumber": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-8.1.2.tgz", - "integrity": "sha512-HaAkGa40P8YNCkT6D4hVNFdvTHL3eIJbZmvPI9XKszQA+A7tkBxKjHva8esI4dfdLeZuq5ksNkyjEiHobPxfIA==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-8.6.0.tgz", + "integrity": "sha512-htkP/MlaYmgDId1Z2+7OQnH9aBd4Ui/Lk6PEmvMvP52ixPSUOv0MrP4/HUSECVXY0TI8GG7Ho03uLf6lieiFvw==", "dev": true, "dependencies": { - "@cspotcode/source-map-support": "^0.7.0", - "@cucumber/ci-environment": "9.0.4", - "@cucumber/cucumber-expressions": "15.0.2", - "@cucumber/gherkin": "23.0.1", + "@cspotcode/source-map-support": "^0.8.0", + "@cucumber/ci-environment": "9.1.0", + "@cucumber/cucumber-expressions": "16.0.0", + "@cucumber/gherkin": "24.0.0", "@cucumber/gherkin-streams": "5.0.1", - "@cucumber/gherkin-utils": "7.0.0", - "@cucumber/html-formatter": "19.1.0", + "@cucumber/gherkin-utils": "8.0.0", + "@cucumber/html-formatter": "20.0.0", "@cucumber/message-streams": "4.0.1", - "@cucumber/messages": "18.0.0", + "@cucumber/messages": "19.1.2", "@cucumber/tag-expressions": "4.1.0", "assertion-error-formatter": "^3.0.0", "capital-case": "^1.0.4", "chalk": "^4.1.2", - "cli-table3": "0.6.1", + "cli-table3": "0.6.2", "commander": "^9.0.0", + "debug": "^4.3.4", "duration": "^0.2.2", "durations": "^3.4.2", "figures": "^3.2.0", "glob": "^7.1.6", + "has-ansi": "^4.0.1", "indent-string": "^4.0.0", + "is-installed-globally": "^0.4.0", "is-stream": "^2.0.0", "knuth-shuffle-seeded": "^1.0.6", "lodash.merge": "^4.6.2", "lodash.mergewith": "^4.6.2", "mz": "^2.7.0", "progress": "^2.0.3", - "resolve": "^1.19.0", "resolve-pkg": "^2.0.0", - "semver": "7.3.5", + "semver": "7.3.7", "stack-chain": "^2.0.0", "string-argv": "^0.3.1", + "strip-ansi": "6.0.1", + "supports-color": "^8.1.1", "tmp": "^0.2.1", "util-arity": "^1.1.0", "verror": "^1.10.0", @@ -1842,14 +1880,44 @@ } }, "node_modules/@cucumber/cucumber-expressions": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-15.0.2.tgz", - "integrity": "sha512-ppN9JL1C5lw3InvM7WnoKZV9La5PW5Vr8c/8J0ZGzdlC8Y+PB7kskGzx7/tzl9kGjq7USQ7MZfwFz5el2sB/GA==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-16.0.0.tgz", + "integrity": "sha512-HTh+Pg7oQ5aLuCkSbD2Q6jBaE40M3R/XaLEz+UqD5d9dZRu6P38W4LTooV5bV6dZgBunlMLK8+6ug2ziYvRddw==", "dev": true, "dependencies": { "regexp-match-indices": "1.0.2" } }, + "node_modules/@cucumber/cucumber/node_modules/@cucumber/gherkin": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-24.0.0.tgz", + "integrity": "sha512-b7OsnvX1B8myDAKMc+RAiUX9bzgtNdjGsiMj10O13xu2HBWIOQ19EqBJ4xLO5CFG/lGk1J/+L0lANQVowxLVBg==", + "dev": true, + "dependencies": { + "@cucumber/messages": "^19.0.0" + } + }, + "node_modules/@cucumber/cucumber/node_modules/@cucumber/messages": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-19.1.2.tgz", + "integrity": "sha512-vhWkNmQco+7tk/DWqpN0/R9KTNvsKsXVfZ7IsJs+dEeWmTuRztklHq8lJalwMSQBl71+2/KqGHzOO4BMTC9wIQ==", + "dev": true, + "dependencies": { + "@types/uuid": "8.3.4", + "class-transformer": "0.5.1", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "node_modules/@cucumber/cucumber/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@cucumber/cucumber/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1881,6 +1949,18 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/@cucumber/cucumber/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@cucumber/cucumber/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1900,14 +1980,26 @@ "dev": true }, "node_modules/@cucumber/cucumber/node_modules/commander": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.2.0.tgz", - "integrity": "sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true, "engines": { "node": "^12.20.0 || >=14" } }, + "node_modules/@cucumber/cucumber/node_modules/has-ansi": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-4.0.1.tgz", + "integrity": "sha512-Qr4RtTm30xvEdqUXbSBVWDu+PrTokJOwe/FU+VdfJPk+MXAPoeOzKpRyrDTnZIJwAkQ4oBLTU53nu0HrkF/Z2A==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@cucumber/cucumber/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1939,9 +2031,9 @@ } }, "node_modules/@cucumber/cucumber/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1954,15 +2046,18 @@ } }, "node_modules/@cucumber/cucumber/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/@cucumber/cucumber/node_modules/tmp": { @@ -1984,12 +2079,12 @@ "dev": true }, "node_modules/@cucumber/gherkin": { - "version": "23.0.1", - "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-23.0.1.tgz", - "integrity": "sha512-WdSf1EfEajZ0ZYHPN6mX8Dl+jPgCLfnMPMq3rgYH6QOkHnBiQdsR0h6ahJj8iW5Zj2lZpMlW7SK7N1LW3c9YLQ==", + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-27.0.0.tgz", + "integrity": "sha512-j5rCsjqzRiC3iVTier3sa0kzyNbkcAmF7xr7jKnyO7qDeK3Z8Ye1P3KSVpeQRMY+KCDJ3WbTDdyxH0FwfA/fIw==", "dev": true, "dependencies": { - "@cucumber/messages": "^18.0.0" + "@cucumber/messages": ">=19.1.4 <=22" } }, "node_modules/@cucumber/gherkin-streams": { @@ -2020,56 +2115,53 @@ } }, "node_modules/@cucumber/gherkin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-7.0.0.tgz", - "integrity": "sha512-tDkSRITTPA6Df501doqeRH3+1jAM4ls6+tlFEVvkvuzTH3C8DXwQ5xBPWmUNmDhR/gJeZ+yj7gDRbDWr7Qc6Zw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-8.0.0.tgz", + "integrity": "sha512-8uIZInEe3cO1cASmy3BA0PbVFUI+xWBnZAxmICbVOPsZaMB85MtESZLafzErgfRQPsHf6uYbVagP7MIjNPM5Jw==", "dev": true, "dependencies": { - "@cucumber/messages": "^17.1.0", + "@cucumber/messages": "^19.0.0", "@teppeis/multimaps": "2.0.0", - "commander": "8.1.0" + "commander": "9.3.0" }, "bin": { "gherkin-utils": "bin/gherkin-utils" } }, "node_modules/@cucumber/gherkin-utils/node_modules/@cucumber/messages": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-17.1.1.tgz", - "integrity": "sha512-KQMn2Ag+1g1CXp/zKQ7LLqmuHjuQwuXw0N2u5SrDk8r72zPt36SxmDSJK7w6HiFTI+3p5ZuzwLi4S5jop3Tx4g==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-19.1.4.tgz", + "integrity": "sha512-Pksl0pnDz2l1+L5Ug85NlG6LWrrklN9qkMxN5Mv+1XZ3T6u580dnE6mVaxjJRdcOq4tR17Pc0RqIDZMyVY1FlA==", "dev": true, "dependencies": { - "@types/uuid": "8.3.1", - "class-transformer": "0.4.0", + "@types/uuid": "8.3.4", + "class-transformer": "0.5.1", "reflect-metadata": "0.1.13", - "uuid": "8.3.2" + "uuid": "9.0.0" } }, - "node_modules/@cucumber/gherkin-utils/node_modules/@types/uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", - "dev": true - }, - "node_modules/@cucumber/gherkin-utils/node_modules/class-transformer": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", - "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==", - "dev": true - }, "node_modules/@cucumber/gherkin-utils/node_modules/commander": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.1.0.tgz", - "integrity": "sha512-mf45ldcuHSYShkplHHGKWb4TrmwQadxOn7v4WuhDJy0ZVoY5JFajaRDKD0PNe5qXzBX0rhovjTnP6Kz9LETcuA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.3.0.tgz", + "integrity": "sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw==", "dev": true, "engines": { - "node": ">= 12" + "node": "^12.20.0 || >=14" + } + }, + "node_modules/@cucumber/gherkin-utils/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/@cucumber/html-formatter": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-19.1.0.tgz", - "integrity": "sha512-VCsRa34SNg9plfziFwOaoCSfsphHUb1Ivk8px8eLJc0rBFLDPDgJcHJtcufAu6AxFamGiptt2dt0XoqVq2Gr/Q==", + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-20.0.0.tgz", + "integrity": "sha512-I6ZzUZ0CkaaPm6QHThoCRmdcuEIV9kJlNjaWvQ3PRkJm0OscQkQ0SXL/j73U30RDPiCAWwtq6ZSeQrgkTnSK+Q==", "dev": true, "peerDependencies": { "@cucumber/messages": ">=18" @@ -2085,15 +2177,24 @@ } }, "node_modules/@cucumber/messages": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-18.0.0.tgz", - "integrity": "sha512-1HmQwrscW0PNABwYB/pOgJms2e6DNZLvBNne3n2cllgxoVDzFgKiO94PIvpBZUaqMcj7FLhHaOyCnYZgK8vDFQ==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-21.0.1.tgz", + "integrity": "sha512-pGR7iURM4SF9Qp1IIpNiVQ77J9kfxMkPOEbyy+zRmGABnWWCsqMpJdfHeh9Mb3VskemVw85++e15JT0PYdcR3g==", "dev": true, "dependencies": { "@types/uuid": "8.3.4", "class-transformer": "0.5.1", "reflect-metadata": "0.1.13", - "uuid": "8.3.2" + "uuid": "9.0.0" + } + }, + "node_modules/@cucumber/messages/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/@cucumber/tag-expressions": { @@ -2269,9 +2370,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", - "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, "engines": { "node": ">=6.0.0" @@ -2287,19 +2388,19 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.12.tgz", - "integrity": "sha512-az/NhpIwP3K33ILr0T2bso+k2E/SLf8Yidd8mHl0n6sCQ4YdyC8qDhZA6kOPDNDBA56ZnIjngVl0U3jREA0BUA==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@nicolo-ribaudo/chokidar-2": { @@ -2441,6 +2542,16 @@ "@types/node": "*" } }, + "node_modules/@types/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", + "dev": true, + "dependencies": { + "@types/minimatch": "^5.1.2", + "@types/node": "*" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", @@ -2559,6 +2670,12 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "dev": true }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, "node_modules/@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -2650,6 +2767,12 @@ "@types/node": "*" } }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.4.tgz", + "integrity": "sha512-GDV68H0mBSN449sa5HEj51E0wfpVQb8xNSMzxf/PrypMFcLTMwJMOM/cgXiv71Mq5drkOQmUGvL1okOZcu6RrQ==", + "dev": true + }, "node_modules/@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -2868,23 +2991,25 @@ } }, "node_modules/@wdio/cucumber-framework": { - "version": "7.19.7", - "resolved": "https://registry.npmjs.org/@wdio/cucumber-framework/-/cucumber-framework-7.19.7.tgz", - "integrity": "sha512-mV4MtioYiR2pEiuQbmVVcop3mFN+m/Ch1dviU8lLeGt8MbL1MR6LnlrDayTw6RC76hOYmziPo44yvrNrUJWBbQ==", + "version": "7.33.0", + "resolved": "https://registry.npmjs.org/@wdio/cucumber-framework/-/cucumber-framework-7.33.0.tgz", + "integrity": "sha512-/b6cn1IaB4bcvKL+B6CwEo65/es1e14gzuo8GdLxjEHJoOzWYf/y7Cs7cjzv2QyzaRevyKGfJmeJVt+dnfOO9Q==", "dev": true, "dependencies": { - "@cucumber/cucumber": "8.1.2", - "@cucumber/gherkin": "23.0.1", + "@cucumber/cucumber": "8.6.0", + "@cucumber/gherkin": "27.0.0", "@cucumber/gherkin-streams": "^5.0.0", - "@cucumber/messages": "18.0.0", + "@cucumber/messages": "21.0.1", + "@types/glob": "^8.1.0", "@types/is-glob": "^4.0.1", "@types/long": "^4.0.1", "@types/mockery": "^1.4.29", - "@wdio/logger": "7.19.0", - "@wdio/types": "7.19.5", - "@wdio/utils": "7.19.7", + "@types/sinonjs__fake-timers": "^8.1.2", + "@wdio/logger": "7.26.0", + "@wdio/types": "7.33.0", + "@wdio/utils": "7.33.0", "expect-webdriverio": "^3.0.0", - "glob": "^7.1.2", + "glob": "^8.0.3", "is-glob": "^4.0.0", "long": "^4.0.0", "mockery": "^2.1.0" @@ -2896,6 +3021,172 @@ "@wdio/cli": "^7.0.0" } }, + "node_modules/@wdio/cucumber-framework/node_modules/@types/node": { + "version": "18.18.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.6.tgz", + "integrity": "sha512-wf3Vz+jCmOQ2HV1YUJuCWdL64adYxumkrxtc+H1VUQlnQI04+5HtH+qZCOE21lBE7gIrt+CwX2Wv8Acrw5Ak6w==", + "dev": true + }, + "node_modules/@wdio/cucumber-framework/node_modules/@wdio/logger": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.26.0.tgz", + "integrity": "sha512-kQj9s5JudAG9qB+zAAcYGPHVfATl2oqKgqj47yjehOQ1zzG33xmtL1ArFbQKWhDG32y1A8sN6b0pIqBEIwgg8Q==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/cucumber-framework/node_modules/@wdio/types": { + "version": "7.33.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.33.0.tgz", + "integrity": "sha512-tNcuN5Kl+i5CffaeTYV1omzAo4rVjiI1m9raIA8ph6iVteWdCzYv2/ImpGgFiBPb7Mf6VokU3+q9Slh5Jitaww==", + "dev": true, + "dependencies": { + "@types/node": "^18.0.0", + "got": "^11.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "^4.6.2" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@wdio/cucumber-framework/node_modules/@wdio/utils": { + "version": "7.33.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.33.0.tgz", + "integrity": "sha512-4kQQ86EvEN6fBY5+u7M08cT6LfJtpk1rHd203xyxmbmV9lpNv/OCl4CsC+SD0jGT0aZZqYSIJ/Pil07pAh5K0g==", + "dev": true, + "dependencies": { + "@wdio/logger": "7.26.0", + "@wdio/types": "7.33.0", + "p-iteration": "^1.1.8" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@wdio/cucumber-framework/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@wdio/cucumber-framework/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@wdio/cucumber-framework/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wdio/cucumber-framework/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@wdio/cucumber-framework/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@wdio/cucumber-framework/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@wdio/cucumber-framework/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wdio/cucumber-framework/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@wdio/cucumber-framework/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@wdio/junit-reporter": { "version": "7.19.7", "resolved": "https://registry.npmjs.org/@wdio/junit-reporter/-/junit-reporter-7.19.7.tgz", @@ -3429,7 +3720,7 @@ "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", "dev": true }, "node_modules/anymatch": { @@ -3569,7 +3860,7 @@ "node_modules/assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, "engines": { "node": ">=0.8" @@ -3651,9 +3942,9 @@ } }, "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -3810,9 +4101,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.20.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", - "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "dev": true, "funding": [ { @@ -3822,14 +4113,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001332", - "electron-to-chromium": "^1.4.118", - "escalade": "^3.1.1", - "node-releases": "^2.0.3", - "picocolors": "^1.0.0" + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -4031,9 +4325,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001335", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz", - "integrity": "sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w==", + "version": "1.0.30001551", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001551.tgz", + "integrity": "sha512-vtBAez47BoGMMzlbYhfXrMV1kvRF2WP/lqiMuDu1Sb4EE4LKEgjopFDSRtZfdVnslNRpOqV/woE+Xgrwj6VQlg==", "dev": true, "funding": [ { @@ -4043,6 +4337,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -4141,9 +4439,9 @@ } }, "node_modules/chromedriver": { - "version": "116.0.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-116.0.0.tgz", - "integrity": "sha512-/TQaRn+RUAYnVqy5Vx8VtU8DvtWosU8QLM2u7BoNM5h55PRQPXF/onHAehEi8Sj/CehdKqH50NFdiumQAUr0DQ==", + "version": "118.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-118.0.1.tgz", + "integrity": "sha512-GlGfyRE47IuSJnuadIiDy89EMDMQFBVWxUmiclLJKzQhFsiWAtcIr/mNOxjljZdsw9IwIOQEkrB9wympKYFPLw==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -4159,7 +4457,7 @@ "chromedriver": "bin/chromedriver" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/class-transformer": { @@ -4193,9 +4491,9 @@ } }, "node_modules/cli-table3": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", - "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz", + "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==", "dev": true, "dependencies": { "string-width": "^4.2.0" @@ -4204,7 +4502,7 @@ "node": "10.* || >= 12.*" }, "optionalDependencies": { - "colors": "1.4.0" + "@colors/colors": "1.5.0" } }, "node_modules/cli-width": { @@ -4274,16 +4572,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -4423,28 +4711,18 @@ "dev": true }, "node_modules/core-js-compat": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.4.tgz", - "integrity": "sha512-dIWcsszDezkFZrfm1cnB4f/J85gyhiCpxbgBdohWCDtSVuAaChTSpPV7ldOQf/Xds2U5xCIJZOK82G4ZPAIswA==", + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.1.tgz", + "integrity": "sha512-6pYKNOgD/j/bkC5xS5IIg6bncid3rfrI42oBH1SQJbsmYPKF7rhzcFzYCcxYMmNQQ0rCEB8WqpW7QHndOggaeQ==", "dev": true, "dependencies": { - "browserslist": "^4.20.3", - "semver": "7.0.0" + "browserslist": "^4.22.1" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" } }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -4813,9 +5091,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.134", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.134.tgz", - "integrity": "sha512-OdD7M2no4Mi8PopfvoOuNcwYDJ2mNFxaBfurA6okG3fLBaMcFah9S+si84FhX+FIWLKkdaiHfl4A+5ep/gOVrg==", + "version": "1.4.562", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.562.tgz", + "integrity": "sha512-kMGVZLP65O2/oH7zzaoIA5hcr4/xPYO6Sa83FrIpWcd7YPPtSlxqwxTd8lJIwKxaiXM6FGsYK4ukyJ40XkW7jg==", "dev": true }, "node_modules/emoji-regex": { @@ -4912,9 +5190,9 @@ } }, "node_modules/es5-ext": { - "version": "0.10.61", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz", - "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==", + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -4929,7 +5207,7 @@ "node_modules/es6-iterator": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", "dev": true, "dependencies": { "d": "1", @@ -5043,9 +5321,9 @@ } }, "node_modules/eslint-config-airbnb-base/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -5586,18 +5864,18 @@ ] }, "node_modules/ext": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", - "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", "dev": true, "dependencies": { - "type": "^2.5.0" + "type": "^2.7.2" } }, "node_modules/ext/node_modules/type": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", - "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", "dev": true }, "node_modules/external-editor": { @@ -6056,6 +6334,21 @@ "node": ">= 6" } }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dev": true, + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -6266,9 +6559,9 @@ } }, "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, "node_modules/http-errors": { @@ -6524,6 +6817,15 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/inquirer": { "version": "8.2.4", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", @@ -6782,6 +7084,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -6827,6 +7145,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -7518,7 +7845,7 @@ "node_modules/knuth-shuffle-seeded": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz", - "integrity": "sha1-AfG2VzOqdUDuCNiwF0Fk0iCB5OE=", + "integrity": "sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg==", "dev": true, "dependencies": { "seed-random": "~2.2.0" @@ -8402,9 +8729,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", - "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "node_modules/normalize-package-data": { @@ -8598,7 +8925,7 @@ "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -8901,7 +9228,7 @@ "node_modules/pad-right": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", - "integrity": "sha1-b7ySQEXSRPKiokRQMGDTv8YAl3Q=", + "integrity": "sha512-4cy8M95ioIGolCoMmm2cMntGR1lPLEbOMzOKu8bzjuJP6JpzEMQcDHmh7hHLYGgob+nKe1YHFMaG4V59HQa89g==", "dev": true, "dependencies": { "repeat-string": "^1.5.2" @@ -9211,9 +9538,9 @@ } }, "node_modules/property-expr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", - "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", "dev": true }, "node_modules/proxy-addr": { @@ -9567,9 +9894,9 @@ } }, "node_modules/regexp-tree": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", - "integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==", + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", "dev": true, "bin": { "regexp-tree": "bin/regexp-tree" @@ -9634,7 +9961,7 @@ "node_modules/repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", "dev": true, "engines": { "node": ">=0.10" @@ -9790,7 +10117,7 @@ "node_modules/seed-random": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", - "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=", + "integrity": "sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ==", "dev": true }, "node_modules/selenium-standalone": { @@ -9846,9 +10173,9 @@ } }, "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -10159,9 +10486,9 @@ ] }, "node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true, "engines": { "node": ">=0.6.19" @@ -10368,7 +10695,7 @@ "node_modules/thenify-all": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "dev": true, "dependencies": { "thenify": ">= 3.1.0 < 4" @@ -10428,7 +10755,7 @@ "node_modules/toposort": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", - "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", "dev": true }, "node_modules/tr46": { @@ -10645,6 +10972,36 @@ "node": ">= 0.8" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/upper-case-first": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", @@ -10672,7 +11029,7 @@ "node_modules/util-arity": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/util-arity/-/util-arity-1.1.0.tgz", - "integrity": "sha1-WdAa8f2z/t4KxOYysKtfbOl8kzA=", + "integrity": "sha512-kkyIsXKwemfSy8ZEoaIz06ApApnWsk5hQO0vLjZS6UkBiGiW++Jsyb8vSBoc0WKlffGoGs5yYy/j5pp8zckrFA==", "dev": true }, "node_modules/util-deprecate": { @@ -10750,7 +11107,7 @@ "node_modules/verror/node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, "node_modules/wcwidth": { @@ -10969,9 +11326,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -11295,12 +11652,13 @@ } }, "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "requires": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" } }, "@babel/compat-data": { @@ -11333,9 +11691,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -11374,22 +11732,36 @@ "dev": true }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } }, "@babel/generator": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.10.tgz", - "integrity": "sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "requires": { - "@babel/types": "^7.17.10", - "@jridgewell/gen-mapping": "^0.1.0", + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } } }, "@babel/helper-annotate-as-pure": { @@ -11424,9 +11796,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -11473,21 +11845,18 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } }, "@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true }, "@babel/helper-explode-assignable-expression": { "version": "7.16.7", @@ -11499,22 +11868,22 @@ } }, "@babel/helper-function-name": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", - "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.16.7", - "@babel/types": "^7.17.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" } }, "@babel/helper-member-expression-to-functions": { @@ -11609,18 +11978,24 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" } }, + "@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true + }, "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { @@ -11653,20 +12028,20 @@ } }, "@babel/highlight": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", - "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.10.tgz", - "integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -12389,9 +12764,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -12432,108 +12807,153 @@ } }, "@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.10.tgz", - "integrity": "sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.10", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.17.9", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.10", - "@babel/types": "^7.17.10", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.10.tgz", - "integrity": "sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, - "@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true }, "@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "requires": { - "@cspotcode/source-map-consumer": "0.8.0" + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } } }, "@cucumber/ci-environment": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-9.0.4.tgz", - "integrity": "sha512-da6H/wtVerhGUP4OCWTOmbNd4+gC1FhAcLzYgn6O68HgQbMwkmV3M8AwtbQWZkfF+Ph7z0M/UQYYdNIDu5V5MA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@cucumber/ci-environment/-/ci-environment-9.1.0.tgz", + "integrity": "sha512-jdnF6APXP3GawMue8kdMxhu6TBhyRUO4KDRxTowf06NtclLjIw2Ybpo9IcIOMvE8kHukvJyM00uxWX+CfS7JgQ==", "dev": true }, "@cucumber/cucumber": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-8.1.2.tgz", - "integrity": "sha512-HaAkGa40P8YNCkT6D4hVNFdvTHL3eIJbZmvPI9XKszQA+A7tkBxKjHva8esI4dfdLeZuq5ksNkyjEiHobPxfIA==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-8.6.0.tgz", + "integrity": "sha512-htkP/MlaYmgDId1Z2+7OQnH9aBd4Ui/Lk6PEmvMvP52ixPSUOv0MrP4/HUSECVXY0TI8GG7Ho03uLf6lieiFvw==", "dev": true, "requires": { - "@cspotcode/source-map-support": "^0.7.0", - "@cucumber/ci-environment": "9.0.4", - "@cucumber/cucumber-expressions": "15.0.2", - "@cucumber/gherkin": "23.0.1", + "@cspotcode/source-map-support": "^0.8.0", + "@cucumber/ci-environment": "9.1.0", + "@cucumber/cucumber-expressions": "16.0.0", + "@cucumber/gherkin": "24.0.0", "@cucumber/gherkin-streams": "5.0.1", - "@cucumber/gherkin-utils": "7.0.0", - "@cucumber/html-formatter": "19.1.0", + "@cucumber/gherkin-utils": "8.0.0", + "@cucumber/html-formatter": "20.0.0", "@cucumber/message-streams": "4.0.1", - "@cucumber/messages": "18.0.0", + "@cucumber/messages": "19.1.2", "@cucumber/tag-expressions": "4.1.0", "assertion-error-formatter": "^3.0.0", "capital-case": "^1.0.4", "chalk": "^4.1.2", - "cli-table3": "0.6.1", + "cli-table3": "0.6.2", "commander": "^9.0.0", + "debug": "^4.3.4", "duration": "^0.2.2", "durations": "^3.4.2", "figures": "^3.2.0", "glob": "^7.1.6", + "has-ansi": "^4.0.1", "indent-string": "^4.0.0", + "is-installed-globally": "^0.4.0", "is-stream": "^2.0.0", "knuth-shuffle-seeded": "^1.0.6", "lodash.merge": "^4.6.2", "lodash.mergewith": "^4.6.2", "mz": "^2.7.0", "progress": "^2.0.3", - "resolve": "^1.19.0", "resolve-pkg": "^2.0.0", - "semver": "7.3.5", + "semver": "7.3.7", "stack-chain": "^2.0.0", "string-argv": "^0.3.1", + "strip-ansi": "6.0.1", + "supports-color": "^8.1.1", "tmp": "^0.2.1", "util-arity": "^1.1.0", "verror": "^1.10.0", "yup": "^0.32.11" }, "dependencies": { + "@cucumber/gherkin": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-24.0.0.tgz", + "integrity": "sha512-b7OsnvX1B8myDAKMc+RAiUX9bzgtNdjGsiMj10O13xu2HBWIOQ19EqBJ4xLO5CFG/lGk1J/+L0lANQVowxLVBg==", + "dev": true, + "requires": { + "@cucumber/messages": "^19.0.0" + } + }, + "@cucumber/messages": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-19.1.2.tgz", + "integrity": "sha512-vhWkNmQco+7tk/DWqpN0/R9KTNvsKsXVfZ7IsJs+dEeWmTuRztklHq8lJalwMSQBl71+2/KqGHzOO4BMTC9wIQ==", + "dev": true, + "requires": { + "@types/uuid": "8.3.4", + "class-transformer": "0.5.1", + "reflect-metadata": "0.1.13", + "uuid": "8.3.2" + } + }, + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -12551,6 +12971,17 @@ "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "color-convert": { @@ -12569,11 +13000,20 @@ "dev": true }, "commander": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.2.0.tgz", - "integrity": "sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true }, + "has-ansi": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-4.0.1.tgz", + "integrity": "sha512-Qr4RtTm30xvEdqUXbSBVWDu+PrTokJOwe/FU+VdfJPk+MXAPoeOzKpRyrDTnZIJwAkQ4oBLTU53nu0HrkF/Z2A==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -12596,18 +13036,18 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" } }, "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -12631,21 +13071,21 @@ } }, "@cucumber/cucumber-expressions": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-15.0.2.tgz", - "integrity": "sha512-ppN9JL1C5lw3InvM7WnoKZV9La5PW5Vr8c/8J0ZGzdlC8Y+PB7kskGzx7/tzl9kGjq7USQ7MZfwFz5el2sB/GA==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-16.0.0.tgz", + "integrity": "sha512-HTh+Pg7oQ5aLuCkSbD2Q6jBaE40M3R/XaLEz+UqD5d9dZRu6P38W4LTooV5bV6dZgBunlMLK8+6ug2ziYvRddw==", "dev": true, "requires": { "regexp-match-indices": "1.0.2" } }, "@cucumber/gherkin": { - "version": "23.0.1", - "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-23.0.1.tgz", - "integrity": "sha512-WdSf1EfEajZ0ZYHPN6mX8Dl+jPgCLfnMPMq3rgYH6QOkHnBiQdsR0h6ahJj8iW5Zj2lZpMlW7SK7N1LW3c9YLQ==", + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-27.0.0.tgz", + "integrity": "sha512-j5rCsjqzRiC3iVTier3sa0kzyNbkcAmF7xr7jKnyO7qDeK3Z8Ye1P3KSVpeQRMY+KCDJ3WbTDdyxH0FwfA/fIw==", "dev": true, "requires": { - "@cucumber/messages": "^18.0.0" + "@cucumber/messages": ">=19.1.4 <=22" } }, "@cucumber/gherkin-streams": { @@ -12667,52 +13107,46 @@ } }, "@cucumber/gherkin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-7.0.0.tgz", - "integrity": "sha512-tDkSRITTPA6Df501doqeRH3+1jAM4ls6+tlFEVvkvuzTH3C8DXwQ5xBPWmUNmDhR/gJeZ+yj7gDRbDWr7Qc6Zw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin-utils/-/gherkin-utils-8.0.0.tgz", + "integrity": "sha512-8uIZInEe3cO1cASmy3BA0PbVFUI+xWBnZAxmICbVOPsZaMB85MtESZLafzErgfRQPsHf6uYbVagP7MIjNPM5Jw==", "dev": true, "requires": { - "@cucumber/messages": "^17.1.0", + "@cucumber/messages": "^19.0.0", "@teppeis/multimaps": "2.0.0", - "commander": "8.1.0" + "commander": "9.3.0" }, "dependencies": { "@cucumber/messages": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-17.1.1.tgz", - "integrity": "sha512-KQMn2Ag+1g1CXp/zKQ7LLqmuHjuQwuXw0N2u5SrDk8r72zPt36SxmDSJK7w6HiFTI+3p5ZuzwLi4S5jop3Tx4g==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-19.1.4.tgz", + "integrity": "sha512-Pksl0pnDz2l1+L5Ug85NlG6LWrrklN9qkMxN5Mv+1XZ3T6u580dnE6mVaxjJRdcOq4tR17Pc0RqIDZMyVY1FlA==", "dev": true, "requires": { - "@types/uuid": "8.3.1", - "class-transformer": "0.4.0", + "@types/uuid": "8.3.4", + "class-transformer": "0.5.1", "reflect-metadata": "0.1.13", - "uuid": "8.3.2" + "uuid": "9.0.0" } }, - "@types/uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", - "dev": true - }, - "class-transformer": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.4.0.tgz", - "integrity": "sha512-ETWD/H2TbWbKEi7m9N4Km5+cw1hNcqJSxlSYhsLsNjQzWWiZIYA1zafxpK9PwVfaZ6AqR5rrjPVUBGESm5tQUA==", + "commander": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.3.0.tgz", + "integrity": "sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw==", "dev": true }, - "commander": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.1.0.tgz", - "integrity": "sha512-mf45ldcuHSYShkplHHGKWb4TrmwQadxOn7v4WuhDJy0ZVoY5JFajaRDKD0PNe5qXzBX0rhovjTnP6Kz9LETcuA==", + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "dev": true } } }, "@cucumber/html-formatter": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-19.1.0.tgz", - "integrity": "sha512-VCsRa34SNg9plfziFwOaoCSfsphHUb1Ivk8px8eLJc0rBFLDPDgJcHJtcufAu6AxFamGiptt2dt0XoqVq2Gr/Q==", + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-20.0.0.tgz", + "integrity": "sha512-I6ZzUZ0CkaaPm6QHThoCRmdcuEIV9kJlNjaWvQ3PRkJm0OscQkQ0SXL/j73U30RDPiCAWwtq6ZSeQrgkTnSK+Q==", "dev": true, "requires": {} }, @@ -12724,15 +13158,23 @@ "requires": {} }, "@cucumber/messages": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-18.0.0.tgz", - "integrity": "sha512-1HmQwrscW0PNABwYB/pOgJms2e6DNZLvBNne3n2cllgxoVDzFgKiO94PIvpBZUaqMcj7FLhHaOyCnYZgK8vDFQ==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-21.0.1.tgz", + "integrity": "sha512-pGR7iURM4SF9Qp1IIpNiVQ77J9kfxMkPOEbyy+zRmGABnWWCsqMpJdfHeh9Mb3VskemVw85++e15JT0PYdcR3g==", "dev": true, "requires": { "@types/uuid": "8.3.4", "class-transformer": "0.5.1", "reflect-metadata": "0.1.13", - "uuid": "8.3.2" + "uuid": "9.0.0" + }, + "dependencies": { + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "dev": true + } } }, "@cucumber/tag-expressions": { @@ -12867,9 +13309,9 @@ } }, "@jridgewell/resolve-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz", - "integrity": "sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true }, "@jridgewell/set-array": { @@ -12879,19 +13321,19 @@ "dev": true }, "@jridgewell/sourcemap-codec": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.12.tgz", - "integrity": "sha512-az/NhpIwP3K33ILr0T2bso+k2E/SLf8Yidd8mHl0n6sCQ4YdyC8qDhZA6kOPDNDBA56ZnIjngVl0U3jREA0BUA==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@nicolo-ribaudo/chokidar-2": { @@ -13021,6 +13463,16 @@ "@types/node": "*" } }, + "@types/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", + "dev": true, + "requires": { + "@types/minimatch": "^5.1.2", + "@types/node": "*" + } + }, "@types/http-cache-semantics": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", @@ -13139,6 +13591,12 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "dev": true }, + "@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, "@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -13230,6 +13688,12 @@ "@types/node": "*" } }, + "@types/sinonjs__fake-timers": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.4.tgz", + "integrity": "sha512-GDV68H0mBSN449sa5HEj51E0wfpVQb8xNSMzxf/PrypMFcLTMwJMOM/cgXiv71Mq5drkOQmUGvL1okOZcu6RrQ==", + "dev": true + }, "@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -13420,26 +13884,149 @@ } }, "@wdio/cucumber-framework": { - "version": "7.19.7", - "resolved": "https://registry.npmjs.org/@wdio/cucumber-framework/-/cucumber-framework-7.19.7.tgz", - "integrity": "sha512-mV4MtioYiR2pEiuQbmVVcop3mFN+m/Ch1dviU8lLeGt8MbL1MR6LnlrDayTw6RC76hOYmziPo44yvrNrUJWBbQ==", + "version": "7.33.0", + "resolved": "https://registry.npmjs.org/@wdio/cucumber-framework/-/cucumber-framework-7.33.0.tgz", + "integrity": "sha512-/b6cn1IaB4bcvKL+B6CwEo65/es1e14gzuo8GdLxjEHJoOzWYf/y7Cs7cjzv2QyzaRevyKGfJmeJVt+dnfOO9Q==", "dev": true, "requires": { - "@cucumber/cucumber": "8.1.2", - "@cucumber/gherkin": "23.0.1", + "@cucumber/cucumber": "8.6.0", + "@cucumber/gherkin": "27.0.0", "@cucumber/gherkin-streams": "^5.0.0", - "@cucumber/messages": "18.0.0", + "@cucumber/messages": "21.0.1", + "@types/glob": "^8.1.0", "@types/is-glob": "^4.0.1", "@types/long": "^4.0.1", "@types/mockery": "^1.4.29", - "@wdio/logger": "7.19.0", - "@wdio/types": "7.19.5", - "@wdio/utils": "7.19.7", + "@types/sinonjs__fake-timers": "^8.1.2", + "@wdio/logger": "7.26.0", + "@wdio/types": "7.33.0", + "@wdio/utils": "7.33.0", "expect-webdriverio": "^3.0.0", - "glob": "^7.1.2", + "glob": "^8.0.3", "is-glob": "^4.0.0", "long": "^4.0.0", "mockery": "^2.1.0" + }, + "dependencies": { + "@types/node": { + "version": "18.18.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.6.tgz", + "integrity": "sha512-wf3Vz+jCmOQ2HV1YUJuCWdL64adYxumkrxtc+H1VUQlnQI04+5HtH+qZCOE21lBE7gIrt+CwX2Wv8Acrw5Ak6w==", + "dev": true + }, + "@wdio/logger": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-7.26.0.tgz", + "integrity": "sha512-kQj9s5JudAG9qB+zAAcYGPHVfATl2oqKgqj47yjehOQ1zzG33xmtL1ArFbQKWhDG32y1A8sN6b0pIqBEIwgg8Q==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "strip-ansi": "^6.0.0" + } + }, + "@wdio/types": { + "version": "7.33.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.33.0.tgz", + "integrity": "sha512-tNcuN5Kl+i5CffaeTYV1omzAo4rVjiI1m9raIA8ph6iVteWdCzYv2/ImpGgFiBPb7Mf6VokU3+q9Slh5Jitaww==", + "dev": true, + "requires": { + "@types/node": "^18.0.0", + "got": "^11.8.1" + } + }, + "@wdio/utils": { + "version": "7.33.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.33.0.tgz", + "integrity": "sha512-4kQQ86EvEN6fBY5+u7M08cT6LfJtpk1rHd203xyxmbmV9lpNv/OCl4CsC+SD0jGT0aZZqYSIJ/Pil07pAh5K0g==", + "dev": true, + "requires": { + "@wdio/logger": "7.26.0", + "@wdio/types": "7.33.0", + "p-iteration": "^1.1.8" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "@wdio/junit-reporter": { @@ -13839,7 +14426,7 @@ "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", "dev": true }, "anymatch": { @@ -13957,7 +14544,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true }, "assertion-error-formatter": { @@ -14027,9 +14614,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -14154,16 +14741,15 @@ "dev": true }, "browserslist": { - "version": "4.20.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", - "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001332", - "electron-to-chromium": "^1.4.118", - "escalade": "^3.1.1", - "node-releases": "^2.0.3", - "picocolors": "^1.0.0" + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" } }, "buffer": { @@ -14305,9 +14891,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001335", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz", - "integrity": "sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w==", + "version": "1.0.30001551", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001551.tgz", + "integrity": "sha512-vtBAez47BoGMMzlbYhfXrMV1kvRF2WP/lqiMuDu1Sb4EE4LKEgjopFDSRtZfdVnslNRpOqV/woE+Xgrwj6VQlg==", "dev": true }, "capital-case": { @@ -14381,9 +14967,9 @@ } }, "chromedriver": { - "version": "116.0.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-116.0.0.tgz", - "integrity": "sha512-/TQaRn+RUAYnVqy5Vx8VtU8DvtWosU8QLM2u7BoNM5h55PRQPXF/onHAehEi8Sj/CehdKqH50NFdiumQAUr0DQ==", + "version": "118.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-118.0.1.tgz", + "integrity": "sha512-GlGfyRE47IuSJnuadIiDy89EMDMQFBVWxUmiclLJKzQhFsiWAtcIr/mNOxjljZdsw9IwIOQEkrB9wympKYFPLw==", "dev": true, "requires": { "@testim/chrome-version": "^1.1.3", @@ -14417,12 +15003,12 @@ "dev": true }, "cli-table3": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", - "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz", + "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==", "dev": true, "requires": { - "colors": "1.4.0", + "@colors/colors": "1.5.0", "string-width": "^4.2.0" } }, @@ -14484,13 +15070,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "optional": true - }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -14597,21 +15176,12 @@ "dev": true }, "core-js-compat": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.4.tgz", - "integrity": "sha512-dIWcsszDezkFZrfm1cnB4f/J85gyhiCpxbgBdohWCDtSVuAaChTSpPV7ldOQf/Xds2U5xCIJZOK82G4ZPAIswA==", + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.1.tgz", + "integrity": "sha512-6pYKNOgD/j/bkC5xS5IIg6bncid3rfrI42oBH1SQJbsmYPKF7rhzcFzYCcxYMmNQQ0rCEB8WqpW7QHndOggaeQ==", "dev": true, "requires": { - "browserslist": "^4.20.3", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } + "browserslist": "^4.22.1" } }, "core-util-is": { @@ -14893,9 +15463,9 @@ } }, "electron-to-chromium": { - "version": "1.4.134", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.134.tgz", - "integrity": "sha512-OdD7M2no4Mi8PopfvoOuNcwYDJ2mNFxaBfurA6okG3fLBaMcFah9S+si84FhX+FIWLKkdaiHfl4A+5ep/gOVrg==", + "version": "1.4.562", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.562.tgz", + "integrity": "sha512-kMGVZLP65O2/oH7zzaoIA5hcr4/xPYO6Sa83FrIpWcd7YPPtSlxqwxTd8lJIwKxaiXM6FGsYK4ukyJ40XkW7jg==", "dev": true }, "emoji-regex": { @@ -14977,9 +15547,9 @@ } }, "es5-ext": { - "version": "0.10.61", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz", - "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==", + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", "dev": true, "requires": { "es6-iterator": "^2.0.3", @@ -14990,7 +15560,7 @@ "es6-iterator": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", "dev": true, "requires": { "d": "1", @@ -15186,9 +15756,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -15504,18 +16074,18 @@ } }, "ext": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", - "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", "dev": true, "requires": { - "type": "^2.5.0" + "type": "^2.7.2" }, "dependencies": { "type": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", - "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", "dev": true } } @@ -15866,6 +16436,15 @@ "is-glob": "^4.0.1" } }, + "global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dev": true, + "requires": { + "ini": "2.0.0" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -16023,9 +16602,9 @@ } }, "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, "http-errors": { @@ -16211,6 +16790,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true + }, "inquirer": { "version": "8.2.4", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", @@ -16393,6 +16978,16 @@ "is-extglob": "^2.1.1" } }, + "is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "requires": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + } + }, "is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -16420,6 +17015,12 @@ "has-tostringtag": "^1.0.0" } }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, "is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -16933,7 +17534,7 @@ "knuth-shuffle-seeded": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz", - "integrity": "sha1-AfG2VzOqdUDuCNiwF0Fk0iCB5OE=", + "integrity": "sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg==", "dev": true, "requires": { "seed-random": "~2.2.0" @@ -17635,9 +18236,9 @@ } }, "node-releases": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", - "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "normalize-package-data": { @@ -17783,7 +18384,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true }, "object-inspect": { @@ -17998,7 +18599,7 @@ "pad-right": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz", - "integrity": "sha1-b7ySQEXSRPKiokRQMGDTv8YAl3Q=", + "integrity": "sha512-4cy8M95ioIGolCoMmm2cMntGR1lPLEbOMzOKu8bzjuJP6JpzEMQcDHmh7hHLYGgob+nKe1YHFMaG4V59HQa89g==", "dev": true, "requires": { "repeat-string": "^1.5.2" @@ -18230,9 +18831,9 @@ "dev": true }, "property-expr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", - "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==", "dev": true }, "proxy-addr": { @@ -18524,9 +19125,9 @@ } }, "regexp-tree": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", - "integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==", + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", "dev": true }, "regexpp": { @@ -18575,7 +19176,7 @@ "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", "dev": true }, "require-directory": { @@ -18701,7 +19302,7 @@ "seed-random": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", - "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ=", + "integrity": "sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ==", "dev": true }, "selenium-standalone": { @@ -18746,9 +19347,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "send": { @@ -19001,9 +19602,9 @@ } }, "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true }, "string-width": { @@ -19159,7 +19760,7 @@ "thenify-all": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "dev": true, "requires": { "thenify": ">= 3.1.0 < 4" @@ -19204,7 +19805,7 @@ "toposort": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", - "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", "dev": true }, "tr46": { @@ -19361,6 +19962,16 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, + "update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, "upper-case-first": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", @@ -19388,7 +19999,7 @@ "util-arity": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/util-arity/-/util-arity-1.1.0.tgz", - "integrity": "sha1-WdAa8f2z/t4KxOYysKtfbOl8kzA=", + "integrity": "sha512-kkyIsXKwemfSy8ZEoaIz06ApApnWsk5hQO0vLjZS6UkBiGiW++Jsyb8vSBoc0WKlffGoGs5yYy/j5pp8zckrFA==", "dev": true }, "util-deprecate": { @@ -19451,7 +20062,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true } } @@ -19631,9 +20242,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "workerpool": { diff --git a/tests/package.json b/tests/package.json index 2ca9dc0bea..816417f307 100644 --- a/tests/package.json +++ b/tests/package.json @@ -42,7 +42,7 @@ "npm-run-all": "^4.1.5", "wdio-chromedriver-service": "^7.3.1", "webdriverio": "^7.16.15", - "chromedriver": "^116.0.0" + "chromedriver": "^118.0.0" }, "dependencies": { "typescript": "^4.9.5" diff --git a/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesDailyModelCycle.feature b/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesDailyModelCycle.feature index a114b0f02a..261bf55923 100644 --- a/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesDailyModelCycle.feature +++ b/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesDailyModelCycle.feature @@ -24,8 +24,8 @@ Feature: Match Unmatch Diff Curves DailyModelCycle When I click the "Add Curve" button Then "Curve0" is added - When I change the "data-source" parameter to "RAP_OPS_130" - Then the "data-source" parameter value matches "RAP_OPS_130" + When I change the "variable" parameter to "Visibility" + Then the "variable" parameter value matches "Visibility" When I click the "Add Curve" button Then "Curve1" is added And I should see a list of curves containing "Curve0,Curve1" diff --git a/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesDieoff.feature b/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesDieoff.feature index 937db5932d..85bee29782 100644 --- a/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesDieoff.feature +++ b/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesDieoff.feature @@ -26,8 +26,8 @@ Feature: Match Unmatch Diff Curves Dieoff When I click the "Add Curve" button Then "Curve0" is added - When I change the "data-source" parameter to "RAP_OPS_130" - Then the "data-source" parameter value matches "RAP_OPS_130" + When I change the "variable" parameter to "Visibility" + Then the "variable" parameter value matches "Visibility" When I click the "Add Curve" button Then "Curve1" is added And I should see a list of curves containing "Curve0,Curve1" diff --git a/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesHistogram.feature b/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesHistogram.feature index c32601d27f..20a0b444e9 100644 --- a/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesHistogram.feature +++ b/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesHistogram.feature @@ -24,8 +24,8 @@ Feature: Match Unmatch Diff Curves Histogram When I click the "Add Curve" button Then "Curve0" is added - When I change the "data-source" parameter to "RAP_OPS_130" - Then the "data-source" parameter value matches "RAP_OPS_130" + When I change the "variable" parameter to "Visibility" + Then the "variable" parameter value matches "Visibility" When I click the "Add Curve" button Then "Curve1" is added And I should see a list of curves containing "Curve0,Curve1" diff --git a/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesThreshold.feature b/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesThreshold.feature index 343b5d7c73..2208f2114b 100644 --- a/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesThreshold.feature +++ b/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesThreshold.feature @@ -24,8 +24,8 @@ Feature: Match Unmatch Diff Curves Threshold When I click the "Add Curve" button Then "Curve0" is added - When I change the "data-source" parameter to "RAP_OPS_130" - Then the "data-source" parameter value matches "RAP_OPS_130" + When I change the "variable" parameter to "Visibility" + Then the "variable" parameter value matches "Visibility" When I click the "Add Curve" button Then "Curve1" is added And I should see a list of curves containing "Curve0,Curve1" diff --git a/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesTimeseries.feature b/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesTimeseries.feature index d261dd1b7f..76d0efd63d 100644 --- a/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesTimeseries.feature +++ b/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesTimeseries.feature @@ -24,8 +24,8 @@ Feature: Match Unmatch Diff Curves Timeseries When I click the "Add Curve" button Then "Curve0" is added - When I change the "data-source" parameter to "RAP_OPS_130" - Then the "data-source" parameter value matches "RAP_OPS_130" + When I change the "variable" parameter to "Visibility" + Then the "variable" parameter value matches "Visibility" When I click the "Add Curve" button Then "Curve1" is added And I should see a list of curves containing "Curve0,Curve1" diff --git a/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesValidTime.feature b/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesValidTime.feature index c92eb229a4..70b225a77e 100644 --- a/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesValidTime.feature +++ b/tests/src/features/cb-metar/basic/matchUnmatchDiffCurvesValidTime.feature @@ -24,8 +24,8 @@ Feature: Match Unmatch Diff Curves Valid Time When I click the "Add Curve" button Then "Curve0" is added - When I change the "data-source" parameter to "RAP_OPS_130" - Then the "data-source" parameter value matches "RAP_OPS_130" + When I change the "variable" parameter to "Visibility" + Then the "variable" parameter value matches "Visibility" When I click the "Add Curve" button Then "Curve1" is added And I should see a list of curves containing "Curve0,Curve1"