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

Add option to use gridded weather data in forecast configuration form [VIPSUTV-733]

parent 21b2ddd1
No related branches found
No related tags found
1 merge request!191Add map module and Open-Meteo support
Showing
with 104 additions and 8 deletions
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<groupId>no.nibio.vips.</groupId> <groupId>no.nibio.vips.</groupId>
<artifactId>VIPSLogic</artifactId> <artifactId>VIPSLogic</artifactId>
<packaging>war</packaging> <packaging>war</packaging>
<version>2024.2</version> <version>2024.3</version>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
......
...@@ -35,6 +35,9 @@ import javax.servlet.ServletException; ...@@ -35,6 +35,9 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.slf4j.LoggerFactory;
import no.nibio.vips.logic.controller.session.ForecastBean; import no.nibio.vips.logic.controller.session.ForecastBean;
import no.nibio.vips.logic.controller.session.OrganismBean; import no.nibio.vips.logic.controller.session.OrganismBean;
import no.nibio.vips.logic.controller.session.UserBean; import no.nibio.vips.logic.controller.session.UserBean;
...@@ -59,6 +62,8 @@ import no.nibio.web.forms.FormValidator; ...@@ -59,6 +62,8 @@ import no.nibio.web.forms.FormValidator;
* @author Tor-Einar Skog <tor-einar.skog@nibio.no> * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
*/ */
public class ForecastConfigurationController extends HttpServlet { public class ForecastConfigurationController extends HttpServlet {
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ForecastConfigurationController.class);
@PersistenceContext(unitName="VIPSLogic-PU") @PersistenceContext(unitName="VIPSLogic-PU")
EntityManager em; EntityManager em;
...@@ -314,10 +319,13 @@ public class ForecastConfigurationController extends HttpServlet { ...@@ -314,10 +319,13 @@ public class ForecastConfigurationController extends HttpServlet {
"forecastConfigurationMultipleNewForm" "forecastConfigurationMultipleNewForm"
:"forecastConfigurationForm"; :"forecastConfigurationForm";
FormValidation formValidation = FormValidator.validateForm(formId,request,getServletContext()); FormValidation formValidation = FormValidator.validateForm(formId,request,getServletContext());
LOGGER.debug("formValidation=" + formValidation.isValid());
// Also validation the model specific fields // Also validation the model specific fields
String modelId = formValidation.getFormField("modelId").getWebValue(); String modelId = formValidation.getFormField("modelId").getWebValue();
FormValidation modelFieldFormValidation = FormValidator.validateForm("models/" + modelId, request, getServletContext()); FormValidation modelFieldFormValidation = FormValidator.validateForm("models/" + modelId, request, getServletContext());
// Additional input check: If the Grid data checkbox is not checked, a
if(formValidation.isValid() && modelFieldFormValidation.isValid()) if(formValidation.isValid() && modelFieldFormValidation.isValid())
{ {
if(formId.equals("forecastConfigurationForm")) if(formId.equals("forecastConfigurationForm"))
......
...@@ -600,6 +600,7 @@ public class ForecastBean { ...@@ -600,6 +600,7 @@ public class ForecastBean {
forecastConfiguration.setCropOrganismId(em.find(Organism.class, formFields.get("cropOrganismId").getValueAsInteger())); forecastConfiguration.setCropOrganismId(em.find(Organism.class, formFields.get("cropOrganismId").getValueAsInteger()));
forecastConfiguration.setPestOrganismId(em.find(Organism.class, formFields.get("pestOrganismId").getValueAsInteger())); forecastConfiguration.setPestOrganismId(em.find(Organism.class, formFields.get("pestOrganismId").getValueAsInteger()));
forecastConfiguration.setIsPrivate(formFields.get("isPrivate").getWebValue() != null); forecastConfiguration.setIsPrivate(formFields.get("isPrivate").getWebValue() != null);
forecastConfiguration.setUseGridWeatherData(formFields.get("useGridWeatherData").getWebValue() != null);
PointOfInterest locationPoi = em.find(PointOfInterest.class, formFields.get("locationPointOfInterestId").getValueAsInteger()); PointOfInterest locationPoi = em.find(PointOfInterest.class, formFields.get("locationPointOfInterestId").getValueAsInteger());
forecastConfiguration.setLocationPointOfInterestId(locationPoi); forecastConfiguration.setLocationPointOfInterestId(locationPoi);
PointOfInterest weatherStationPoi = em.find(PointOfInterestWeatherStation.class, formFields.get("weatherStationPointOfInterestId").getValueAsInteger()); PointOfInterest weatherStationPoi = em.find(PointOfInterestWeatherStation.class, formFields.get("weatherStationPointOfInterestId").getValueAsInteger());
......
...@@ -34,7 +34,7 @@ public class SessionControllerGetter { ...@@ -34,7 +34,7 @@ public class SessionControllerGetter {
// This obviously has to be changed when changing the application name in Maven // This obviously has to be changed when changing the application name in Maven
// TODO: Refactor out to System properties (e.g. in standalone.xml in JBoss/WildFly) // TODO: Refactor out to System properties (e.g. in standalone.xml in JBoss/WildFly)
public static final String JNDI_PATH = "java:global/VIPSLogic-2024.2/"; public static final String JNDI_PATH = "java:global/VIPSLogic-2024.3/";
public static SchedulingBean getSchedulingBean() public static SchedulingBean getSchedulingBean()
{ {
......
...@@ -50,7 +50,7 @@ import org.hibernate.annotations.TypeDef; ...@@ -50,7 +50,7 @@ import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs; import org.hibernate.annotations.TypeDefs;
/** /**
* @copyright 2014-2016 <a href="http://www.nibio.no/">NIBIO</a> * @copyright 2014-2024 <a href="http://www.nibio.no/">NIBIO</a>
* @author Tor-Einar Skog <tor-einar.skog@nibio.no> * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
*/ */
@Entity @Entity
...@@ -123,7 +123,17 @@ public class ForecastConfiguration implements Serializable, Comparable { ...@@ -123,7 +123,17 @@ public class ForecastConfiguration implements Serializable, Comparable {
private Organism pestOrganismId; private Organism pestOrganismId;
@Column(name = "is_private") @Column(name = "is_private")
private Boolean isPrivate; private Boolean isPrivate;
@Column(name = "use_grid_weather_data")
private Boolean useGridWeatherData;
public Boolean getUseGridWeatherData() {
return useGridWeatherData;
}
public void setUseGridWeatherData(Boolean useGridWeatherData) {
this.useGridWeatherData = useGridWeatherData;
}
@Type(type = "IntegerArray") @Type(type = "IntegerArray")
@Column(name = "grid_weather_station_point_of_interest_ids") @Column(name = "grid_weather_station_point_of_interest_ids")
private Integer[] gridWeatherStationPointOfInterestIds; private Integer[] gridWeatherStationPointOfInterestIds;
......
...@@ -44,6 +44,7 @@ import no.nibio.vips.logic.controller.session.SessionControllerGetter; ...@@ -44,6 +44,7 @@ import no.nibio.vips.logic.controller.session.SessionControllerGetter;
import no.nibio.vips.logic.controller.session.UserBean; import no.nibio.vips.logic.controller.session.UserBean;
import no.nibio.vips.logic.i18n.SessionLocaleUtil; import no.nibio.vips.logic.i18n.SessionLocaleUtil;
import org.apache.commons.validator.routines.EmailValidator; import org.apache.commons.validator.routines.EmailValidator;
import org.slf4j.LoggerFactory;
/** /**
* Uses form configuration set in JSON files in [WARFILE]/formdefinitions/, or * Uses form configuration set in JSON files in [WARFILE]/formdefinitions/, or
...@@ -57,6 +58,8 @@ import org.apache.commons.validator.routines.EmailValidator; ...@@ -57,6 +58,8 @@ import org.apache.commons.validator.routines.EmailValidator;
* @author Tor-Einar Skog <tor-einar.skog@nibio.no> * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
*/ */
public class FormValidator { public class FormValidator {
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(FormValidator.class);
@EJB @EJB
UserBean userBean; UserBean userBean;
...@@ -340,6 +343,7 @@ public class FormValidator { ...@@ -340,6 +343,7 @@ public class FormValidator {
{ {
field.setValid(false); field.setValid(false);
field.setValidationMessage(resourceBundle.getString("fieldIsRequired")); field.setValidationMessage(resourceBundle.getString("fieldIsRequired"));
LOGGER.debug(field.getName() + " with a value of " + field.getWebValue() + " is considered to be NULL");
} }
} }
......
-- Adding this property when adding support for gridded weather datasources in VIPS
ALTER TABLE forecast_configuration
ADD COLUMN use_grid_weather_data BOOLEAN DEFAULT FALSE;
\ No newline at end of file
...@@ -1052,3 +1052,5 @@ privacyStatement=Privacy statement ...@@ -1052,3 +1052,5 @@ privacyStatement=Privacy statement
privacyStatementFileName=Privacy_statement_NIBIO-VIPS.pdf privacyStatementFileName=Privacy_statement_NIBIO-VIPS.pdf
thresholdDSVMax=DSV threshold for high infection risk thresholdDSVMax=DSV threshold for high infection risk
thresholdDSVTempMin=Minimum temperature for DSV calculation thresholdDSVTempMin=Minimum temperature for DSV calculation
useGridWeatherData=Use grid weather data
doNotUse=Do not use
...@@ -1046,3 +1046,5 @@ privacyStatement=Privacy statement ...@@ -1046,3 +1046,5 @@ privacyStatement=Privacy statement
privacyStatementFileName=Privacy_statement_NIBIO-VIPS.pdf privacyStatementFileName=Privacy_statement_NIBIO-VIPS.pdf
thresholdDSVMax=DSV threshold for high infection risk thresholdDSVMax=DSV threshold for high infection risk
thresholdDSVTempMin=Minimum temperature for DSV calculation thresholdDSVTempMin=Minimum temperature for DSV calculation
useGridWeatherData=Use grid weather data
doNotUse=Do not use
...@@ -1044,3 +1044,5 @@ privacyStatement=Privacy statement ...@@ -1044,3 +1044,5 @@ privacyStatement=Privacy statement
privacyStatementFileName=Privacy_statement_NIBIO-VIPS.pdf privacyStatementFileName=Privacy_statement_NIBIO-VIPS.pdf
thresholdDSVMax=DSV threshold for high infection risk thresholdDSVMax=DSV threshold for high infection risk
thresholdDSVTempMin=Minimum temperature for DSV calculation thresholdDSVTempMin=Minimum temperature for DSV calculation
useGridWeatherData=Use grid weather data
doNotUse=Do not use
...@@ -1052,3 +1052,5 @@ privacyStatement=Personvernerkl\u00e6ring ...@@ -1052,3 +1052,5 @@ privacyStatement=Personvernerkl\u00e6ring
privacyStatementFileName=Personvernerklaering_NIBIO-VIPS.pdf privacyStatementFileName=Personvernerklaering_NIBIO-VIPS.pdf
thresholdDSVMax=DSV-terskel for h\u00f8y infeksjonsrisiko thresholdDSVMax=DSV-terskel for h\u00f8y infeksjonsrisiko
thresholdDSVTempMin=Minimumstemperatur for beregning av DSV thresholdDSVTempMin=Minimumstemperatur for beregning av DSV
useGridWeatherData=Bruk v\u00e6rdata fra rutenett
doNotUse=Ikke bruk
...@@ -1046,3 +1046,5 @@ privacyStatement=Privacy statement ...@@ -1046,3 +1046,5 @@ privacyStatement=Privacy statement
privacyStatementFileName=Privacy_statement_NIBIO-VIPS.pdf privacyStatementFileName=Privacy_statement_NIBIO-VIPS.pdf
thresholdDSVMax=DSV threshold for high infection risk thresholdDSVMax=DSV threshold for high infection risk
thresholdDSVTempMin=Minimum temperature for DSV calculation thresholdDSVTempMin=Minimum temperature for DSV calculation
useGridWeatherData=Use grid weather data
doNotUse=Do not use
...@@ -1040,3 +1040,5 @@ privacyStatement=Privacy statement ...@@ -1040,3 +1040,5 @@ privacyStatement=Privacy statement
privacyStatementFileName=Privacy_statement_NIBIO-VIPS.pdf privacyStatementFileName=Privacy_statement_NIBIO-VIPS.pdf
thresholdDSVMax=DSV threshold for high infection risk thresholdDSVMax=DSV threshold for high infection risk
thresholdDSVTempMin=Minimum temperature for DSV calculation thresholdDSVTempMin=Minimum temperature for DSV calculation
useGridWeatherData=Use grid weather data
doNotUse=Do not use
...@@ -57,6 +57,11 @@ ...@@ -57,6 +57,11 @@
"dataType" : "STRING", "dataType" : "STRING",
"required" : false "required" : false
}, },
{
"name" : "useGridWeatherData",
"dataType" : "STRING",
"required" : false
},
{ {
"name" : "locationPointOfInterestId", "name" : "locationPointOfInterestId",
"dataType" : "INTEGER", "dataType" : "INTEGER",
......
...@@ -268,7 +268,8 @@ function validateFieldActual(fieldEl, theForm, formDefinitionKey) ...@@ -268,7 +268,8 @@ function validateFieldActual(fieldEl, theForm, formDefinitionKey)
// Single select field - check for nullValue // Single select field - check for nullValue
if(fieldDefinition.fieldType === fieldTypes.TYPE_SELECT_SINGLE) if(fieldDefinition.fieldType === fieldTypes.TYPE_SELECT_SINGLE)
{ {
webValue = fieldEl.options[fieldEl.selectedIndex].value; // Fallback if this is not a select list (could be a readonly list using a twin hidden field)
webValue = webValue = fieldEl.options != undefined ? fieldEl.options[fieldEl.selectedIndex].value : fieldEl.value;
if(fieldDefinition.nullValue === webValue && fieldDefinition.required === true) if(fieldDefinition.nullValue === webValue && fieldDefinition.required === true)
{ {
invalidizeField(fieldEl, theForm, getI18nMsg("fieldIsRequired",null)); invalidizeField(fieldEl, theForm, getI18nMsg("fieldIsRequired",null));
...@@ -471,7 +472,7 @@ function validateFieldActual(fieldEl, theForm, formDefinitionKey) ...@@ -471,7 +472,7 @@ function validateFieldActual(fieldEl, theForm, formDefinitionKey)
} }
/** /**
* Recursive function to travers upwards in tree until we find the form * Recursive function to traverse upwards in tree until we find the form
* for the given element * for the given element
* @param {DOMElement} fieldEl * @param {DOMElement} fieldEl
* @returns {DOMelement} the form * @returns {DOMelement} the form
......
...@@ -190,6 +190,47 @@ ...@@ -190,6 +190,47 @@
} }
} }
}; };
var handleUseGridWeatherDataClicked = function(theCheckBox, weatherStationPointOfInterestId) {
weatherStationList = document.getElementById("weatherStationPointOfInterestId");
weatherStationPointOfInterestIdHiddenField = document.getElementById("weatherStationPointOfInterestIdHidden");
if(theCheckBox.checked)
{
// Select weatherStationId -2
weatherStationList.selectedIndex = 0;
// Disable the weatherstation select list
weatherStationList.disabled=true;
weatherStationList.name="weatherStationPointOfInterestIdDisabled";
// Enable the hidden field
weatherStationPointOfInterestIdHiddenField.disabled=false
weatherStationPointOfInterestIdHiddenField.name="weatherStationPointOfInterestId";
}
else
{
// Select weatherStationId -1 OR the optionally provided weatherStationPointOfInterestId
if(weatherStationPointOfInterestId == undefined || weatherStationPointOfInterestId == null)
{
weatherStationList.selectedIndex = 1;
}
else
{
for(let i=0;i<weatherStationList.options.length;i++)
{
weatherStationList.options[i].selected = parseInt(weatherStationList.options[i].value) == weatherStationPointOfInterestId;
}
}
// Enable the weather station select list
weatherStationList.disabled=false;
weatherStationList.name="weatherStationPointOfInterestId";
// Disable the hidden field
weatherStationPointOfInterestIdHiddenField.disabled=true
weatherStationPointOfInterestIdHiddenField.name="weatherStationPointOfInterestIdDisabled";
}
};
// Setting weather station select list state correct on page load
handleUseGridWeatherDataClicked(document.getElementById("useGridWeatherData")<#if forecastConfiguration.weatherStationPointOfInterestId?has_content>,${forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId}</#if>);
</script> </script>
</#macro> </#macro>
<#macro custom_css> <#macro custom_css>
...@@ -253,6 +294,7 @@ ...@@ -253,6 +294,7 @@
<#if ! user.isSuperUser() && ! user.isOrganizationAdmin()> readonly="readonly" disabled="disabled"</#if>/> <#if ! user.isSuperUser() && ! user.isOrganizationAdmin()> readonly="readonly" disabled="disabled"</#if>/>
</label> </label>
${i18nBundle.isPrivate} ${i18nBundle.isPrivate}
<span class="help-block" id="${formId}_isPrivate_validation"></span>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
...@@ -272,7 +314,7 @@ ...@@ -272,7 +314,7 @@
<#if !multipleNew?has_content || !multipleNew> <#if !multipleNew?has_content || !multipleNew>
<div class="form-group"> <div class="form-group">
<label for="locationPointOfInterestId">${i18nBundle.locationPointOfInterestId}</label> <label for="locationPointOfInterestId">${i18nBundle.locationPointOfInterestId}</label>
<select class="form-control" name="locationPointOfInterestId" onblur="validateField(this);"> <select class="form-control" id="locationPointOfInterestId" name="locationPointOfInterestId" onblur="validateField(this);">
<option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.locationPointOfInterestId?lower_case}</option> <option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.locationPointOfInterestId?lower_case}</option>
<#list locationPointOfInterests?sort_by("name") as poi> <#list locationPointOfInterests?sort_by("name") as poi>
<option value="${poi.pointOfInterestId}"<#if forecastConfiguration.locationPointOfInterestId?has_content && poi.pointOfInterestId == forecastConfiguration.locationPointOfInterestId.pointOfInterestId> selected="selected"</#if>>${poi.name}</option> <option value="${poi.pointOfInterestId}"<#if forecastConfiguration.locationPointOfInterestId?has_content && poi.pointOfInterestId == forecastConfiguration.locationPointOfInterestId.pointOfInterestId> selected="selected"</#if>>${poi.name}</option>
...@@ -282,14 +324,24 @@ ...@@ -282,14 +324,24 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="weatherStationPointOfInterestId">${i18nBundle.weatherStationPointOfInterestId}</label> <label for="weatherStationPointOfInterestId">${i18nBundle.weatherStationPointOfInterestId}</label>
<select class="form-control" name="weatherStationPointOfInterestId" onblur="validateField(this);"> <select class="form-control" id="weatherStationPointOfInterestId" name="weatherStationPointOfInterestId" onblur="if(!document.getElementById('useGridWeatherData').checked) {validateField(this);};">
<option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.weatherStationPointOfInterestId?lower_case}</option> <option value="-2">${i18nBundle.doNotUse} ${i18nBundle.weatherStationPointOfInterestId?lower_case}</option>
<option value="-1"<#if !forecastConfiguration.weatherStationPointOfInterestId?has_content && !forecastConfiguration.useGridWeatherData> selected="selected"</#if>>${i18nBundle.pleaseSelect} ${i18nBundle.weatherStationPointOfInterestId?lower_case}</option>
<#list weatherStationPointOfInterests?sort_by("name") as poi> <#list weatherStationPointOfInterests?sort_by("name") as poi>
<option value="${poi.pointOfInterestId}"<#if forecastConfiguration.weatherStationPointOfInterestId?has_content && poi.pointOfInterestId == forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId> selected="selected"</#if>>${poi.name}</option> <option value="${poi.pointOfInterestId}"<#if forecastConfiguration.weatherStationPointOfInterestId?has_content && poi.pointOfInterestId == forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId> selected="selected"</#if>>${poi.name}</option>
</#list> </#list>
</select> </select>
<span class="help-block" id="${formId}_weatherStationPointOfInterestId_validation"></span> <span class="help-block" id="${formId}_weatherStationPointOfInterestId_validation"></span>
</div> </div>
<input type="hidden" id="weatherStationPointOfInterestIdHidden" name="weatherStationPointOfInterestIdDisabled" value="-2" disabled="disabled"/>
<div class="form-group">
<div class="checkbox">
<label>
<input type="checkbox" id="useGridWeatherData" name="useGridWeatherData"<#if forecastConfiguration.useGridWeatherData?has_content && forecastConfiguration.useGridWeatherData == true> checked="checked"</#if> onclick="handleUseGridWeatherDataClicked(this);"/>
</label>
${i18nBundle.useGridWeatherData}
<span class="help-block" id="${formId}_useGridWeatherData_validation"></span>
</div>
<#else> <#else>
<input type="hidden" name="multipleNew" value="true"/> <input type="hidden" name="multipleNew" value="true"/>
<div class="form-group"> <div class="form-group">
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment