Skip to content

Commit

Permalink
fully linted surface
Browse files Browse the repository at this point in the history
  • Loading branch information
mollybsmith-noaa committed Nov 9, 2023
1 parent 25dc81a commit 8f83c58
Show file tree
Hide file tree
Showing 13 changed files with 1,135 additions and 1,220 deletions.
14 changes: 1 addition & 13 deletions apps/surface/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,11 @@
"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",
"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"
}
}
1 change: 1 addition & 0 deletions apps/surface/client/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
255 changes: 142 additions & 113 deletions apps/surface/server/dataFunctions/data_contour.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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" &&
Expand All @@ -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: <br>${queryResult.error}<br> query: <br>${statement}<br>`;
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: <br>${queryResult.error}<br> query: <br>${statement}<br>`;
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
Expand Down
Loading

0 comments on commit 8f83c58

Please sign in to comment.