diff --git a/nb-configuration.xml b/nb-configuration.xml index 1bd992ca9ddc25bbebf62272ea26f5c5ceb51d77..2f11ad2cceb58419d0c43844e6368745831a97c2 100755 --- a/nb-configuration.xml +++ b/nb-configuration.xml @@ -26,5 +26,6 @@ Any value defined here will override the pom.xml file value but is only applicab <org-netbeans-modules-maven-j2ee.netbeans_2e_hint_2e_deploy_2e_server>WildFly</org-netbeans-modules-maven-j2ee.netbeans_2e_hint_2e_deploy_2e_server> <org-netbeans-modules-projectapi.jsf_2e_language>Facelets</org-netbeans-modules-projectapi.jsf_2e_language> <org-netbeans-modules-maven-jaxws._5f_C_5f_DMIWeatherService_2e_svc>https://dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc?wsdl</org-netbeans-modules-maven-jaxws._5f_C_5f_DMIWeatherService_2e_svc> + <netbeans.compile.on.save>none</netbeans.compile.on.save> </properties> </project-shared-configuration> diff --git a/src/jax-ws-catalog.xml b/src/jax-ws-catalog.xml index c074fd31684adeb88bef92ad56e99be552150899..f6401c8f6762828e08fbbb03755addae0840bbae 100644 --- a/src/jax-ws-catalog.xml +++ b/src/jax-ws-catalog.xml @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system"> <system systemId="https://dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc" uri="wsdl/dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc.xml"/> + <system systemId="https://dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc?singleWsdl" uri="wsdl/dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc.singlewsdl.wsdl"/> <system systemId="https://dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc?wsdl" uri="wsdl/dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc.wsdl"/> <system systemId="https://dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc?xsd=xsd0" uri="wsdl/dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc.xsd_xsd0.xsd"/> <system systemId="https://dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc?xsd=xsd1" uri="wsdl/dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc.xsd_xsd1.xsd"/> <system systemId="https://dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc?xsd=xsd2" uri="wsdl/dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc.xsd_xsd2.xsd"/> <system systemId="https://dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc?xsd=xsd3" uri="wsdl/dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc.xsd_xsd3.xsd"/> <system systemId="https://dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc?xsd=xsd4" uri="wsdl/dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc.xsd_xsd4.xsd"/> - <system systemId="https://dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc?singleWsdl" uri="wsdl/dmiweatherservice-plant.dlbr.dk/DMIWeatherService.svc.singlewsdl.wsdl"/> </catalog> \ No newline at end of file diff --git a/src/main/java/no/nibio/vips/logic/VIPSLogicApplication.java b/src/main/java/no/nibio/vips/logic/VIPSLogicApplication.java index 78c6e42ce60a0b75d6803ccdc5aa05715cfc3872..255cbaf060bea1ef562b31c305d4ae6b8dc1df16 100755 --- a/src/main/java/no/nibio/vips/logic/VIPSLogicApplication.java +++ b/src/main/java/no/nibio/vips/logic/VIPSLogicApplication.java @@ -54,6 +54,7 @@ public class VIPSLogicApplication extends Application resources.add(no.nibio.vips.logic.modules.applefruitmoth.AppleFruitMothService.class); resources.add(no.nibio.vips.logic.service.ObservationService.class); resources.add(no.nibio.vips.logic.service.WeatherProxyService.class); + resources.add(no.nibio.vips.logic.service.ModelFormService.class); resources.add(no.nibio.vips.logic.service.JacksonConfig.class); //resources.add(no.nibio.vips.coremanager.service.ManagerResourceImpl.class); } @@ -70,6 +71,7 @@ public class VIPSLogicApplication extends Application resources.add(no.nibio.vips.logic.modules.roughage.RoughageService.class); resources.add(no.nibio.vips.logic.service.JacksonConfig.class); resources.add(no.nibio.vips.logic.service.LogicService.class); + resources.add(no.nibio.vips.logic.service.ModelFormService.class); resources.add(no.nibio.vips.logic.service.ObservationService.class); resources.add(no.nibio.vips.logic.service.VIPSMobileService.class); resources.add(no.nibio.vips.logic.service.WeatherProxyService.class); diff --git a/src/main/java/no/nibio/vips/logic/controller/servlet/LoginController.java b/src/main/java/no/nibio/vips/logic/controller/servlet/LoginController.java index 9f5dde0951e54e4e1423b5eb197fc46fd3ae715c..8c169a334ec470129cd3655e3b3555279826dadb 100755 --- a/src/main/java/no/nibio/vips/logic/controller/servlet/LoginController.java +++ b/src/main/java/no/nibio/vips/logic/controller/servlet/LoginController.java @@ -120,7 +120,7 @@ public class LoginController extends HttpServlet { if(user != null) { request.getSession().setAttribute("user", user); - UUID uUUID = this.handleRememberUser(request, response, user); + UUID uUUID = this.handleRememberUser(request, response, user, returnUUID); if(returnUUID) { nextPage += (nextPage.contains("?") ? "&": "?") + "returnUUID=" + uUUID.toString(); @@ -193,7 +193,7 @@ public class LoginController extends HttpServlet { if(user != null && user.getUserStatusId().equals(Globals.USER_STATUS_APPROVED)) { request.getSession().setAttribute("user", user); - UUID uUUID = this.handleRememberUser(request, response, user); + UUID uUUID = this.handleRememberUser(request, response, user, returnUUID); if(returnUUID) { nextPage += (nextPage.contains("?") ? "&": "?") + "returnUUID=" + uUUID.toString(); @@ -282,7 +282,7 @@ public class LoginController extends HttpServlet { if(user != null && user.getUserStatusId().equals(Globals.USER_STATUS_APPROVED)) { request.getSession().setAttribute("user", user); - UUID uUUID = this.handleRememberUser(request, response, user); + UUID uUUID = this.handleRememberUser(request, response, user, returnUUID); // All is well, return object ObjectMapper mapper = new ObjectMapper(); user.setUserUuid(uUUID); @@ -362,20 +362,23 @@ public class LoginController extends HttpServlet { * @param response * @param user */ - private UUID handleRememberUser(HttpServletRequest request, HttpServletResponse response, VipsLogicUser user) + private UUID handleRememberUser(HttpServletRequest request, HttpServletResponse response, VipsLogicUser user, Boolean returnUUID) { String rememberUser = request.getParameter("rememberUser") != null ? request.getParameter("rememberUser") : (String) request.getSession().getAttribute("rememberUser"); request.getSession().removeAttribute("rememberUser"); - if(rememberUser != null && rememberUser.equals("on")) + if(returnUUID || (rememberUser != null && rememberUser.equals("on"))) { UserUuid uUUID = SessionControllerGetter.getUserBean().createAndPersistUserUuid(user); - Cookie rememberedUser = new Cookie("rememberedUser", uUUID.getUserUuidPK().getUserUuid().toString()); - rememberedUser.setPath("/"); - rememberedUser.setMaxAge(Globals.DEFAULT_UUID_VALIDITY_DURATION_DAYS * 24 * 60 * 60); - response.addCookie(rememberedUser); + if(rememberUser != null && rememberUser.equals("on")) + { + Cookie rememberedUser = new Cookie("rememberedUser", uUUID.getUserUuidPK().getUserUuid().toString()); + rememberedUser.setPath("/"); + rememberedUser.setMaxAge(Globals.DEFAULT_UUID_VALIDITY_DURATION_DAYS * 24 * 60 * 60); + response.addCookie(rememberedUser); + } return uUUID.getUserUuidPK().getUserUuid(); } else diff --git a/src/main/java/no/nibio/vips/logic/controller/servlet/PointOfInterestController.java b/src/main/java/no/nibio/vips/logic/controller/servlet/PointOfInterestController.java index d98f5e6c7c126e49ab0830417d5410cf640b01fb..fc34a6f0f4b4043a49d4cfd09f504c34f99532da 100755 --- a/src/main/java/no/nibio/vips/logic/controller/servlet/PointOfInterestController.java +++ b/src/main/java/no/nibio/vips/logic/controller/servlet/PointOfInterestController.java @@ -311,26 +311,28 @@ public class PointOfInterestController extends HttpServlet { weatherStation = SessionControllerGetter.getPointOfInterestBean().storeWeatherStation(weatherStation); Map<String, FormField> externalResourceIdentifiers = formValidation.getMultipleMapFormFields().get("externalResourceIdentifier"); - for(String key:externalResourceIdentifiers.keySet()) + if(externalResourceIdentifiers != null) { - Integer externalResourceId = Integer.valueOf(key); - FormField identifierField = externalResourceIdentifiers.get(key); - if(identifierField.getWebValue() == null || identifierField.getWebValue().isEmpty()) + for(String key:externalResourceIdentifiers.keySet()) { - // We delete existing if unset - SessionControllerGetter.getPointOfInterestBean().deletePointOfInterestExternalResource(weatherStation.getPointOfInterestId(), externalResourceId); - } - else - { - - PointOfInterestExternalResource poiExternalResource = new PointOfInterestExternalResource(); - PointOfInterestExternalResourcePK pk = new PointOfInterestExternalResourcePK(weatherStation.getPointOfInterestId(), externalResourceId); - poiExternalResource.setPointOfInterestExternalResourcePK(pk); - poiExternalResource.setResourceIdentifier(identifierField.getWebValue()); - SessionControllerGetter.getPointOfInterestBean().storePointOfInterestExternalResource(poiExternalResource); + Integer externalResourceId = Integer.valueOf(key); + FormField identifierField = externalResourceIdentifiers.get(key); + if(identifierField.getWebValue() == null || identifierField.getWebValue().isEmpty()) + { + // We delete existing if unset + SessionControllerGetter.getPointOfInterestBean().deletePointOfInterestExternalResource(weatherStation.getPointOfInterestId(), externalResourceId); + } + else + { + + PointOfInterestExternalResource poiExternalResource = new PointOfInterestExternalResource(); + PointOfInterestExternalResourcePK pk = new PointOfInterestExternalResourcePK(weatherStation.getPointOfInterestId(), externalResourceId); + poiExternalResource.setPointOfInterestExternalResourcePK(pk); + poiExternalResource.setResourceIdentifier(identifierField.getWebValue()); + SessionControllerGetter.getPointOfInterestBean().storePointOfInterestExternalResource(poiExternalResource); + } } } - // Redirect to form response.sendRedirect(new StringBuilder(Globals.PROTOCOL + "://") .append(ServletUtil.getServerName(request)) diff --git a/src/main/java/no/nibio/vips/logic/controller/session/ForecastBean.java b/src/main/java/no/nibio/vips/logic/controller/session/ForecastBean.java index 71d487cfc981054d8e79bf1db1c9570465f6c158..f06f13638eba40806db40747f377ef80f02bf261 100755 --- a/src/main/java/no/nibio/vips/logic/controller/session/ForecastBean.java +++ b/src/main/java/no/nibio/vips/logic/controller/session/ForecastBean.java @@ -324,7 +324,7 @@ public class ForecastBean { .setParameter("organizationIds", organizationIds) .getResultList(); - if(!organizationUsers.isEmpty()) + if(!organizationUsers.isEmpty() && ! modelIds.isEmpty()) { return em .createNamedQuery("ForecastConfiguration.findByVipsLogicUserIdsAndModelIdsAndDate") diff --git a/src/main/java/no/nibio/vips/logic/controller/session/PointOfInterestBean.java b/src/main/java/no/nibio/vips/logic/controller/session/PointOfInterestBean.java index 8f1534f66f9046e2ed3fb78086db4697fafcb8ca..30180926c14fda836a4e30867a0db480bce741f7 100755 --- a/src/main/java/no/nibio/vips/logic/controller/session/PointOfInterestBean.java +++ b/src/main/java/no/nibio/vips/logic/controller/session/PointOfInterestBean.java @@ -30,7 +30,6 @@ import de.micromata.opengis.kml.v_2_2_0.Point; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.ResourceBundle; import java.util.Set; @@ -252,8 +251,14 @@ public class PointOfInterestBean { * @return */ public List<PointOfInterestWeatherStation> getWeatherstationsForOrganization(Organization organization, Boolean active) { + + if(organization == null) + { + return new ArrayList<>(); + } // Avoid nulls active = active == null ? true : active; + List<PointOfInterestWeatherStation> retVal = em.createNamedQuery("PointOfInterestWeatherStation.findByActivityAndOrganizationId", PointOfInterestWeatherStation.class) .setParameter("organizationId", organization) .setParameter("active", active) @@ -455,5 +460,10 @@ public class PointOfInterestBean { } + public WeatherStationDataSource getWeatherStationDataSource(String dataSourceName){ + return em.createNamedQuery("WeatherStationDataSource.findByName", WeatherStationDataSource.class) + .setParameter("name", dataSourceName) + .getSingleResult(); + } } diff --git a/src/main/java/no/nibio/vips/logic/controller/session/SchedulingBean.java b/src/main/java/no/nibio/vips/logic/controller/session/SchedulingBean.java index 7fa9924f042dc7277bcc80c4ca3ff39a94a10505..2356256e90efc92919c68920323f4007b02f4bb3 100755 --- a/src/main/java/no/nibio/vips/logic/controller/session/SchedulingBean.java +++ b/src/main/java/no/nibio/vips/logic/controller/session/SchedulingBean.java @@ -198,17 +198,22 @@ public class SchedulingBean { SchedulingPattern everyNightPattern = new SchedulingPattern("0 6 * * *"); deleteAllExpiredUserUuidsCollector.getTasks().add(everyNightPattern, VipsLogicTaskFactory.createVipsLogicTask(VipsLogicTaskFactory.DELETE_ALL_EXPIRED_UUIDS_TASK)); - // Sendt forecast notifications + // Send forecast notifications VIPSLogicTaskCollector sendForecastNotificationsCollector = new VIPSLogicTaskCollector(-1); SchedulingPattern morningAndAfternoonPattern = new SchedulingPattern("45 6,12 * * *"); sendForecastNotificationsCollector.getTasks().add(morningAndAfternoonPattern, VipsLogicTaskFactory.createVipsLogicTask(VipsLogicTaskFactory.SEND_FORECAST_EVENT_NOTIFICATIONS_TASK)); + // Run grid models + VIPSLogicTaskCollector runGridModelsCollector = new VIPSLogicTaskCollector(-1); + runGridModelsCollector.getTasks().add(everyNightPattern, VipsLogicTaskFactory.createVipsLogicTask(VipsLogicTaskFactory.RUN_GRID_MODELS_TASK)); + List<TaskCollector> definedTasks = new ArrayList<>(); definedTasks.add(modelRunCollector); definedTasks.add(cacheHandlerCollector); definedTasks.add(summariesCollector); definedTasks.add(deleteAllExpiredUserUuidsCollector); - definedTasks.add((sendForecastNotificationsCollector)); + definedTasks.add(sendForecastNotificationsCollector); + definedTasks.add(runGridModelsCollector); return definedTasks; } diff --git a/src/main/java/no/nibio/vips/logic/controller/session/UserBean.java b/src/main/java/no/nibio/vips/logic/controller/session/UserBean.java index bc2e135ee21b184ba6cc08b65ce1f264c77f686c..25ca99b446296785deaa2b8683a2ad46eba40c98 100755 --- a/src/main/java/no/nibio/vips/logic/controller/session/UserBean.java +++ b/src/main/java/no/nibio/vips/logic/controller/session/UserBean.java @@ -721,14 +721,18 @@ public class UserBean { } } - // TODO: Put in config file? - private final static String[] USER_COUNTRY_CODES = {"NO","SE","BA","LV","US"}; - public List<Country> getUserCountries() { - return em.createNamedQuery("Country.findByCountryCodes") - .setParameter("countryCodes",Arrays.asList(UserBean.USER_COUNTRY_CODES)) - .getResultList(); + try + { + return em.createNamedQuery("Country.findByCountryCodes") + .setParameter("countryCodes",Arrays.asList(System.getProperty("no.nibio.vips.logic.USER_COUNTRY_CODES").split(","))) + .getResultList(); + } + catch(NullPointerException ex) + { + return new ArrayList<>(); + } } public List<OrganizationGroup> getOrganizationGroups(Organization organization) { 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 d2856d038f94bec0e2aab92dce020011e4bc75ef..de0e904122e95c3a264c86ab3a6ee1595fd14270 100755 --- a/src/main/java/no/nibio/vips/logic/entity/ForecastConfiguration.java +++ b/src/main/java/no/nibio/vips/logic/entity/ForecastConfiguration.java @@ -44,7 +44,11 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import java.util.List; import java.util.TimeZone; import javax.persistence.Transient; +import no.nibio.vips.logic.util.IntegerArrayUserType; import no.nibio.vips.util.WeatherUtil; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.hibernate.annotations.TypeDefs; /** * @copyright 2014-2016 <a href="http://www.nibio.no/">NIBIO</a> @@ -53,6 +57,7 @@ import no.nibio.vips.util.WeatherUtil; @Entity @Table(name = "forecast_configuration") @XmlRootElement +@TypeDefs( {@TypeDef( name= "IntegerArray", typeClass = IntegerArrayUserType.class)}) @NamedQueries({ @NamedQuery(name = "ForecastConfiguration.findAll", query = "SELECT f FROM ForecastConfiguration f WHERE f.isPrivate = FALSE"), @NamedQuery(name = "ForecastConfiguration.findByForecastConfigurationId", query = "SELECT f FROM ForecastConfiguration f WHERE f.forecastConfigurationId = :forecastConfigurationId"), @@ -117,6 +122,10 @@ public class ForecastConfiguration implements Serializable, Comparable { @Column(name = "is_private") private Boolean isPrivate; + @Type(type = "IntegerArray") + @Column(name = "grid_weather_station_point_of_interest_ids") + private Integer[] gridWeatherStationPointOfInterestIds; + @Transient private WeatherUtil weatherUtil; @@ -391,4 +400,18 @@ public class ForecastConfiguration implements Serializable, Comparable { public void setIsPrivate(Boolean isPrivate) { this.isPrivate = isPrivate; } + + /** + * @return the gridWeatherStationPointOfInterestIds + */ + public Integer[] getGridWeatherStationPointOfInterestIds() { + return gridWeatherStationPointOfInterestIds; + } + + /** + * @param gridWeatherStationPointOfInterestIds the gridWeatherStationPointOfInterestIds to set + */ + public void setGridWeatherStationPointOfInterestIds(Integer[] gridWeatherStationPointOfInterestIds) { + this.gridWeatherStationPointOfInterestIds = gridWeatherStationPointOfInterestIds; + } } 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 46397415e483e57315bc37d7600783dd08844168..b948b010975321fd4679785c194b5b5a810cfa09 100755 --- a/src/main/java/no/nibio/vips/logic/entity/Organization.java +++ b/src/main/java/no/nibio/vips/logic/entity/Organization.java @@ -83,6 +83,7 @@ public class Organization implements Serializable { private Integer defaultMapZoom; @Column(name = "default_time_zone") private String defaultTimeZone; + @JsonIgnore @Column(name = "default_vips_core_user_id") private Integer defaultVipsCoreUserId; @JsonIgnore diff --git a/src/main/java/no/nibio/vips/logic/entity/PreparationType.java b/src/main/java/no/nibio/vips/logic/entity/PreparationType.java index 12c2d1d413ed41604b38caa6a52eee8ac34ada49..623c0234587d2dca2e6cafe8716137b77fc37a5f 100755 --- a/src/main/java/no/nibio/vips/logic/entity/PreparationType.java +++ b/src/main/java/no/nibio/vips/logic/entity/PreparationType.java @@ -19,6 +19,7 @@ package no.nibio.vips.logic.entity; +import com.fasterxml.jackson.annotation.JsonIgnore; import java.io.Serializable; import java.util.Collection; import javax.persistence.Basic; @@ -55,6 +56,7 @@ public class PreparationType implements Serializable { @Size(max = 255) @Column(name = "preparation_type_name") private String preparationTypeName; + @JsonIgnore @OneToMany(mappedBy = "preparationTypeId") private Collection<Preparation> preparationCollection; diff --git a/src/main/java/no/nibio/vips/logic/messaging/MessagingBean.java b/src/main/java/no/nibio/vips/logic/messaging/MessagingBean.java index 971ac29c57481604e04eba28ec81de5c324aea16..c35279641cc1014f62c2aa79121dd59c00e7f506 100755 --- a/src/main/java/no/nibio/vips/logic/messaging/MessagingBean.java +++ b/src/main/java/no/nibio/vips/logic/messaging/MessagingBean.java @@ -60,7 +60,7 @@ public class MessagingBean { public void sendUniversalMessage(Message message) { - String msgDownloadUrlTpl = "https://www.vips-landbruk.no/"; + String msgDownloadUrlTpl = "https://www.vips-landbruk.no/messages/" + message.getMessageId() + "/"; // Create a universal message from the message // TODO: When UniversalMessage has changed, pick UniversalMessage uMessage = new UniversalMessage(); @@ -401,7 +401,7 @@ public class MessagingBean { { return; } - String msgDownloadUrlTpl = "https://www.vips-landbruk.no/"; + String msgDownloadUrlTpl = "https://www.vips-landbruk.no/observations/" + observation.getObservationId() + "/"; // Create a universal message from the message // TODO: When UniversalMessage has changed, pick UniversalMessage uMessage = new UniversalMessage(); @@ -412,7 +412,9 @@ public class MessagingBean { uMessage.setExpiresAt(cal.getTime()); // For locale, we assume observer's language VipsLogicUser observer = em.find(VipsLogicUser.class, observation.getUserId()); - uMessage.addMessageLocalVersion(observer.getPreferredLocale(), observation.getObservationHeading(), "", observation.getObservationText(), msgDownloadUrlTpl); + uMessage.addMessageLocalVersion(observer.getPreferredLocale(), observation.getObservationHeading(), "", + observation.getObservationText() + , msgDownloadUrlTpl); // Find the suscribers, create distribution list uMessage.setDistributionList(this.getObservationNotificationSubscribers(observation)); diff --git a/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/AppleFruitMothBean.java b/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/AppleFruitMothBean.java index 1776077651f8bbd4d1adf75dd781106c966a5564..92c07b8aae75114b2892e04652b4d70278160e34 100755 --- a/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/AppleFruitMothBean.java +++ b/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/AppleFruitMothBean.java @@ -31,11 +31,14 @@ import de.micromata.opengis.kml.v_2_2_0.Units; import de.micromata.opengis.kml.v_2_2_0.Vec2; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; import javax.ejb.LocalBean; import javax.ejb.Stateless; import javax.persistence.EntityManager; +import javax.persistence.NoResultException; import javax.persistence.PersistenceContext; +import javax.persistence.Query; import no.nibio.vips.logic.util.Globals; /** @@ -54,6 +57,22 @@ public class AppleFruitMothBean { List<ObservationSite> sites = em.createNamedQuery("ObservationSite.findAll").getResultList(); return sites; } + + public List<ObservationSite> getObservationSitesWithClusterCountLastUpdate(){ + List<ObservationSite> sites = this.getObservationSites(); + Query qu = em.createNativeQuery("SELECT max(last_updated) FROM applefruitmoth.observation_site_point_season_data osd WHERE osd.observation_site_point_id IN (SELECT observation_site_point_id FROM applefruitmoth.observation_site_point WHERE observation_site_id = :observationSiteId)"); + sites.stream().forEach(site->{ + qu.setParameter("observationSiteId", site.getObservationSiteId()); + try + { + Date lastUpdated = (Date) qu.getSingleResult(); + site.setLastUpdatedClusterCount(lastUpdated); + } + catch(NoResultException ex){} + }); + + return sites; + } public ObservationSite getObservationSite(Integer observationSiteId) { return em.find(ObservationSite.class, observationSiteId); diff --git a/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/AppleFruitMothController.java b/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/AppleFruitMothController.java index c048e907b747dfe2c4904983027447bccdfa9efa..e4f3ca5df594f6b3d04c8c7f6137eef0e083a1b0 100755 --- a/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/AppleFruitMothController.java +++ b/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/AppleFruitMothController.java @@ -70,7 +70,7 @@ public class AppleFruitMothController extends HttpServlet { if(action == null) { // Show map and list of existing stations - List<ObservationSite> observationSites = SessionControllerGetter.getAppleFruitMothBean().getObservationSites(); + List<ObservationSite> observationSites = SessionControllerGetter.getAppleFruitMothBean().getObservationSitesWithClusterCountLastUpdate(); request.setAttribute("observationSites", observationSites); request.getRequestDispatcher("/appleFruitMothStationList.ftl").forward(request, response); } @@ -284,9 +284,11 @@ public class AppleFruitMothController extends HttpServlet { ObservationSite observationSite = SessionControllerGetter.getAppleFruitMothBean().getObservationSite(observationSiteId); ObservationSiteSeasonCommonData seasonCommonData = observationSite.getCommonDataForSeason(currentSeason); - seasonCommonData.setRemarks(request.getParameter("remarks")); - SessionControllerGetter.getAppleFruitMothBean().storeObservationSitePointSeasonCommonData(seasonCommonData); - + if(seasonCommonData != null) + { + seasonCommonData.setRemarks(request.getParameter("remarks") != null ? request.getParameter("remarks") : ""); + SessionControllerGetter.getAppleFruitMothBean().storeObservationSitePointSeasonCommonData(seasonCommonData); + } //System.out.println("sitePointSetLength=" + observationSite.getObservationSitePointSet().size()); treeNames.stream().forEach(treeName->{ try { @@ -311,6 +313,7 @@ public class AppleFruitMothController extends HttpServlet { newData.setAutumnDate(autumnDate); newData.setSpringValue(springValue); newData.setSpringDate(springDate); + newData.setLastUpdated(new Date()); SessionControllerGetter.getAppleFruitMothBean().storeObservationSitePointSeasonData(newData); //dataSet.add(newData); diff --git a/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/ObservationSite.java b/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/ObservationSite.java index 716bd8a3e8ad935fab205a6e72c86f355b10b8e5..bf96fa03642907de80bd4e7c0b8caef084f8b059 100755 --- a/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/ObservationSite.java +++ b/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/ObservationSite.java @@ -21,6 +21,7 @@ package no.nibio.vips.logic.modules.applefruitmoth; import com.vividsolutions.jts.geom.Geometry; import java.io.Serializable; +import java.util.Date; import java.util.HashSet; import java.util.Set; import javax.persistence.Basic; @@ -78,6 +79,8 @@ public class ObservationSite implements Serializable, Comparable { @JoinColumn(name = "gis_id", referencedColumnName="gis_id") @OneToOne private Gis gisId; + @Transient + private Date lastUpdatedClusterCount; public ObservationSite() { @@ -184,4 +187,18 @@ public class ObservationSite implements Serializable, Comparable { public void setGisId(Gis gisId) { this.gisId = gisId; } + + /** + * @return the lastUpdatedClusterCount + */ + public Date getLastUpdatedClusterCount() { + return lastUpdatedClusterCount; + } + + /** + * @param lastUpdatedClusterCount the lastUpdatedClusterCount to set + */ + public void setLastUpdatedClusterCount(Date lastUpdatedClusterCount) { + this.lastUpdatedClusterCount = lastUpdatedClusterCount; + } } diff --git a/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/ObservationSitePointSeasonData.java b/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/ObservationSitePointSeasonData.java index f45d325c969924f9220ef071324ffa32e27297a0..7dc105836277ec94e71dd6801e0d76cf3249e011 100755 --- a/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/ObservationSitePointSeasonData.java +++ b/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/ObservationSitePointSeasonData.java @@ -67,6 +67,9 @@ public class ObservationSitePointSeasonData implements Serializable { @JoinColumn(name = "observation_site_point_id", referencedColumnName = "observation_site_point_id", insertable = false, updatable = false) @ManyToOne(optional = false) private ObservationSitePoint observationSitePoint; + @Column(name = "last_updated") + @Temporal(TemporalType.DATE) + private Date lastUpdated; public ObservationSitePointSeasonData() { } @@ -152,4 +155,18 @@ public class ObservationSitePointSeasonData implements Serializable { return "no.nibio.vips.logic.modules.applefruitmoth.ObservationSitePointSeasonData[ observationSitePointSeasonDataPK=" + observationSitePointSeasonDataPK + " ]"; } + /** + * @return the lastUpdated + */ + public Date getLastUpdated() { + return lastUpdated; + } + + /** + * @param lastUpdated the lastUpdated to set + */ + public void setLastUpdated(Date lastUpdated) { + this.lastUpdated = lastUpdated; + } + } diff --git a/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/ObservationSiteSeasonCommonData.java b/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/ObservationSiteSeasonCommonData.java index 27842f454f9411a4a4d509667ab30aa9cdc10fdd..fbe051b142a539472f03319bd1f3c6529ff5cca0 100755 --- a/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/ObservationSiteSeasonCommonData.java +++ b/src/main/java/no/nibio/vips/logic/modules/applefruitmoth/ObservationSiteSeasonCommonData.java @@ -30,6 +30,8 @@ import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; import javax.xml.bind.annotation.XmlRootElement; /** @@ -59,6 +61,7 @@ public class ObservationSiteSeasonCommonData implements Serializable { @Column(name = "remarks") private String remarks; @Column(name = "last_updated") + @Temporal(TemporalType.DATE) private Date lastUpdated; @JoinColumn(name = "observation_site_id", referencedColumnName = "observation_site_id", insertable = false, updatable = false) @ManyToOne(optional = false) diff --git a/src/main/java/no/nibio/vips/logic/modules/roughage/RoughageService.java b/src/main/java/no/nibio/vips/logic/modules/roughage/RoughageService.java index cfe05d635f1d1905a2e17de9ada75aefcbefc266..c8cd224ac8b10bc85198d61f974bcf2cdd017217 100755 --- a/src/main/java/no/nibio/vips/logic/modules/roughage/RoughageService.java +++ b/src/main/java/no/nibio/vips/logic/modules/roughage/RoughageService.java @@ -369,6 +369,12 @@ public class RoughageService { Organization org = em.find(Organization.class, organizationId); Integer VIPSCoreUserId = org.getDefaultVipsCoreUserId(); + /*System.out.println("Antall ordinære værdata: " + observations.size()); + for(WeatherObservation obs:observations) + { + System.out.println(obs.toString()); + }*/ + List<Result> results; try { @@ -376,6 +382,7 @@ public class RoughageService { } catch(RunModelException ex) { + //System.out.println("Feilen skjer med ordinære værdata"); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ex.getMessage()).build(); } // NORMALDATA @@ -402,6 +409,10 @@ public class RoughageService { latestObsDate ); + // The observations are tainted with the _NORMAL prefix, must clean up + // before sending to model + observations.stream().forEach(obs->obs.setElementMeasurementTypeId(obs.getElementMeasurementTypeId().substring(0, obs.getElementMeasurementTypeId().indexOf("_NORMAL")))); + // Add waterings to normal data precipitation? if( wateringAffectsNormalData != null && wateringAffectsNormalData.equals("true") @@ -418,6 +429,7 @@ public class RoughageService { //System.out.println("Old: " + obs.getValue() + ", new: " + (obs.getValue() + wateringMap.get(obs.getTimeMeasured()))); obs.setValue(obs.getValue() + wateringMap.get(obs.getTimeMeasured())); } + } } /*System.out.println("Antall normaldata: " + observations.size()); diff --git a/src/main/java/no/nibio/vips/logic/scheduling/model/grid/preprocessor/ZymoseptoriaSimpleRiskGridModelPreprocessor.java b/src/main/java/no/nibio/vips/logic/scheduling/model/grid/preprocessor/ZymoseptoriaSimpleRiskGridModelPreprocessor.java index 4c76b48195fe64b623248a0fc6f89e7f8fc6897d..4dd372e9dbc7864a752b36e597a0fc5e0f9aa684 100644 --- a/src/main/java/no/nibio/vips/logic/scheduling/model/grid/preprocessor/ZymoseptoriaSimpleRiskGridModelPreprocessor.java +++ b/src/main/java/no/nibio/vips/logic/scheduling/model/grid/preprocessor/ZymoseptoriaSimpleRiskGridModelPreprocessor.java @@ -21,6 +21,8 @@ package no.nibio.vips.logic.scheduling.model.grid.preprocessor; import com.vividsolutions.jts.geom.Coordinate; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -46,26 +48,48 @@ public class ZymoseptoriaSimpleRiskGridModelPreprocessor extends ModelRunPreproc @Override public ModelConfiguration getModelConfiguration(ForecastConfiguration configuration) throws PreprocessorException { + ModelConfiguration modelConfig = new ModelConfiguration(); // Which weather stations?? // Ilseng and Apelsvoll to start with ;-) - List<PointOfInterestWeatherStation> stations = Stream.of(new Integer[]{50,91,87}).map( + List<PointOfInterestWeatherStation> stations = Stream.of(configuration.getGridWeatherStationPointOfInterestIds()).map( stationId -> (PointOfInterestWeatherStation) SessionControllerGetter.getPointOfInterestBean().getPointOfInterest(stationId) ).collect(Collectors.toList()); List<PointWeatherObservationList> allObs = new ArrayList<>(); - + WeatherUtil wUtil = new WeatherUtil(); for(PointOfInterestWeatherStation station:stations) { Coordinate coordinate = new Coordinate(station.getPointOfInterest().getLongitude(), station.getPointOfInterest().getLatitude()); List<WeatherObservation> stationObs = getStationObs(station, configuration); - PointWeatherObservationList pointObs = new PointWeatherObservationList(coordinate, stationObs); + try + { + // We need TM, UM and RR. BT is optional + List<WeatherObservation> mandatory = wUtil.filterWeatherObservationsByParameter(stationObs, new HashSet<>(Arrays.asList("TM","UM","RR"))); + stationObs = wUtil.checkForAndFixHourlyTimeSeriesHolesMultiParameter(mandatory, 6); + } + catch(WeatherObservationListException ex) + { + throw new PreprocessorException("Problem with station #" + station.getPointOfInterestId() + ": " + ex.getMessage()); + } + // Checking for BT + try + { + List<WeatherObservation> BT = wUtil.filterWeatherObservationsByParameter(stationObs, new HashSet<>(Arrays.asList("BT"))); + stationObs.addAll(wUtil.checkForAndFixHourlyTimeSeriesHoles(BT, 6)); + } + catch(WeatherObservationListException ex) + { + + } + + PointWeatherObservationList pointObs = new PointWeatherObservationList(coordinate, stationObs, station.getTimeZone()); allObs.add(pointObs); } modelConfig.setConfigParameter("multiPointWeatherObservations", allObs); modelConfig.setModelId(this.getModelId()); - modelConfig.setConfigParameter("timeZone", stations.get(0).getTimeZone()); + modelConfig.setConfigParameter("timeZone", configuration.getTimeZone()); return modelConfig; } diff --git a/src/main/java/no/nibio/vips/logic/scheduling/model/preprocessor/FinnCerealModelsPreprocessor.java b/src/main/java/no/nibio/vips/logic/scheduling/model/preprocessor/FinnCerealModelsPreprocessor.java index 790d467bb0fe60f94a173561336098e7bdf480bd..d648ac9f093e615a3665ae430e2adbe418c5f032 100644 --- a/src/main/java/no/nibio/vips/logic/scheduling/model/preprocessor/FinnCerealModelsPreprocessor.java +++ b/src/main/java/no/nibio/vips/logic/scheduling/model/preprocessor/FinnCerealModelsPreprocessor.java @@ -2,10 +2,7 @@ package no.nibio.vips.logic.scheduling.model.preprocessor; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; -import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; @@ -15,7 +12,6 @@ import no.nibio.vips.logic.entity.ForecastConfiguration; import no.nibio.vips.logic.entity.PointOfInterestWeatherStation; import no.nibio.vips.logic.scheduling.model.ModelRunPreprocessor; import no.nibio.vips.logic.scheduling.model.PreprocessorException; -import no.nibio.vips.model.ConfigValidationException; import no.nibio.vips.util.WeatherElements; import no.nibio.vips.util.WeatherObservationListException; import no.nibio.vips.util.WeatherUtil; 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 70d882c833888b18315e44019826620fa2855104..1a057da43767ec296470e3242448b463fbd2fd2e 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 @@ -115,20 +115,25 @@ public class RunAllForecastConfigurationsTask extends VipsLogicTask{ ex.getMessage(), SchedulingUtil.MESSAGE_STATUS_DANGER) ); - //System.out.println("Error caught"); - continue; + //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); } else { noForecastConfigurationsFound = true; + //System.out.println("noForecastConfigurationsFound == true!!"); } + //System.out.println("Current completeness=" + tec.getTaskExecutor().getCompleteness()); } } @@ -138,9 +143,12 @@ public class RunAllForecastConfigurationsTask extends VipsLogicTask{ 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) { - //System.out.println("Error detected"); + //System.out.println("Error detected, RuntimeException thrown just after this"); tec.setStatusMessage(errorMessage.toString()); throw new RuntimeException(); } diff --git a/src/main/java/no/nibio/vips/logic/service/LogicService.java b/src/main/java/no/nibio/vips/logic/service/LogicService.java index 837b41de094bf1270fe5a50bcfd35fd585211f48..c3f4a7d0e3989d6b320450b6f4c2981e7a16c9b1 100755 --- a/src/main/java/no/nibio/vips/logic/service/LogicService.java +++ b/src/main/java/no/nibio/vips/logic/service/LogicService.java @@ -859,6 +859,14 @@ public class LogicService { } } + @GET + @Path("organization") + @Produces("application/json;charset=UTF-8") + public Response getOrganizations() + { + return Response.ok().entity(SessionControllerGetter.getUserBean().getOrganizations()).build(); + } + /** * Get the client to use for calling VIPSCoreManager REST services programmatically * @return diff --git a/src/main/java/no/nibio/vips/logic/service/ModelFormService.java b/src/main/java/no/nibio/vips/logic/service/ModelFormService.java new file mode 100644 index 0000000000000000000000000000000000000000..3a00a583c3c5499536ece87dc2298010cb88c671 --- /dev/null +++ b/src/main/java/no/nibio/vips/logic/service/ModelFormService.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2018 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSLogic. + * 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. + * + * 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 + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with VIPSLogic. If not, see <http://www.nibio.no/licenses/>. + * + */ + +package no.nibio.vips.logic.service; + +import com.vividsolutions.jts.geom.Coordinate; +import com.webcohesion.enunciate.metadata.Facet; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Response; +import no.nibio.vips.entity.ModelConfiguration; +import no.nibio.vips.entity.Result; +import no.nibio.vips.entity.WeatherObservation; +import no.nibio.vips.gis.GISUtil; +import no.nibio.vips.logic.entity.Organization; +import no.nibio.vips.logic.entity.PointOfInterestWeatherStation; +import no.nibio.vips.logic.entity.WeatherStationDataSource; +import no.nibio.vips.logic.util.RunModelException; +import no.nibio.vips.logic.util.SessionControllerGetter; +import no.nibio.vips.logic.util.SystemTime; +import no.nibio.vips.util.ParseRESTParamUtil; +import no.nibio.vips.util.WeatherElements; +import no.nibio.vips.util.WeatherUtil; +import no.nibio.vips.util.XDate; +import no.nibio.vips.util.weather.WeatherDataSourceException; +import no.nibio.vips.util.weather.WeatherDataSourceUtil; + +/** + * This is a collection of services for models run from forms (not as part of batch) + * @copyright 2018 <a href="http://www.nibio.no/">NIBIO</a> + * @author Tor-Einar Skog <tor-einar.skog@nibio.no> + */ +@Path("rest/modelform") +@Facet("restricted") +public class ModelFormService { + + @GET + @Path("SEPTORIAHU/runmodel") + @Produces("application/json;charset=UTF-8") + public Response runSeptoriaHumidityModel( + @QueryParam("organizationId_countryCode") String organizationId_countryCode, + @QueryParam("weatherStationId") String weatherStationId, // Could be special ID from Danish system, + @QueryParam("dateSpraying1") String dateSpraying1, + @QueryParam("dateSpraying2") String dateSpraying2, + @QueryParam("dateGs31") String dateGs31, + @QueryParam("date3rdUpperLeafEmerging") String date3rdUpperLeafEmerging, + @QueryParam("date2ndUpperLeafEmerging") String date2ndUpperLeafEmerging, + @QueryParam("dateUpperLeafEmerging") String dateUpperLeafEmerging, + @QueryParam("dateGs75") String dateGs75, + @QueryParam("thresholdRelativeHumidity") Double thresholdRelativeHumidity, + @QueryParam("thresholdLeafWetness") Double thresholdLeafWetness, + @QueryParam("thresholdPrecipitation") Double thresholdPrecipitation, + @QueryParam("slidingHoursPast") Integer slidingHoursPast, + @QueryParam("slidingHoursAhead") Integer slidingHoursAhead, + @QueryParam("thresholdHumidPeriodHours") Integer thresholdHumidPeriodHours, + @QueryParam("sprayingProtectionDays") Integer sprayingProtectionDays, + @QueryParam("leafLifeTime") Integer leafLifeTime + ){ + try + { + ModelConfiguration mConf = new ModelConfiguration(); + mConf.setModelId("SEPTORIAHU"); + mConf.setConfigParameter("dateSpraying1", dateSpraying1); + mConf.setConfigParameter("dateSpraying2", dateSpraying2); + mConf.setConfigParameter("dateGs31", dateGs31); + mConf.setConfigParameter("date3rdUpperLeafEmerging", date3rdUpperLeafEmerging); + mConf.setConfigParameter("date2ndUpperLeafEmerging", date2ndUpperLeafEmerging); + mConf.setConfigParameter("dateUpperLeafEmerging", dateUpperLeafEmerging); + mConf.setConfigParameter("dateGs75", dateGs75); + mConf.setConfigParameter("thresholdRelativeHumidity", thresholdRelativeHumidity); + mConf.setConfigParameter("thresholdLeafWetness", thresholdLeafWetness); + mConf.setConfigParameter("thresholdPrecipitation", thresholdPrecipitation); + mConf.setConfigParameter("slidingHoursPast", slidingHoursPast); + mConf.setConfigParameter("slidingHoursAhead", slidingHoursAhead); + mConf.setConfigParameter("thresholdHumidPeriodHours", thresholdHumidPeriodHours); + mConf.setConfigParameter("sprayingProtectionDays", sprayingProtectionDays); + mConf.setConfigParameter("leafLifeTime", leafLifeTime); + + // Data parsing + + Integer organizationId = Integer.valueOf(organizationId_countryCode.split("_")[0]); + Organization organization = SessionControllerGetter.getUserBean().getOrganization(organizationId); + mConf.setConfigParameter("timeZone", organization.getDefaultTimeZone()); + TimeZone timeZone = TimeZone.getTimeZone(organization.getDefaultTimeZone()); + ParseRESTParamUtil pUtil = new ParseRESTParamUtil(); + // Start time is gs31, easy + Date gs31 = pUtil.parseISODate(dateGs31,timeZone); + XDate startTime = new XDate(gs31); + startTime.addDays(-1); + // End time is whatever comes first of the day after tomorrow or Gs75 + Date gs75 = pUtil.parseISODate(dateGs75, timeZone); + XDate dayAfterTomorrow = new XDate(SystemTime.getSystemTime()); + dayAfterTomorrow.addDays(2); + WeatherUtil wUtil = new WeatherUtil(); + dayAfterTomorrow = new XDate(wUtil.pragmaticAdjustmentToMidnight(dayAfterTomorrow, timeZone)); + // The first check here is to see if the systemtime is too early + Date endTime = dayAfterTomorrow.after(gs75) ? gs75 : dayAfterTomorrow; + + String countryCode = organizationId_countryCode.split("_")[1]; + List<WeatherObservation> observations; + WeatherDataSourceUtil wdsUtil = new WeatherDataSourceUtil(); + PointOfInterestWeatherStation ws; + if(countryCode.toLowerCase().equals("dk")){ + // Create a synthetic weather station to pass into the system + // Weather station id is a UTM32N coordinate, e.g. E552700N6322400 + String[] parts = weatherStationId.split("N"); + Integer UTM32vE = Integer.valueOf(parts[0].substring(1)); + Integer UTM32vN = Integer.valueOf(parts[1]); + GISUtil gisUtil = new GISUtil(); + Coordinate UTMc = new Coordinate(UTM32vE, UTM32vN); + Coordinate coordinate = gisUtil.convertCoordinate(UTMc, "EPSG:32632", "EPSG:4326"); + WeatherStationDataSource wsds = SessionControllerGetter.getPointOfInterestBean().getWeatherStationDataSource("DMI PointWeb"); + ws = new PointOfInterestWeatherStation(); + ws.setWeatherStationDataSourceId(wsds); + ws.setWeatherStationRemoteId(coordinate.y + "," + coordinate.x);// For some reason, The transformation switches X/Y + ws.setTimeZone(organization.getDefaultTimeZone()); + + //observations.stream().forEach(obs->System.out.println(obs.toString())); + } + else + { + // Weather station id maps to a regular weather station + ws = (PointOfInterestWeatherStation) SessionControllerGetter.getPointOfInterestBean().getPointOfInterest(Integer.valueOf(weatherStationId)); + } + observations = wdsUtil.getWeatherObservations( + ws, + WeatherObservation.LOG_INTERVAL_ID_1H, + new String[]{ + WeatherElements.TEMPERATURE_MEAN, + WeatherElements.PRECIPITATION, + WeatherElements.RELATIVE_HUMIDITY_MEAN, + WeatherElements.LEAF_WETNESS + }, + startTime, + endTime + ); + mConf.setConfigParameter("observations",observations); + + Integer VIPSCoreUserId = organization.getDefaultVipsCoreUserId(); + + List<Result>results = SessionControllerGetter.getForecastBean().runForecast(mConf, VIPSCoreUserId); + + return Response.ok().entity(results).build(); + } + catch(WeatherDataSourceException | RunModelException ex) + { + return Response.serverError().entity(ex.getMessage()).build(); + } + } +} diff --git a/src/main/java/no/nibio/vips/logic/service/WeatherProxyService.java b/src/main/java/no/nibio/vips/logic/service/WeatherProxyService.java index 842641bc29029c226f571007d988250cf022bb55..4f91248f398f8998233260616d2f2e6f25260ed3 100755 --- a/src/main/java/no/nibio/vips/logic/service/WeatherProxyService.java +++ b/src/main/java/no/nibio/vips/logic/service/WeatherProxyService.java @@ -23,7 +23,9 @@ import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.GeometryFactory; import com.webcohesion.enunciate.metadata.Facet; import java.io.UnsupportedEncodingException; +import java.net.URI; import java.net.URLDecoder; +import java.text.MessageFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -40,9 +42,11 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; import no.nibio.vips.entity.PointWeatherObservationList; import no.nibio.vips.entity.WeatherObservation; import no.nibio.vips.logic.util.SystemTime; +import no.nibio.vips.util.XDate; import no.nibio.vips.util.weather.ALabDataParser; import no.nibio.vips.util.weather.FruitWebDavisDataParser; import no.nibio.vips.util.weather.MetosAPIDataParser; @@ -90,6 +94,41 @@ public class WeatherProxyService { return Response.ok().entity(observations).build(); } + @GET + @POST + @Path("fmi/{stationId}") + @GZIP + @Produces("application/json;charset=UTF-8") + public Response getMetosRIMProWeatherData( + @PathParam("stationId") String stationId, + @FormParam("timeZone") String timeZonePOST, + @QueryParam("timeZone") String timeZoneGET, + @FormParam("startDate") String startDatePOST, + @QueryParam("startDate") String startDateGET, + @FormParam("endDate") String endDatePOST, + @QueryParam("endDate") String endDateGET + ) + { + String FMI_URL_TEMPLATE="http://www.cropinfra.com:8080/weather/resources/fmi/temporal/vips/{0}/{1}/{2}"; + List<WeatherObservation> observations; + try + { + String timeZoneParam = timeZonePOST != null ? timeZonePOST : timeZoneGET != null ? timeZoneGET : "UTC"; + TimeZone timeZone = TimeZone.getTimeZone(timeZoneParam); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + format.setTimeZone(timeZone); + String startDateParam = startDatePOST != null ? startDatePOST : startDateGET; + XDate startDate1 = new XDate(format.parse(startDateParam)); + String endDateParam = endDatePOST != null ? endDatePOST : endDateGET; + XDate endDate1 = new XDate(format.parse(endDateParam)); + URI uri = UriBuilder.fromUri(MessageFormat.format(FMI_URL_TEMPLATE, stationId, startDate1.getISOUTCFormat(), endDate1.getISOUTCFormat())).build(); + //observations = new MetosRIMProDataParser().getWeatherObservations(stationId, timeZone, startDate1); + return Response.temporaryRedirect(uri).build(); + } catch (ParseException | NullPointerException ex) { + return Response.serverError().entity(ex).build(); + } + } + @GET @POST @Path("metosapi/{stationId}") diff --git a/src/main/java/no/nibio/vips/logic/util/Globals.java b/src/main/java/no/nibio/vips/logic/util/Globals.java index 4874cf7f0983eee6228d569bcf86eea3cb55c75a..1f12188e6379392d360feade1dc8fbfdc12ce2f3 100755 --- a/src/main/java/no/nibio/vips/logic/util/Globals.java +++ b/src/main/java/no/nibio/vips/logic/util/Globals.java @@ -70,6 +70,7 @@ public class Globals { public static String[] availableTimeZones = { "UTC", "Etc/GMT-1", + "Europe/Copenhagen", "Europe/Oslo", "Europe/Sarajevo", "Europe/Stockholm", diff --git a/src/main/java/no/nibio/vips/util/weather/MetosAPIDataParser.java b/src/main/java/no/nibio/vips/util/weather/MetosAPIDataParser.java index 322d28cfe59ebf42db0bed1c17af6220d9555c2e..e42cc6568e46a7d1c2ec5289d7ef7eb58e19c6a3 100644 --- a/src/main/java/no/nibio/vips/util/weather/MetosAPIDataParser.java +++ b/src/main/java/no/nibio/vips/util/weather/MetosAPIDataParser.java @@ -230,7 +230,7 @@ public class MetosAPIDataParser { try { String accessToken = this.getToken(); - HttpGet httpget = new HttpGet("https://api.fieldclimate.com/v1/data/optimized/000024A0/hourly/from/" + (startDate.getTime() / 1000)); + HttpGet httpget = new HttpGet("https://api.fieldclimate.com/v1/data/optimized/" + stationId + "/hourly/from/" + (startDate.getTime() / 1000)); httpget.addHeader("Accept", "application/json"); httpget.addHeader("Authorization", "Bearer " + accessToken); diff --git a/src/main/java/no/nibio/vips/util/weather/WeatherDataSourceUtil.java b/src/main/java/no/nibio/vips/util/weather/WeatherDataSourceUtil.java index 3e94f1becf4cb2ed3391df2cce4e124fd890433f..e79b30a8169d8e02191051c01225b65059a437fc 100755 --- a/src/main/java/no/nibio/vips/util/weather/WeatherDataSourceUtil.java +++ b/src/main/java/no/nibio/vips/util/weather/WeatherDataSourceUtil.java @@ -189,6 +189,7 @@ public class WeatherDataSourceUtil { URLOutput = IOUtils.toString(URLStream); List<WeatherObservation> preliminaryResult = this.getWeatherObservations(URLOutput); List<WeatherObservation> filteredObservations = new ArrayList<>(); + //System.out.println(this.getClass().getName() + "/preliminaryResult.size()=" + preliminaryResult.size()); preliminaryResult.stream().filter((candidateObs) -> ( candidateObs.getLogIntervalId().equals(logIntervalId) && Arrays.asList(elementMeasurementTypes).contains(candidateObs.getElementMeasurementTypeId()) @@ -196,6 +197,7 @@ public class WeatherDataSourceUtil { ).forEachOrdered((candidateObs) -> { filteredObservations.add(candidateObs); }); + //System.out.println(this.getClass().getName() + "/filteredObservations.size()=" + filteredObservations.size()); return filteredObservations; } catch (IOException ex) { StringBuilder errorMessage = new StringBuilder().append("Could not fetch weather observations from URI ").append(URL.toString()).append(".\n"); diff --git a/src/main/java/no/nibio/vips/util/weather/dnmipointweb/DMIPointWebDataParser.java b/src/main/java/no/nibio/vips/util/weather/dnmipointweb/DMIPointWebDataParser.java index cf704d3132e63304eab0fa11118495f0687cd23d..36e7a08a24101257e25d55fb2a8eb191e56d28b8 100644 --- a/src/main/java/no/nibio/vips/util/weather/dnmipointweb/DMIPointWebDataParser.java +++ b/src/main/java/no/nibio/vips/util/weather/dnmipointweb/DMIPointWebDataParser.java @@ -19,7 +19,10 @@ package no.nibio.vips.util.weather.dnmipointweb; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; @@ -55,15 +58,16 @@ public class DMIPointWebDataParser { public List<WeatherObservation> getData(Double longitude, Double latitude, Date dateFrom, Date dateTo) { - List<WeatherObservation> retVal = new ArrayList<>(); + List<WeatherObservation> allObservations = new ArrayList<>(); + TimeZone danishTZ = TimeZone.getTimeZone("Europe/Copenhagen"); try { IWeatherService proxy = new WeatherService().getSslOffloadedBasicHttpBindingIWeatherService(); UseableArrayOfWeatherDataSource wdsource = new UseableArrayOfWeatherDataSource(); wdsource.add(WeatherDataSource.OBS); wdsource.add(WeatherDataSource.FORECAST); - wdsource.add(WeatherDataSource.NORMAL); - - GregorianCalendar gc = new GregorianCalendar(TimeZone.getTimeZone("Europe/Copenhagen")); + //wdsource.add(WeatherDataSource.NORMAL); + + GregorianCalendar gc = new GregorianCalendar(danishTZ); gc.setTime(dateFrom); XMLGregorianCalendar calFrom = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc); XMLGregorianCalendar calTo = null; @@ -79,7 +83,8 @@ public class DMIPointWebDataParser { listedParam -> wdparam.add(listedParam) ); - WeatherResponse result = proxy.getWeatherData( + //WeatherResponse result = proxy.getWeatherData( + WeatherResponse result = proxy.getWeatherDataExtended( String.valueOf(latitude).replace(".", ","), // Latitude ("X") Decimal separator is comma! String.valueOf(longitude).replace(".", ","), // Longitude ("Y") Decimal separator is comma! false, // true if UTM, false if decimal degrees @@ -90,18 +95,19 @@ public class DMIPointWebDataParser { calTo, // End of period with data wdparam, // Set of requested parameters 0.0, // Base temperature value - false // Use base temperature? (t/f) + false, // Use base temperature? (t/f) + true // Get 6 days of forecasts (only for the proxy.getWeatherDataExtended method) ); - - ArrayOfWeatherDataModel value = result.getWeahterDataList().getValue(); value.getWeatherDataModel().stream().forEach(weatherDataModel -> { DMIPointWebDataParser.PARAM_MAP.keySet().stream().forEach( VIPSParam->{ WeatherObservation obs = this.getWeatherObservation(VIPSParam, weatherDataModel); if(obs != null) - retVal.add(obs); + { + allObservations.add(obs); + } } ); } @@ -110,8 +116,24 @@ public class DMIPointWebDataParser { } catch (Exception ex) { Logger.getLogger(DMIPointWebDataParser.class.getName()).log(Level.SEVERE, null, ex); } - System.out.println("Number of extracted weather data = " + retVal.size()); - return retVal; + //System.out.println("Number of extracted weather data = " + retVal.size()); + // After "now", the DMI service provides forecast values seamlessly. After approx 48 hours these + // values turn into 6 hour intervals, which we can't use. We need to weed them out + Collections.sort(allObservations); + ZonedDateTime now = ZonedDateTime.now(); + ZonedDateTime lastValidObsTime = null; + ZoneId danishZ = ZoneId.of("Europe/Copenhagen"); + List<WeatherObservation> filteredObservations = new ArrayList<>(); + for(WeatherObservation obs:allObservations) + { + ZonedDateTime obsTime = ZonedDateTime.ofInstant(obs.getTimeMeasured().toInstant(),danishZ); + if(lastValidObsTime == null || obsTime.isBefore(now) || obsTime.minusHours(6).isBefore(lastValidObsTime)) + { + filteredObservations.add(obs); + lastValidObsTime = obsTime; + } + } + return allObservations; } private WeatherObservation getWeatherObservation(String VIPSParam, WeatherDataModel wDataModel) { diff --git a/src/main/java/no/nibio/vips/util/weather/metnothredds/MetNoThreddsDataParser.java b/src/main/java/no/nibio/vips/util/weather/metnothredds/MetNoThreddsDataParser.java index 5688971da1930b031e0c9d4141431d6be5c3b142..852901e3c9bc4a60c9df9178f4b4534e6f492253 100644 --- a/src/main/java/no/nibio/vips/util/weather/metnothredds/MetNoThreddsDataParser.java +++ b/src/main/java/no/nibio/vips/util/weather/metnothredds/MetNoThreddsDataParser.java @@ -297,7 +297,7 @@ public class MetNoThreddsDataParser { Map<Long, WeatherObservation> hashedObsForPoint = tempRetVal.get(c); List<WeatherObservation> obsList = new ArrayList(hashedObsForPoint.values()); Collections.sort(obsList); - PointWeatherObservationList obsForPoint = new PointWeatherObservationList(c,obsList); + PointWeatherObservationList obsForPoint = new PointWeatherObservationList(c,obsList,"UTC"); retVal.add(obsForPoint); } return retVal; diff --git a/src/main/java/no/nibio/web/forms/FormField.java b/src/main/java/no/nibio/web/forms/FormField.java index 4da5599561df0955b2fefb9c9c47adabd4926f1c..65e23918e16b8888104ca29dfe69439ca0273245 100755 --- a/src/main/java/no/nibio/web/forms/FormField.java +++ b/src/main/java/no/nibio/web/forms/FormField.java @@ -20,6 +20,7 @@ package no.nibio.web.forms; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Point; @@ -42,6 +43,7 @@ import java.util.TimeZone; * @copyright 2013 <a href="http://www.nibio.no/">NIBIO</a> * @author Tor-Einar Skog <tor-einar.skog@nibio.no> */ +@JsonIgnoreProperties(ignoreUnknown = true) public class FormField { public final static String DATA_TYPE_STRING = "STRING"; public final static String DATA_TYPE_INTEGER = "INTEGER"; diff --git a/src/main/java/no/nibio/web/forms/FormSelectOption.java b/src/main/java/no/nibio/web/forms/FormSelectOption.java index 9f510e37a1c867c7ed60d728575e6b2e6468a4ec..941ae5a03473b68ca6dcc33d071c0d67655e40eb 100755 --- a/src/main/java/no/nibio/web/forms/FormSelectOption.java +++ b/src/main/java/no/nibio/web/forms/FormSelectOption.java @@ -19,6 +19,8 @@ package no.nibio.web.forms; +import com.fasterxml.jackson.annotation.JsonIgnore; + /** * Represents an option in a select field * @copyright 2013 <a href="http://www.nibio.no/">NIBIO</a> diff --git a/src/main/resources/db/migration/V3__ForecastConfig_Gridstations.sql b/src/main/resources/db/migration/V3__ForecastConfig_Gridstations.sql new file mode 100644 index 0000000000000000000000000000000000000000..e994d1f290a7f7268b617663dc7c158f57680882 --- /dev/null +++ b/src/main/resources/db/migration/V3__ForecastConfig_Gridstations.sql @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 NIBIO <http://www.nibio.no/>. + * + * This file is part of VIPSLogic. + * 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. + * + * 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 + * NIBIO Open Source License for more details. + * + * You should have received a copy of the NIBIO Open Source License + * along with VIPSLogic. If not, see <http://www.nibio.no/licenses/>. + * + */ +/** + * Author: Tor-Einar Skog + * Created: May 2, 2018 + */ + +ALTER TABLE forecast_configuration +ADD COLUMN grid_weather_station_point_of_interest_ids INTEGER[]; \ No newline at end of file diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties index 26eae52a23c2dba8934029232f8cbcc11063bdbd..cb32a49a824de62c28fa4a1f29bbd6818d5f90e9 100755 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties @@ -453,3 +453,7 @@ vipsLogicRole_8=Apple Fruit Moth Rowanberry Cluster Counter pointOfInterestType_4=Region task_RunGridModelsTask_name=Run grid models task_RunGridModelsTask_description=Run models that cover several locations +currentDate=Current date +days=Days +older=Older +lastUpdated=Last updated diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties index 3af974595514b02ea7cd37927c89cd475d0c7b9b..aa1bb34b1129629b176339e1cfa2e986517f49b2 100755 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_bs.properties @@ -453,3 +453,7 @@ vipsLogicRole_8=Apple Fruit Moth Rowanberry Cluster Counter pointOfInterestType_4=Region task_RunGridModelsTask_name=Run grid models task_RunGridModelsTask_description=Test +currentDate=Current date +days=Days +older=Older +lastUpdated=Last updated diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties index eaa5d435a8bd68ce9d57cf4006fd7afef59fe9ec..abd9465f410c934e078d8958bc4851f3021a5aad 100755 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_hr.properties @@ -452,3 +452,7 @@ vipsLogicRole_8=Apple Fruit Moth Rowanberry Cluster Counter pointOfInterestType_4=Region task_RunGridModelsTask_name=Run grid models task_RunGridModelsTask_description=Test +currentDate=Current date +days=Days +older=Older +lastUpdated=Last updated diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties index 2a899f2b96badcbdff19f96b2ead9933873ecb4f..a74b126a962e160a85b3f8feb1cc648f9462cb1e 100755 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_nb.properties @@ -453,3 +453,7 @@ vipsLogicRole_8=Rogneb\u00e6rm\u00f8llklaseteller pointOfInterestType_4=Region task_RunGridModelsTask_name=Kj\u00f8r GRID-baserte modeller task_RunGridModelsTask_description=Kj\u00f8r modeller som spenner over flere lokaliteter +currentDate=Gjeldende dato +days=Dager +older=Eldre +lastUpdated=Sist oppdatert diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties index f722af4d1a0f29ef27d2142d7ddd68117206cc13..1565845e361f6a34262336a3aa6452659fe69c9b 100755 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_sr.properties @@ -453,3 +453,7 @@ vipsLogicRole_8=Apple Fruit Moth Rowanberry Cluster Counter pointOfInterestType_4=Region task_RunGridModelsTask_name=Run grid models task_RunGridModelsTask_description=Test +currentDate=Current date +days=Days +older=Older +lastUpdated=Last updated diff --git a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties index 979001d6760254038ceed8c5326ee8f551dc8519..b275b893ee4e90de28867535ca644cba7afdbfc7 100755 --- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties +++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts_zh_CN.properties @@ -450,3 +450,7 @@ vipsLogicRole_8=Apple Fruit Moth Rowanberry Cluster Counter pointOfInterestType_4=Region task_RunGridModelsTask_name=Run grid models task_RunGridModelsTask_description=Test +currentDate=Current date +days=Days +older=Older +lastUpdated=Last updated diff --git a/src/main/webapp/formdefinitions/appleFruitMothStationForm.json b/src/main/webapp/formdefinitions/appleFruitMothStationForm.json index 2954c06b241566e7a6310b864a1780916b8c9226..e5e22d4611d28cf3d0df5684ebab85444a6618b5 100755 --- a/src/main/webapp/formdefinitions/appleFruitMothStationForm.json +++ b/src/main/webapp/formdefinitions/appleFruitMothStationForm.json @@ -1,6 +1,6 @@ { "_licenseNote": [ - "Copyright (c) 2014-2015 NIBIO <http://www.nibio.no/>. ", + "Copyright (c) 2014-2018 NIBIO <http://www.nibio.no/>. ", "", "This file is part of VIPSLogic. ", "VIPSLogic is free software: you can redistribute it and/or modify ", @@ -16,7 +16,7 @@ "You should have received a copy of the NIBIO Open Source License ", "along with VIPSLogic. If not, see <http://www.nibio.no/licenses/>. " ], - "_comment" : "Structure of the weatherStationForm and how to validate it", + "_comment" : "Structure of the appleFruitMothStationForm and how to validate it", "fields": [ { "name" : "observationSiteId", diff --git a/src/main/webapp/formdefinitions/models/FINNCEREAL.json b/src/main/webapp/formdefinitions/models/FINNCEREAL.json index 93aec56a3cb138f3f034e49b48f2122a29c92778..979a52b430d35d31a80211d9f4ba021a19c399e8 100644 --- a/src/main/webapp/formdefinitions/models/FINNCEREAL.json +++ b/src/main/webapp/formdefinitions/models/FINNCEREAL.json @@ -1,77 +1,77 @@ -{ - "_licensenbte": [ - "Copyright (c) 2014 NIBIO <http://www.nibio.nb/>. ", - "", - "This file is part of VIPSLogic. ", - "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. ", - "", - "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 ", - "NIBIO Open Source License for more details. ", - "", - "You should have received a copy of the NIBIO Open Source License ", - "along with VIPSLogic. If nbt, see <http://www.nibio.nb/licenses/>. " - ], - "_comment" : "Sowing date, previous crop, tillage method, and current crop disease susceptibility for the specific field", - "fields": [ - { - "name" : "modelName", - "dataType" : "STRING", - "fieldType" : "SELECT_SINGLE", - "required" : true, - "options": [ - {"value":"PyrenophoraTeres", "label":{"en":"Net Blotch","nb":"?","fi":"Verkkolaikku"}, "selected":"true"}, - {"value":"DrechsleraTriticiRepentis", "label":{"en":"Tan Spot","nb":"","fi":"Ruskolaikku"}}, - {"value":"StagonosporaNodorum", "label":{"en" : "Stagonspora spot", "nb":"?", "fi":"Pistelaikku"}} - ] - }, - { - "name" : "fieldSowingDate", - "dataType" : "DATE", - "dateFormat" : "yyyy-MM-dd", - "required" : true - }, - { - "name" : "previousCrop", - "dataType" : "STRING", - "fieldType" : "SELECT_SINGLE", - "required" : true, - "options": [ - {"value":"Wheat", "label":{"en":"Wheat","nb":"Hvete","fi":"Vehn"}, "selected":"true"}, - {"value":"Barley", "label":{"en":"Barley","nb":"Barley","fi":"Kaura"}}, - {"value":"Oat", "label":{"en" : "Oats", "nb":"?", "fi":"Ohra"}}, - {"value":"Grass", "label":{"en" : "Grass", "nb":"?","fi":"Hein"}}, - {"value":"Rapeseed", "label":{"en" : "Turnip rape", "nb":"?","fi":"Rypsi"}}, - {"value":"SugarBeet", "label":{"en" : "Sugar beet", "nb":"?","fi":"Sokerijuurikas"}}, - {"value":"Pulse", "label":{"en" : "Legumes", "nb":"?","fi":"Herneet"}} - ] - }, - { - "name" : "tillageMethod", - "dataType" : "STRING", - "fieldType" : "SELECT_SINGLE", - "required" : true, - "options": [ - {"value":"Intensive", "label":{"en":"Ploughing","nb":"?","fi":"Kynt"}, "selected":"true"}, - {"value":"Reduced", "label":{"en":"Reduced tillage","nb":"?","fi":"Kevytmuokkaus"}}, - {"value":"None", "label":{"en" : "Direct seeding", "nb":"?","fi":"Suorakylv"}} - ] - }, - { - "name" : "cropSusceptibility", - "dataType" : "STRING", - "fieldType" : "SELECT_SINGLE", - "required" : true, - "options": [ - {"value":"Normal", "label":{"en":"Normal","nb":"?", "fi":"normaali"}, "selected":"true"}, - {"value":"Resistant", "label":{"en":"Resistant","nb":"?","fi":"Resistentti"}}, - {"value":"Sensitive", "label":{"en" : "Susceptible", "nb":"?", "fi":"Haavoittuva"}} - ] - } - - ] +{ + "_licensenote": [ + "Copyright (c) 2018 NIBIO <http://www.nibio.nb/>. ", + "", + "This file is part of VIPSLogic. ", + "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. ", + "", + "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 ", + "NIBIO Open Source License for more details. ", + "", + "You should have received a copy of the NIBIO Open Source License ", + "along with VIPSLogic. If not, see <http://www.nibio.nb/licenses/>. " + ], + "_comment" : "Sowing date, previous crop, tillage method, and current crop disease susceptibility for the specific field", + "fields": [ + { + "name" : "modelName", + "dataType" : "STRING", + "fieldType" : "SELECT_SINGLE", + "required" : true, + "selectOptions": [ + {"value":"PyrenophoraTeres", "label":{"en":"Net Blotch","nb":"Bladflekk","fi":"Verkkolaikku"}, "selected":"true"}, + {"value":"DrechsleraTriticiRepentis", "label":{"en":"Tan Spot","nb":"Tan Spot","fi":"Ruskolaikku"}}, + {"value":"StagonosporaNodorum", "label":{"en" : "Stagonspora spot", "nb":"Stagonspora spot", "fi":"Pistelaikku"}} + ] + }, + { + "name" : "fieldSowingDate", + "dataType" : "DATE", + "dateFormat" : "yyyy-MM-dd", + "required" : true + }, + { + "name" : "previousCrop", + "dataType" : "STRING", + "fieldType" : "SELECT_SINGLE", + "required" : true, + "selectOptions": [ + {"value":"Wheat", "label":{"en":"Wheat","nb":"Hvete","fi":"Vehnä"}, "selected":"true"}, + {"value":"Barley", "label":{"en":"Barley","nb":"Barley","fi":"Kaura"}}, + {"value":"Oats", "label":{"en" : "Oats", "nb":"Havre", "fi":"Ohra"}}, + {"value":"Grass", "label":{"en" : "Grass", "nb":"Gras","fi":"Heinä"}}, + {"value":"Rapeseed", "label":{"en" : "Turnip rape", "nb":"Raps","fi":"Rypsi"}}, + {"value":"SugarBeet", "label":{"en" : "Sugar beet", "nb":"Bete","fi":"Sokerijuurikas"}}, + {"value":"Legumes", "label":{"en" : "Legumes", "nb":"Belgfrukter","fi":"Herneet"}} + ] + }, + { + "name" : "tillageMethod", + "dataType" : "STRING", + "fieldType" : "SELECT_SINGLE", + "required" : true, + "selectOptions": [ + {"value":"Intensive", "label":{"en":"Ploughing","nb":"Pløying","fi":"Kyntö"}, "selected":"true"}, + {"value":"Reduced", "label":{"en":"Reduced tillage","nb":"Redusert pløying","fi":"Kevytmuokkaus"}}, + {"value":"None", "label":{"en" : "Direct seeding", "nb":"Direktesåing","fi":"Suorakylvö"}} + ] + }, + { + "name" : "cropSusceptibility", + "dataType" : "STRING", + "fieldType" : "SELECT_SINGLE", + "required" : true, + "selectOptions": [ + {"value":"Normal", "label":{"en":"Normal","nb":"Normal", "fi":"normaali"}, "selected":"true"}, + {"value":"Resistant", "label":{"en":"Resistant","nb":"Resistent","fi":"Resistentti"}}, + {"value":"Sensitive", "label":{"en" : "Susceptible", "nb":"Sårbar", "fi":"Haavoittuva"}} + ] + } + + ] } \ No newline at end of file diff --git a/src/main/webapp/formdefinitions/models/NAERSTADMO.json b/src/main/webapp/formdefinitions/models/NAERSTADMO.json index 448b0f6e489f3a1a8ec523e8a04073efaa56a6e4..ed54bdb5c5fafe1a3d9fa0f7f8080b422c531449 100755 --- a/src/main/webapp/formdefinitions/models/NAERSTADMO.json +++ b/src/main/webapp/formdefinitions/models/NAERSTADMO.json @@ -17,5 +17,5 @@ "along with VIPSLogic. If not, see <http://www.nibio.no/licenses/>. " ], "_comment" : "Structure of the specific fields for NAERSTADMO", - "fields": [] + "fields": [] } \ No newline at end of file diff --git a/src/main/webapp/js/appleFruitMothObservationSitesMap.js b/src/main/webapp/js/appleFruitMothObservationSitesMap.js index ce67f1497ba504fbfb4e6d385c8d2b1997c92460..dd2e3e5a139ba6b22bf04fa33d405b3905594850 100755 --- a/src/main/webapp/js/appleFruitMothObservationSitesMap.js +++ b/src/main/webapp/js/appleFruitMothObservationSitesMap.js @@ -83,7 +83,7 @@ var initMap = function(container, mapAttribution) trigger: 'manual', html: true, placement: "auto top", - title: "<a href='/applefruitmoth?action=viewObservationSite&observationSiteId=" + feature.getId() + "'>" + feature.get("name") + "</a>", + title: "<a href='/applefruitmoth?action=viewObservationSite&observationSiteId=" + feature.getId() + "'>" + feature.get("siteName") + "</a>", content: generatePopupContents(feature.get("warningStatus"), feature.get("description")) }); diff --git a/src/main/webapp/js/forecastConfigurationForm.js b/src/main/webapp/js/forecastConfigurationForm.js index 6dfca064b7eaeead2c79f3edc75c5dbc6b106417..1d15d5137f2932af3ec395d261aea883c33730e1 100755 --- a/src/main/webapp/js/forecastConfigurationForm.js +++ b/src/main/webapp/js/forecastConfigurationForm.js @@ -120,7 +120,7 @@ function fetchModelSpecificFieldValuesCallback(fieldValues) //console.log(fieldValues[i].forecastModelConfigurationPK.modelConfigParameter + ": " + fieldName); if(theForm[fieldName] !== null && theForm[fieldName] !== undefined) { - theForm[fieldName].value=fieldValues[i].parameterValue; + theForm[fieldName].value=fieldValues[i].parameterValue; } } } @@ -164,6 +164,12 @@ function createFieldHTML(modelId, formId, fieldDefinition) var inputType = getHTMLInputType(fieldDefinition.dataType); fieldHTML = '<input type="' + inputType + '" class="form-control" name="' + fieldDefinition.name + '" placeholder="' + getI18nMsg(fieldDefinition.name) + '" onblur="validateField(this,\'' + modelId + '\');" />'; } + else if(fieldDefinition.fieldType === fieldTypes.TYPE_SELECT_SINGLE || fieldDefinition.fieldType === fieldTypes.TYPE_SELECT_MULTIPLE) + { + fieldHTML = '<select class="form-control" name="' + fieldDefinition.name + '"onblur="validateField(this,\'' + modelId + '\')" ' + (fieldDefinition.fieldType === fieldTypes.TYPE_SELECT_MULTIPLE ? ' multiple="multiple"' : '') + '>'; + fieldHTML += getLocalizedOptionsHTML(fieldDefinition.selectOptions); + fieldHTML += '</select>'; + } return [ replaceParams(FIELD_PREFIX,[fieldDefinition.name, getI18nMsg(fieldDefinition.name)]), @@ -205,4 +211,4 @@ function getHTMLInputType(dataType) default: return "text"; } -} \ No newline at end of file +} diff --git a/src/main/webapp/js/util.js b/src/main/webapp/js/util.js index 382314a03733ac00c4f2db4f687366bccd530939..74c3f49d49d602f7e47fd1adcf4a010fb2d2bf0d 100755 --- a/src/main/webapp/js/util.js +++ b/src/main/webapp/js/util.js @@ -112,7 +112,7 @@ function getLocalizedOrganismName(organism, language) } /** - * Depends on the value of currentLanguage and defaultLanguage in /currentLanguage.js + * Depends on the value of currentLanguage and defaultLanguage in /js/environment.js * @param cropCategory * @returns {String} */ @@ -146,6 +146,35 @@ function getLocalizedCropCategoryName(cropCategory) return "Unnamed"; } +function getLocalizedOptionsHTML(optionsList) { + var translatedOptionsHTML = ""; + var languages = [environment.currentLanguage, environment.defaultLanguage, "en"]; + + for(var i in optionsList){ + var option = optionsList[i]; + var label = null; + var labelList = option.label; + for(var j in languages) + { + for(var k in labelList) + { + if(k === languages[j]) + { + label = labelList[k]; + break; + } + } + if(label !== null) + { + break; + } + } + translatedOptionsHTML += '<option value="' + option.value + '"' + (option.selected === "true" ? ' selected="selected"' : '') + ">" + label + "</option>"; + } + + return translatedOptionsHTML; +} + /** Ensure that we're able to handle both a unix timestamp and an ISO timestamp * * @param {type} ambiguousValue diff --git a/src/main/webapp/map_applications/fireblight/js/map.js b/src/main/webapp/map_applications/fireblight/js/map.js index 45c681b5fa18dbbe73e2eccc014b3ed5a16b9d35..451244d0b1c1e5826197e8f714b6fb8b95ae8e8f 100755 --- a/src/main/webapp/map_applications/fireblight/js/map.js +++ b/src/main/webapp/map_applications/fireblight/js/map.js @@ -9,61 +9,8 @@ var registration = false; function initMap() { - // OpenStreetMap background layer - var osm = new ol.layer.Tile({ - 'title': 'OSM', - type: 'base', - visible: true, - source: new ol.source.OSM({ - attributions: [ - new ol.Attribution({ - html: "Kartgrunnlag: Statens kartverk (<a href='//creativecommons.org/licenses/by-sa/3.0/no/' target='new'>cc-by-sa-3.0</a>)" - }) - ] - }) - }); - // Detailed map of Norway in shades of grey - var topo2graatone = new ol.layer.Tile({ - title: "Gråtone", - type: 'base', - visible: true, - source: new ol.source.TileWMS({ - attributions: [ - new ol.Attribution({ - html: "Kartgrunnlag: Statens kartverk (<a href='//creativecommons.org/licenses/by-sa/3.0/no/' target='new'>cc-by-sa-3.0</a>)" - }) - ], - url: "//opencache.statkart.no/gatekeeper/gk/gk.open?", - params: { - LAYERS: 'topo2graatone', - VERSION: '1.1.1' - } - }) - }); - - // Detailed Norway map in colours - var topo2 = - new ol.layer.Tile({ - title: "Farger", - type: 'base', - visible: true, - source: new ol.source.TileWMS({ - attributions: [ - new ol.Attribution({ - html: "Kartgrunnlag: Statens kartverk (<a href='//creativecommons.org/licenses/by/4.0/deed.no' target='new'>cc-by-sa-4.0</a>)" - }) - ], - url: "//opencache.statkart.no/gatekeeper/gk/gk.open?", - params: { - LAYERS: 'topo2', - VERSION: '1.1.1' - } - }) - }); - - // The layer for putting the data - var features = new ol.Collection(); + var features = new ol.Collection(); var iconRadius = 10; @@ -150,64 +97,69 @@ var styles = { }); + var parser = new ol.format.WMTSCapabilities(); + fetch('https://opencache.statkart.no/gatekeeper/gk/gk.open_wmts?Version=1.0.0&service=wmts&request=getcapabilities').then(function(response) { + return response.text(); + }).then(function(text) { + var result = parser.read(text); + var options = ol.source.WMTS.optionsFromCapabilities(result, { + layer: 'topo4', + matrixSet: 'EPSG:3857' + }); + var topo4 = + new ol.layer.Tile({ + opacity: 1, + source: new ol.source.WMTS(/** @type {!olx.source.WMTSOptions} */ (options)) + }); + - map = new ol.Map({ - target: 'map', - layers: [ - //topo2graatone, - topo2, - //osm, - featureOverlay, - newFeatureOverlay - ], - view: new ol.View({ - center: ol.proj.fromLonLat([8.5, 60.8]), - zoom: 6 - }) - }); - - // TODO feature properties must be synchronized - var lastYear = new Date().getFullYear() - 1; - // Population the season select list, setting last year as default selected - initSeasonSelectList(lastYear); - getAndRenderObservations(lastYear); - /* - $.getJSON("/rest/observation/filter/1/geoJSON?from=" + lastYear + "-01-01&pestId=" + paerebrann.organismId, function(geoData){ - //console.info(geoData) - var format = new ol.format.GeoJSON(); + map = new ol.Map({ + target: 'map', + layers: [ + //topo2graatone, + topo4, + //osm, + featureOverlay, + newFeatureOverlay + ], + view: new ol.View({ + center: ol.proj.fromLonLat([8.5, 60.8]), + zoom: 6 + }) + }); - var drawnfeatures = format.readFeatures(geoData, { - //dataProjection: "EPSG:32633", - dataProjection: "EPSG:4326", - featureProjection: map.getView().getProjection().getCode() + // TODO feature properties must be synchronized + var lastYear = new Date().getFullYear() - 1; + // Population the season select list, setting last year as default selected + initSeasonSelectList(lastYear); + getAndRenderObservations(lastYear); + + + map.on('click', function(evt){ + //features = [] + var feature = map.forEachFeatureAtPixel( + evt.pixel, function(ft, l) { return ft; } + ); + + var vectorSource = newFeatureOverlay.getSource(); + // Remove any new features already created + vectorSource.clear(); + + if (feature) { + // Create a fake icon for highlighting + var fakeFeature = createFeature(feature.getGeometry().getCoordinates()); + vectorSource.addFeature(fakeFeature); + displayFeature(feature); + } + else if(registration) + { + var newFeature = createFeature(map.getCoordinateFromPixel(evt.pixel)); + vectorSource.addFeature(newFeature); + editFeature(newFeature.getId()); + } }); - //featureOverlay.clear(true); - featureOverlay.getSource().addFeatures(drawnfeatures); - });*/ - map.on('click', function(evt){ - //features = [] - var feature = map.forEachFeatureAtPixel( - evt.pixel, function(ft, l) { return ft; } - ); - - var vectorSource = newFeatureOverlay.getSource(); - // Remove any new features already created - vectorSource.clear(); - - if (feature) { - // Create a fake icon for highlighting - var fakeFeature = createFeature(feature.getGeometry().getCoordinates()); - vectorSource.addFeature(fakeFeature); - displayFeature(feature); - } - else if(registration) - { - var newFeature = createFeature(map.getCoordinateFromPixel(evt.pixel)); - vectorSource.addFeature(newFeature); - editFeature(newFeature.getId()); - } - }); + }); // END FETCH } diff --git a/src/main/webapp/public/nordic_septoria_whs/nordic_septoria_whs.js b/src/main/webapp/public/nordic_septoria_whs/nordic_septoria_whs.js index 9a25f8847289675d1f0685c5d74a33390ac9c980..f992d1b1111ded2d3c921e91be1ac9aff80e0586 100644 --- a/src/main/webapp/public/nordic_septoria_whs/nordic_septoria_whs.js +++ b/src/main/webapp/public/nordic_septoria_whs/nordic_septoria_whs.js @@ -178,18 +178,15 @@ function parseJSONDate(theDateString) function getTodayAtMidnight() { - var retVal = new Date(); - retVal.setHours(0); - retVal.setMinutes(0); - retVal.setSeconds(0); - retVal.setMilliseconds(0); + var now = new Date(); - //return retVal; + var midnightString = now.getFullYear() + "-" + + ('0' + (now.getMonth()+1)).slice(-2) + "-" + + ('0' + (now.getDate()-1)).slice(-2) + + "T22:00:00.000+0000"; + return new Date(midnightString); // OR RETURN A FIXED DATE FOR TESTING - retVal.setFullYear(2017); - retVal.setMonth(8); // September - retVal.setDate(9); - return retVal;//new Date("2017-09-09T22:00:00.000+0000"); + return new Date("2017-09-07T22:00:00.000+0000"); } diff --git a/src/main/webapp/templates/appleFruitMothClusterForm.ftl b/src/main/webapp/templates/appleFruitMothClusterForm.ftl index a1042cc8a4388122573e59732e2c97857281157d..4e50de7fa029e049a34873971691b4b45d817799 100644 --- a/src/main/webapp/templates/appleFruitMothClusterForm.ftl +++ b/src/main/webapp/templates/appleFruitMothClusterForm.ftl @@ -24,12 +24,14 @@ <#if messageKey?has_content> <div class="alert alert-success">${.now}: ${i18nBundle(messageKey)}</div> </#if> + <button type="button" class="btn btn-default" onclick="window.location='/applefruitmoth';"><i class="fa fa-arrow-left" aria-hidden="true"></i> Tilbake til stasjonsliste</button> + <#if user.isSuperUser() || user.isAppleFruitMothAdministrator() ><button class="btn btn-default" type="button" onclick="window.location='/applefruitmoth?action=viewObservationSite&observationSiteId=${observationSite.observationSiteId}';">Rediger ${observationSite.observationSiteName}</button></#if> + <div class="singleBlockContainer"> <div class="row"> <form role="form" action="/applefruitmoth?action=clusterSubmit" method="POST" onsubmit="return validateForm(this)"> <div class="col-md-8"> - <button type="button" class="btn btn-default" onclick="window.location='/applefruitmoth';"><i class="fa fa-arrow-left" aria-hidden="true"></i> Tilbake</button> <p> Man trenger ikke registrere informasjon for alle trær (A-T) i lista. Et tre kan ha bare høsttallet registrert, men ikke bare vårtallet diff --git a/src/main/webapp/templates/appleFruitMothStationForm.ftl b/src/main/webapp/templates/appleFruitMothStationForm.ftl index 07e4436c892907f5d6539dcf116496d4709f3236..a8de8304e7b13ba5e9f760492e165b97e42a7b65 100755 --- a/src/main/webapp/templates/appleFruitMothStationForm.ftl +++ b/src/main/webapp/templates/appleFruitMothStationForm.ftl @@ -26,6 +26,7 @@ <script src="/js/resourcebundle.js"></script> <script type="text/javascript" src="/js/3rdparty/ol.js"></script> <script type="text/javascript" src="/js/constants.js"></script> + <script type="text/javascript" src="/js/validateForm.js"></script> <script type="text/javascript" src="/js/appleFruitMothObservationSiteMap.js"></script> <script type="text/javascript"> $(document).ready(function() { @@ -35,16 +36,17 @@ </#macro> <#macro page_contents> <h1>${observationSite.observationSiteName!i18nBundle.newObservationSite} ${currentSeason}</h1> -<button type="button" class="btn btn-default" onclick="window.location='/applefruitmoth';"><i class="fa fa-arrow-left" aria-hidden="true"></i> ${i18nBundle.back}</button> +<button type="button" class="btn btn-default" onclick="window.location='/applefruitmoth';"><i class="fa fa-arrow-left" aria-hidden="true"></i> Tilbake til stasjonsliste</button> <button type="button" class="btn btn-default" onclick="window.location='/applefruitmoth?action=registerCluster&observationSiteId=${observationSite.observationSiteId}¤tSeason=${currentSeason}';">Registrer klasetall</button> <div class="singleBlockContainer"> <div class="row"> <div class="col-md-6"> - <#assign formId = "observationSiteForm"> + <#assign formId = "appleFruitMothStationForm"> <#if messageKey?has_content> <div class="alert alert-success">${.now}: ${i18nBundle(messageKey)}</div> </#if> + <#if observationSiteSeasonCommonData??><div class="alert alert-info">${i18nBundle.lastUpdated}: ${observationSiteSeasonCommonData.lastUpdated}</div></#if> <form id="${formId}" role="form" action="/applefruitmoth?action=observationSiteSubmit" method="POST"> <input type="hidden" name="observationSiteId" value="${observationSite.observationSiteId}"/> <div class="form-group"> @@ -74,12 +76,12 @@ </div> <div class="form-group"> <label for="degreeOfParasitation">${i18nBundle.degreeOfParasitation} (${i18nBundle.internal})</label> - <input type="number" class="form-control" name="degreeOfParasitation" placeholder="${i18nBundle.degreeOfParasitation}" value="<#if observationSiteSeasonCommonData??>${observationSiteSeasonCommonData.degreeOfParasitation!""}</#if>" onblur="validateField(this);" /> + <input type="number" step="any" class="form-control" name="degreeOfParasitation" placeholder="${i18nBundle.degreeOfParasitation}" value="<#if observationSiteSeasonCommonData??>${observationSiteSeasonCommonData.degreeOfParasitation?c!""}</#if>" onblur="validateField(this);" /> <span class="help-block" id="${formId}_degreeOfParasitation_validation"></span> </div> <div class="form-group"> <label for="thousandBerrySample">${i18nBundle.thousandBerrySample} (${i18nBundle.internal})</label> - <input type="number" class="form-control" name="thousandBerrySample" placeholder="${i18nBundle.thousandBerrySample}" value="<#if observationSiteSeasonCommonData??>${observationSiteSeasonCommonData.thousandBerrySample!""}</#if>" onblur="validateField(this);" /> + <input type="number" step="any" class="form-control" name="thousandBerrySample" placeholder="${i18nBundle.thousandBerrySample}" value="<#if observationSiteSeasonCommonData??>${observationSiteSeasonCommonData.thousandBerrySample?c!""}</#if>" onblur="validateField(this);" /> <span class="help-block" id="${formId}_thousandBerrySample_validation"></span> </div> <div class="form-group"> @@ -102,10 +104,10 @@ <tr><td>Antall trær med økning i klasetall over vinteren</td><td>${treesWithIncreasingValues}</td></tr> <tr><td>Antall trær med nedgang i klasetall over vinteren</td><td>${treesWithDecreasingValues}</td></tr> <tr><td>Antall trær med uendret klasetall over vinteren</td><td>${treesWithUnchangedValues}</td></tr> - <tr><td>Angrepstall</td><td>${attackNumber!"[Data mangler]"}%</td></tr> + <tr><td>Angrepstall</td><td><#if attackNumber??>${attackNumber?c}<#else>[Data mangler]</#if></td></tr> <tr><td>Nødvendig antall klaser ${currentSeason}</td><td>${neededClusters!"[Data mangler]"}</td></tr> - <tr><td>Prognosetall ${currentSeason}</td><td>${forecastNumber!"[Data mangler]"}</td></tr> - <tr><td>Klasebrøk</td><td>${clusterFraction!"[Data mangler]"}</td></tr> + <tr><td>Prognosetall ${currentSeason}</td><td><#if forecastNumber??>${forecastNumber}<#else>[Data mangler]</#if></td></tr> + <tr><td>Klasebrøk</td><td><#if clusterFraction??>${clusterFraction?c}<#else>[Data mangler]</#if></td></tr> </tbody> </table> diff --git a/src/main/webapp/templates/appleFruitMothStationList.ftl b/src/main/webapp/templates/appleFruitMothStationList.ftl index d725953bf69a56c06f9d115c0416eb0f99478e90..e86ade492a79a7366d676b3a6d7a80aae52ccb8f 100755 --- a/src/main/webapp/templates/appleFruitMothStationList.ftl +++ b/src/main/webapp/templates/appleFruitMothStationList.ftl @@ -39,11 +39,16 @@ <div class="row"> <div class="col-md-6"> <table class="table table-striped"> + <tr> + <td colspan="3"></td> + <th>Klasetall sist oppdatert</th> + </tr> <#list observationSites as observationSite> <tr> <td>${observationSite.observationSiteName}</td> <td><#if user.isSuperUser() || user.isAppleFruitMothAdministrator() ><button class="btn btn-default" onclick="window.location='/applefruitmoth?action=viewObservationSite&observationSiteId=${observationSite.observationSiteId}';">Rediger</button></#if></td> <td><button class="btn btn-default" onclick="window.location='/applefruitmoth?action=registerCluster&observationSiteId=${observationSite.observationSiteId}';">Registrer klasetall</button></td> + <td>${observationSite.lastUpdatedClusterCount!"Ukjent"}</td> </tr> </#list> </table> diff --git a/src/main/webapp/templates/forecastConfigurationForm.ftl b/src/main/webapp/templates/forecastConfigurationForm.ftl index 637f74c785b5590f0edd79c023f5e52c15a621cb..803f5600cc96fd4fede4fbd35d3b2b0d9494d572 100755 --- a/src/main/webapp/templates/forecastConfigurationForm.ftl +++ b/src/main/webapp/templates/forecastConfigurationForm.ftl @@ -26,6 +26,7 @@ <link href="//code.jquery.com/ui/1.10.3/themes/redmond/jquery-ui.css" rel="stylesheet" /> <script type="text/javascript" src="/js/3rdparty/modernizr_custom.js"></script> <script type="text/javascript" src="/js/3rdparty/moment.min.js"></script> + <script type="text/javascript" src="/js/environment.js"></script> <script type="text/javascript" src="/js/util.js"></script> <script type="text/javascript"> $(document).ready(function() { @@ -44,8 +45,11 @@ renderModelSpecificFields("${formId}"); } - - initCropCategories(); + + if(document.getElementById("cropCategoryIdList") != null) + { + initCropCategories(); + } sortListAlphabetically(document.getElementById("modelId"),1); }); @@ -60,7 +64,7 @@ } function renderCropCategories() { - var cropCategoryIdList = document.getElementById("cropCategoryIdList"); + var cropCategoryIdList = document.getElementById("cropCategoryIdList"); for(var i in cropCategories) { var cropCategory = cropCategories[i]; diff --git a/src/main/webapp/templates/observationMap.ftl b/src/main/webapp/templates/observationMap.ftl index f9f67e0c7b4370babdbe2b7a39afe36bf5be0499..4adc17aa76a196a5c2ca470c2d7d841a49e0b5d6 100755 --- a/src/main/webapp/templates/observationMap.ftl +++ b/src/main/webapp/templates/observationMap.ftl @@ -95,9 +95,9 @@ var html = "<ul>"; for(var i in colors) { - html += '<li><i style="color: ' + colors[i][1] + ';" class="fa fa-square" aria-hidden="true"></i> ' + (i > 0 ? colors[i-1][0] + 1 : '0') + '-' + colors[i][0] + ' dager</li>'; + html += '<li><i style="color: ' + colors[i][1] + ';" class="fa fa-square" aria-hidden="true"></i> ' + (i > 0 ? colors[i-1][0] + 1 : '0') + '-' + colors[i][0] + ' ${i18nBundle.days?lower_case}</li>'; } - html += '<li><i style="color: black;" class="fa fa-square" aria-hidden="true"></i> Eldre</li>'; + html += '<li><i style="color: black;" class="fa fa-square" aria-hidden="true"></i> ${i18nBundle.older}</li>'; html += "</ul>"; lBox.innerHTML = html; } @@ -159,7 +159,7 @@ <div id="observationMap" class="map" style="position:relative;"> <div id="popover"></div> <div class="form-group" id="progressBar"> - <label for="dayInPeriod">Gjeldende dato: <span id="dayInPeriodDate">${to?date}</span></label><br/> + <label for="dayInPeriod">${i18nBundle.currentDate}: <span id="dayInPeriodDate">${to?date}</span></label><br/> <button type="button" id="playButton" class="btn" onclick="togglePlay(this);"><i class="fa fa-play" aria-hidden="true"></i></button> <input type="range" id="dayInPeriod" name="dayInPeriod" min="1" max="${periodDays}" step="1" value="${periodDays}" oninput="updateCurrentDate(this);" onchange="updateMap(this)"/> diff --git a/src/test/java/no/nibio/vips/util/weather/MetosAPIDataParserTest.java b/src/test/java/no/nibio/vips/util/weather/MetosAPIDataParserTest.java index 78f553258053142f9236135f1bd523c8acd7176c..555d900980222172c33838b0974bd35253b91c60 100644 --- a/src/test/java/no/nibio/vips/util/weather/MetosAPIDataParserTest.java +++ b/src/test/java/no/nibio/vips/util/weather/MetosAPIDataParserTest.java @@ -79,7 +79,7 @@ public class MetosAPIDataParserTest { MetosAPIDataParser instance = new MetosAPIDataParser(); List<WeatherObservation> result = instance.getWeatherObservations(stationId, timeZone, startDate); assertNotNull( result); - //result.stream().forEach(obs -> System.out.println(obs.toString())); + result.stream().forEach(obs -> System.out.println(obs.toString())); }