From 7fee3ccd676d470b1fcd9a930b98055cb47aab3f Mon Sep 17 00:00:00 2001
From: lewa <lene.wasskog@nibio.no>
Date: Fri, 30 Aug 2024 08:31:42 +0200
Subject: [PATCH] feat: Fix types of inserted values, add poi Id, add missing
 header style

---
 .../nibio/vips/logic/entity/Observation.java  |   2 +-
 .../vips/logic/util/ExcelFileGenerator.java   | 137 ++++++++++++------
 .../vips/logic/i18n/vipslogictexts.properties |   2 +-
 .../logic/i18n/vipslogictexts_nb.properties   |  15 +-
 4 files changed, 99 insertions(+), 57 deletions(-)

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 fae7f4f5..7d971682 100755
--- a/src/main/java/no/nibio/vips/logic/entity/Observation.java
+++ b/src/main/java/no/nibio/vips/logic/entity/Observation.java
@@ -703,7 +703,7 @@ public class Observation implements Serializable, no.nibio.vips.observation.Obse
                 // Specific geoInfo trumps location. This is to be interpreted 
                 // as that the observation has been geographically masked by
                 // choice of the observer
-                this.locationPointOfInterestId,
+                this.location != null ? this.location.getPointOfInterestId() : null,
                 this.location != null ? this.location.getName() : null,
                 this.location != null && this.geoinfo == null ? this.location.getGeoJSON() : this.getGeoinfo(),
                 this.getObservationHeading(),
diff --git a/src/main/java/no/nibio/vips/logic/util/ExcelFileGenerator.java b/src/main/java/no/nibio/vips/logic/util/ExcelFileGenerator.java
index 0be1c944..0a2ad0d9 100644
--- a/src/main/java/no/nibio/vips/logic/util/ExcelFileGenerator.java
+++ b/src/main/java/no/nibio/vips/logic/util/ExcelFileGenerator.java
@@ -31,18 +31,19 @@ public final class ExcelFileGenerator {
     private enum ColumnIndex {
         ID(false, 0, 0, "observationId"),
         DATE(false, 1, 1, "timeOfObservation"),
-        POI_NAME(false, 2, 2, "location"),
-        OBSERVER_ID(true, null, 3, "observerId"),
-        OBSERVER_NAME(true, null, 4, "observer"),
-        OBSERVATION_TIME_SERIES_ID(false, 3, 5, "observationTimeSeriesId"),
-        OBSERVATION_TIME_SERIES_LABEL(false, 4, 6, "observationTimeSeriesLabel"),
-        ORGANISM(false, 5, 7, "organism"),
-        CROP_ORGANISM(false, 6, 8, "cropOrganismId"),
-        HEADING(false, 7, 9, "observationHeading"),
-        DESCRIPTION(false, 8, 10, "observationText"),
-        BROADCAST(false, 9, 11, "isBroadcast"),
-        POSITIVE(false, 10, 12, "isPositiveRegistration"),
-        INDEX_DATA(false, 11, 13, null);
+        POI_ID(false, 2, 2, "locationPointOfInterestId"),
+        POI_NAME(false, 3, 3, "location"),
+        OBSERVER_ID(true, null, 4, "observerId"),
+        OBSERVER_NAME(true, null, 5, "observer"),
+        OBSERVATION_TIME_SERIES_ID(false, 4, 6, "observationTimeSeriesId"),
+        OBSERVATION_TIME_SERIES_LABEL(false, 5, 7, "observationTimeSeriesLabel"),
+        ORGANISM(false, 6, 8, "organism"),
+        CROP_ORGANISM(false, 7, 9, "cropOrganismId"),
+        HEADING(false, 8, 10, "observationHeading"),
+        DESCRIPTION(false, 9, 11, "observationText"),
+        BROADCAST(false, 10, 12, "isBroadcast"),
+        POSITIVE(false, 11, 13, "isPositiveRegistration"),
+        INDEX_DATA(false, 12, 14, null);
 
         private final boolean isSensitive;
         private final Integer openIndex;
@@ -78,13 +79,18 @@ public final class ExcelFileGenerator {
     public static byte[] generateExcel(VipsLogicUser user, ULocale locale, LocalDateTime now, String fromStr, String toStr, List<ObservationListItem> observations) throws IOException {
         ResourceBundle rb = ResourceBundle.getBundle("no.nibio.vips.logic.i18n.vipslogictexts", locale.toLocale());
         boolean isAdmin = user != null && (user.isSuperUser() || user.isOrganizationAdmin());
-        LOGGER.info("Create Excel file containing {} observations for {} user", observations.size(),  isAdmin ? "admin": "regular");
+        LOGGER.info("Create Excel file containing {} observations for {} user", observations.size(), isAdmin ? "admin" : "regular");
         try (XSSFWorkbook workbook = new XSSFWorkbook();
              ByteArrayOutputStream out = new ByteArrayOutputStream()) {
 
+            Font font = workbook.createFont();
+            font.setBold(true);
+            CellStyle headerStyle = workbook.createCellStyle();
+            headerStyle.setFont(font);
+
             // Create main sheet for all observations, with header row
             Sheet mainSheet = workbook.createSheet(rb.getString("allObservations"));
-            createHeaderRow(isAdmin, mainSheet, rb);
+            createHeaderRow(isAdmin, mainSheet, headerStyle, rb);
 
             int mainSheetRowIndex = 1;
             // Add one row for each observation in list of all observations
@@ -97,7 +103,7 @@ public final class ExcelFileGenerator {
             Sheet metaSheet = workbook.createSheet(rb.getString("downloadInfo"));
             Row userRow = metaSheet.createRow(0);
             userRow.createCell(0).setCellValue(rb.getString("downloadedBy"));
-            userRow.createCell(1).setCellValue(user != null ? user.getFullName(): rb.getString("unregisteredUser"));
+            userRow.createCell(1).setCellValue(user != null ? user.getFullName() : rb.getString("unregisteredUser"));
             Row timeRow = metaSheet.createRow(1);
             timeRow.createCell(0).setCellValue(rb.getString("downloadedTime"));
             timeRow.createCell(1).setCellValue(DATE_TIME_FORMATTER.format(now));
@@ -121,13 +127,15 @@ public final class ExcelFileGenerator {
                 ObservationListItem firstObservationForPest = observationsForPest.get(0);
                 String pestName = firstObservationForPest.getOrganismName();
                 Sheet pestSheet = workbook.createSheet(sanitizeSheetName(pestId, pestName));
-                Row headerRow = createHeaderRow(isAdmin, pestSheet, rb);
+                Row headerRow = createHeaderRow(isAdmin, pestSheet, headerStyle, rb);
 
                 // Add column titles for observation data
                 Map<String, String> dataColumnTitles = getObservationDataColumnTitles(firstObservationForPest.getObservationDataSchema());
                 int pestSheetColIndex = ColumnIndex.INDEX_DATA.getIndex(isAdmin);
                 for (String key : dataColumnTitles.keySet()) {
-                    headerRow.createCell(pestSheetColIndex++).setCellValue(dataColumnTitles.get(key));
+                    Cell cell = headerRow.createCell(pestSheetColIndex++);
+                    cell.setCellStyle(headerStyle);
+                    cell.setCellValue(dataColumnTitles.get(key));
                 }
 
                 int pestSheetRowIndex = 1;
@@ -139,12 +147,7 @@ public final class ExcelFileGenerator {
                         if (observationDataMap != null) {
                             pestSheetColIndex = ColumnIndex.INDEX_DATA.getIndex(isAdmin);
                             for (String key : dataColumnTitles.keySet()) {
-                                Object value = observationDataMap.get(key);
-                                if (value instanceof Number) {
-                                    row.createCell(pestSheetColIndex++).setCellValue(((Number) value).intValue());
-                                } else {
-                                    row.createCell(pestSheetColIndex++).setCellValue(value != null ? value.toString() : "");
-                                }
+                                pestSheetColIndex = addValueToCell(row, pestSheetColIndex, observationDataMap.get(key));
                             }
                         }
                     }
@@ -229,20 +232,17 @@ public final class ExcelFileGenerator {
     /**
      * Create first row of given sheet, with column titles dependent on user privileges
      *
-     * @param isAdmin Whether the user is a logged in admin
-     * @param sheet   The sheet to which a row will be added
-     * @param rb      A resource bundle enabling localized messages
+     * @param isAdmin     Whether the user is a logged in admin
+     * @param sheet       The sheet to which a row will be added
+     * @param headerStyle The style to be applied to each cell in the header row
+     * @param rb          A resource bundle enabling localized messages
      * @return the newly created header row
      */
-    public static Row createHeaderRow(boolean isAdmin, Sheet sheet, ResourceBundle rb) {
-        Font font = sheet.getWorkbook().createFont();
-        font.setBold(true);
-        CellStyle style = sheet.getWorkbook().createCellStyle();
-        style.setFont(font);
+    public static Row createHeaderRow(boolean isAdmin, Sheet sheet, CellStyle headerStyle, ResourceBundle rb) {
         Row headerRow = sheet.createRow(0);
         for (ColumnIndex columnIndex : ColumnIndex.forUser(isAdmin)) {
             Cell cell = headerRow.createCell(columnIndex.getIndex(isAdmin));
-            cell.setCellStyle(style);
+            cell.setCellStyle(headerStyle);
             cell.setCellValue(columnIndex.getColumnHeading(rb));
         }
         return headerRow;
@@ -260,28 +260,64 @@ public final class ExcelFileGenerator {
     private static Row createItemRow(boolean isAdmin, Sheet sheet, int rowIndex, ObservationListItem item, ResourceBundle rb) throws JsonProcessingException {
         LocalDate localDateOfObservation = item.getTimeOfObservation().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
         Row row = sheet.createRow(rowIndex);
-        addObservationLinkToFirstCol(isAdmin, row, item.getObservationId());
-        row.createCell(ColumnIndex.DATE.getIndex(isAdmin)).setCellValue(localDateOfObservation.format(DATE_FORMATTER));
-        row.createCell(ColumnIndex.POI_NAME.getIndex(isAdmin)).setCellValue(item.getLocationPointOfInterestName());
+        addObservationLinkToIdCol(isAdmin, row, item.getObservationId());
+        addValueToCell(row, ColumnIndex.DATE.getIndex(isAdmin), localDateOfObservation.format(DATE_FORMATTER));
+
+        if (item.getLocationPointOfInterestId() != null) {
+            addValueToCell(row, ColumnIndex.POI_ID.getIndex(isAdmin), item.getLocationPointOfInterestId());
+            addValueToCell(row, ColumnIndex.POI_NAME.getIndex(isAdmin), item.getLocationPointOfInterestName());
+        }
 
         if (isAdmin) {
-            row.createCell(ColumnIndex.OBSERVER_ID.getIndex(isAdmin)).setCellValue(item.getObserverId());
-            row.createCell(ColumnIndex.OBSERVER_NAME.getIndex(isAdmin)).setCellValue(item.getObserverName());
+            addValueToCell(row, ColumnIndex.OBSERVER_ID.getIndex(isAdmin), item.getObserverId());
+            addValueToCell(row, ColumnIndex.OBSERVER_NAME.getIndex(isAdmin), item.getObserverName());
         }
-        if(item.getObservationTimeSeriesId() != null) {
-            row.createCell(ColumnIndex.OBSERVATION_TIME_SERIES_ID.getIndex(isAdmin)).setCellValue(item.getObservationTimeSeriesId());
-            row.createCell(ColumnIndex.OBSERVATION_TIME_SERIES_LABEL.getIndex(isAdmin)).setCellValue(item.getObservationTimeSeriesLabel());
+        if (item.getObservationTimeSeriesId() != null) {
+            addValueToCell(row, ColumnIndex.OBSERVATION_TIME_SERIES_ID.getIndex(isAdmin), item.getObservationTimeSeriesId());
+            addValueToCell(row, ColumnIndex.OBSERVATION_TIME_SERIES_LABEL.getIndex(isAdmin), item.getObservationTimeSeriesLabel());
         }
-        row.createCell(ColumnIndex.ORGANISM.getIndex(isAdmin)).setCellValue(item.getOrganismName());
-        row.createCell(ColumnIndex.CROP_ORGANISM.getIndex(isAdmin)).setCellValue(item.getCropOrganismName());
-        row.createCell(ColumnIndex.HEADING.getIndex(isAdmin)).setCellValue(item.getObservationHeading());
-        row.createCell(ColumnIndex.DESCRIPTION.getIndex(isAdmin)).setCellValue(item.getObservationText());
+        addValueToCell(row, ColumnIndex.ORGANISM.getIndex(isAdmin), item.getOrganismName());
+        addValueToCell(row, ColumnIndex.CROP_ORGANISM.getIndex(isAdmin), item.getCropOrganismName());
+        addValueToCell(row, ColumnIndex.HEADING.getIndex(isAdmin), item.getObservationHeading());
+        addValueToCell(row, ColumnIndex.DESCRIPTION.getIndex(isAdmin), item.getObservationText());
 
-        row.createCell(ColumnIndex.BROADCAST.getIndex(isAdmin)).setCellValue(getBooleanStringValue(rb, item.getBroadcastMessage()));
-        row.createCell(ColumnIndex.POSITIVE.getIndex(isAdmin)).setCellValue(getBooleanStringValue(rb, item.getIsPositive()));
+        addValueToCell(row, ColumnIndex.BROADCAST.getIndex(isAdmin), getBooleanStringValue(rb, item.getBroadcastMessage()));
+        addValueToCell(row, ColumnIndex.POSITIVE.getIndex(isAdmin), getBooleanStringValue(rb, item.getIsPositive()));
         return row;
     }
 
+    /**
+     * Add value to cell
+     *
+     * @param row      The row to which the value should be added
+     * @param colIndex The index of the column to add the value to
+     * @param value    The value
+     * @return The next index value
+     */
+    private static Integer addValueToCell(Row row, Integer colIndex, Object value) {
+        Integer index = colIndex;
+        if (value == null) {
+            row.createCell(index++).setCellValue("");
+        } else if (value instanceof Number) {
+            row.createCell(index++).setCellValue(((Number) value).intValue());
+        } else {
+            try {
+                int intValue = Integer.parseInt(value.toString());
+                row.createCell(index++).setCellValue(intValue);
+            } catch (NumberFormatException e) {
+                row.createCell(index++).setCellValue(value.toString());
+            }
+        }
+        return index;
+    }
+
+    /**
+     * Get a localized String representing either true or false
+     *
+     * @param rb    The resource bundle to get the localized string from
+     * @param value Either true or false
+     * @return A localized String
+     */
     private static String getBooleanStringValue(ResourceBundle rb, Boolean value) {
         if (value == null) {
             return null;
@@ -291,7 +327,14 @@ public final class ExcelFileGenerator {
         return rb.getString("no");
     }
 
-    private static void addObservationLinkToFirstCol(boolean isAdmin, Row row, Integer observationId) {
+    /**
+     * Add link to observation details in column containing the observation Id
+     *
+     * @param isAdmin       Whether or not the user is admin
+     * @param row           A reference to the current row
+     * @param observationId The id of the observation
+     */
+    private static void addObservationLinkToIdCol(boolean isAdmin, Row row, Integer observationId) {
         Cell cell = row.createCell(ColumnIndex.ID.getIndex(isAdmin));
         cell.setCellValue(observationId);
 
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 8d980b99..61a9d67b 100755
--- a/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties
+++ b/src/main/resources/no/nibio/vips/logic/i18n/vipslogictexts.properties
@@ -426,7 +426,7 @@ locationIsPrivate = Location is private
 
 locationIsPublic = Location is public
 
-locationPointOfInterestId = Location
+locationPointOfInterestId = Location Id
 
 logInterval = Log interval
 
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 afa6a491..96690693 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
@@ -421,11 +421,10 @@ listSelectedCropCategoryOnTop = List kulturer fra valgt gruppe \u00f8verst
 localName = Lokalt navn
 location=Sted
 
-locationIsPrivate = Lokalitet skal ikke offentliggj\u00f8res
+locationIsPrivate = Sted skal ikke offentliggj\u00f8res
 
-locationIsPublic = Lokaliteten kan vises offentlig
-
-locationPointOfInterestId = Lokalitet
+locationIsPublic = Sted kan vises offentlig
+locationPointOfInterestId=Sted-Id
 
 logInterval = M\u00e5leintervall
 
@@ -559,7 +558,7 @@ allObservations = Alle observasjoner
 
 observationDeleted = Observasjonen ble slettet
 
-observationHeading = Observasjonstittel
+observationHeading = Tittel
 
 observationMap = Observasjonskart
 
@@ -1055,9 +1054,9 @@ privacyStatement=Personvernerkl\u00e6ring
 privacyStatementFileName=Personvernerklaering_NIBIO-VIPS.pdf
 thresholdDSVMax=DSV-terskel for h\u00f8y infeksjonsrisiko
 thresholdDSVTempMin=Minimumstemperatur for beregning av DSV
-observationTimeSeriesId=Tidsserie
-observationTimeSeriesLabel=Tidsseriemerkelapp
-observationId=Observasjon
+observationTimeSeriesId=Tidsserie-Id
+observationTimeSeriesLabel=Tidsserie
+observationId=Observasjon-Id
 isBroadcast=Er kringkastet
 yes=Ja
 no=Nei
-- 
GitLab