Skip to content
Snippets Groups Projects
Commit 058cf760 authored by Tor-Einar Skog's avatar Tor-Einar Skog
Browse files

feat: Turned cydiapomonella.js into a module

parent eac3bcb4
No related branches found
No related tags found
No related merge requests found
const weatherStationDataURL = "https://lmt.nibio.no/services/rest/vips/getdata/forecastfallback"; class CydiaPomonella
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": "" },
{ "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"]);
});
}
async function runModel() { weatherStationDataURL = "https://lmt.nibio.no/services/rest/vips/getdata/forecastfallback";
let wsSelect = document.getElementById("weatherStationId"); gridDataURL = "https://weather.vips.nibio.no/rest/grid/openmeteo/"; // TODO: Revider APIet
let selectedWeatherStationId = wsSelect.options[wsSelect.selectedIndex].value; TIMEZONE = "Europe/Oslo";
if(selectedWeatherStationId == "-1") DATE_FORMAT = "YYYY-MM-DD";
{ HEATSUM_BASE_TEMP = 10.0;
return;
appleWeatherStations = [
{ "id": 11, "name": "Apelsvoll" },
{ "id": 12, "name": "Balestrand" },
{ "id": 13, "name": "" },
{ "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(); async runModel() {
try let wsSelect = document.getElementById("weatherStationId");
{ let selectedWeatherStationId = wsSelect.options[wsSelect.selectedIndex].value;
// Get the hourly data for the past X days if(selectedWeatherStationId == "-1")
const hourlyDataResponse = await fetch( {
weatherStationDataURL + "?weatherStationId=" + selectedWeatherStationId return;
+ "&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);
} }
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 // Get the daily RR data for the past X days
const dailyData = getDailyRRData(hourlyData); 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() renderHeatSumResult(selectedWeatherStationId, heatSum, numberOfDays, heatSumStartDate, heatSumEndDate)
{
document.getElementById("heatSumContainer").style.display="block";
let wsSelect = document.getElementById("weatherStationId");
let selectedWeatherStationId = wsSelect.options[wsSelect.selectedIndex].value;
if(selectedWeatherStationId == "-1")
{ {
return; document.getElementById("heatSumBaseTemp").innerHTML=this.HEATSUM_BASE_TEMP;
} this.appleWeatherStations.forEach((ws)=>{
// Get daily temperature data for the heat sum if(ws.id == parseInt(selectedWeatherStationId))
const heatSumStartDate = setToTZMidnight(moment(document.getElementById("heatSumStartDate").value)); {
const heatSumEndDate = setToTZMidnight(moment().tz(TIMEZONE).subtract(1,"days")); document.getElementById("selectedWeatherStationName").innerHTML = ws.name;
}
const heatSumDataResponse = await fetch( });
weatherStationDataURL + "?weatherStationId=" + selectedWeatherStationId document.getElementById("heatSumStartDateDisplay").innerHTML = heatSumStartDate.format(this.DATE_FORMAT);
+ "&elementMeasurementTypes[]=TM" document.getElementById("heatSumEndDateDisplay").innerHTML = heatSumEndDate.format(this.DATE_FORMAT);
+ "&timeZone=" + TIMEZONE document.getElementById("numberOfDays").innerHTML = numberOfDays;
+ "&startDate=" + heatSumStartDate.format(DATE_FORMAT) + "&startTime=0" document.getElementById("heatSum").innerHTML = heatSum.toFixed(1);
+ "&endDate=" + heatSumEndDate.format(DATE_FORMAT) + "&endTime=23"
+ "&logIntervalId=2"
);
if(! heatSumDataResponse.ok){
throw new Error("Response status: " + heatSumDataResponse.status);
} }
const heatSumData = await heatSumDataResponse.json(); getDailyRRData(hourlyData)
{
renderHeatSumResult(selectedWeatherStationId, calculateHeatSum(heatSumData), heatSumData.length, heatSumStartDate, heatSumEndDate); let RRBuckets = {};
} for(let i=0; i< hourlyData.length;i++)
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))
{ {
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 dailyRRData = [];
{ Object.keys(RRBuckets).forEach((key) => {
let RRBuckets = {}; let dateBucket = RRBuckets[key];
for(let i=0; i< hourlyData.length;i++) 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]; document.getElementById("weatherData").style.display="block";
if(obs.elementMeasurementTypeId == "RR") 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)); //console.info("currentDate=" + currentDate.format());
if(RRBuckets[date.unix()] == null) 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 = []; getStartDateHourly = function()
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)
{ {
//console.info("currentDate=" + currentDate.format()); let daysBack = undefined;
let nightTemps = getNightTempsForDate(moment(currentDate).tz(TIMEZONE), hourlyData); document.getElementsByName("radioDays").forEach((radio)=>{
//console.info(nightTemps); if(radio.checked)
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())
{ {
rowHTML += "<td>" + obs.value.toFixed(1) + "</td>"; daysBack = radio.value;
} }
}); });
rowHTML += "</tr>" return this.setToTZMidnight(moment()).subtract(daysBack,"days");
tableBody.innerHTML += rowHTML;
currentDate.add(1,"days");
} }
}
function getStartDateHourly() getEndDateHourly()
{ {
let daysBack = undefined; return moment().tz(this.TIMEZONE).hour(12);
document.getElementsByName("radioDays").forEach((radio)=>{ }
if(radio.checked)
/**
*
* @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());
} }
}); weatherData.forEach((wd)=>{
return setToTZMidnight(moment()).subtract(daysBack,"days"); let timeMeasured = moment(wd.timeMeasured).unix();
} //console.info(timeMeasured);
if(wd.elementMeasurementTypeId == "TM" && timestamps.indexOf(timeMeasured) >=0)
function getEndDateHourly() {
{ nightTemps[timestamps.indexOf(timeMeasured)] = wd.value;
return moment().tz(TIMEZONE).hour(12); //console.info("Found " + wd.timeMeasured + "at index " + tsArray.indexOf(timeMeasured));
} }
});
return nightTemps;
//console.info(tsArray);
}
/** setToTZMidnight(theDate)
*
* @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++)
{ {
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) export default CydiaPomonella;
{ \ No newline at end of file
return theDate.tz(TIMEZONE).hour(0).minute(0).second(0).millisecond(0);
}
\ No newline at end of file
...@@ -22,17 +22,21 @@ Created: 2024-10-28 ...@@ -22,17 +22,21 @@ Created: 2024-10-28
{% block title%}{% trans "Cydia pomonella" %}{%endblock%} {% block title%}{% trans "Cydia pomonella" %}{%endblock%}
{% block extendJS %} {% block extendJS %}
<script src="{% static "js/moment.min.js" %}"></script> <script type="text/javascript" src="{% static "js/moment.min.js" %}"></script>
<script src="{% static "js/moment-timezone.js" %}"></script> <script type="text/javascript" src="{% static "js/moment-timezone.js" %}"></script>
<script src="{% static "js/moment-timezone-with-data.min.js" %}"></script> <script type="text/javascript" src="{% static "js/moment-timezone-with-data.min.js" %}"></script>
<script src="{% static "js/cydiapomonella.js" %}"></script>
{% endblock %} {% endblock %}
{% block customJS %} {% block customJS %}
<script type="text/javascript"> <script type="module">
import CydiaPomonella from "../static/js/cydiapomonella.js";
const cydiaPomonella = new CydiaPomonella();
window.onload = (event) => { 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> </script>
{% endblock %} {% endblock %}
...@@ -53,6 +57,7 @@ Created: 2024-10-28 ...@@ -53,6 +57,7 @@ Created: 2024-10-28
</div> </div>
<div class="row"> <div class="row">
<div class="col-sm-4"> <div class="col-sm-4">
<select name="weatherStationName" id="weatherStationId" class="form-control" onchange="runModel();"> <select name="weatherStationName" id="weatherStationId" class="form-control" onchange="runModel();">
<option value="-1">-- {% trans "Select weather station" %} --</option> <option value="-1">-- {% trans "Select weather station" %} --</option>
</select> </select>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment