From 058cf7604e48b293125c5066533ec896f43c1b58 Mon Sep 17 00:00:00 2001 From: Tor-Einar Skog <tor-einar.skog@nibio.no> Date: Wed, 30 Oct 2024 10:31:44 +0100 Subject: [PATCH] feat: Turned cydiapomonella.js into a module --- cydiapomonella/static/js/cydiapomonella.js | 420 +++++++++--------- .../templates/cydiapomonella/index.html | 17 +- 2 files changed, 223 insertions(+), 214 deletions(-) diff --git a/cydiapomonella/static/js/cydiapomonella.js b/cydiapomonella/static/js/cydiapomonella.js index 8d34962f..86440fcb 100644 --- a/cydiapomonella/static/js/cydiapomonella.js +++ b/cydiapomonella/static/js/cydiapomonella.js @@ -1,247 +1,251 @@ -const weatherStationDataURL = "https://lmt.nibio.no/services/rest/vips/getdata/forecastfallback"; -const gridDataURL = "https://weather.vips.nibio.no/rest/grid/openmeteo/"; // TODO: Revider APIet -const TIMEZONE = "Europe/Oslo"; -const DATE_FORMAT = "YYYY-MM-DD"; -const HEATSUM_BASE_TEMP = 10.0; - -const appleWeatherStations = [ - { "id": 11, "name": "Apelsvoll" }, - { "id": 12, "name": "Balestrand" }, - { "id": 13, "name": "Bø" }, - { "id": 86, "name": "Darbu" }, - { "id": 19, "name": "Gjerpen" }, - { "id": 21, "name": "Gvarv" }, - { "id": 22, "name": "Hjelmeland" }, - { "id": 25, "name": "Hønefoss" }, - { "id": 27, "name": "Kise" }, - { "id": 29, "name": "Landvik" }, - { "id": 30, "name": "Lier" }, - { "id": 65, "name": "Ljøsne" }, - { "id": 32, "name": "Lyngdal" }, - { "id": 35, "name": "Njøs" }, - { "id": 41, "name": "Rygge" }, - { "id": 42, "name": "Sande" }, - { "id": 131, "name": "Sandefjord" }, - { "id": 64, "name": "Slinde" }, - { "id": 47, "name": "Svelvik" }, - { "id": 91, "name": "Søve" }, - { "id": 61, "name": "Åsbakken" } -]; - -function renderWeatherstations() { - let wsSelect = document.getElementById("weatherStationId"); - appleWeatherStations.forEach((ws) => { - wsSelect.options[wsSelect.options.length] = new Option(ws["name"], ws["id"]); - }); -} +class CydiaPomonella +{ -async function runModel() { - let wsSelect = document.getElementById("weatherStationId"); - let selectedWeatherStationId = wsSelect.options[wsSelect.selectedIndex].value; - if(selectedWeatherStationId == "-1") - { - return; + weatherStationDataURL = "https://lmt.nibio.no/services/rest/vips/getdata/forecastfallback"; + gridDataURL = "https://weather.vips.nibio.no/rest/grid/openmeteo/"; // TODO: Revider APIet + TIMEZONE = "Europe/Oslo"; + DATE_FORMAT = "YYYY-MM-DD"; + HEATSUM_BASE_TEMP = 10.0; + + appleWeatherStations = [ + { "id": 11, "name": "Apelsvoll" }, + { "id": 12, "name": "Balestrand" }, + { "id": 13, "name": "Bø" }, + { "id": 86, "name": "Darbu" }, + { "id": 19, "name": "Gjerpen" }, + { "id": 21, "name": "Gvarv" }, + { "id": 22, "name": "Hjelmeland" }, + { "id": 25, "name": "Hønefoss" }, + { "id": 27, "name": "Kise" }, + { "id": 29, "name": "Landvik" }, + { "id": 30, "name": "Lier" }, + { "id": 65, "name": "Ljøsne" }, + { "id": 32, "name": "Lyngdal" }, + { "id": 35, "name": "Njøs" }, + { "id": 41, "name": "Rygge" }, + { "id": 42, "name": "Sande" }, + { "id": 131, "name": "Sandefjord" }, + { "id": 64, "name": "Slinde" }, + { "id": 47, "name": "Svelvik" }, + { "id": 91, "name": "Søve" }, + { "id": 61, "name": "Åsbakken" } + ]; + + renderWeatherstations = function () { + let wsSelect = document.getElementById("weatherStationId"); + this.appleWeatherStations.forEach((ws) => { + wsSelect.options[wsSelect.options.length] = new Option(ws["name"], ws["id"]); + }); } - let startDateHourly = getStartDateHourly(); - let endDateHourly = getEndDateHourly(); - try - { - // Get the hourly data for the past X days - const hourlyDataResponse = await fetch( - weatherStationDataURL + "?weatherStationId=" + selectedWeatherStationId - + "&elementMeasurementTypes[]=RR&elementMeasurementTypes[]=TM" - + "&timeZone=" + TIMEZONE - + "&startDate=" + startDateHourly.format(DATE_FORMAT) + "&startTime=0" - + "&endDate=" + endDateHourly.format(DATE_FORMAT) + "&endTime=12" - + "&logIntervalId=1" - ); - if(! hourlyDataResponse.ok){ - throw new Error("Response status: " + hourlyDataResponse.status); + + async runModel() { + let wsSelect = document.getElementById("weatherStationId"); + let selectedWeatherStationId = wsSelect.options[wsSelect.selectedIndex].value; + if(selectedWeatherStationId == "-1") + { + return; } + let startDateHourly = this.getStartDateHourly(); + let endDateHourly = this.getEndDateHourly(); + try + { + // Get the hourly data for the past X days + const hourlyDataResponse = await fetch( + this.weatherStationDataURL + "?weatherStationId=" + selectedWeatherStationId + + "&elementMeasurementTypes[]=RR&elementMeasurementTypes[]=TM" + + "&timeZone=" + this.TIMEZONE + + "&startDate=" + startDateHourly.format(this.DATE_FORMAT) + "&startTime=0" + + "&endDate=" + endDateHourly.format(this.DATE_FORMAT) + "&endTime=12" + + "&logIntervalId=1" + ); + if(! hourlyDataResponse.ok){ + throw new Error("Response status: " + hourlyDataResponse.status); + } - const hourlyData = await hourlyDataResponse.json(); + const hourlyData = await hourlyDataResponse.json(); - // Get the daily RR data for the past X days - const dailyData = getDailyRRData(hourlyData); + // Get the daily RR data for the past X days + const dailyData = this.getDailyRRData(hourlyData); - renderTable(hourlyData, dailyData); + this.renderTable(hourlyData, dailyData); - runHeatSumCalculations(); + this.runHeatSumCalculations(); + + } + catch(error) + { + console.error(error.message); + } + } + + async runHeatSumCalculations() + { + document.getElementById("heatSumContainer").style.display="block"; + let wsSelect = document.getElementById("weatherStationId"); + let selectedWeatherStationId = wsSelect.options[wsSelect.selectedIndex].value; + if(selectedWeatherStationId == "-1") + { + return; + } + // Get daily temperature data for the heat sum + const heatSumStartDate = this.setToTZMidnight(moment(document.getElementById("heatSumStartDate").value)); + const heatSumEndDate = this.setToTZMidnight(moment().tz(this.TIMEZONE).subtract(1,"days")); + const heatSumDataResponse = await fetch( + this.weatherStationDataURL + "?weatherStationId=" + selectedWeatherStationId + + "&elementMeasurementTypes[]=TM" + + "&timeZone=" + this.TIMEZONE + + "&startDate=" + heatSumStartDate.format(this.DATE_FORMAT) + "&startTime=0" + + "&endDate=" + heatSumEndDate.format(this.DATE_FORMAT) + "&endTime=23" + + "&logIntervalId=2" + ); + + if(! heatSumDataResponse.ok){ + throw new Error("Response status: " + heatSumDataResponse.status); + } - //calculateHeatSum() + const heatSumData = await heatSumDataResponse.json(); + + this.renderHeatSumResult(selectedWeatherStationId, this.calculateHeatSum(heatSumData), heatSumData.length, heatSumStartDate, heatSumEndDate); } - catch(error) + + calculateHeatSum(heatSumData) { - console.error(error.message); + let heatSum = 0.0; + heatSumData.forEach((obs) =>{ + heatSum += Math.max(0.0, (obs.value - this.HEATSUM_BASE_TEMP)); + }); + return heatSum; } -} -async function runHeatSumCalculations() -{ - document.getElementById("heatSumContainer").style.display="block"; - let wsSelect = document.getElementById("weatherStationId"); - let selectedWeatherStationId = wsSelect.options[wsSelect.selectedIndex].value; - if(selectedWeatherStationId == "-1") + renderHeatSumResult(selectedWeatherStationId, heatSum, numberOfDays, heatSumStartDate, heatSumEndDate) { - return; - } - // Get daily temperature data for the heat sum - const heatSumStartDate = setToTZMidnight(moment(document.getElementById("heatSumStartDate").value)); - const heatSumEndDate = setToTZMidnight(moment().tz(TIMEZONE).subtract(1,"days")); - - const heatSumDataResponse = await fetch( - weatherStationDataURL + "?weatherStationId=" + selectedWeatherStationId - + "&elementMeasurementTypes[]=TM" - + "&timeZone=" + TIMEZONE - + "&startDate=" + heatSumStartDate.format(DATE_FORMAT) + "&startTime=0" - + "&endDate=" + heatSumEndDate.format(DATE_FORMAT) + "&endTime=23" - + "&logIntervalId=2" - ); - - if(! heatSumDataResponse.ok){ - throw new Error("Response status: " + heatSumDataResponse.status); + document.getElementById("heatSumBaseTemp").innerHTML=this.HEATSUM_BASE_TEMP; + this.appleWeatherStations.forEach((ws)=>{ + if(ws.id == parseInt(selectedWeatherStationId)) + { + document.getElementById("selectedWeatherStationName").innerHTML = ws.name; + } + }); + document.getElementById("heatSumStartDateDisplay").innerHTML = heatSumStartDate.format(this.DATE_FORMAT); + document.getElementById("heatSumEndDateDisplay").innerHTML = heatSumEndDate.format(this.DATE_FORMAT); + document.getElementById("numberOfDays").innerHTML = numberOfDays; + document.getElementById("heatSum").innerHTML = heatSum.toFixed(1); } - const heatSumData = await heatSumDataResponse.json(); - - renderHeatSumResult(selectedWeatherStationId, calculateHeatSum(heatSumData), heatSumData.length, heatSumStartDate, heatSumEndDate); -} - -function calculateHeatSum(heatSumData) -{ - let heatSum = 0.0; - heatSumData.forEach((obs) =>{ - heatSum += Math.max(0.0, (obs.value - HEATSUM_BASE_TEMP)); - }); - return heatSum; -} - -function renderHeatSumResult(selectedWeatherStationId, heatSum, numberOfDays, heatSumStartDate, heatSumEndDate) -{ - document.getElementById("heatSumBaseTemp").innerHTML=HEATSUM_BASE_TEMP; - appleWeatherStations.forEach((ws)=>{ - if(ws.id == parseInt(selectedWeatherStationId)) + getDailyRRData(hourlyData) + { + let RRBuckets = {}; + for(let i=0; i< hourlyData.length;i++) { - document.getElementById("selectedWeatherStationName").innerHTML = ws.name; + let obs = hourlyData[i]; + if(obs.elementMeasurementTypeId == "RR") + { + let date = this.setToTZMidnight(moment(obs.timeMeasured)); + if(RRBuckets[date.unix()] == null) + { + RRBuckets[date.unix()] = []; + } + RRBuckets[date.unix()].push(obs.value); + } } - }); - document.getElementById("heatSumStartDateDisplay").innerHTML = heatSumStartDate.format(DATE_FORMAT); - document.getElementById("heatSumEndDateDisplay").innerHTML = heatSumEndDate.format(DATE_FORMAT); - document.getElementById("numberOfDays").innerHTML = numberOfDays; - document.getElementById("heatSum").innerHTML = heatSum.toFixed(1); -} -function getDailyRRData(hourlyData) -{ - let RRBuckets = {}; - for(let i=0; i< hourlyData.length;i++) + let dailyRRData = []; + Object.keys(RRBuckets).forEach((key) => { + let dateBucket = RRBuckets[key]; + let RRSum = dateBucket.reduce((accumulator, currentValue) => accumulator + currentValue, 0); + let RR = { + timeMeasured: moment.unix(key).toISOString(), + elementMeasurementTypeId: "RR", + logIntervalId: 1, + value: RRSum + } + dailyRRData.push(RR); + }); + + return dailyRRData; + } + + renderTable(hourlyData, dailyData) { - let obs = hourlyData[i]; - if(obs.elementMeasurementTypeId == "RR") + document.getElementById("weatherData").style.display="block"; + let tableBody = document.getElementById("tableBody"); + tableBody.innerHTML = ""; + let currentDate = this.getStartDateHourly(); + let today = this.setToTZMidnight(moment()); + //console.info("today=" + today.format()); + while(currentDate < today) { - let date = setToTZMidnight(moment(obs.timeMeasured)); - if(RRBuckets[date.unix()] == null) + //console.info("currentDate=" + currentDate.format()); + let nightTemps = this.getNightTempsForDate(moment(currentDate).tz(this.TIMEZONE), hourlyData); + //console.info(nightTemps); + let rowHTML = "<tr><td>" + currentDate.format(this.DATE_FORMAT) + "</td>"; + + for(let i=0; i<nightTemps.length;i++) { - RRBuckets[date.unix()] = []; + rowHTML += "<td>" + nightTemps[i].toFixed(1) + "</td>"; } - RRBuckets[date.unix()].push(obs.value); + dailyData.forEach((obs) =>{ + if(moment(obs.timeMeasured).unix() == currentDate.unix()) + { + rowHTML += "<td>" + obs.value.toFixed(1) + "</td>"; + } + }); + rowHTML += "</tr>" + tableBody.innerHTML += rowHTML; + currentDate.add(1,"days"); } } - let dailyRRData = []; - Object.keys(RRBuckets).forEach((key) => { - let dateBucket = RRBuckets[key]; - let RRSum = dateBucket.reduce((accumulator, currentValue) => accumulator + currentValue, 0); - let RR = { - timeMeasured: moment.unix(key).toISOString(), - elementMeasurementTypeId: "RR", - logIntervalId: 1, - value: RRSum - } - dailyRRData.push(RR); - }); - - return dailyRRData; -} - -function renderTable(hourlyData, dailyData) -{ - document.getElementById("weatherData").style.display="block"; - let tableBody = document.getElementById("tableBody"); - tableBody.innerHTML = ""; - let currentDate = getStartDateHourly(); - let today = setToTZMidnight(moment()); - //console.info("today=" + today.format()); - while(currentDate < today) + getStartDateHourly = function() { - //console.info("currentDate=" + currentDate.format()); - let nightTemps = getNightTempsForDate(moment(currentDate).tz(TIMEZONE), hourlyData); - //console.info(nightTemps); - let rowHTML = "<tr><td>" + currentDate.format(DATE_FORMAT) + "</td>"; - - for(let i=0; i<nightTemps.length;i++) - { - rowHTML += "<td>" + nightTemps[i].toFixed(1) + "</td>"; - } - dailyData.forEach((obs) =>{ - if(moment(obs.timeMeasured).unix() == currentDate.unix()) + let daysBack = undefined; + document.getElementsByName("radioDays").forEach((radio)=>{ + if(radio.checked) { - rowHTML += "<td>" + obs.value.toFixed(1) + "</td>"; + daysBack = radio.value; } }); - rowHTML += "</tr>" - tableBody.innerHTML += rowHTML; - currentDate.add(1,"days"); + return this.setToTZMidnight(moment()).subtract(daysBack,"days"); } -} -function getStartDateHourly() -{ - let daysBack = undefined; - document.getElementsByName("radioDays").forEach((radio)=>{ - if(radio.checked) + getEndDateHourly() + { + return moment().tz(this.TIMEZONE).hour(12); + } + + /** + * + * @param date + * @param {Array} weatherData + * @return array[temp@19,temp@20,temp@21,temp@22, temp@23] + */ + getNightTempsForDate(date, weatherData) + { + // Create an array of the desired timestamps + let timestamps = []; + let nightTemps = []; + for(let i=19;i<=23;i++) { - daysBack = radio.value; + timestamps.push(date.hour(i).minute(0).second(0).millisecond(0).unix()); } - }); - return setToTZMidnight(moment()).subtract(daysBack,"days"); -} - -function getEndDateHourly() -{ - return moment().tz(TIMEZONE).hour(12); -} + weatherData.forEach((wd)=>{ + let timeMeasured = moment(wd.timeMeasured).unix(); + //console.info(timeMeasured); + if(wd.elementMeasurementTypeId == "TM" && timestamps.indexOf(timeMeasured) >=0) + { + nightTemps[timestamps.indexOf(timeMeasured)] = wd.value; + //console.info("Found " + wd.timeMeasured + "at index " + tsArray.indexOf(timeMeasured)); + } + }); + return nightTemps; + //console.info(tsArray); + } -/** - * - * @param date - * @param {Array} weatherData - * @return array[temp@19,temp@20,temp@21,temp@22, temp@23] - */ -function getNightTempsForDate(date, weatherData) -{ - // Create an array of the desired timestamps - let timestamps = []; - let nightTemps = []; - for(let i=19;i<=23;i++) + setToTZMidnight(theDate) { - timestamps.push(date.hour(i).minute(0).second(0).millisecond(0).unix()); + return theDate.tz(this.TIMEZONE).hour(0).minute(0).second(0).millisecond(0); } - weatherData.forEach((wd)=>{ - let timeMeasured = moment(wd.timeMeasured).unix(); - //console.info(timeMeasured); - if(wd.elementMeasurementTypeId == "TM" && timestamps.indexOf(timeMeasured) >=0) - { - nightTemps[timestamps.indexOf(timeMeasured)] = wd.value; - //console.info("Found " + wd.timeMeasured + "at index " + tsArray.indexOf(timeMeasured)); - } - }); - return nightTemps; - //console.info(tsArray); } -function setToTZMidnight(theDate) -{ - return theDate.tz(TIMEZONE).hour(0).minute(0).second(0).millisecond(0); -} \ No newline at end of file +export default CydiaPomonella; \ No newline at end of file diff --git a/cydiapomonella/templates/cydiapomonella/index.html b/cydiapomonella/templates/cydiapomonella/index.html index d0f9921e..ea7c11af 100644 --- a/cydiapomonella/templates/cydiapomonella/index.html +++ b/cydiapomonella/templates/cydiapomonella/index.html @@ -22,17 +22,21 @@ Created: 2024-10-28 {% block title%}{% trans "Cydia pomonella" %}{%endblock%} {% block extendJS %} -<script src="{% static "js/moment.min.js" %}"></script> -<script src="{% static "js/moment-timezone.js" %}"></script> -<script src="{% static "js/moment-timezone-with-data.min.js" %}"></script> -<script src="{% static "js/cydiapomonella.js" %}"></script> +<script type="text/javascript" src="{% static "js/moment.min.js" %}"></script> +<script type="text/javascript" src="{% static "js/moment-timezone.js" %}"></script> +<script type="text/javascript" src="{% static "js/moment-timezone-with-data.min.js" %}"></script> {% endblock %} {% block customJS %} -<script type="text/javascript"> +<script type="module"> + import CydiaPomonella from "../static/js/cydiapomonella.js"; + const cydiaPomonella = new CydiaPomonella(); window.onload = (event) => { - renderWeatherstations(); + cydiaPomonella.renderWeatherstations(); } + // We need to do it this way to keep the "this" reference of the class + window.runModel = function () {cydiaPomonella.runModel()}; + window.runHeatSumCalculations = function () {cydiaPomonella.runHeatSumCalculations();} </script> {% endblock %} @@ -53,6 +57,7 @@ Created: 2024-10-28 </div> <div class="row"> <div class="col-sm-4"> + <select name="weatherStationName" id="weatherStationId" class="form-control" onchange="runModel();"> <option value="-1">-- {% trans "Select weather station" %} --</option> </select> -- GitLab