Commit e02c4e2a authored by Pithon Kabiro's avatar Pithon Kabiro
Browse files

Merge branch 'wip_select-sensors-dropdown-list-7' into 'master'

Update logic for drop-down list

Deactivate 'draw chart' button during async task. In addition, improve 
legibilty by replacing multiple if's with 'case' statement

See merge request !18
Showing with 243 additions and 162 deletions
+243 -162
......@@ -24,6 +24,8 @@ import { drawColumnChartHighcharts } from "./src_modules/chartColumn.mjs";
import {
showLoadingSpinner,
hideLoadingSpinner,
disableDrawChartButton,
enableDrawChartButton,
} from "./src_modules/loadingIndicator.mjs";
import { vanillaSelectBox } from "./thirdparty/vanillaSelectBox.mjs";
......@@ -138,7 +140,7 @@ const drawChartUsingSelectedOptions = async function () {
const selectedOptionsAllDropDownLists =
getSelectedOptionsFromAllDropDownLists();
// Note: The aggregation type + duration and chart type are the first and
// Note: The chart type amd aggregation type + duration are the first and
// third elements respectively, we have ignored the second and fourth elements
const [selectedChartTypeArr, , selectedAggregationTypeDurationArr] =
selectedOptionsAllDropDownLists;
......@@ -178,71 +180,74 @@ const drawChartUsingSelectedOptions = async function () {
);
// Create copies of the arrays of building(s) + data point(s) + sampling rate
const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr = [
const selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr = [
...selectedBuildingsDataPointsSamplingRateAbbrevNestedArr,
];
const selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr = [
const selectedBuildingsDataPointsSamplingRateAbbrevTempDiffCopyArr = [
...selectedBuildingsDataPointsSamplingRateAbbrevNestedArr,
];
// Check if we have non-computed
const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr =
// Check if we have raw observations
const selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr =
checkIfSelectedOptionsContainTemperatureDifference(
selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr
selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr
)
? deleteTemperatureDifferenceOptions(
selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr
selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr
)
: selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr;
: selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr;
// Check if we have computed / dT
const selectedBuildingsDataPointsSamplingRateAbbrevComputedArr =
// Check if we have dT (temperature difference)
const selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr =
checkIfSelectedOptionsContainTemperatureDifference(
selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr
selectedBuildingsDataPointsSamplingRateAbbrevTempDiffCopyArr
)
? extractTemperatureDifferenceOptions(
selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr
selectedBuildingsDataPointsSamplingRateAbbrevTempDiffCopyArr
)
: [];
// Display the loading indicator
showLoadingSpinner();
// Fetch the observations + metadata / non-computed
const observationsPlusMetadataNonComputedArr =
selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr.length === 0
// Disable the 'draw chart' button
disableDrawChartButton();
// Fetch the observations + metadata / raw observations
const observationsRawPlusMetadataArr =
selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr.length === 0
? [[], []]
: await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
BASE_URL,
QUERY_PARAMS_COMBINED,
selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr
selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr
);
// Fetch the observations + metadata / computed (dT)
const observationsPlusMetadataComputedArr =
selectedBuildingsDataPointsSamplingRateAbbrevComputedArr.length === 0
// Fetch the observations + metadata / temperature difference (dT)
const observationsTempDiffPlusMetadataArr =
selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr.length === 0
? [[], []]
: await calculateVorlaufMinusRuecklaufTemperature(
BASE_URL,
QUERY_PARAMS_COMBINED,
extractBuildingPlusSamplingRate(
selectedBuildingsDataPointsSamplingRateAbbrevComputedArr
selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr
)
);
// Extract the combined arrays for observations and metadata / non-computed
const [observationsNestedNonComputedArr, metadataNestedNonComputedArr] =
observationsPlusMetadataNonComputedArr;
// Extract the combined arrays for observations and metadata / raw observations
const [observationsRawNestedArr, metadataTempDiffNestedArr] =
observationsRawPlusMetadataArr;
// Extract the combined arrays for observations and metadata / computed (dT)
// Extract the combined arrays for observations and metadata / temperature difference (dT)
const [observationsNestedComputedArr, metadataNestedComputedArr] =
observationsPlusMetadataComputedArr;
observationsTempDiffPlusMetadataArr;
// Create a combined array of observations and metadata
const observationsPlusMetadataCombined = [
[...observationsNestedNonComputedArr, ...observationsNestedComputedArr],
[...metadataNestedNonComputedArr, ...metadataNestedComputedArr],
[...observationsRawNestedArr, ...observationsNestedComputedArr],
[...metadataTempDiffNestedArr, ...metadataNestedComputedArr],
];
const [observationsComboNestedArr, metadataComboNestedArr] =
......@@ -253,8 +258,8 @@ const drawChartUsingSelectedOptions = async function () {
(metadataObj) => formatDatastreamMetadataForChart(metadataObj)
);
// Extract the formatted metadata properties - used by ALL chart types
const extractedFormattedDatastreamProperties =
// Extract the formatted metadata properties for the raw observations - used by ALL chart types
const rawObservationsExtractedFormattedDatastreamProperties =
extractPropertiesFromFormattedDatastreamMetadata(
formattedMetadataNestedArr,
false
......@@ -272,64 +277,70 @@ const drawChartUsingSelectedOptions = async function () {
const aggregationStartDate = "2020-01-01";
const aggregationEndDate = "2020-12-31";
// Extract observations within the user-specified start and end date - used by MULTIPLE chart types
const observationsAggregationNestedArr = observationsComboNestedArr.map(
(obsArr) =>
// Create final array of observations- used by MULTIPLE chart types
// If we are performing aggregation, it was envisioned that the user would
// select observations that fall within a start and end date
const observationsComboNestedFinalArr =
selectedAggregationType === "None (raw data)"
? observationsComboNestedArr
: observationsComboNestedArr.map((observationsArr) =>
extractObservationsWithinDatesInterval(
obsArr,
"60min",
observationsArr,
selectedSamplingRateAbbrev,
aggregationStartDate,
aggregationEndDate
)
);
// Unique calendar dates - used by MULTIPLE chart types
const uniqueCalendarDatesNestedArr = observationsAggregationNestedArr.map(
const uniqueCalendarDatesNestedArr = observationsComboNestedFinalArr.map(
(observationsArr) =>
extractUniqueCalendarDatesFromTimestamp(observationsArr)
);
for (const selectedChartType of selectedChartTypeArr) {
if (selectedChartType === "Heatmap") {
// We are interested in raw observations
switch (selectedChartType) {
// We are interested in raw observations ONLY
case "Heatmap":
if (
selectedAggregationOptionsAreValid &&
selectedBuildingDataPointsOptionsAreValid
) {
drawHeatmapBasedOnSelectedOptions(
observationsComboNestedArr,
extractedFormattedDatastreamProperties
observationsComboNestedFinalArr,
rawObservationsExtractedFormattedDatastreamProperties
);
}
}
break;
if (selectedChartType === "Scatter Plot") {
// We are interested in raw observations
// We are interested in raw observations ONLY
case "Scatter Plot":
if (
selectedAggregationOptionsAreValid &&
selectedBuildingDataPointsOptionsAreValid
) {
drawScatterPlotFromChartSelection(
observationsComboNestedArr,
extractedFormattedDatastreamProperties
observationsComboNestedFinalArr,
rawObservationsExtractedFormattedDatastreamProperties
);
}
}
if (selectedChartType === "Line") {
// We are interested in raw observations or aggregated observations
break;
// We are interested in BOTH raw observations and aggregated observations
case "Line":
// Raw observations
if (selectedAggregationType === "None (raw data)") {
// Create formatted array(s) for observations
const formattedRawObservationsLineChartNestedArr =
observationsComboNestedArr.map((observationsArr) =>
formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
observationsComboNestedFinalArr.map((observationsArr) =>
formatSensorThingsApiResponseForLineOrColumnChart(
observationsArr
)
);
drawLineChartHighcharts(
formattedRawObservationsLineChartNestedArr,
extractedFormattedDatastreamProperties
rawObservationsExtractedFormattedDatastreamProperties
);
}
// Aggregated observations
......@@ -337,28 +348,29 @@ const drawChartUsingSelectedOptions = async function () {
drawLineChartBasedOnSelectedAggregationOptions(
selectedAggregationType,
selectedAggregationDuration,
observationsAggregationNestedArr,
observationsComboNestedFinalArr,
selectedSamplingRateAbbrev,
uniqueCalendarDatesNestedArr,
formattedMetadataNestedArr
);
}
}
if (selectedChartType === "Column") {
// We are interested in raw observations or aggregated observations
break;
// We are interested in BOTH raw observations and aggregated observations
case "Column":
// Raw observations
if (selectedAggregationType === "None (raw data)") {
// Create formatted array(s) for observations
const formattedRawObservationsColumnChartNestedArr =
observationsComboNestedArr.map((observationsArr) =>
formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
observationsComboNestedFinalArr.map((observationsArr) =>
formatSensorThingsApiResponseForLineOrColumnChart(
observationsArr
)
);
drawColumnChartHighcharts(
formattedRawObservationsColumnChartNestedArr,
extractedFormattedDatastreamProperties
rawObservationsExtractedFormattedDatastreamProperties
);
}
// Aggregated observations
......@@ -366,17 +378,24 @@ const drawChartUsingSelectedOptions = async function () {
drawColumnChartBasedOnSelectedAggregationOptions(
selectedAggregationType,
selectedAggregationDuration,
observationsAggregationNestedArr,
observationsComboNestedFinalArr,
selectedSamplingRateAbbrev,
uniqueCalendarDatesNestedArr,
formattedMetadataNestedArr
);
}
break;
default:
throw new Error("None of the chart type options were selected");
}
}
} catch (err) {
console.error(err);
} finally {
// Enable the 'draw chart' button
enableDrawChartButton();
// Hide the loading indicator
hideLoadingSpinner();
}
......
......@@ -5,6 +5,16 @@ import {
extractObservationValuesWithinMonthInterval,
} from "./aggregateHelpers.mjs";
/**
* Remove `null` observation values from an array of observation values
*
* @param {Array} obsValuesArr An array of observation values
* @returns {Array} An array with `null` observation values removed
*/
const filterOutNullObservationValues = function (obsValuesArr) {
return obsValuesArr.filter((obs) => obs !== null);
};
/**
* Calculate the minimum observation values that fall within a time interval delimited by a start date and end date
* @param {Array} obsValuesForDaysIntervalArr An array of observation values that fall within our time interval
......@@ -13,7 +23,10 @@ import {
const calculateMinimumObservationValuesWithinDatesInterval = function (
obsValuesForDaysIntervalArr
) {
return Math.min(...obsValuesForDaysIntervalArr);
// If the observation value is `null`, skip the calculation
return obsValuesForDaysIntervalArr.reduce((previousValue, obsValue) =>
obsValue === null ? null : Math.min(previousValue, obsValue)
);
};
/**
......@@ -24,7 +37,10 @@ const calculateMinimumObservationValuesWithinDatesInterval = function (
const calculateMaximumObservationValuesWithinDatesInterval = function (
obsValuesForDaysIntervalArr
) {
return Math.max(...obsValuesForDaysIntervalArr);
// If the observation value is `null`, skip the calculation
return obsValuesForDaysIntervalArr.reduce((previousValue, obsValue) =>
obsValue === null ? null : Math.max(previousValue, obsValue)
);
};
/**
......@@ -35,8 +51,10 @@ const calculateMaximumObservationValuesWithinDatesInterval = function (
const calculateSumOfObservationValuesWithinDatesInterval = function (
obsValuesForDaysIntervalArr
) {
return obsValuesForDaysIntervalArr.reduce(
(accumulator, obsValue) => accumulator + obsValue
// Remove the `null` observation values before calculating the sum
return filterOutNullObservationValues(obsValuesForDaysIntervalArr).reduce(
(previousValue, obsValue) => previousValue + obsValue,
0
);
};
......@@ -48,10 +66,11 @@ const calculateSumOfObservationValuesWithinDatesInterval = function (
const calculateAverageOfObservationValuesWithinDatesInterval = function (
obsValuesForDaysIntervalArr
) {
// The observation values array should only include non-`null` values
return (
calculateSumOfObservationValuesWithinDatesInterval(
obsValuesForDaysIntervalArr
) / obsValuesForDaysIntervalArr.length
) / filterOutNullObservationValues(obsValuesForDaysIntervalArr).length
);
};
......@@ -63,7 +82,10 @@ const calculateAverageOfObservationValuesWithinDatesInterval = function (
const calculateMinimumObservationValuesWithinMonthInterval = function (
obsValuesForMonthIntervalArr
) {
return Math.min(...obsValuesForMonthIntervalArr);
// If the observation value is `null`, skip the calculation
return obsValuesForMonthIntervalArr.reduce((previousValue, obsValue) =>
obsValue === null ? null : Math.min(previousValue, obsValue)
);
};
/**
......@@ -74,7 +96,10 @@ const calculateMinimumObservationValuesWithinMonthInterval = function (
const calculateMaximumObservationValuesWithinMonthInterval = function (
obsValuesForMonthIntervalArr
) {
return Math.max(...obsValuesForMonthIntervalArr);
// If the observation value is `null`, skip the calculation
return obsValuesForMonthIntervalArr.reduce((previousValue, obsValue) =>
obsValue === null ? null : Math.max(previousValue, obsValue)
);
};
/**
......@@ -85,8 +110,10 @@ const calculateMaximumObservationValuesWithinMonthInterval = function (
const calculateSumOfObservationValuesWithinMonthInterval = function (
obsValuesForMonthIntervalArr
) {
return obsValuesForMonthIntervalArr.reduce(
(accumulator, obsValue) => accumulator + obsValue
// Remove the `null` observation values before calculating the sum
return filterOutNullObservationValues(obsValuesForMonthIntervalArr).reduce(
(previousValue, obsValue) => previousValue + obsValue,
0
);
};
......@@ -98,10 +125,11 @@ const calculateSumOfObservationValuesWithinMonthInterval = function (
const calculateAverageOfObservationValuesWithinMonthInterval = function (
obsValuesForMonthIntervalArr
) {
// The observation values array should only include non-`null` values
return (
calculateSumOfObservationValuesWithinMonthInterval(
obsValuesForMonthIntervalArr
) / obsValuesForMonthIntervalArr.length
) / filterOutNullObservationValues(obsValuesForMonthIntervalArr).length
);
};
......
......@@ -131,10 +131,12 @@ const drawColumnChartHighcharts = function (
title: {
text: textChartTitle,
"align": "center",
},
subtitle: {
text: textChartSubtitle,
"align": "center",
},
xAxis: {
......@@ -143,7 +145,6 @@ const drawColumnChartHighcharts = function (
},
yAxis: {
min: 0,
title: {
text: `${phenomenonName} [${unitOfMeasurementSymbol}]`,
},
......@@ -155,10 +156,10 @@ const drawColumnChartHighcharts = function (
// this.x -- common for all points
// this.points -- an array containing properties for each series
// Note that our `reduce` method is in this format:
// ((accumulator, currentValue, currentIndex) => {...}, initialValue)
// ((previousValue, currentValue, currentIndex) => {...}, initialValue)
return this.points.reduce(
(accumulator, point, i) =>
`${accumulator} <br/> <span style="color:${point.color}">${
(previousValue, point, i) =>
`${previousValue} <br/> <span style="color:${point.color}">${
point.series.name
}</span>: <b>${point.y.toFixed(2)} ${
unitOfMeasurementSymbolsArr[i]
......
......@@ -48,7 +48,8 @@ const calculateMinMaxValuesForHeatmapColorAxis = function (
const maxValue = Math.trunc(Math.max(...obsValueArr));
// Calculate the closest multiple of 5
const minObsValue = minValue - (minValue % 5);
const minObsValue =
minValue > 0 ? minValue - (minValue % 5) : minValue + (minValue % 5);
const maxObsValue = maxValue + (5 - (maxValue % 5));
return { minObsValue, maxObsValue };
......@@ -98,13 +99,13 @@ const drawHeatMapHighcharts = function (
title: {
text: TEXT_CHART_TITLE,
align: "left",
align: "center",
x: 40,
},
subtitle: {
text: TEXT_CHART_SUBTITLE,
align: "left",
align: "center",
x: 40,
},
......
......@@ -134,12 +134,12 @@ const drawLineChartHighcharts = function (
title: {
text: textChartTitle,
"align": "left",
"align": "center",
},
subtitle: {
text: textChartSubtitle,
align: "left",
align: "center",
},
tooltip: {
......
......@@ -231,12 +231,12 @@ const drawScatterPlotHighcharts = function (
title: {
text: CHART_TITLE,
"align": "left",
"align": "center",
},
subtitle: {
text: CHART_SUBTITLE,
"align": "left",
"align": "center",
},
xAxis: {
......
"use strict";
/**
* Get the selected option(s) from a dropdown list
*
......
"use strict";
import { drawLineChartHighcharts } from "./chartLine.mjs";
import {
......
......@@ -2,7 +2,7 @@
/**
* Show a loading indicator at the start of an async task. The indicator consists of a spinner and a transluscent mask placed on top of page elements
* @returns {undefined}
* @returns {undefined} undefined
*/
const showLoadingSpinner = function () {
const loadingIndicatorMask = document.querySelector("#loadingIndicator");
......@@ -14,7 +14,7 @@ const showLoadingSpinner = function () {
/**
* Hide the loading indicator after completion of the async tasks
* @returns {undefined}
* @returns {undefined} undefined
*/
const hideLoadingSpinner = function () {
const loadingIndicatorMask = document.querySelector("#loadingIndicator");
......@@ -24,4 +24,27 @@ const hideLoadingSpinner = function () {
loadingIconSpinner.style.display = "none";
};
export { showLoadingSpinner, hideLoadingSpinner };
/**
* Disable the button used to trigger the drawing of charts
*
* @returns {undefined} undefined
*/
const disableDrawChartButton = function () {
document.querySelector("#btn-draw-chart").disabled = true;
};
/**
* Enable the button used to trigger the drawing of charts
*
* @returns {undefined} undefined
*/
const enableDrawChartButton = function () {
document.querySelector("#btn-draw-chart").disabled = false;
};
export {
showLoadingSpinner,
hideLoadingSpinner,
disableDrawChartButton,
enableDrawChartButton,
};
/*!
* Start Bootstrap - SB Admin v6.0.2 (https://startbootstrap.com/template/sb-admin)
* Copyright 2013-2020 Start Bootstrap
* Start Bootstrap - SB Admin v7.0.3 (https://startbootstrap.com/template/sb-admin)
* Copyright 2013-2021 Start Bootstrap
* Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-sb-admin/blob/master/LICENSE)
*/
(function($) {
"use strict";
// Add active state to sidbar nav links
var path = window.location.href; // because the 'href' property of the DOM element is the absolute path
$("#layoutSidenav_nav .sb-sidenav a.nav-link").each(function() {
if (this.href === path) {
$(this).addClass("active");
}
});
//
// Scripts
//
window.addEventListener("DOMContentLoaded", (event) => {
// Toggle the side navigation
$("#sidebarToggle").on("click", function(e) {
e.preventDefault();
$("body").toggleClass("sb-sidenav-toggled");
const sidebarToggle = document.body.querySelector("#sidebarToggle");
if (sidebarToggle) {
// Uncomment Below to persist sidebar toggle between refreshes
// if (localStorage.getItem('sb|sidebar-toggle') === 'true') {
// document.body.classList.toggle('sb-sidenav-toggled');
// }
sidebarToggle.addEventListener("click", (event) => {
event.preventDefault();
document.body.classList.toggle("sb-sidenav-toggled");
localStorage.setItem(
"sb|sidebar-toggle",
document.body.classList.contains("sb-sidenav-toggled")
);
});
})(jQuery);
}
});
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment