diff --git a/src/main/java/no/bioforsk/vips/logic/VIPSLogicApplication.java b/src/main/java/no/bioforsk/vips/logic/VIPSLogicApplication.java index 74b3d3ab71c90f01bb175fc938f8a55bf8010bf7..c3bf5213855c454bdb71ad24e6494ecfda5223fb 100644 --- a/src/main/java/no/bioforsk/vips/logic/VIPSLogicApplication.java +++ b/src/main/java/no/bioforsk/vips/logic/VIPSLogicApplication.java @@ -46,6 +46,7 @@ public class VIPSLogicApplication extends Application */ private void addRestResourceClassesManually(Set<Class<?>> resources) { resources.add(no.bioforsk.vips.logic.service.LogicService.class); + //resources.add(no.bioforsk.vips.logic.service.JacksonConfig.class); //resources.add(no.bioforsk.vips.coremanager.service.ManagerResourceImpl.class); } @@ -55,6 +56,7 @@ public class VIPSLogicApplication extends Application * given list with all resources defined in the project. */ private void addRestResourceClasses(Set<Class<?>> resources) { + resources.add(no.bioforsk.vips.logic.service.JacksonConfig.class); resources.add(no.bioforsk.vips.logic.service.LogicService.class); } diff --git a/src/main/java/no/bioforsk/vips/logic/controller/servlet/PointOfInterestController.java b/src/main/java/no/bioforsk/vips/logic/controller/servlet/PointOfInterestController.java index 2994972179f2e6909e673164df809a9a7f1e083a..a257190ef2b24df79bddaeeb3085c504ee7b45ef 100644 --- a/src/main/java/no/bioforsk/vips/logic/controller/servlet/PointOfInterestController.java +++ b/src/main/java/no/bioforsk/vips/logic/controller/servlet/PointOfInterestController.java @@ -209,10 +209,10 @@ public class PointOfInterestController extends HttpServlet { weatherStation.setWeatherStationRemoteId(formValidation.getFormField("weatherStationRemoteId").getWebValue()); weatherStation.setTimeZone(formValidation.getFormField("timeZone").getWebValue()); weatherStation.setCountryCode(em.find(Country.class, formValidation.getFormField("countryCode").getWebValue())); - if(weatherStation.getPointOfInterestType() == null) + /*if(weatherStation.getPointOfInterestType() == null) { - weatherStation.setPointOfInterestType(em.find(PointOfInterestType.class, Globals.POI_TYPE_WEATHERSTATION)); - } + //weatherStation.setPointOfInterestType(em.find(PointOfInterestType.class, Globals.POI_TYPE_WEATHERSTATION)); + }*/ // If userId is set from form, always update if(user.isSuperUser() && !formValidation.getFormField("userId").isEmpty()) { diff --git a/src/main/java/no/bioforsk/vips/logic/scheduling/model/preprocessor/PlasmoparaViticolaModelPreprocessor.java b/src/main/java/no/bioforsk/vips/logic/scheduling/model/preprocessor/PlasmoparaViticolaModelPreprocessor.java new file mode 100644 index 0000000000000000000000000000000000000000..5796436b80c4d0fc0b14e51cea8b7b03db082b0b --- /dev/null +++ b/src/main/java/no/bioforsk/vips/logic/scheduling/model/preprocessor/PlasmoparaViticolaModelPreprocessor.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2015 Bioforsk <http://www.bioforsk.no/>. + * + * This file is part of VIPSLogic. + * VIPSLogic is free software: you can redistribute it and/or modify + * it under the terms of the Bioforsk Open Source License as published by + * Bioforsk, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSLogic 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 + * Bioforsk Open Source License for more details. + * + * You should have received a copy of the Bioforsk Open Source License + * along with VIPSLogic. If not, see <http://www.bioforsk.no/licenses/>. + * + */ + +package no.bioforsk.vips.logic.scheduling.model.preprocessor; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; +import no.bioforsk.vips.entity.ModelConfiguration; +import no.bioforsk.vips.entity.WeatherObservation; +import no.bioforsk.vips.logic.entity.ForecastConfiguration; +import no.bioforsk.vips.logic.entity.PointOfInterestWeatherStation; +import no.bioforsk.vips.logic.scheduling.model.ModelRunPreprocessor; +import no.bioforsk.vips.logic.scheduling.model.PreprocessorException; +import no.bioforsk.vips.logic.util.SystemTime; +import no.bioforsk.vips.model.ConfigValidationException; +import no.bioforsk.vips.util.WeatherElements; +import no.bioforsk.vips.util.WeatherObservationListException; +import no.bioforsk.vips.util.WeatherUtil; + +/** + * @copyright 2015 <a href="http://www.bioforsk.no/">Bioforsk</a> + * @author Tor-Einar Skog <tor-einar.skog@bioforsk.no> + */ +public class PlasmoparaViticolaModelPreprocessor extends ModelRunPreprocessor{ + + @Override + public ModelConfiguration getModelConfiguration(ForecastConfiguration configuration) throws PreprocessorException { + PointOfInterestWeatherStation weatherStation = (PointOfInterestWeatherStation) configuration.getWeatherStationPointOfInterestId(); + // What timezone is the calculation for + TimeZone timeZone = TimeZone.getTimeZone(weatherStation.getTimeZone()); + // Getting January 1st + Calendar cal = Calendar.getInstance(timeZone); + cal.setTime(SystemTime.getSystemTime()); + cal.add(Calendar.DATE, 3); + WeatherUtil wUtil = new WeatherUtil(); + Date endDate = wUtil.normalizeToExactDate(cal.getTime(), timeZone); + cal.set(Calendar.MONTH, Calendar.JANUARY); + cal.set(Calendar.DATE, 1); + Date startDate = wUtil.normalizeToExactDate(cal.getTime(), timeZone); + + List<WeatherObservation> observations = getWeatherObservations( + weatherStation.getDataFetchUri(), + WeatherObservation.LOG_INTERVAL_ID_1H, + new String[]{ + WeatherElements.TEMPERATURE_MEAN, + WeatherElements.PRECIPITATION + }, + startDate, + endDate, + timeZone); + // TODO: weather data validation + ModelConfiguration retVal = new ModelConfiguration(); + retVal.setModelId(this.getModelId()); + retVal.setConfigParameter("timeZone", timeZone.getID()); + retVal.setConfigParameter("observations", observations); + + return retVal; + } + + @Override + public String getModelId() { + return "PLASMOVITI"; + } + +} diff --git a/src/main/java/no/bioforsk/vips/logic/service/JacksonConfig.java b/src/main/java/no/bioforsk/vips/logic/service/JacksonConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..c317c5e4ec9b2bad38ea416f605b5c455dd93e99 --- /dev/null +++ b/src/main/java/no/bioforsk/vips/logic/service/JacksonConfig.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015 Bioforsk <http://www.bioforsk.no/>. + * + * This file is part of VIPSLogic. + * VIPSLogic is free software: you can redistribute it and/or modify + * it under the terms of the Bioforsk Open Source License as published by + * Bioforsk, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSLogic 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 + * Bioforsk Open Source License for more details. + * + * You should have received a copy of the Bioforsk Open Source License + * along with VIPSLogic. If not, see <http://www.bioforsk.no/licenses/>. + * + */ + +package no.bioforsk.vips.logic.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import javax.ws.rs.ext.ContextResolver; +import javax.ws.rs.ext.Provider; + +/** + * Add this to no.bioforsk.vips.logic.VIPSLogicApplication if you want all + * dates to be serialized as ISO formatted date strings + * @copyright 2015 <a href="http://www.bioforsk.no/">Bioforsk</a> + * @author Tor-Einar Skog <tor-einar.skog@bioforsk.no> + */ +@Provider +public class JacksonConfig implements ContextResolver<ObjectMapper> +{ + private final ObjectMapper objectMapper; + + public JacksonConfig() throws Exception + { + objectMapper = new ObjectMapper().configure( + SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + } + + @Override + public ObjectMapper getContext(Class<?> arg0) + { + return objectMapper; + } + } \ No newline at end of file diff --git a/src/main/java/no/bioforsk/vips/logic/service/LogicService.java b/src/main/java/no/bioforsk/vips/logic/service/LogicService.java index a5c08af5f4583aa541fd008dbbdbd7282097d0e6..92ddb12a6b4d788b0104e5350d8ea4bc456f2fb9 100644 --- a/src/main/java/no/bioforsk/vips/logic/service/LogicService.java +++ b/src/main/java/no/bioforsk/vips/logic/service/LogicService.java @@ -26,8 +26,11 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @@ -38,12 +41,12 @@ import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import no.bioforsk.vips.coremanager.service.ManagerResource; +import no.bioforsk.vips.entity.WeatherObservation; import no.bioforsk.vips.logic.authenticate.PasswordValidationException; import no.bioforsk.vips.logic.entity.ForecastResult; import no.bioforsk.vips.logic.i18n.SessionLocaleUtil; import no.bioforsk.vips.logic.entity.ForecastConfiguration; import no.bioforsk.vips.logic.entity.ForecastModelConfiguration; -import no.bioforsk.vips.logic.entity.ForecastSummary; import no.bioforsk.vips.logic.entity.Message; import no.bioforsk.vips.logic.entity.MessageTag; import no.bioforsk.vips.logic.entity.Observation; @@ -52,6 +55,8 @@ import no.bioforsk.vips.logic.entity.VipsLogicUser; import no.bioforsk.vips.logic.util.SessionControllerGetter; import no.bioforsk.vips.logic.util.SystemTime; import no.bioforsk.vips.util.ServletUtil; +import no.bioforsk.vips.util.weather.MetosDataParser; +import no.bioforsk.vips.util.weather.ParseWeatherDataException; import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget; import org.jboss.resteasy.spi.HttpRequest; @@ -268,6 +273,21 @@ public class LogicService { return Response.ok().entity(observations).build(); } + @GET + @POST + @Path("weather/proxy/metos/{stationId}") + @Produces("application/json;charset=UTF-8") + public Response getMetosWeatherData(@PathParam("stationId") String stationId) + { + List<WeatherObservation> observations; + try { + observations = new MetosDataParser().getWeatherObservations(stationId); + } catch (ParseWeatherDataException ex) { + return Response.serverError().entity(ex).build(); + } + return Response.ok().entity(observations).build(); + } + private ManagerResource getManagerResource() { Client client = ClientBuilder.newClient(); diff --git a/src/main/java/no/bioforsk/vips/util/weather/MetosDataParser.java b/src/main/java/no/bioforsk/vips/util/weather/MetosDataParser.java new file mode 100644 index 0000000000000000000000000000000000000000..e426643f4f6c464801023b1eecaf42194d02de37 --- /dev/null +++ b/src/main/java/no/bioforsk/vips/util/weather/MetosDataParser.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2015 Bioforsk <http://www.bioforsk.no/>. + * + * This file is part of VIPSLogic. + * VIPSLogic is free software: you can redistribute it and/or modify + * it under the terms of the Bioforsk Open Source License as published by + * Bioforsk, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSLogic 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 + * Bioforsk Open Source License for more details. + * + * You should have received a copy of the Bioforsk Open Source License + * along with VIPSLogic. If not, see <http://www.bioforsk.no/licenses/>. + * + */ + +package no.bioforsk.vips.util.weather; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.text.MessageFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import no.bioforsk.vips.entity.WeatherObservation; + +/** + * Reads/parses data from a Metos weather station + * For future versions (need authentication per station): http://www.fieldclimate.com/json_manual/ + * @copyright 2015 <a href="http://www.bioforsk.no/">Bioforsk</a> + * @author Tor-Einar Skog <tor-einar.skog@bioforsk.no> + */ +public class MetosDataParser { + public final static String METOS_URL_TEMPLATE = "http://www.fieldclimate.com/rimpro/all_sensors_csv.php?s={0}"; + // Structure of file from Metos, including aggregation type + private final static String[][] elementMeasurementTypes = { + {"Q0","AVG"}, + {"RR","SUM"}, + {"FM2","AVG"}, + {"BATTERY","AVG"}, + {"BT","SUM"}, + {"TM","AVG"}, + {"UM","AVG"} + }; + + /** + * Using output designed for RIMPro, parsing into WeatherObservations + * @param stationID the METOS station ID + * @return + */ + public List<WeatherObservation> getWeatherObservations(String stationID) throws ParseWeatherDataException + { + List<WeatherObservation> retVal = new ArrayList<>(); + SimpleDateFormat dFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + try { + URL metosURL = new URL(MessageFormat.format(MetosDataParser.METOS_URL_TEMPLATE, stationID)); + BufferedReader in = new BufferedReader( + new InputStreamReader(metosURL.openStream())); + + String inputLine; + Date testTimestamp = null; + Date timestamp = null; + Boolean shouldBe00Now = true; + String[] data00 = null; + String[] data30 = null; + while ((inputLine = in.readLine()) != null) + { + String[] data = inputLine.split(";"); + // Skip empty lines + if(data.length <= 1) + { + continue; + } + // Check for valid start of line + try { + + testTimestamp = dFormat.parse(data[0] + " " + data[1]); + } catch (ParseException ex) { + continue; + } + // Data comes in half-hour chunks (resolution = 30 minutes) + if(data[1].split(":")[1].equals("00") && shouldBe00Now) + { + data00 = data; + timestamp = testTimestamp; + shouldBe00Now = false; + continue; // So that we summarize only after :30 data has been set too + } + else if(data[1].split(":")[1].equals("30") && !shouldBe00Now) + { + data30 = data; + shouldBe00Now = true; + } + else + { + throw new ParseWeatherDataException("Doesn't make sense!"); + } + + for(int i=0;i<MetosDataParser.elementMeasurementTypes.length;i++) + { + Double aggregateValue = null; + Double value00 = Double.valueOf(data00[i+2].replaceAll(",",".")); + Double value30 = Double.valueOf(data30[i+2].replaceAll(",",".")); + if(MetosDataParser.elementMeasurementTypes[i][1].equals("AVG")) + { + aggregateValue = (value00 + value30) / 2; + } + else + { + aggregateValue = (value00 + value30); + } + + WeatherObservation obs = new WeatherObservation(); + obs.setTimeMeasured(timestamp); + obs.setLogIntervalId(WeatherObservation.LOG_INTERVAL_ID_1H); + obs.setElementMeasurementTypeId(MetosDataParser.elementMeasurementTypes[i][0]); + obs.setValue(aggregateValue); + retVal.add(obs); + } + } + + in.close(); + } catch (IOException ex) { + throw new ParseWeatherDataException(ex.getMessage()); + } + return retVal; + } +} diff --git a/src/main/java/no/bioforsk/vips/util/weather/ParseWeatherDataException.java b/src/main/java/no/bioforsk/vips/util/weather/ParseWeatherDataException.java new file mode 100644 index 0000000000000000000000000000000000000000..9d520bd17ab6de95c06926aa765372fc2ef2ffa7 --- /dev/null +++ b/src/main/java/no/bioforsk/vips/util/weather/ParseWeatherDataException.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015 Bioforsk <http://www.bioforsk.no/>. + * + * This file is part of VIPSLogic. + * VIPSLogic is free software: you can redistribute it and/or modify + * it under the terms of the Bioforsk Open Source License as published by + * Bioforsk, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSLogic 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 + * Bioforsk Open Source License for more details. + * + * You should have received a copy of the Bioforsk Open Source License + * along with VIPSLogic. If not, see <http://www.bioforsk.no/licenses/>. + * + */ + +package no.bioforsk.vips.util.weather; + +/** + * @copyright 2015 <a href="http://www.bioforsk.no/">Bioforsk</a> + * @author Tor-Einar Skog <tor-einar.skog@bioforsk.no> + */ +public class ParseWeatherDataException extends Exception { + + /** + * Creates a new instance of <code>ParseWeatherDataException</code> without detail message. + */ + public ParseWeatherDataException() { + } + + + /** + * Constructs an instance of <code>ParseWeatherDataException</code> with the specified detail message. + * @param msg the detail message. + */ + public ParseWeatherDataException(String msg) { + super(msg); + } +} diff --git a/src/main/webapp/formdefinitions/models/PLASMOVITI.json b/src/main/webapp/formdefinitions/models/PLASMOVITI.json new file mode 100644 index 0000000000000000000000000000000000000000..8e79b552e7f92607ecb45bab6d67acb2363ba8e9 --- /dev/null +++ b/src/main/webapp/formdefinitions/models/PLASMOVITI.json @@ -0,0 +1,22 @@ +{ + "_licenseNote": [ + "Copyright (c) 2014 Bioforsk <http://www.bioforsk.no/>. ", + "", + "This file is part of VIPSLogic. ", + "VIPSLogic is free software: you can redistribute it and/or modify ", + "it under the terms of the Bioforsk Open Source License as published by ", + "Bioforsk, either version 1 of the License, or (at your option) any ", + "later version. ", + "", + "VIPSLogic 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 ", + "Bioforsk Open Source License for more details. ", + "", + "You should have received a copy of the Bioforsk Open Source License ", + "along with VIPSLogic. If not, see <http://www.bioforsk.no/licenses/>. " + ], + "_comment" : "Structure of the specific fields for PLASMOVITI", + "fields": [ + ] +} \ No newline at end of file diff --git a/src/main/webapp/js/forecastConfigurationForm.js b/src/main/webapp/js/forecastConfigurationForm.js index 097db3f16bf34713d1ba97b9f9e4de3912fcfc24..08ced515514b8033de9a9aa3f310b79ad8336e9b 100644 --- a/src/main/webapp/js/forecastConfigurationForm.js +++ b/src/main/webapp/js/forecastConfigurationForm.js @@ -58,14 +58,23 @@ function renderModelSpecificFields() */ function renderModelSpecificFieldsCallback(modelId) { + var fieldSet = document.getElementById("modelSpecificFields"); var formDefinition = formDefinitions[modelId]; + + // If there are no specific fields, erase what was there + if(formDefinition.fields.length === 0) + { + fieldSet.innerHTML = ""; + fieldSet.style.display="none"; + return; + } + var fieldsHTML = ["<legend>" + getI18nMsg("specificFieldsForX", [getI18nMsg(modelId)])+ "</legend>"]; for(var i in formDefinition.fields){ var fieldDefinition = formDefinition.fields[i]; fieldsHTML.push(createFieldHTML(modelId, fieldDefinition)); } - var fieldSet = document.getElementById("modelSpecificFields"); fieldSet.innerHTML = fieldsHTML.join("\n"); fieldSet.style.display="block"; diff --git a/src/test/java/no/bioforsk/vips/util/weather/MetosDataParserTest.java b/src/test/java/no/bioforsk/vips/util/weather/MetosDataParserTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5e8854dd14adc09135b16e66629d2f1fd8d9cf2d --- /dev/null +++ b/src/test/java/no/bioforsk/vips/util/weather/MetosDataParserTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015 Bioforsk <http://www.bioforsk.no/>. + * + * This file is part of VIPSLogic. + * VIPSLogic is free software: you can redistribute it and/or modify + * it under the terms of the Bioforsk Open Source License as published by + * Bioforsk, either version 1 of the License, or (at your option) any + * later version. + * + * VIPSLogic 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 + * Bioforsk Open Source License for more details. + * + * You should have received a copy of the Bioforsk Open Source License + * along with VIPSLogic. If not, see <http://www.bioforsk.no/licenses/>. + * + */ +package no.bioforsk.vips.util.weather; + +import java.util.List; +import no.bioforsk.vips.entity.WeatherObservation; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author treinar + */ +public class MetosDataParserTest { + + public MetosDataParserTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of getWeatherObservations method, of class MetosDataParser. + * Could be time consuming, so we test only when needed + */ + //@Test + public void testGetWeatherObservations() throws Exception { + System.out.println("getWeatherObservations"); + String stationID = "00002085"; + MetosDataParser instance = new MetosDataParser(); + //List<WeatherObservation> expResult = null; + Integer expResult = 0; + List<WeatherObservation> result = instance.getWeatherObservations(stationID); + assertNotNull(result); + assertNotSame(expResult, result.size()); + System.out.println("Result sample: " + result.get(0).toString()); + + } + +}