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

First test version (with mock result data)

parent c69fcc1b
Branches
No related tags found
2 merge requests!13Saddlegallmidge form idec 372,!12feat: Add test page (spatial) with mapserver layer in openlayers map
...@@ -143,6 +143,7 @@ INSTALLED_APPS = ( ...@@ -143,6 +143,7 @@ INSTALLED_APPS = (
'observations', 'observations',
'information', 'information',
'cerealblotchmodels', 'cerealblotchmodels',
'ipmd',
'calculators', 'calculators',
'roughage', 'roughage',
'applefruitmoth', 'applefruitmoth',
......
...@@ -57,6 +57,7 @@ else: ...@@ -57,6 +57,7 @@ else:
re_path(r'^observations/', include('observations.urls', namespace = "observations")), re_path(r'^observations/', include('observations.urls', namespace = "observations")),
re_path(r'^information/', include('information.urls', namespace = "information")), re_path(r'^information/', include('information.urls', namespace = "information")),
re_path(r'^blotch/', include('cerealblotchmodels.urls', namespace = "cerealblotchmodels")), re_path(r'^blotch/', include('cerealblotchmodels.urls', namespace = "cerealblotchmodels")),
re_path(r'^ipmd/', include('ipmd.urls', namespace = "ipmd")),
re_path(r'^calculators/', include('calculators.urls', namespace = "calculators")), re_path(r'^calculators/', include('calculators.urls', namespace = "calculators")),
re_path(r'^roughage/', include('roughage.urls', namespace = "roughage")), re_path(r'^roughage/', include('roughage.urls', namespace = "roughage")),
re_path(r'^security/', include('security.urls', namespace = "security")), re_path(r'^security/', include('security.urls', namespace = "security")),
......
from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class IpmdConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'ipmd'
from django.db import models
# Create your models here.
/*
* Copyright (c) 2023 NIBIO <http://www.nibio.no/>.
*
* This file is part of VIPSWeb.
* VIPSLogic is free software: you can redistribute it and/or modify
* it under the terms of the NIBIO Open Source License as published by
* NIBIO, either version 1 of the License, or (at your option) any
* later version.
*
* VIPSWeb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* NIBIO Open Source License for more details.
*
* You should have received a copy of the NIBIO Open Source License
* along with VIPSWeb. If not, see <http://www.nibio.no/licenses/>.
*
*/
const ipmdDSSApiURL = "https://platform.ipmdecisions.net/api/dss/";
const ipmdWeatherApiURL = "https://platform.ipmdecisions.net/api/wx/";
async function getModelMetadata(dssId,modelId) {
const response = await fetch(ipmdDSSApiURL + "rest/dss/" + dssId);
const dss = await response.json();
for(let i=0; i< dss.models.length;i++)
{
let model = dss.models[i];
if(model.id == modelId)
{
return model;
}
}
return null;
}
async function getModelInputSchema(dssId,modelId) {
const response = await fetch(ipmdDSSApiURL + "rest/model/" + dssId + "/" + modelId + "/input_schema/ui_form");
return await response.json();
}
async function getWeatherDatasource(weatherDatasourceId)
{
const response = await fetch(ipmdWeatherApiURL + "rest/weatherdatasource/" + weatherDatasourceId);
return await response.json();
}
function getWeatherStationList(weatherDatasource){
geoJson = JSON.parse(weatherDatasource.spatial.geoJSON);
let stationList = [];
for(let i=0;i<geoJson.features.length;i++)
{
let feature = geoJson.features[i];
let station = {"id":feature.id, "name":feature.properties.name}
stationList.push(station);
}
return stationList;
}
async function getLocationWeatherData(endpoint, weatherStationId, parameters, interval, dateStart, dateEnd){
const response = await fetch(endpoint
+ "?timeStart=" + dateStart
+ "&timeEnd=" + dateEnd
+ "&interval=" + interval
+ "&weatherStationId=" + weatherStationId
+ "&parameters=" + parameters.join(",")
);
return await response.json();
}
async function runModel(endpoint, inputData)
{
const response = await fetch(endpoint, {
method: "POST",
mode: "cors",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(inputData)
});
return await response.json();
}
function getDateArray(timestart, interval, length)
{
let dateArray = [];
currentTime = moment(timestart);
for(let i=0;i< length; i++)
{
dateArray.push(currentTime.format("YYYY-MM-DD"));
currentTime.add(interval,'seconds');
}
return dateArray;
}
\ No newline at end of file
{% extends "base.html" %}
{% load static %}
{% comment %}
#
# Copyright (c) 2023 NIBIO <http://www.nibio.no/>.
#
# This file is part of VIPSWeb.
# VIPSWeb is free software: you can redistribute it and/or modify
# it under the terms of the NIBIO Open Source License as published by
# NIBIO, either version 1 of the License, or (at your option) any
# later version.
#
# VIPSWeb is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# NIBIO Open Source License for more details.
#
# You should have received a copy of the NIBIO Open Source License
# along with VIPSWeb. If not, see <http://www.nibio.no/licenses/>.
#
{% endcomment %}
{% load i18n %}
{% block title%}{% trans "IPM Decisions models" %}{%endblock%}
{% block content %}
<h1>{% trans "IPM Decisions models" %}</h1>
<ul>
<li><a href="/ipmd/saddlegallmidge">{% trans "Saddle gall midge" %}</a></li>
</ul>
{% endblock %}
\ No newline at end of file
{% extends "base_with_date_picker.html" %}
{% load static %}
{% comment %}
#
# Copyright (c) 2023 NIBIO <http://www.nibio.no/>.
#
# This file is part of VIPSWeb.
# VIPSWeb is free software: you can redistribute it and/or modify
# it under the terms of the NIBIO Open Source License as published by
# NIBIO, either version 1 of the License, or (at your option) any
# later version.
#
# VIPSWeb is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# NIBIO Open Source License for more details.
#
# You should have received a copy of the NIBIO Open Source License
# along with VIPSWeb. If not, see <http://www.nibio.no/licenses/>.
#
{% endcomment %}
{% load i18n %}
{% block title %}{% trans "Saddle gall midge" %}{% endblock %}
{% block content %}
<div class="singleBlockContainer">
<h1>{% trans "Saddle gall midge" %}</h1>
<div id="inputForm"></div>
<div id="weatherStations" style="display: none;">
<h2>Weather stations</h2>
<select class="form-control" name="weatherStationId" id="weatherStationId"></select>
</div>
<button class="btn btn-primary" type="button" onclick="submitData();">Submit</button>
<div>
<canvas id="myChart"></canvas>
</div>
<pre id="modelDescription"></pre>
</div>
{% endblock %}
{% block customJS %}
<script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor@latest/dist/jsoneditor.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script type="text/javascript" src="{% static "js/3rdparty/moment.min.js" %}"></script>
<script type="text/javascript" src="{% static "ipmd/js/ipmdlib.js" %}"></script>
<script type="text/javascript">
// Page globals
var modelMetaData = undefined;
var weatherDatasource = undefined;
var editor = undefined;
const selectList = document.getElementById("weatherStationId");
async function initPage() {
modelMetaData = await getModelMetadata("adas.dss","HAPDMA");
document.getElementById("modelDescription").innerHTML= await modelMetaData["description"];
inputFormSchema = await getModelInputSchema("adas.dss","HAPDMA")
console.info(inputFormSchema);
const element = document.getElementById('inputForm');
editor = new JSONEditor(element,
{
ajax: true,
schema: inputFormSchema,
theme: "bootstrap4"
});
let fullSchema = JSON.parse(modelMetaData["execution"]["input_schema"]);
if(fullSchema["properties"]["weatherData"] !== undefined)
{
console.info("Adding weather stations");
// Pull weather stations from web service, render list
weatherDatasource = await getWeatherDatasource("no.nibio.lmt");
stationList = await getWeatherStationList(weatherDatasource);
stationList.sort((a, b) => {
return (a.name < b.name) ? -1 : (a.name > b.name) ? 1 : 0;
});
for(let i=0;i<stationList.length; i++)
{
selectList.add(new Option(stationList[i].name, stationList[i].id))
}
document.getElementById("weatherStations").style.display = "block";
//console.info(weatherDatasource);
}
}
initPage();
async function submitData(){
console.info("submitData!");
let inputData = editor.getValue();
console.info(inputData);
// Add hidden parameters
let fullSchema = JSON.parse(modelMetaData["execution"]["input_schema"]);
const hiddenParameters = modelMetaData["execution"]["input_schema_categories"]["hidden"];
console.info(hiddenParameters);
for(let i=0;i<hiddenParameters.length;i++)
{
let hiddenParameter = hiddenParameters[i];
inputData[hiddenParameter] = fullSchema["properties"][hiddenParameter]["default"];
}
// Check for weatherData element. Assuming it's at the root node
if(fullSchema["properties"]["weatherData"] !== undefined)
{
console.info("Need to get weather data!");
let weatherStationId = selectList.options[selectList.selectedIndex].value;
let weatherData = await getLocationWeatherData(
weatherDatasource.endpoint,
weatherStationId,
(function (){
let parameterList = []
modelMetaData.input.weather_parameters.forEach(function(weatherParameter){
parameterList.push(weatherParameter.parameter_code)
})
return parameterList;
}()),
3600,
inputData.optionalData.startDate,
inputData.optionalData.endDate,
);
inputData["weatherData"] = weatherData;
}
// Ready to call server?
//console.info(JSON.stringify(inputData));
//let result = await runModel(modelMetaData.execution.endpoint, inputData);
let result = mockResult;
//console.info(result);
displayResult(result);
};
function displayResult(result) {
let chartData = [];
// Generate dates
let dates = getDateArray(result.timeStart, 86400, result.locationResult[0].length);
//console.info(dates);
for(let i=0; i< result.resultParameters.length;i++)
{
let dataset = {label:result.resultParameters[i], data: []}
for(let j=0;j<dates.length;j++)
{
dataset.data.push({x:dates[j],y:result.locationResult[0].data[j][i]});
}
chartData.push(dataset);
}
const ctx = document.getElementById('myChart');
new Chart(ctx, {
type: "line",
data: {
datasets: chartData
}
})
}
// Prototype result!!!
const mockResult={
"timeStart": "2023-06-30T22:00:00+00:00",
"timeEnd": "2023-08-10T22:00:00+00:00",
"interval": 86400,
"resultParameters": [
"Cumulative_DayDegrees",
"StartOfEmergenceThreshold",
"FirstThreshold",
"SecondThreshold",
"ThirdThreshold"
],
"locationResult": [
{
"longitude": 10.62687,
"latitude": 62.10944,
"altitude": 478.0,
"data": [
[
12.957083333333332,
500.0,
750.47,
1023.93,
1500.0
],
[
26.471249999999998,
500.0,
750.47,
1023.93,
1500.0
],
[
38.164583333333333,
500.0,
750.47,
1023.93,
1500.0
],
[
50.12833333333333,
500.0,
750.47,
1023.93,
1500.0
],
[
63.424166666666665,
500.0,
750.47,
1023.93,
1500.0
],
[
76.315416666666664,
500.0,
750.47,
1023.93,
1500.0
],
[
90.9525,
500.0,
750.47,
1023.93,
1500.0
],
[
106.4175,
500.0,
750.47,
1023.93,
1500.0
],
[
124.07208333333334,
500.0,
750.47,
1023.93,
1500.0
],
[
143.23041666666666,
500.0,
750.47,
1023.93,
1500.0
],
[
158.54916666666665,
500.0,
750.47,
1023.93,
1500.0
],
[
173.02458333333331,
500.0,
750.47,
1023.93,
1500.0
],
[
186.06041666666664,
500.0,
750.47,
1023.93,
1500.0
],
[
198.31124999999997,
500.0,
750.47,
1023.93,
1500.0
],
[
210.43499999999997,
500.0,
750.47,
1023.93,
1500.0
],
[
224.8820833333333,
500.0,
750.47,
1023.93,
1500.0
],
[
239.05541666666665,
500.0,
750.47,
1023.93,
1500.0
],
[
251.71874999999997,
500.0,
750.47,
1023.93,
1500.0
],
[
263.06991666666664,
500.0,
750.47,
1023.93,
1500.0
],
[
274.32391666666666,
500.0,
750.47,
1023.93,
1500.0
],
[
284.68975,
500.0,
750.47,
1023.93,
1500.0
],
[
296.90266666666668,
500.0,
750.47,
1023.93,
1500.0
],
[
309.94766666666669,
500.0,
750.47,
1023.93,
1500.0
],
[
322.764,
500.0,
750.47,
1023.93,
1500.0
],
[
334.34983333333332,
500.0,
750.47,
1023.93,
1500.0
],
[
346.98191666666668,
500.0,
750.47,
1023.93,
1500.0
],
[
359.79066666666665,
500.0,
750.47,
1023.93,
1500.0
],
[
372.52816666666666,
500.0,
750.47,
1023.93,
1500.0
],
[
386.75358333333332,
500.0,
750.47,
1023.93,
1500.0
],
[
402.21066666666667,
500.0,
750.47,
1023.93,
1500.0
],
[
416.82858333333331,
500.0,
750.47,
1023.93,
1500.0
],
[
428.63025,
500.0,
750.47,
1023.93,
1500.0
],
[
442.98983333333331,
500.0,
750.47,
1023.93,
1500.0
],
[
456.554,
500.0,
750.47,
1023.93,
1500.0
],
[
470.31691666666666,
500.0,
750.47,
1023.93,
1500.0
],
[
483.14233333333334,
500.0,
750.47,
1023.93,
1500.0
],
[
497.56275,
500.0,
750.47,
1023.93,
1500.0
],
[
508.33566666666667,
500.0,
750.47,
1023.93,
1500.0
],
[
521.79775,
500.0,
750.47,
1023.93,
1500.0
],
[
533.56191666666666,
500.0,
750.47,
1023.93,
1500.0
],
[
545.3566992753623,
500.0,
750.47,
1023.93,
1500.0
]
],
"warningStatus": [
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
4,
4,
4,
4
],
"width": 5,
"length": 41
}
],
"message": "Start of cumulative emergence: 07/08/2023 .",
"messageType": 0
};
</script>
{% endblock %}
\ No newline at end of file
from django.test import TestCase
# Create your tests here.
#
# Copyright (c) 2023 NIBIO <http://www.nibio.no/>.
#
# This file is part of VIPSWeb.
# VIPSWeb is free software: you can redistribute it and/or modify
# it under the terms of the NIBIO Open Source License as published by
# NIBIO, either version 1 of the License, or (at your option) any
# later version.
#
# VIPSWeb is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# NIBIO Open Source License for more details.
#
# You should have received a copy of the NIBIO Open Source License
# along with VIPSWeb. If not, see <http://www.nibio.no/licenses/>.
#
from django.urls import re_path
from ipmd import views
app_name = "ipmd"
urlpatterns = [
# ex: /forecasts/
re_path(r'^$', views.index, name='index'),
re_path(r'saddlegallmidge/', views.saddlegallmidgeform, name='saddlegallmidgeform')
]
\ No newline at end of file
from django.shortcuts import render
# Create your views here.
def index(request):
context = {}
return render(request, 'ipmd/index.html', context)
def saddlegallmidgeform(request):
context = {}
return render(request, 'ipmd/saddlegallmidgeform.html', context)
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment