diff --git a/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java b/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java index ed10c3b40559003610fb2ff20702fcc3b014690b..e205348f4a9e392b952760fb525d084c284033a6 100755 --- a/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java +++ b/src/main/java/no/nibio/vips/logic/controller/session/ObservationBean.java @@ -29,18 +29,9 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.ResourceBundle; -import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; import javax.ejb.EJB; import javax.ejb.Stateless; @@ -78,6 +69,9 @@ public class ObservationBean { @EJB UserBean userBean; + @EJB + ObservationTimeSeriesBean observationTimeSeriesBean; + public List<Observation> getObservations(Integer organizationId) { Organization organization = em.find(Organization.class, organizationId); List<Observation> observations = em.createNamedQuery("Observation.findByOrganizationId") @@ -508,6 +502,30 @@ public class ObservationBean { return observations; } + public List<Observation> getObservationsWithTimeSeries(List<Observation> observations) { + Set<Integer> observationTimeSeriesIds = observations.stream() + .map(Observation::getObservationTimeSeriesId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + if (observationTimeSeriesIds.isEmpty()) { + return observations; + } + + List<ObservationTimeSeries> timeSeriesList = observationTimeSeriesBean.getObservationTimeSeriesList(observationTimeSeriesIds); + Map<Integer, ObservationTimeSeries> timeSeriesMap = timeSeriesList.stream() + .collect(Collectors.toMap(ObservationTimeSeries::getObservationTimeSeriesId, Function.identity())); + + observations.stream() + .filter(o -> o.getObservationTimeSeriesId() != null) + .forEach(o -> o.setObservationTimeSeries(timeSeriesMap.get(o.getObservationTimeSeriesId()))); + + for (Observation o : observations) { + LOGGER.info("{}", o); + } + + return observations; + } private List<Observation> getObservationsWithObservers(List<Observation> observations) { Set<Integer> userIds = new HashSet<>(); observations.stream().filter((o) -> (o.getUserId() != null)).forEach((o) -> { @@ -555,13 +573,13 @@ public class ObservationBean { public List<Observation> getFilteredObservations( Integer organizationId, + Integer observationTimeSeriesId, Integer pestId, Integer cropId, List<Integer> cropCategoryId, Date from, Date to, - Boolean isPositive - ) { + Boolean isPositive) { // The minimum SQL String sql = "SELECT * FROM public.observation \n" + "WHERE status_type_id = :statusTypeId \n " + @@ -571,6 +589,11 @@ public class ObservationBean { parameters.put("statusTypeId", ObservationStatusType.STATUS_APPROVED); parameters.put("organizationId", organizationId); + // Filter for observation time series + if (observationTimeSeriesId != null && observationTimeSeriesId > 0) { + sql += "AND observation_time_series_id = :observationTimeSeriesId \n"; + parameters.put("observationTimeSeriesId", observationTimeSeriesId); + } // Filter for pest if (pestId != null && pestId > 0) { sql += "AND organism_id = :organismId \n"; @@ -637,11 +660,10 @@ public class ObservationBean { //start = new Date(); retVal = this.getObservationsWithLocations(retVal); //System.out.println("Finding locations took " + (new Date().getTime() - start.getTime()) + " milliseconds"); + retVal = this.getObservationsWithTimeSeries(retVal); } - return retVal; - } public List<Organism> getObservedPests(Integer organizationId) { diff --git a/src/main/java/no/nibio/vips/logic/controller/session/ObservationTimeSeriesBean.java b/src/main/java/no/nibio/vips/logic/controller/session/ObservationTimeSeriesBean.java index eb1ff5752e4d1eff1a37f6809dc23631409f396b..9f11580789bd659f6df481824248564746e2bbb8 100644 --- a/src/main/java/no/nibio/vips/logic/controller/session/ObservationTimeSeriesBean.java +++ b/src/main/java/no/nibio/vips/logic/controller/session/ObservationTimeSeriesBean.java @@ -18,15 +18,14 @@ package no.nibio.vips.logic.controller.session; -import no.nibio.vips.logic.entity.ObservationTimeSeries; -import no.nibio.vips.logic.entity.PointOfInterest; -import no.nibio.vips.logic.entity.VipsLogicUser; - +import java.util.*; import javax.ejb.EJB; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; -import java.util.*; +import no.nibio.vips.logic.entity.ObservationTimeSeries; +import no.nibio.vips.logic.entity.PointOfInterest; +import no.nibio.vips.logic.entity.VipsLogicUser; @Stateless public class ObservationTimeSeriesBean { @@ -49,8 +48,13 @@ public class ObservationTimeSeriesBean { return resultList; } - public ObservationTimeSeries getObservationTimeSeries(Integer id) { - ObservationTimeSeries ots = em.find(ObservationTimeSeries.class, id); + /** + * Get observation time series with given id. Enrich object with user information before returning. + * @param observationTimeSeriesId the id of the observation time series to retrieve + * @return the observation time series with the given id + */ + public ObservationTimeSeries getObservationTimeSeries(Integer observationTimeSeriesId) { + ObservationTimeSeries ots = em.find(ObservationTimeSeries.class, observationTimeSeriesId); if (ots != null) { ots.setUser(em.find(VipsLogicUser.class, ots.getUserId())); if (ots.getLastModifiedBy() != null) { @@ -60,6 +64,12 @@ public class ObservationTimeSeriesBean { return ots; } + public List<ObservationTimeSeries> getObservationTimeSeriesList(Set<Integer> observationTimeSeriesIds) { + return em.createNamedQuery("ObservationTimeSeries.findByObservationTimeSeriesIds") + .setParameter("observationTimeSeriesIds", observationTimeSeriesIds) + .getResultList(); + } + /** * @param ots the observation time series * @return The merged object diff --git a/src/main/java/no/nibio/vips/logic/entity/Observation.java b/src/main/java/no/nibio/vips/logic/entity/Observation.java index b0b092d053042e9187d30be2022dd9108e84cc7f..241e0c14ad16f334804c251a2f47dc698315c2f1 100755 --- a/src/main/java/no/nibio/vips/logic/entity/Observation.java +++ b/src/main/java/no/nibio/vips/logic/entity/Observation.java @@ -558,6 +558,11 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse this.observationIllustrationSet = observationIllustrationSet; } + @Transient + public Integer getObservationTimeSeriesId() { + return observationTimeSeries != null ? observationTimeSeries.getObservationTimeSeriesId() : null; + } + /** * @return the observation time series */ @@ -686,11 +691,13 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse } return new ObservationListItem( this.getObservationId(), + this.getObservationTimeSeriesId(), this.getTimeOfObservation(), this.getOrganismId(), ! this.getOrganism().getLocalName(locale).trim().isBlank() ? this.getOrganism().getLocalName(locale) : this.getOrganism().getLatinName(), this.getCropOrganismId(), ! this.getCropOrganism().getLocalName(locale).trim().isBlank() ? this.getCropOrganism().getLocalName(locale) : this.getCropOrganism().getLatinName(), + this.observationTimeSeries != null ? this.observationTimeSeries.getLabel() : null, // Specific geoInfo trumps location. This is to be interpreted // as that the observation has been geographically masked by // choice of the observer diff --git a/src/main/java/no/nibio/vips/logic/entity/ObservationTimeSeries.java b/src/main/java/no/nibio/vips/logic/entity/ObservationTimeSeries.java index b222732163440a174fb531b8d86a2fcfc4dd4ec4..d7adac276d965c3bae93528548e0128be8b5c0f3 100644 --- a/src/main/java/no/nibio/vips/logic/entity/ObservationTimeSeries.java +++ b/src/main/java/no/nibio/vips/logic/entity/ObservationTimeSeries.java @@ -35,6 +35,7 @@ import java.util.Date; @NamedQueries({ @NamedQuery(name = "ObservationTimeSeries.findAll", query = "SELECT ots FROM ObservationTimeSeries ots"), @NamedQuery(name = "ObservationTimeSeries.findByObservationTimeSeriesId", query = "SELECT ots FROM ObservationTimeSeries ots WHERE ots.observationTimeSeriesId = :id"), + @NamedQuery(name = "ObservationTimeSeries.findByObservationTimeSeriesIds", query = "SELECT ots FROM ObservationTimeSeries ots WHERE ots.observationTimeSeriesId IN :observationTimeSeriesIds"), @NamedQuery(name = "ObservationTimeSeries.findByOrganizationId", query = "SELECT ots FROM ObservationTimeSeries ots WHERE ots.userId IN(SELECT v.userId FROM VipsLogicUser v WHERE v.organizationId = :organizationId OR v.organizationId IN(SELECT o.organizationId FROM Organization o WHERE o.parentOrganizationId = :organizationId))"), @NamedQuery(name = "ObservationTimeSeries.findByUserId", query = "SELECT ots FROM ObservationTimeSeries ots WHERE ots.userId IN(:userId)") }) @@ -163,6 +164,21 @@ public class ObservationTimeSeries implements Serializable { this.name = name; } + /** + * This logic if also implemented in the observation app, see the function timeSeriesLabel in CommonUtil.vue + * @return the first eight letters of the given name, in uppercase with spaces removed + */ + @Transient + public String getLabel() { + if (this.name == null) { + return null; + } + String timeSeriesName = this.name; + timeSeriesName = timeSeriesName.replaceAll("\\s", ""); + timeSeriesName = timeSeriesName.substring(0, Math.min(timeSeriesName.length(), 8)); + return timeSeriesName.toUpperCase(); + } + /** * @return the description of the observation time series */ diff --git a/src/main/java/no/nibio/vips/logic/entity/rest/ObservationListItem.java b/src/main/java/no/nibio/vips/logic/entity/rest/ObservationListItem.java index 51c5a348f92692caab202fe87a1a49ea7c705d54..a4dd12f901526bfda95fb6c5edadc1c793218772 100644 --- a/src/main/java/no/nibio/vips/logic/entity/rest/ObservationListItem.java +++ b/src/main/java/no/nibio/vips/logic/entity/rest/ObservationListItem.java @@ -27,9 +27,10 @@ import no.nibio.vips.observationdata.ObservationDataSchema; * @author Tor-Einar Skog <tor-einar.skog@nibio.no> */ public class ObservationListItem implements Comparable{ - private Integer observationId, organismId, cropOrganismId; + private Integer observationId, observationTimeSeriesId, organismId, cropOrganismId; private Date timeOfObservation; private String organismName, cropOrganismName; + private String observationTimeSeriesLabel; private String geoInfo; private String observationHeading; private String observationData; @@ -40,11 +41,13 @@ public class ObservationListItem implements Comparable{ public ObservationListItem( Integer observationId, + Integer observationTimeSeriesId, Date timeOfObservation, Integer organismId, String organismName, Integer cropOrganismId, String cropOrganismName, + String observationTimeSeriesLabel, String geoinfo, String observationHeading, Boolean broadcastMessage, @@ -54,11 +57,13 @@ public class ObservationListItem implements Comparable{ ObservationDataSchema observationDataSchema ){ this.observationId = observationId; + this.observationTimeSeriesId = observationTimeSeriesId; this.timeOfObservation = timeOfObservation; this.organismId = organismId; this.organismName = organismName; this.cropOrganismId = cropOrganismId; this.cropOrganismName = cropOrganismName; + this.observationTimeSeriesLabel = observationTimeSeriesLabel; this.geoInfo = geoinfo; this.observationHeading = observationHeading; this.broadcastMessage = broadcastMessage; @@ -93,6 +98,20 @@ public class ObservationListItem implements Comparable{ this.observationId = observationId; } + /** + * @return the observationTimeSeriesId + */ + public Integer getObservationTimeSeriesId() { + return observationTimeSeriesId; + } + + /** + * @param observationTimeSeriesId the observationTimeSeriesId to set + */ + public void setObservationTimeSeriesId(Integer observationTimeSeriesId) { + this.observationTimeSeriesId = observationTimeSeriesId; + } + /** * @return the timeOfObservation */ @@ -135,6 +154,14 @@ public class ObservationListItem implements Comparable{ this.cropOrganismName = cropOrganismName; } + public String getObservationTimeSeriesLabel() { + return observationTimeSeriesLabel; + } + + public void setObservationTimeSeriesLabel(String observationTimeSeriesLabel) { + this.observationTimeSeriesLabel = observationTimeSeriesLabel; + } + /** * @return the geoInfo */ diff --git a/src/main/java/no/nibio/vips/logic/service/ObservationService.java b/src/main/java/no/nibio/vips/logic/service/ObservationService.java index ba9704572df2336831fff1d303ca1c5ae9e3d015..57382908126aae8fcc0d164bc18a8e298640b270 100755 --- a/src/main/java/no/nibio/vips/logic/service/ObservationService.java +++ b/src/main/java/no/nibio/vips/logic/service/ObservationService.java @@ -109,6 +109,7 @@ public class ObservationService { @TypeHint(Observation[].class) public Response getFilteredObservations( @PathParam("organizationId") Integer organizationId, + @QueryParam("observationTimeSeriesId") Integer observationTimeSeriesId, @QueryParam("pestId") Integer pestId, @QueryParam("cropId") Integer cropId, @QueryParam("cropCategoryId") List<Integer> cropCategoryId, @@ -118,6 +119,7 @@ public class ObservationService { ) { return Response.ok().entity(getFilteredObservationsFromBackend( organizationId, + observationTimeSeriesId, pestId, cropId, cropCategoryId, @@ -143,6 +145,7 @@ public class ObservationService { @TypeHint(ObservationListItem.class) public Response getFilteredObservationListItemsAsJson( @PathParam("organizationId") Integer organizationId, + @QueryParam("observationTimeSeriesId") Integer observationTimeSeriesId, @QueryParam("pestId") Integer pestId, @QueryParam("cropId") Integer cropId, @QueryParam("cropCategoryId") List<Integer> cropCategoryId, @@ -152,11 +155,12 @@ public class ObservationService { @QueryParam("locale") String localeStr, @QueryParam("isPositive") Boolean isPositive ) { - return Response.ok().entity(this.getFilteredObservationListItems(organizationId, pestId, cropId, cropCategoryId, fromStr, toStr, userUUID, localeStr, isPositive)).build(); + return Response.ok().entity(this.getFilteredObservationListItems(organizationId, observationTimeSeriesId, pestId, cropId, cropCategoryId, fromStr, toStr, userUUID, localeStr, isPositive)).build(); } private List<ObservationListItem> getFilteredObservationListItems( Integer organizationId, + Integer observationTimeSeriesId, Integer pestId, Integer cropId, List<Integer> cropCategoryId, @@ -164,8 +168,7 @@ public class ObservationService { String toStr, String userUUID, String localeStr, - Boolean isPositive - ) { + Boolean isPositive) { VipsLogicUser user = (VipsLogicUser) httpServletRequest.getSession().getAttribute("user"); if (user == null && userUUID != null) { @@ -178,6 +181,7 @@ public class ObservationService { LOGGER.info("Get filtered observations for user {}", user != null ? user.getUserId() : "<no user>"); List<ObservationListItem> observations = getFilteredObservationsFromBackend( organizationId, + observationTimeSeriesId, pestId, cropId, cropCategoryId, @@ -219,6 +223,7 @@ public class ObservationService { @TypeHint(ObservationListItem.class) public Response getFilteredObservationListItemsAsCSV( @PathParam("organizationId") Integer organizationId, + @QueryParam("observationTimeSeriesId") Integer observationTimeSeriesId, @QueryParam("pestId") Integer pestId, @QueryParam("cropId") Integer cropId, @QueryParam("cropCategoryId") List<Integer> cropCategoryId, @@ -228,7 +233,7 @@ public class ObservationService { @QueryParam("locale") String localeStr, @QueryParam("isPositive") Boolean isPositive ) { - List<ObservationListItem> observations = this.getFilteredObservationListItems(organizationId, pestId, cropId, cropCategoryId, fromStr, toStr, userUUID, localeStr, isPositive); + List<ObservationListItem> observations = this.getFilteredObservationListItems(organizationId, observationTimeSeriesId, pestId, cropId, cropCategoryId, fromStr, toStr, userUUID, localeStr, isPositive); Collections.sort(observations); String retVal = "ObservationID;organismName;cropOrganismName;timeOfObservation;lat/lon;observationHeading;observationData"; GISUtil gisUtil = new GISUtil(); @@ -251,23 +256,24 @@ public class ObservationService { } /** - * @param organizationId Database ID of the organization - * @param pestId Database ID of the pest - * @param cropId Database ID of the crop - * @param cropCategoryId cropCategoryId Database IDs of the crop category/categories - * @param fromStr format "yyyy-MM-dd" - * @param toStr format "yyyy-MM-dd" + * @param organizationId Database ID of the organization + * @param observationTimeSeriesId Database ID of the observation time series + * @param pestId Database ID of the pest + * @param cropId Database ID of the crop + * @param cropCategoryId cropCategoryId Database IDs of the crop category/categories + * @param fromStr format "yyyy-MM-dd" + * @param toStr format "yyyy-MM-dd" * @return Observation objects for which the user is authorized to observe with properties relevant for lists */ private List<Observation> getFilteredObservationsFromBackend( Integer organizationId, + Integer observationTimeSeriesId, Integer pestId, Integer cropId, List<Integer> cropCategoryId, String fromStr, String toStr, - Boolean isPositive - ) { + Boolean isPositive) { SimpleDateFormat format = new SimpleDateFormat(Globals.defaultDateFormat); //TODO Set correct timeZone!!! Date from = null; @@ -281,6 +287,7 @@ public class ObservationService { return observationBean.getFilteredObservations( organizationId, + observationTimeSeriesId, pestId, cropId, cropCategoryId, @@ -405,6 +412,7 @@ public class ObservationService { @TypeHint(GeoJSON.class) public Response getFilteredObservationsAsGeoJSON( @PathParam("organizationId") Integer organizationId, + @QueryParam("observationTimeSeriesId") Integer observationTimeSeriesId, @QueryParam("pestId") Integer pestId, @QueryParam("cropId") Integer cropId, @QueryParam("cropCategoryId") List<Integer> cropCategoryId, @@ -426,6 +434,7 @@ public class ObservationService { List<Observation> filteredObservations = this.getFilteredObservationsFromBackend( organizationId, + observationTimeSeriesId, pestId, cropId, cropCategoryId, @@ -758,6 +767,7 @@ public class ObservationService { /** * @param organizationId Database id of the organization + * @param observationTimeSeriesId Database id of the observation time series * @param pestId Database id of the pest * @param cropId Database id of the crop * @param cropCategoryId Database ids of the crop categories @@ -768,6 +778,7 @@ public class ObservationService { */ private List<Observation> getFilteredObservationsFromBackend( Integer organizationId, + Integer observationTimeSeriesId, Integer pestId, Integer cropId, List<Integer> cropCategoryId, @@ -776,7 +787,7 @@ public class ObservationService { Boolean isPositive, VipsLogicUser user ) { - List<Observation> filteredObservations = this.getFilteredObservationsFromBackend(organizationId, pestId, cropId, cropCategoryId, fromStr, toStr, isPositive); + List<Observation> filteredObservations = this.getFilteredObservationsFromBackend(organizationId, observationTimeSeriesId, pestId, cropId, cropCategoryId, fromStr, toStr, isPositive); // If superuser or orgadmin: Return everything, unchanged, uncensored if (user != null && (user.isSuperUser() || user.isOrganizationAdmin())) { diff --git a/src/main/java/no/nibio/vips/logic/service/ObservationTimeSeriesService.java b/src/main/java/no/nibio/vips/logic/service/ObservationTimeSeriesService.java index 643e0ea134d665a8fd6fe14e85366c2c333ffdf4..3d428239d30a2104f26008039d5173f0d150ce8c 100644 --- a/src/main/java/no/nibio/vips/logic/service/ObservationTimeSeriesService.java +++ b/src/main/java/no/nibio/vips/logic/service/ObservationTimeSeriesService.java @@ -107,15 +107,14 @@ public class ObservationTimeSeriesService { if (requester == null && userUUID != null) { requester = userBean.findVipsLogicUser(UUID.fromString(userUUID)); } + observationTimeSeriesBean.enrichObservationTimeSeriesWithPointOfInterest(ots); boolean requesterNotValidUser = requester == null; boolean requesterRegularUser = requester != null && !requester.isSuperUser() && !requester.isOrganizationAdmin(); boolean requesterNotCreator = requester != null && !ots.getUserId().equals(requester.getUserId()); - if (requesterNotValidUser || requesterRegularUser) { // Mask for all users except creator, super and orgadmin if (!(ots.getLocationIsPrivate() && (requesterNotValidUser || requesterNotCreator)) && ots.getPolygonService() != null) { - observationTimeSeriesBean.enrichObservationTimeSeriesWithPointOfInterest(ots); this.maskLocation(ots.getPolygonService(), ots); } }