From 2cbe908c88a402313a33f0b3e4e799724a94bf04 Mon Sep 17 00:00:00 2001 From: Tor-Einar Skog <tor-einar.skog@nibio.no> Date: Tue, 27 Aug 2024 13:39:06 +0200 Subject: [PATCH] Handle grid based forecast configuration in the backend --- .../logic/entity/ForecastConfiguration.java | 25 +++++++++- .../nibio/vips/logic/entity/Organization.java | 15 ++++++ .../AppleScabModelPreprocessor.java | 47 ++++++------------ .../RunAllForecastConfigurationsTask.java | 48 +++++++++++-------- ...ation_Property_Default_Grid_DataSource.sql | 3 ++ .../templates/forecastConfigurationForm.ftl | 7 ++- 6 files changed, 89 insertions(+), 56 deletions(-) create mode 100644 src/main/resources/db/migration/V19__Add_Organization_Property_Default_Grid_DataSource.sql diff --git a/src/main/java/no/nibio/vips/logic/entity/ForecastConfiguration.java b/src/main/java/no/nibio/vips/logic/entity/ForecastConfiguration.java index c7f11e7d..7c91f595 100755 --- a/src/main/java/no/nibio/vips/logic/entity/ForecastConfiguration.java +++ b/src/main/java/no/nibio/vips/logic/entity/ForecastConfiguration.java @@ -127,7 +127,7 @@ public class ForecastConfiguration implements Serializable, Comparable { private Boolean useGridWeatherData; public Boolean getUseGridWeatherData() { - return useGridWeatherData; + return useGridWeatherData != null ? this.useGridWeatherData:false; } public void setUseGridWeatherData(Boolean useGridWeatherData) { @@ -257,6 +257,29 @@ public class ForecastConfiguration implements Serializable, Comparable { * @return the weatherStationPointOfInterestId */ public PointOfInterest getWeatherStationPointOfInterestId() { + if( this.getUseGridWeatherData() && this.getVipsLogicUserId().getOrganizationId().getDefaultGridWeatherStationDataSource() != null) + { + // Create a "weather station" with coordinates from the location + // And the default grid weather data source for the current organization (get location owner's organization) + PointOfInterestWeatherStation gridStation = new PointOfInterestWeatherStation(); + gridStation.setLatitude(this.getLocationPointOfInterestId().getLatitude()); + gridStation.setLongitude(this.getLocationPointOfInterestId().getLongitude()); + gridStation.setName("GRID-punkt for " + this.getLocationPointOfInterestId().getName()); // TODO Translate!!! + gridStation.setTimeZone(this.getLocationPointOfInterestId().getTimeZone()); + gridStation.setWeatherStationDataSourceId(this.getVipsLogicUserId().getOrganizationId().getDefaultGridWeatherStationDataSource()); + gridStation.setUser(this.getVipsLogicUserId()); + gridStation.setWeatherForecastProviderId(null); + gridStation.setWeatherStationRemoteId(gridStation.getLongitude() + "_" + gridStation.getLatitude()); + gridStation.setGisGeom(this.getLocationPointOfInterestId().getGisGeom()); + gridStation.setAltitude(this.getLocationPointOfInterestId().getAltitude()); + gridStation.setCountryCode(this.getLocationPointOfInterestId().getCountryCode()); + gridStation.setIsForecastLocation(true); + gridStation.setPointOfInterestTypeId(PointOfInterestType.POINT_OF_INTEREST_TYPE_WEATHER_STATION); + gridStation.setUser(this.getVipsLogicUserId()); + gridStation.setProperties(this.getLocationPointOfInterestId().getProperties()); + return gridStation; + } + return weatherStationPointOfInterestId; } diff --git a/src/main/java/no/nibio/vips/logic/entity/Organization.java b/src/main/java/no/nibio/vips/logic/entity/Organization.java index 4e5d1bd8..4e5d7e67 100755 --- a/src/main/java/no/nibio/vips/logic/entity/Organization.java +++ b/src/main/java/no/nibio/vips/logic/entity/Organization.java @@ -111,6 +111,21 @@ public class Organization implements Serializable { @OneToOne @JsonIgnore private VipsLogicUser archiveUser; + + @JoinColumn(name = "default_grid_weather_station_data_source_id", referencedColumnName = "weather_station_data_source_id") + @ManyToOne + private WeatherStationDataSource defaultGridWeatherStationDataSource; + + public WeatherStationDataSource getDefaultGridWeatherStationDataSource() { + return defaultGridWeatherStationDataSource; + } + + public void setDefaultGridWeatherStationDataSource(WeatherStationDataSource defaultGridWeatherStationDataSource) { + this.defaultGridWeatherStationDataSource = defaultGridWeatherStationDataSource; + } + + + public Organization() { } diff --git a/src/main/java/no/nibio/vips/logic/scheduling/model/preprocessor/AppleScabModelPreprocessor.java b/src/main/java/no/nibio/vips/logic/scheduling/model/preprocessor/AppleScabModelPreprocessor.java index 374785eb..230ffcb7 100755 --- a/src/main/java/no/nibio/vips/logic/scheduling/model/preprocessor/AppleScabModelPreprocessor.java +++ b/src/main/java/no/nibio/vips/logic/scheduling/model/preprocessor/AppleScabModelPreprocessor.java @@ -27,6 +27,10 @@ import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.TimeZone; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import no.nibio.vips.entity.ModelConfiguration; import no.nibio.vips.entity.WeatherObservation; import no.nibio.vips.logic.entity.ForecastConfiguration; @@ -48,17 +52,13 @@ import no.nibio.vips.util.weather.WeatherDataSourceUtil; * @author Tor-Einar Skog <tor-einar.skog@nibio.no> */ public class AppleScabModelPreprocessor extends ModelRunPreprocessor{ - - private final boolean DEBUG = false; + private static Logger LOGGER = LoggerFactory.getLogger(AppleScabModelPreprocessor.class); public final static String APPLESCABM_START_DATE_ASCOSPORE_MATURITY = "APPLESCABM_START_DATE_ASCOSPORE_MATURITY"; @Override public ModelConfiguration getModelConfiguration(ForecastConfiguration configuration) throws PreprocessorException { - if(DEBUG) - { - System.out.println("getModelConfiguration"); - } + LOGGER.debug("AppleScabModelPreprocessor.getModelConfiguration() called"); //configuration.getDateStart(); PointOfInterestWeatherStation weatherStation = (PointOfInterestWeatherStation) configuration.getWeatherStationPointOfInterestId(); // What timezone is the calculation for @@ -92,10 +92,8 @@ public class AppleScabModelPreprocessor extends ModelRunPreprocessor{ // Use Jackson to parse JSON from server // Weather data collections - if(DEBUG) - { - System.out.println("Getting weather data at " + new Date().toString()); - } + LOGGER.debug("Getting weather data at " + new Date().toString()); + WeatherDataSourceUtil wdsUtil = new WeatherDataSourceUtil(); List<WeatherObservation> observations; try { @@ -135,22 +133,17 @@ public class AppleScabModelPreprocessor extends ModelRunPreprocessor{ // We do nothing } - - if(DEBUG) - { - System.out.println("Finished getting weather data at " + new Date().toString()); - } + LOGGER.debug("Finished getting weather data at " + new Date().toString()); + try { observations = validateAndSanitizeObservations(observations, startDateAscosporeMaturity); } catch (ConfigValidationException | WeatherObservationListException ex) { //ex.printStackTrace(); throw new PreprocessorException(ex.getMessage()); } - if(DEBUG) - { - System.out.println("Observations=" + observations.toString()); - } + //LOGGER.debug("Observations=" + observations.toString()); + // Create the complete model configuration object ModelConfiguration retVal = new ModelConfiguration(); @@ -176,11 +169,7 @@ public class AppleScabModelPreprocessor extends ModelRunPreprocessor{ * @throws WeatherObservationListException */ private List<WeatherObservation> validateAndSanitizeObservations(List<WeatherObservation> observations, Date firstTimeStamp) throws ConfigValidationException, WeatherObservationListException { - if(DEBUG) - { - System.out.println("validateAndSanitizeObservations"); - } - + WeatherUtil wUtil = new WeatherUtil(); // First we remove all duplicates @@ -241,14 +230,7 @@ public class AppleScabModelPreprocessor extends ModelRunPreprocessor{ // Problems with weather observations - // Holes in series - if(DEBUG) - { - System.out.println("checkForAndFixHourlyTimeSeriesHoles"); - //System.out.println(wUtil.dumpWeatherObservationList(RR)); - } - - + // Unequal length of lists if ( @@ -257,6 +239,7 @@ public class AppleScabModelPreprocessor extends ModelRunPreprocessor{ || RR.size() != TM.size() ) { + LOGGER.debug("Unequal lists lengt: RR=" + RR.size() + ", TM=" + TM.size() + ", BT=" + BT.size()); UM = wUtil.fixHourlyValuesForParameters( UM, new HashSet(Arrays.asList("UM")), diff --git a/src/main/java/no/nibio/vips/logic/scheduling/tasks/RunAllForecastConfigurationsTask.java b/src/main/java/no/nibio/vips/logic/scheduling/tasks/RunAllForecastConfigurationsTask.java index 7a5cf687..b0978823 100755 --- a/src/main/java/no/nibio/vips/logic/scheduling/tasks/RunAllForecastConfigurationsTask.java +++ b/src/main/java/no/nibio/vips/logic/scheduling/tasks/RunAllForecastConfigurationsTask.java @@ -24,6 +24,10 @@ import java.util.Collections; import java.util.List; import java.util.Map; import javax.ejb.EJB; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import no.nibio.vips.i18n.I18nImpl; import no.nibio.vips.logic.controller.session.ForecastBean; import no.nibio.vips.logic.controller.session.PointOfInterestBean; @@ -46,6 +50,8 @@ import no.nibio.web.forms.FormField; * @author Tor-Einar Skog <tor-einar.skog@nibio.no> */ public class RunAllForecastConfigurationsTask extends VipsLogicTask{ + + private static Logger LOGGER = LoggerFactory.getLogger(RunAllForecastConfigurationsTask.class); private I18nImpl i18n; //private boolean DEBUG=true; @@ -91,24 +97,30 @@ public class RunAllForecastConfigurationsTask extends VipsLogicTask{ noForecastConfigurationsFound = false; for(ForecastConfiguration forecastConfiguration:currentForecastConfigurations) { - if( - weatherStationPointOfInterestId == null - || weatherStationPointOfInterestId <= 0 - || (forecastConfiguration.getWeatherStationPointOfInterestId() != null && forecastConfiguration.getWeatherStationPointOfInterestId().getPointOfInterestId().equals(weatherStationPointOfInterestId)) + if(forecastConfiguration.getUseGridWeatherData() && forecastConfiguration.getWeatherStationPointOfInterestId() == null) + { + errorMessage.append( + SchedulingUtil.createSchedulingMessageHTML( + "Error with forecast #" + forecastConfiguration.getForecastConfigurationId() + " (" + forecastConfiguration.getLocationPointOfInterestId().getName() + " - " + modelInformationMap.get(forecastConfiguration.getModelId()).getDefaultName() + ")", + "The forecast is configured to use gridded weather data, but the organization " + forecastConfiguration.getVipsLogicUserId().getOrganizationId().getOrganizationName() + " has not set its gridded weather data source. Please contact the system administrator.", + SchedulingUtil.MESSAGE_STATUS_WARNING ) + ); + totalNumberofForecastConfigurations++; + } + else if( + weatherStationPointOfInterestId == null + || weatherStationPointOfInterestId <= 0 + || (forecastConfiguration.getWeatherStationPointOfInterestId() != null && forecastConfiguration.getWeatherStationPointOfInterestId().getPointOfInterestId().equals(weatherStationPointOfInterestId)) + ) + { try { totalNumberofForecastConfigurations++; - //System.out.println("Running forecast #" + forecastConfiguration.getForecastConfigurationId()); + LOGGER.debug("Running forecast #" + forecastConfiguration.getForecastConfigurationId()); SessionControllerGetter.getForecastBean().runForecast(forecastConfiguration); - /* - if(DEBUG && totalNumberofForecastConfigurations == 2) - { - throw new RunModelException("This is a test!!!"); - }*/ numberOfCompletedForecastConfigurations++; - //System.out.println("All went well"); } catch (PreprocessorException | RunModelException ex) { @@ -119,38 +131,32 @@ public class RunAllForecastConfigurationsTask extends VipsLogicTask{ ex.getMessage(), SchedulingUtil.MESSAGE_STATUS_DANGER) ); - //System.out.println("########################### Error caught: " + errorMessage); - //System.out.println("numberOfCompletedForecastConfigurations=" + numberOfCompletedForecastConfigurations); - //System.out.println("totalNumberofForecastConfigurations=" + totalNumberofForecastConfigurations); - //continue; } } if(totalNumberofForecastConfigurations > 0) { noForecastConfigurationsFound = false; - double completeness = (double) numberOfCompletedForecastConfigurations/totalNumberofForecastConfigurations; - tec.setCompleteness(completeness); + tec.setCompleteness(Double.valueOf(numberOfCompletedForecastConfigurations)/Double.valueOf(totalNumberofForecastConfigurations)); } else { noForecastConfigurationsFound = true; - //System.out.println("noForecastConfigurationsFound == true!!"); + } - //System.out.println("Current completeness=" + tec.getTaskExecutor().getCompleteness()); } } } + if(noForecastConfigurationsFound) { tec.setCompleteness(1.0); tec.setStatusMessage("No current forecast configurations were found"); } - //System.out.println("Total completeness=" + tec.getTaskExecutor().getCompleteness()); - if(tec.getTaskExecutor().getCompleteness() != 1.0) + if(tec.getTaskExecutor().getCompleteness() != 1.0 || ! errorMessage.isEmpty()) { //System.out.println("Error detected, RuntimeException thrown just after this"); tec.setStatusMessage(errorMessage.toString()); diff --git a/src/main/resources/db/migration/V19__Add_Organization_Property_Default_Grid_DataSource.sql b/src/main/resources/db/migration/V19__Add_Organization_Property_Default_Grid_DataSource.sql new file mode 100644 index 00000000..b254b3e4 --- /dev/null +++ b/src/main/resources/db/migration/V19__Add_Organization_Property_Default_Grid_DataSource.sql @@ -0,0 +1,3 @@ +-- Adding this property when adding support for gridded weather datasources in VIPS +ALTER TABLE organization +ADD COLUMN default_grid_weather_station_data_source_id INTEGER REFERENCES weather_station_data_source(weather_station_data_source_id) DEFAULT NULL; \ No newline at end of file diff --git a/src/main/webapp/templates/forecastConfigurationForm.ftl b/src/main/webapp/templates/forecastConfigurationForm.ftl index 234ef983..5b331818 100755 --- a/src/main/webapp/templates/forecastConfigurationForm.ftl +++ b/src/main/webapp/templates/forecastConfigurationForm.ftl @@ -229,7 +229,7 @@ }; // Setting weather station select list state correct on page load - handleUseGridWeatherDataClicked(document.getElementById("useGridWeatherData")<#if forecastConfiguration.weatherStationPointOfInterestId?has_content>,${forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId}</#if>); + handleUseGridWeatherDataClicked(document.getElementById("useGridWeatherData")<#if forecastConfiguration.weatherStationPointOfInterestId?has_content && forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId?has_content>,${forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId}</#if>); </script> </#macro> @@ -328,7 +328,10 @@ <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> - <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 + && forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId?has_content + && poi.pointOfInterestId == forecastConfiguration.weatherStationPointOfInterestId.pointOfInterestId> selected="selected"</#if>>${poi.name}</option> </#list> </select> <span class="help-block" id="${formId}_weatherStationPointOfInterestId_validation"></span> -- GitLab