From d511b77b7fd744d4025bb816d2fe90ff33e1c040 Mon Sep 17 00:00:00 2001 From: frank Date: Fri, 15 May 2026 12:41:03 -0500 Subject: [PATCH 1/4] init --- .../controller/SubmissionController.java | 5 +- .../model/packager/AbstractPackager.java | 4 + .../vireo/model/packager/ExcelPackager.java | 79 +++++++++++++++++++ .../tdl/vireo/model/packager/Packager.java | 2 + src/main/resources/application.yml | 2 + 5 files changed, 91 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/tdl/vireo/controller/SubmissionController.java b/src/main/java/org/tdl/vireo/controller/SubmissionController.java index 103759de1d..f2c207283f 100644 --- a/src/main/java/org/tdl/vireo/controller/SubmissionController.java +++ b/src/main/java/org/tdl/vireo/controller/SubmissionController.java @@ -193,6 +193,9 @@ public class SubmissionController { @Value("${app.documentType.rename:}") private String documentTypesToRename; + @Value("${app.secondaryDelimiter:'|'}") + private String secondaryDelimiter; + @RequestMapping("/all") @PreAuthorize("hasRole('ADMIN')") public ApiResponse getAll() { @@ -579,7 +582,7 @@ private void processBatchExport( // Create a streaming workbook with a window size of 100 rows // (only this many rows will be kept in memory at once) try (SXSSFWorkbook workbook = new SXSSFWorkbook(100)) { - List submissions = submissionRepo.batchDynamicSubmissionQuery(filter, columns); + List submissions = submissionRepo.batchDynamicSubmissionQuery(filter, columns, secondaryDelimiter); // Enable compression for temporary files workbook.setCompressTempFiles(true); diff --git a/src/main/java/org/tdl/vireo/model/packager/AbstractPackager.java b/src/main/java/org/tdl/vireo/model/packager/AbstractPackager.java index 5055b5633c..566addb2ac 100644 --- a/src/main/java/org/tdl/vireo/model/packager/AbstractPackager.java +++ b/src/main/java/org/tdl/vireo/model/packager/AbstractPackager.java @@ -67,6 +67,10 @@ public EP packageExport(Submission submission, List column throw new UnsupportedFormatterException("Exporter does not support submission list columns!"); } + public EP packageExport(Submission submission, List columns, String delimiter) throws UnsupportedFormatterException { + throw new UnsupportedFormatterException("Exporter does not support submission list columns and delimiter!"); + } + public EP packageExport(Submission submission, Map ds_docs) throws UnsupportedFormatterException { throw new UnsupportedFormatterException("Exporter does not support multiple docs !"); } diff --git a/src/main/java/org/tdl/vireo/model/packager/ExcelPackager.java b/src/main/java/org/tdl/vireo/model/packager/ExcelPackager.java index abc6e80bdb..0896b3046d 100644 --- a/src/main/java/org/tdl/vireo/model/packager/ExcelPackager.java +++ b/src/main/java/org/tdl/vireo/model/packager/ExcelPackager.java @@ -140,4 +140,83 @@ public ExcelExportPackage packageExport(Submission submission, List columns, String delimiter) { + Map row = new HashMap(); + columns.forEach(column -> { + Optional predicate = Optional.ofNullable(column.getPredicate()); + if (predicate.isPresent()) { + List fieldValues = new ArrayList(); + for (FieldValue fieldValue : submission.getFieldValues()) { + if (fieldValue.getFieldPredicate().getValue().equals(predicate.get().trim())) { + fieldValues.add(fieldValue.getValue()); + row.put(column.getTitle(), String.join(", ", fieldValues)); + } else { + row.put(column.getTitle(), String.join(delimiter+" ", fieldValues)); + } + } + } else { + if (column.getValuePath().size() > 0) { + String[] valuePath = column.getValuePath().toArray(new String[column.getValuePath().size()]); + try { + if(column.getValuePath().size() > 1){ + valuePath = new String[] {valuePath[0]}; + } + submission.getCommitteeContactEmail(); + Object valueAsObject = EntityUtility.getValueFromPath(submission, valuePath); + + String value = ""; + + if (valueAsObject instanceof Calendar) { + Calendar calendar = (Calendar) valueAsObject; + value = simpleDateFormat.format(calendar.getTime()); + } else if (valueAsObject instanceof Date) { + Date date = (Date) valueAsObject; + value = simpleDateFormat.format(date); + } else if (valueAsObject instanceof Set && ((Set) valueAsObject).stream().allMatch(o -> o instanceof CustomActionValue)) { + StringBuilder sb = new StringBuilder(); + ((Set) valueAsObject).forEach(o -> { + CustomActionValue customActionValue = (CustomActionValue) o; + if (customActionValue.getValue()) { + sb.append("☑ "); + } else { + sb.append("☐ "); + } + sb.append(customActionValue.getDefinition().getLabel()+"\n"); + }); + value = sb.toString(); + } else if (valueAsObject instanceof SubmissionStatus){ + SubmissionStatus submissionStatus = (SubmissionStatus) valueAsObject; + value = submissionStatus.getName().toString(); + } else if (valueAsObject instanceof User){ + User user = (User) valueAsObject; + value = user.getName().toString(); + } else if (valueAsObject instanceof ActionLog){ + ActionLog actionLog = (ActionLog) valueAsObject; + switch (column.getTitle()){ + case "Event Time": + Calendar actionDate = (Calendar) actionLog.getActionDate(); + value = simpleDateFormat.format(actionDate.getTime()); + break; + case "Last Event": + value = actionLog.getEntry().toString(); + break; + default: + break; + } + } else { + value = valueAsObject.toString(); + } + row.put(column.getTitle(), value.toString()); + } catch (Exception exception) { + logger.warn("Unable to get value from " + String.join(",", valuePath)); + } + } else { + logger.warn("Column " + column.getTitle() + " has no predicate or value path!"); + } + } + }); + return new ExcelExportPackage(submission, "Excel", row); + } + } diff --git a/src/main/java/org/tdl/vireo/model/packager/Packager.java b/src/main/java/org/tdl/vireo/model/packager/Packager.java index 08f0012353..c4d88f25a4 100644 --- a/src/main/java/org/tdl/vireo/model/packager/Packager.java +++ b/src/main/java/org/tdl/vireo/model/packager/Packager.java @@ -26,6 +26,8 @@ public interface Packager { public EP packageExport(Submission submission, List columns); + public EP packageExport(Submission submission, List columns, String delimiter); + public EP packageExport(Submission submission, Map dsDocs); } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 796d9b7931..6a982b3cd7 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -137,6 +137,8 @@ app: # edu.tamu.weaver.auth.service.UserCredentialsService authority.admins: admin@tdl.org,aggieJack@tamu.edu + # ExcelExport secondary delimiter for keywords within CSV, otherwise multiple keywords appear as separate columns + secondaryDelimiter: '|' security: # edu.tamu.weaver.auth.service.CryptoService From 8f65fc42b2e6857b47ea81723064a6c10ce90f21 Mon Sep 17 00:00:00 2001 From: frank Date: Fri, 15 May 2026 13:48:57 -0500 Subject: [PATCH 2/4] small fixes --- .../java/org/tdl/vireo/controller/SubmissionController.java | 4 ++-- src/main/java/org/tdl/vireo/utility/PackagerUtility.java | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/tdl/vireo/controller/SubmissionController.java b/src/main/java/org/tdl/vireo/controller/SubmissionController.java index f2c207283f..fa808236bf 100644 --- a/src/main/java/org/tdl/vireo/controller/SubmissionController.java +++ b/src/main/java/org/tdl/vireo/controller/SubmissionController.java @@ -582,7 +582,7 @@ private void processBatchExport( // Create a streaming workbook with a window size of 100 rows // (only this many rows will be kept in memory at once) try (SXSSFWorkbook workbook = new SXSSFWorkbook(100)) { - List submissions = submissionRepo.batchDynamicSubmissionQuery(filter, columns, secondaryDelimiter); + List submissions = submissionRepo.batchDynamicSubmissionQuery(filter, columns); // Enable compression for temporary files workbook.setCompressTempFiles(true); @@ -600,7 +600,7 @@ private void processBatchExport( // Stream data rows for (Submission submission : submissions) { - ExportPackage exportPackage = packagerUtility.packageExport(packager, submission, columns); + ExportPackage exportPackage = packagerUtility.packageExport(packager, submission, columns, secondaryDelimiter); if (exportPackage.isMap()) { Map rowData = (Map) exportPackage.getPayload(); Row row = worksheet.createRow(rowCount++); diff --git a/src/main/java/org/tdl/vireo/utility/PackagerUtility.java b/src/main/java/org/tdl/vireo/utility/PackagerUtility.java index fcbf3c0fbc..93d69e1658 100644 --- a/src/main/java/org/tdl/vireo/utility/PackagerUtility.java +++ b/src/main/java/org/tdl/vireo/utility/PackagerUtility.java @@ -45,6 +45,10 @@ public ExportPackage packageExport(Packager packager, Submission submission, return packager.packageExport(submission, columns); } + public ExportPackage packageExport(Packager packager, Submission submission, List columns, String delimiter) { + return packager.packageExport(submission, columns, delimiter); + } + public ExportPackage packageExport(Packager packager, Submission submission, Map dsDocs) { return packager.packageExport(submission, dsDocs); } From 1d3d6e437c5e99be0203991ddc9862d84eb0753d Mon Sep 17 00:00:00 2001 From: cstarcher Date: Sun, 17 May 2026 07:24:13 -0500 Subject: [PATCH 3/4] replace delimeter for all repeatable field profiles --- .../vireo/model/packager/ExcelPackager.java | 107 ++++-------------- .../model/packager/ExcelPackagerTest.java | 73 ++++++++++++ 2 files changed, 96 insertions(+), 84 deletions(-) create mode 100644 src/test/java/org/tdl/vireo/model/packager/ExcelPackagerTest.java diff --git a/src/main/java/org/tdl/vireo/model/packager/ExcelPackager.java b/src/main/java/org/tdl/vireo/model/packager/ExcelPackager.java index 0896b3046d..124b7472df 100644 --- a/src/main/java/org/tdl/vireo/model/packager/ExcelPackager.java +++ b/src/main/java/org/tdl/vireo/model/packager/ExcelPackager.java @@ -64,97 +64,21 @@ public ExcelExportPackage packageExport(Submission submission, String manifest) @Override public ExcelExportPackage packageExport(Submission submission, List columns) { - Map row = new HashMap(); - columns.forEach(column -> { - Optional predicate = Optional.ofNullable(column.getPredicate()); - if (predicate.isPresent()) { - List fieldValues = new ArrayList(); - for (FieldValue fieldValue : submission.getFieldValues()) { - if (fieldValue.getFieldPredicate().getValue().equals(predicate.get().trim())) { - fieldValues.add(fieldValue.getValue()); - row.put(column.getTitle(), String.join(", ", fieldValues)); - } else { - row.put(column.getTitle(), String.join(", ", fieldValues)); } - } - } else { - if (column.getValuePath().size() > 0) { - String[] valuePath = column.getValuePath().toArray(new String[column.getValuePath().size()]); - try { - if(column.getValuePath().size() > 1){ - valuePath = new String[] {valuePath[0]}; - } - submission.getCommitteeContactEmail(); - Object valueAsObject = EntityUtility.getValueFromPath(submission, valuePath); - - String value = ""; - - if (valueAsObject instanceof Calendar) { - Calendar calendar = (Calendar) valueAsObject; - value = simpleDateFormat.format(calendar.getTime()); - } else if (valueAsObject instanceof Date) { - Date date = (Date) valueAsObject; - value = simpleDateFormat.format(date); - } else if (valueAsObject instanceof Set && ((Set) valueAsObject).stream().allMatch(o -> o instanceof CustomActionValue)) { - StringBuilder sb = new StringBuilder(); - ((Set) valueAsObject).forEach(o -> { - CustomActionValue customActionValue = (CustomActionValue) o; - if (customActionValue.getValue()) { - sb.append("☑ "); - } else { - sb.append("☐ "); - } - sb.append(customActionValue.getDefinition().getLabel()+"\n"); - }); - value = sb.toString(); - } else if (valueAsObject instanceof SubmissionStatus){ - SubmissionStatus submissionStatus = (SubmissionStatus) valueAsObject; - value = submissionStatus.getName().toString(); - } else if (valueAsObject instanceof User){ - User user = (User) valueAsObject; - value = user.getName().toString(); - } else if (valueAsObject instanceof ActionLog){ - ActionLog actionLog = (ActionLog) valueAsObject; - switch (column.getTitle()){ - case "Event Time": - Calendar actionDate = (Calendar) actionLog.getActionDate(); - value = simpleDateFormat.format(actionDate.getTime()); - break; - case "Last Event": - value = actionLog.getEntry().toString(); - break; - default: - break; - } - } else { - value = valueAsObject.toString(); - } - row.put(column.getTitle(), value.toString()); - } catch (Exception exception) { - logger.warn("Unable to get value from " + String.join(",", valuePath)); - } - } else { - logger.warn("Column " + column.getTitle() + " has no predicate or value path!"); - } - } - }); - return new ExcelExportPackage(submission, "Excel", row); + return new ExcelExportPackage(submission, "Excel", buildRow(submission, columns, ", ")); } @Override public ExcelExportPackage packageExport(Submission submission, List columns, String delimiter) { + return new ExcelExportPackage(submission, "Excel", buildRow(submission, columns, delimiter)); + } + + private Map buildRow(Submission submission, List columns, String fieldValueDelimiter) { Map row = new HashMap(); columns.forEach(column -> { Optional predicate = Optional.ofNullable(column.getPredicate()); if (predicate.isPresent()) { - List fieldValues = new ArrayList(); - for (FieldValue fieldValue : submission.getFieldValues()) { - if (fieldValue.getFieldPredicate().getValue().equals(predicate.get().trim())) { - fieldValues.add(fieldValue.getValue()); - row.put(column.getTitle(), String.join(", ", fieldValues)); - } else { - row.put(column.getTitle(), String.join(delimiter+" ", fieldValues)); - } - } + List fieldValues = getFieldValues(submission, predicate.get().trim()); + row.put(column.getTitle(), String.join(fieldValueDelimiter, fieldValues)); } else { if (column.getValuePath().size() > 0) { String[] valuePath = column.getValuePath().toArray(new String[column.getValuePath().size()]); @@ -216,7 +140,22 @@ public ExcelExportPackage packageExport(Submission submission, List getFieldValues(Submission submission, String predicate) { + List fieldValues = new ArrayList(); + for (FieldValue fieldValue : submission.getFieldValues()) { + if (matchesPredicate(fieldValue, predicate)) { + fieldValues.add(fieldValue.getValue()); + } + } + return fieldValues; + } + + private boolean matchesPredicate(FieldValue fieldValue, String predicate) { + String fieldPredicate = fieldValue.getFieldPredicate().getValue(); + return fieldPredicate.equals(predicate) || fieldPredicate.startsWith(predicate + "."); } } diff --git a/src/test/java/org/tdl/vireo/model/packager/ExcelPackagerTest.java b/src/test/java/org/tdl/vireo/model/packager/ExcelPackagerTest.java new file mode 100644 index 0000000000..60adfd876a --- /dev/null +++ b/src/test/java/org/tdl/vireo/model/packager/ExcelPackagerTest.java @@ -0,0 +1,73 @@ +package org.tdl.vireo.model.packager; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import org.junit.jupiter.api.Test; +import org.tdl.vireo.model.FieldPredicate; +import org.tdl.vireo.model.FieldValue; +import org.tdl.vireo.model.Sort; +import org.tdl.vireo.model.Submission; +import org.tdl.vireo.model.SubmissionListColumn; +import org.tdl.vireo.model.export.ExcelExportPackage; +import org.springframework.test.util.ReflectionTestUtils; + +public class ExcelPackagerTest { + + @SuppressWarnings("unchecked") + @Test + public void testPackageExportUsesRequestedDelimiterForExactPredicateMatches() { + ExcelPackager packager = new ExcelPackager(); + Submission submission = new Submission(); + SubmissionListColumn column = new SubmissionListColumn("Keywords", Sort.NONE, "dc.subject"); + + Set fieldValues = new LinkedHashSet<>(); + fieldValues.add(createFieldValue("dc.subject", "alpha")); + fieldValues.add(createFieldValue("dc.subject", "beta")); + fieldValues.add(createFieldValue("dc.title", "ignored")); + + ReflectionTestUtils.setField(submission, "fieldValues", fieldValues); + + ExcelExportPackage exportPackage = packager.packageExport(submission, Arrays.asList(column), "|"); + Map row = (Map) exportPackage.getPayload(); + + assertEquals("alpha|beta", row.get("Keywords"), "Repeatable values should be joined once with the requested delimiter."); + } + + @SuppressWarnings("unchecked") + @Test + public void testPackageExportMatchesSuffixedPredicates() { + ExcelPackager packager = new ExcelPackager(); + Submission submission = new Submission(); + SubmissionListColumn column = new SubmissionListColumn("Non-Chairing Committee Members", Sort.NONE, "dc.contributor.committeeMember"); + + Set fieldValues = new LinkedHashSet<>(); + fieldValues.add(createFieldValue("dc.contributor.committeeMember.0", "Alice Smith")); + fieldValues.add(createFieldValue("dc.contributor.committeeMember.1", "Bob Jones")); + + ReflectionTestUtils.setField(submission, "fieldValues", fieldValues); + + ExcelExportPackage exportPackage = packager.packageExport(submission, Arrays.asList(column), "|"); + Map row = (Map) exportPackage.getPayload(); + + assertEquals("Alice Smith|Bob Jones", row.get("Non-Chairing Committee Members"), "Suffixed repeatable predicates should be included in the same exported cell."); + } + + private FieldValue createFieldValue(String predicateValue, String value) { + FieldPredicate fieldPredicate = new FieldPredicate(); + fieldPredicate.setId((long) predicateValue.hashCode()); + fieldPredicate.setValue(predicateValue); + + FieldValue fieldValue = new FieldValue(); + fieldValue.setId((long) (predicateValue + value).hashCode()); + fieldValue.setFieldPredicate(fieldPredicate); + fieldValue.setValue(value); + + return fieldValue; + } + +} \ No newline at end of file From 3f3a73d6012cec29d2e68f1818db8530ad38c068 Mon Sep 17 00:00:00 2001 From: smutniak Date: Wed, 20 May 2026 15:10:42 -0500 Subject: [PATCH 4/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../java/org/tdl/vireo/controller/SubmissionController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/tdl/vireo/controller/SubmissionController.java b/src/main/java/org/tdl/vireo/controller/SubmissionController.java index fa808236bf..c39c111d81 100644 --- a/src/main/java/org/tdl/vireo/controller/SubmissionController.java +++ b/src/main/java/org/tdl/vireo/controller/SubmissionController.java @@ -193,7 +193,7 @@ public class SubmissionController { @Value("${app.documentType.rename:}") private String documentTypesToRename; - @Value("${app.secondaryDelimiter:'|'}") + @Value("${app.secondaryDelimiter:|}") private String secondaryDelimiter; @RequestMapping("/all")