From f38903e5efa63015630f34af2fdb2f39ad888ba8 Mon Sep 17 00:00:00 2001
From: Pithon Kabiro <pithon.kabiro@hft-stuttgart.de>
Date: Fri, 24 Sep 2021 13:48:52 +0200
Subject: [PATCH 1/5] New functions: calculate daily min/max values

Calculate the minimum and maximum of observation values for a daily
interval
---
 public/js/src_modules/aggregate.mjs | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/public/js/src_modules/aggregate.mjs b/public/js/src_modules/aggregate.mjs
index 6691c1c..97c2c07 100644
--- a/public/js/src_modules/aggregate.mjs
+++ b/public/js/src_modules/aggregate.mjs
@@ -5,6 +5,28 @@ import {
   extractObservationValuesWithinMonthInterval,
 } from "./aggregateHelpers.mjs";
 
+/**
+ * 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
+ * @returns {Number} A floating-point number representing the minimum of observation values
+ */
+const calculateMinimumObservationValuesWithinDatesInterval = function (
+  obsValuesForDaysIntervalArr
+) {
+  return Math.min(...obsValuesForDaysIntervalArr);
+};
+
+/**
+ * Calculate the maximum 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
+ * @returns {Number} A floating-point number representing the maximum of observation values
+ */
+const calculateMaximumObservationValuesWithinDatesInterval = function (
+  obsValuesForDaysIntervalArr
+) {
+  return Math.max(...obsValuesForDaysIntervalArr);
+};
+
 /**
  * Calculate the sum of 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
-- 
GitLab


From f4bc5f523a174b8d45d65f9b7294a50d8d11012e Mon Sep 17 00:00:00 2001
From: Pithon Kabiro <pithon.kabiro@hft-stuttgart.de>
Date: Fri, 24 Sep 2021 13:50:04 +0200
Subject: [PATCH 2/5] New functions: calculate monthly min/max values

Calculate the minimum and maximum of observation values for a monthly
interval
---
 public/js/src_modules/aggregate.mjs | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/public/js/src_modules/aggregate.mjs b/public/js/src_modules/aggregate.mjs
index 97c2c07..b38603f 100644
--- a/public/js/src_modules/aggregate.mjs
+++ b/public/js/src_modules/aggregate.mjs
@@ -55,6 +55,28 @@ const calculateAverageOfObservationValuesWithinDatesInterval = function (
   );
 };
 
+/**
+ * Calculate the minimum of observation values that fall within a time interval delimited by the first day and last day of a calendar month
+ * @param {Array} obsValuesForMonthIntervalArr An array of observation values that fall within one calendar month
+ * @returns {Number} A floating-point number representing the minimum of observation values within one calendar month
+ */
+const calculateMinimumObservationValuesWithinMonthInterval = function (
+  obsValuesForMonthIntervalArr
+) {
+  return Math.min(...obsValuesForMonthIntervalArr);
+};
+
+/**
+ * Calculate the maximum of observation values that fall within a time interval delimited by the first day and last day of a calendar month
+ * @param {Array} obsValuesForMonthIntervalArr An array of observation values that fall within one calendar month
+ * @returns {Number} A floating-point number representing the maximum of observation values within one calendar month
+ */
+const calculateMaximumObservationValuesWithinMonthInterval = function (
+  obsValuesForMonthIntervalArr
+) {
+  return Math.max(...obsValuesForMonthIntervalArr);
+};
+
 /**
  * Calculate the sum of observation values that fall within a time interval delimited by the first day and last day of a calendar month
  * @param {Array} obsValuesForMonthIntervalArr An array of observation values that fall within one calendar month
-- 
GitLab


From 0af389b70dd35f06dbb2ef10888088d88826c6d2 Mon Sep 17 00:00:00 2001
From: Pithon Kabiro <pithon.kabiro@hft-stuttgart.de>
Date: Fri, 24 Sep 2021 13:52:14 +0200
Subject: [PATCH 3/5] New functions: calculate daily or monthly min/max

These functions wrap the logic of the two functions that calculate the
minimum or maximum observation values for an interval delimited by
calendar dates as well as delimited by calendar months
---
 public/js/src_modules/aggregate.mjs | 98 +++++++++++++++++++++++++++++
 1 file changed, 98 insertions(+)

diff --git a/public/js/src_modules/aggregate.mjs b/public/js/src_modules/aggregate.mjs
index b38603f..324716d 100644
--- a/public/js/src_modules/aggregate.mjs
+++ b/public/js/src_modules/aggregate.mjs
@@ -105,6 +105,102 @@ const calculateAverageOfObservationValuesWithinMonthInterval = function (
   );
 };
 
+/**
+ * Calculate the minimum of observation values within a time interval delimited by a start date and end date. The time interval may be daily or monthly
+ * @param {Array} obsNestedArr A 1*N array that contains N nested arrays of observations (timestamp + value)
+ * @param {String} samplingRate The sampling rate of observations as a string, e.g. "15min", "60min"
+ * @param {Array} uniqueCalendarDatesOrMonthsArr A 1*N array of unique calendar dates or calendar months strings
+ * @param {String} aggregationInterval The aggregation interval as a string e.g. "daily", "monthly"
+ * @returns {Array} A 1*N array that contains N nested arrays of minimum values
+ */
+const calculateMinimumObservationValuesWithinInterval = function (
+  obsNestedArr,
+  samplingRate,
+  uniqueCalendarDatesOrMonthsArr,
+  aggregationInterval
+) {
+  // Calculate minimum values of observations - daily
+  // Note the use of the two nested `map` methods
+  if (aggregationInterval === "daily") {
+    return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarDatesArr, i) =>
+      uniqueCalendarDatesArr.map((uniqueCalendarDate) =>
+        calculateMinimumObservationValuesWithinDatesInterval(
+          extractObservationValuesWithinDatesInterval(
+            obsNestedArr[i],
+            samplingRate,
+            uniqueCalendarDate,
+            uniqueCalendarDate
+          )
+        )
+      )
+    );
+  }
+
+  // Calculate minimum values of observations - monthly
+  // Note the use of the two nested `map` methods
+  if (aggregationInterval === "monthly") {
+    return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) =>
+      uniqueCalendarMonthsArr.map((uniqueCalendarMonth) =>
+        calculateMinimumObservationValuesWithinMonthInterval(
+          extractObservationValuesWithinMonthInterval(
+            obsNestedArr[i],
+            samplingRate,
+            uniqueCalendarMonth
+          )
+        )
+      )
+    );
+  }
+};
+
+/**
+ * Calculate the maximum of observation values within a time interval delimited by a start date and end date. The time interval may be daily or monthly
+ * @param {Array} obsNestedArr A 1*N array that contains N nested arrays of observations (timestamp + value)
+ * @param {String} samplingRate The sampling rate of observations as a string, e.g. "15min", "60min"
+ * @param {Array} uniqueCalendarDatesOrMonthsArr A 1*N array of unique calendar dates or calendar months strings
+ * @param {String} aggregationInterval The aggregation interval as a string e.g. "daily", "monthly"
+ * @returns {Array} A 1*N array that contains N nested arrays of maximum values
+ */
+const calculateMaximumObservationValuesWithinInterval = function (
+  obsNestedArr,
+  samplingRate,
+  uniqueCalendarDatesOrMonthsArr,
+  aggregationInterval
+) {
+  // Calculate maximum values of observations - daily
+  // Note the use of the two nested `map` methods
+  if (aggregationInterval === "daily") {
+    return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarDatesArr, i) =>
+      uniqueCalendarDatesArr.map((uniqueCalendarDate) =>
+        calculateMaximumObservationValuesWithinDatesInterval(
+          extractObservationValuesWithinDatesInterval(
+            obsNestedArr[i],
+            samplingRate,
+            uniqueCalendarDate,
+            uniqueCalendarDate
+          )
+        )
+      )
+    );
+  }
+
+  // Calculate maximum values of observations - monthly
+  // Note the use of the two nested `map` methods
+  if (aggregationInterval === "monthly") {
+    return uniqueCalendarDatesOrMonthsArr.map((uniqueCalendarMonthsArr, i) =>
+      uniqueCalendarMonthsArr.map((uniqueCalendarMonth) =>
+        calculateMaximumObservationValuesWithinMonthInterval(
+          extractObservationValuesWithinMonthInterval(
+            obsNestedArr[i],
+            samplingRate,
+            uniqueCalendarMonth
+          )
+        )
+      )
+    );
+  }
+};
+
 /**
  * Calculate the sum of observation values within a time interval delimited by a start date and end date. The time interval may be daily or monthly
  * @param {Array} obsNestedArr A 1*N array that contains N nested arrays of observations (timestamp + value)
@@ -202,6 +298,8 @@ const calculateAverageOfObservationValuesWithinInterval = function (
 };
 
 export {
+  calculateMinimumObservationValuesWithinInterval,
+  calculateMaximumObservationValuesWithinInterval,
   calculateSumOfObservationValuesWithinInterval,
   calculateAverageOfObservationValuesWithinInterval,
 };
-- 
GitLab


From 854d5927d1b7e43755b2e36fc4b37edf6ff6eda0 Mon Sep 17 00:00:00 2001
From: Pithon Kabiro <pithon.kabiro@hft-stuttgart.de>
Date: Fri, 24 Sep 2021 13:55:57 +0200
Subject: [PATCH 4/5] Edit function: extract metadata properties

Add two new aggregation type strings. Now there a four supported
aggregation type strings
---
 public/js/src_modules/fetchedDataProcessing.mjs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/public/js/src_modules/fetchedDataProcessing.mjs b/public/js/src_modules/fetchedDataProcessing.mjs
index 7d40960..ec6ee92 100644
--- a/public/js/src_modules/fetchedDataProcessing.mjs
+++ b/public/js/src_modules/fetchedDataProcessing.mjs
@@ -119,11 +119,13 @@ const extractPropertiesFromFormattedDatastreamMetadata = function (
 
   if (
     isMetadataForAggregation &&
+    aggregationType !== "minimum" &&
+    aggregationType !== "maximum" &&
     aggregationType !== "sum" &&
     aggregationType !== "average"
   )
     throw new Error(
-      `The supported aggegation type strings are "sum" or "average"`
+      `The supported aggegation type strings are "minimum", "maximum", "sum" or "average"`
     );
 
   // Create arrays from the properties of the formatted datastream metadata
-- 
GitLab


From 2a36331e682ddef38ab7009fd8d3e21f64ce01d2 Mon Sep 17 00:00:00 2001
From: Pithon Kabiro <pithon.kabiro@hft-stuttgart.de>
Date: Fri, 24 Sep 2021 13:58:18 +0200
Subject: [PATCH 5/5] New functions: test plotting of line charts

Plot line charts using daily minimum and monthly maximum values
---
 public/js/appChart.js | 174 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 174 insertions(+)

diff --git a/public/js/appChart.js b/public/js/appChart.js
index 72fdec5..4a49952 100644
--- a/public/js/appChart.js
+++ b/public/js/appChart.js
@@ -41,6 +41,8 @@ import {
 } from "./src_modules/aggregateHelpers.mjs";
 
 import {
+  calculateMinimumObservationValuesWithinInterval,
+  calculateMaximumObservationValuesWithinInterval,
   calculateSumOfObservationValuesWithinInterval,
   calculateAverageOfObservationValuesWithinInterval,
 } from "./src_modules/aggregate.mjs";
@@ -585,6 +587,176 @@ const drawLineChartDailyAverageTest = async function () {
   }
 };
 
+/**
+ * Test drawing of line chart using aggregation / minimum result - daily
+ */
+const drawLineChartDailyMinTest = async function () {
+  try {
+    const sensorsOfInterestNestedArr = [
+      ["125", "vl", "60min"],
+      ["225", "vl", "60min"],
+    ];
+
+    const observationsPlusMetadata =
+      await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
+        BASE_URL,
+        QUERY_PARAMS_COMBINED,
+        sensorsOfInterestNestedArr
+      );
+
+    // Extract the observations and metadata for each sensor
+    // Array elements in same order as input array
+    const [obsNestedArr, metadataNestedArr] = observationsPlusMetadata;
+
+    // User-specified start date and end date
+    const startDate = "2020-02-01";
+    const endDate = "2020-05-31";
+
+    // Extract observations within the user-specified start and end date
+    const observationsNestedArr = obsNestedArr.map((obsArr) =>
+      extractObservationsWithinDatesInterval(
+        obsArr,
+        "60min",
+        startDate,
+        endDate
+      )
+    );
+
+    // Unique calendar dates
+    const uniqueCalendarDatesNestedArr = observationsNestedArr.map(
+      (observationsArr) =>
+        extractUniqueCalendarDatesFromTimestamp(observationsArr)
+    );
+
+    // Calculate average of values of observations - daily
+    const observationsMinimumDailyNestedArr =
+      calculateMinimumObservationValuesWithinInterval(
+        observationsNestedArr,
+        "60min",
+        uniqueCalendarDatesNestedArr,
+        "daily"
+      );
+
+    // Format the observations - daily
+    const formattedObservationsMinimumDailyNestedArr =
+      observationsMinimumDailyNestedArr.map((obsMinDailyArr, i) =>
+        formatAggregationResultForColumnChart(
+          uniqueCalendarDatesNestedArr[i],
+          obsMinDailyArr
+        )
+      );
+
+    // Format the metadata
+    const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) =>
+      formatDatastreamMetadataForChart(metadataObj)
+    );
+
+    // Extract the formatted metadata properties
+    const extractedFormattedDatastreamProperties =
+      extractPropertiesFromFormattedDatastreamMetadata(
+        formattedMetadataNestedArr,
+        true,
+        "daily",
+        "minimum"
+      );
+
+    drawLineChartHighcharts(
+      formattedObservationsMinimumDailyNestedArr,
+      extractedFormattedDatastreamProperties
+    );
+  } catch (err) {
+    console.error(err);
+  }
+};
+
+/**
+ * Test drawing of line chart using aggregation / maximum result - daily
+ */
+const drawLineChartMonthlyMaxTest = async function () {
+  try {
+    const sensorsOfInterestNestedArr = [
+      ["125", "vl", "60min"],
+      ["225", "vl", "60min"],
+    ];
+
+    const observationsPlusMetadata =
+      await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
+        BASE_URL,
+        QUERY_PARAMS_COMBINED,
+        sensorsOfInterestNestedArr
+      );
+
+    // Extract the observations and metadata for each sensor
+    // Array elements in same order as input array
+    const [obsNestedArr, metadataNestedArr] = observationsPlusMetadata;
+
+    // User-specified start date and end date
+    const startDate = "2020-02-01";
+    const endDate = "2020-12-31";
+
+    // Extract observations within the user-specified start and end date
+    const observationsNestedArr = obsNestedArr.map((obsArr) =>
+      extractObservationsWithinDatesInterval(
+        obsArr,
+        "60min",
+        startDate,
+        endDate
+      )
+    );
+
+    // Unique calendar dates
+    const uniqueCalendarDatesNestedArr = observationsNestedArr.map(
+      (observationsArr) =>
+        extractUniqueCalendarDatesFromTimestamp(observationsArr)
+    );
+
+    // Unique calendar months
+    const uniqueCalendarMonthsNestedArr = uniqueCalendarDatesNestedArr.map(
+      (uniqueCalendarDatesArr) =>
+        extractUniqueCalendarMonthsFromCalendarDates(uniqueCalendarDatesArr)
+    );
+
+    // Calculate maximum values of observations - monthly
+    const observationsMaxMonthlyNestedArr =
+      calculateMaximumObservationValuesWithinInterval(
+        observationsNestedArr,
+        "60min",
+        uniqueCalendarMonthsNestedArr,
+        "monthly"
+      );
+
+    // Format the observations - monthly
+    const formattedObservationsMaxMonthlyNestedArr =
+      observationsMaxMonthlyNestedArr.map((obsMaxMonthlyArr, i) =>
+        formatAggregationResultForColumnChart(
+          uniqueCalendarMonthsNestedArr[i],
+          obsMaxMonthlyArr
+        )
+      );
+
+    // Format the metadata
+    const formattedMetadataNestedArr = metadataNestedArr.map((metadataObj) =>
+      formatDatastreamMetadataForChart(metadataObj)
+    );
+
+    // Extract the formatted metadata properties
+    const extractedFormattedDatastreamProperties =
+      extractPropertiesFromFormattedDatastreamMetadata(
+        formattedMetadataNestedArr,
+        true,
+        "monthly",
+        "maximum"
+      );
+
+    drawLineChartHighcharts(
+      formattedObservationsMaxMonthlyNestedArr,
+      extractedFormattedDatastreamProperties
+    );
+  } catch (err) {
+    console.error(err);
+  }
+};
+
 // drawScatterPlotHCTest2();
 // drawHeatmapHCUsingTempDifference();
 // testLineChartMultipleSeries();
@@ -593,3 +765,5 @@ const drawLineChartDailyAverageTest = async function () {
 // drawColumnChartNonAggregationTest();
 // drawLineChartMonthlyAverageTest();
 // drawLineChartDailyAverageTest();
+// drawLineChartDailyMinTest();
+// drawLineChartMonthlyMaxTest();
-- 
GitLab