Skip to content
Snippets Groups Projects
Commit 1d9aad4b authored by Lene Wasskog's avatar Lene Wasskog
Browse files

feat: First working excel file generator for observations [VIPSUTV-710]

parent 21b2ddd1
Branches
No related tags found
1 merge request!191Add map module and Open-Meteo support
...@@ -266,6 +266,21 @@ ...@@ -266,6 +266,21 @@
<version>2.0.11</version> <version>2.0.11</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
...@@ -32,6 +32,7 @@ import no.nibio.vips.logic.entity.rest.ObservationListItem; ...@@ -32,6 +32,7 @@ import no.nibio.vips.logic.entity.rest.ObservationListItem;
import no.nibio.vips.logic.entity.rest.PointMappingResponse; import no.nibio.vips.logic.entity.rest.PointMappingResponse;
import no.nibio.vips.logic.entity.rest.ReferencedPoint; import no.nibio.vips.logic.entity.rest.ReferencedPoint;
import no.nibio.vips.logic.messaging.MessagingBean; import no.nibio.vips.logic.messaging.MessagingBean;
import no.nibio.vips.logic.util.ExcelFileGenerator;
import no.nibio.vips.logic.util.GISEntityUtil; import no.nibio.vips.logic.util.GISEntityUtil;
import no.nibio.vips.logic.util.Globals; import no.nibio.vips.logic.util.Globals;
import org.jboss.resteasy.annotations.GZIP; import org.jboss.resteasy.annotations.GZIP;
...@@ -59,6 +60,8 @@ import java.net.URI; ...@@ -59,6 +60,8 @@ import java.net.URI;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -130,12 +133,13 @@ public class ObservationService { ...@@ -130,12 +133,13 @@ public class ObservationService {
} }
/** /**
* @param organizationId Database ID of the organization * @param organizationId Database ID of the organization
* @param pestId Database ID of the pest * @param observationTimeSeriesId Database ID of the observation time series
* @param cropId Database ID of the crop * @param pestId Database ID of the pest
* @param cropCategoryId cropCategoryId Database IDs of the crop category/categories * @param cropId Database ID of the crop
* @param fromStr format "yyyy-MM-dd" * @param cropCategoryId cropCategoryId Database IDs of the crop category/categories
* @param toStr format "yyyy-MM-dd" * @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 * @return Observation objects for which the user is authorized to observe with properties relevant for lists
*/ */
@GET @GET
...@@ -158,6 +162,55 @@ public class ObservationService { ...@@ -158,6 +162,55 @@ public class ObservationService {
return Response.ok().entity(this.getFilteredObservationListItems(organizationId, observationTimeSeriesId, 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();
} }
/**
* @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
*/
@GET
@Path("list/filter/{organizationId}/xlsx")
@GZIP
@Produces("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
public Response getFilteredObservationListItemsAsXlsx(
@PathParam("organizationId") Integer organizationId,
@QueryParam("observationTimeSeriesId") Integer observationTimeSeriesId,
@QueryParam("pestId") Integer pestId,
@QueryParam("cropId") Integer cropId,
@QueryParam("cropCategoryId") List<Integer> cropCategoryId,
@QueryParam("from") String fromStr,
@QueryParam("to") String toStr,
@QueryParam("userUUID") String userUUID,
@QueryParam("locale") String localeStr,
@QueryParam("isPositive") Boolean isPositive
) {
LOGGER.info("Generate xlsx file for observations");
VipsLogicUser user = getVipsLogicUser(userUUID);
ULocale locale = new ULocale(localeStr != null ? localeStr :
user != null ? user.getOrganizationId().getDefaultLocale() :
userBean.getOrganization(organizationId).getDefaultLocale());
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
String filenameTimestamp = now.format(formatter);
try {
byte[] excelFile = ExcelFileGenerator.generateExcel(getFilteredObservationListItems(organizationId, observationTimeSeriesId, pestId, cropId, cropCategoryId, fromStr, toStr, userUUID, localeStr, isPositive), locale);
return Response
.ok(excelFile)
.header("Content-Disposition", "attachment; filename=\"" +filenameTimestamp+ "-observations.xlsx\"")
.build();
} catch (IOException e) {
return Response.serverError().entity("Error generating Excel file: " + e.getMessage()).build();
}
}
private List<ObservationListItem> getFilteredObservationListItems( private List<ObservationListItem> getFilteredObservationListItems(
Integer organizationId, Integer organizationId,
Integer observationTimeSeriesId, Integer observationTimeSeriesId,
...@@ -169,11 +222,7 @@ public class ObservationService { ...@@ -169,11 +222,7 @@ public class ObservationService {
String userUUID, String userUUID,
String localeStr, String localeStr,
Boolean isPositive) { Boolean isPositive) {
VipsLogicUser user = (VipsLogicUser) httpServletRequest.getSession().getAttribute("user"); VipsLogicUser user = getVipsLogicUser(userUUID);
if (user == null && userUUID != null) {
user = userBean.findVipsLogicUser(UUID.fromString(userUUID));
}
ULocale locale = new ULocale(localeStr != null ? localeStr : ULocale locale = new ULocale(localeStr != null ? localeStr :
user != null ? user.getOrganizationId().getDefaultLocale() : user != null ? user.getOrganizationId().getDefaultLocale() :
userBean.getOrganization(organizationId).getDefaultLocale()); userBean.getOrganization(organizationId).getDefaultLocale());
...@@ -766,14 +815,14 @@ public class ObservationService { ...@@ -766,14 +815,14 @@ public class ObservationService {
} }
/** /**
* @param organizationId Database id of the organization * @param organizationId Database id of the organization
* @param observationTimeSeriesId Database id of the observation time series * @param observationTimeSeriesId Database id of the observation time series
* @param pestId Database id of the pest * @param pestId Database id of the pest
* @param cropId Database id of the crop * @param cropId Database id of the crop
* @param cropCategoryId Database ids of the crop categories * @param cropCategoryId Database ids of the crop categories
* @param fromStr format "yyyy-MM-dd" * @param fromStr format "yyyy-MM-dd"
* @param toStr format "yyyy-MM-dd" * @param toStr format "yyyy-MM-dd"
* @param user The user that requests this (used for authorization) * @param user The user that requests this (used for authorization)
* @return A list of observations that meets the filter criteria * @return A list of observations that meets the filter criteria
*/ */
private List<Observation> getFilteredObservationsFromBackend( private List<Observation> getFilteredObservationsFromBackend(
...@@ -803,10 +852,10 @@ public class ObservationService { ...@@ -803,10 +852,10 @@ public class ObservationService {
LOGGER.info("Return {} masked public observations for unregistered user", retVal.size()); LOGGER.info("Return {} masked public observations for unregistered user", retVal.size());
return sortObservationsByDateAndId(retVal); return sortObservationsByDateAndId(retVal);
} }
// Else: This is a registered user without special privileges. Show public observations + user's own // Else: This is a registered user without special privileges. Show public observations + user's own
// Making sure we don't add duplicates // Making sure we don't add duplicates
Set<Integer> obsIds = retVal.stream().map(o->o.getObservationId()).collect(Collectors.toSet()); Set<Integer> obsIds = retVal.stream().map(o -> o.getObservationId()).collect(Collectors.toSet());
retVal.addAll(observationBean.getObservationsForUser(user).stream().filter(o->!obsIds.contains(o.getObservationId())).collect(Collectors.toList())); retVal.addAll(observationBean.getObservationsForUser(user).stream().filter(o -> !obsIds.contains(o.getObservationId())).collect(Collectors.toList()));
LOGGER.info("Return {} masked public observations and user's own observations for registered user {}", retVal.size(), user.getUserId()); LOGGER.info("Return {} masked public observations and user's own observations for registered user {}", retVal.size(), user.getUserId());
return sortObservationsByDateAndId(retVal); return sortObservationsByDateAndId(retVal);
} }
...@@ -1085,6 +1134,21 @@ public class ObservationService { ...@@ -1085,6 +1134,21 @@ public class ObservationService {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
/**
* Find VipsLogic user from session or given userUUID
*
* @param userUUID the UUID of the user
* @return the corresponding VipsLogicUser
*/
private VipsLogicUser getVipsLogicUser(String userUUID) {
VipsLogicUser user = (VipsLogicUser) httpServletRequest.getSession().getAttribute("user");
if (user == null && userUUID != null) {
user = userBean.findVipsLogicUser(UUID.fromString(userUUID));
}
return user;
}
/** /**
* Utility method for getting the string value of a given property in a given map. * Utility method for getting the string value of a given property in a given map.
* *
......
package no.nibio.vips.logic.util;
import com.ibm.icu.util.ULocale;
import no.nibio.vips.logic.entity.rest.ObservationListItem;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.ResourceBundle;
public class ExcelFileGenerator {
public static byte[] generateExcel(List<ObservationListItem> filteredObservationListItems, ULocale locale) throws IOException {
try (XSSFWorkbook workbook = new XSSFWorkbook();
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
ResourceBundle rb = ResourceBundle.getBundle("no.nibio.vips.logic.i18n.vipslogictexts", locale.toLocale());
// Prøvde å bruke i18n, fikk Invalid char (/) found at index (13) in sheet name 'Observasjoner/f?rstefunn'
Sheet sheet = workbook.createSheet("Observations");
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue(rb.getString("timeOfObservation"));
headerRow.createCell(1).setCellValue(rb.getString("organism"));
headerRow.createCell(2).setCellValue(rb.getString("cropOrganismId"));
headerRow.createCell(3).setCellValue(rb.getString("observationHeading"));
int rowNum = 1;
for (ObservationListItem item : filteredObservationListItems) {
// TODO Better way of getting current timezone?
LocalDate localDateOfObservation = item.getTimeOfObservation().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(localDateOfObservation.format(formatter));
row.createCell(1).setCellValue(item.getOrganismName());
row.createCell(2).setCellValue(item.getCropOrganismName());
row.createCell(3).setCellValue(item.getObservationHeading());
}
// Auto-size columns to fit content
for (int i = 0; i <= 3; i++) {
sheet.autoSizeColumn(i);
}
workbook.write(out);
return out.toByteArray();
}
}
}
...@@ -354,7 +354,7 @@ greeting = Velkommen til ...@@ -354,7 +354,7 @@ greeting = Velkommen til
groupMembers = Gruppemedlemmer groupMembers = Gruppemedlemmer
heading = Overskrift heading = Tittel
help = Hjelp help = Hjelp
...@@ -558,7 +558,7 @@ observationDataField_unit = M\u00e5leenhet ...@@ -558,7 +558,7 @@ observationDataField_unit = M\u00e5leenhet
observationDeleted = Observasjonen ble slettet observationDeleted = Observasjonen ble slettet
observationHeading = Observasjons-overskrift observationHeading = Observasjonstittel
observationMap = Observasjonskart observationMap = Observasjonskart
...@@ -894,7 +894,7 @@ thresholdRelativeHumidity = Terskelverdi relativ luftfuktighet (%) ...@@ -894,7 +894,7 @@ thresholdRelativeHumidity = Terskelverdi relativ luftfuktighet (%)
tillageMethod = Jordarbeiding tillageMethod = Jordarbeiding
timeOfObservation = Observasjonstidspunkt timeOfObservation = Observasjonsdato
timeZone = Tidssone timeZone = Tidssone
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment