diff --git a/Java/SuperUtilities/.idea/uiDesigner.xml b/Java/SuperUtilities/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/Java/SuperUtilities/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/export/CustomExporter.java b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/export/CustomExporter.java index 4f8a21a..cc524cf 100644 --- a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/export/CustomExporter.java +++ b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/export/CustomExporter.java @@ -7,7 +7,9 @@ import com.nuix.superutilities.SuperUtilities; import com.nuix.superutilities.loadfiles.*; +import com.nuix.superutilities.misc.BoundedProgressInfo; import com.nuix.superutilities.misc.FormatUtility; +import com.nuix.superutilities.misc.PeriodicGatedConsumer; import com.nuix.superutilities.misc.PlaceholderResolver; import com.nuix.superutilities.reporting.SimpleWorksheet; import com.nuix.superutilities.reporting.SimpleXlsx; @@ -57,31 +59,35 @@ public class CustomExporter { private SimpleTextFileWriter errorLog = null; private boolean exportText = false; - private Map textExportSettings = new HashMap(); + private Map textExportSettings = new HashMap<>(); private String textFileNameTemplate = "{export_directory}\\TEXT\\{guid}.txt"; private boolean exportNatives = false; - private Map emailExportSettings = new HashMap(); + private Map emailExportSettings = new HashMap<>(); private String nativeFileNameTemplate = "{export_directory}\\NATIVES\\{guid}.{extension}"; private boolean exportPdfs = false; - private Map pdfExportSettings = new HashMap(); + private Map pdfExportSettings = new HashMap<>(); private String pdfFileNameTemplate = "{export_directory}\\PDF\\{guid}.pdf"; private boolean exportTiffs = false; - private Map tiffExportSettings = new HashMap(); + private Map tiffExportSettings = new HashMap<>(); private String tiffFileNameTemplate = "{export_directory}\\IMAGE\\{guid}.{extension}"; private boolean exportJson = false; private String jsonFileNameTemplate = "{export_directory}\\JSON\\{guid}.json"; private JsonExporter jsonExporter = null; + private Consumer messageLoggedCallback = null; + private PeriodicGatedConsumer progressCallback = null; + /** * -- SETTER -- * Sets whether DAT contents should additionally be exported as an XLSX spreadsheet. */ @Setter private boolean exportXlsx = false; + /** * -- SETTER -- * Sets whether final DAT will be kept by moving it to final export directory/ @@ -89,6 +95,12 @@ public class CustomExporter { @Setter private boolean keepOriginalDat = false; + /** + * Whether to skip export of natives for slip-sheeted items. + */ + @Setter + private boolean skipNativesSlipsheetedItems = false; + /** * -- SETTER -- * Allows you to provide a Map of headers to rename. Intended to provide a way to rename headers that Nuix automatically adds @@ -129,6 +141,31 @@ public class CustomExporter { public CustomExporter() { } + /*** + * Provides a callback to be invoked when progress is made during initial batch export or during + * restructuring phase. Will be wrapped in a {@link PeriodicGatedConsumer} with an interval of 5 seconds + * if not already an instance of {@link PeriodicGatedConsumer}. + * @param progressCallback The progress info consumer. + */ + public void whenProgressEventOccurs(Consumer progressCallback) { + if (progressCallback instanceof PeriodicGatedConsumer) { + this.progressCallback = (PeriodicGatedConsumer) progressCallback; + } else { + this.progressCallback = new PeriodicGatedConsumer<>(progressCallback, 5000); + } + } + + private void fireProgressEvent(String stage, long current, long total) { + BoundedProgressInfo info = new BoundedProgressInfo(stage, current, total); + if (progressCallback != null) { + progressCallback.accept(info); + } + } + + public void whenMessageLogged(Consumer messageLoggedCallback) { + this.messageLoggedCallback = messageLoggedCallback; + } + /*** * Assigns a dynamically calculated placeholder to this instance. * @param placeholderName Placeholder name with "{" or "}". For example "my_value". Placeholder in templates can then be referred to using "{my_value}". It is @@ -245,7 +282,13 @@ public void setColumnRemovals(Collection columnHeaders) { private void logInfo(String format, Object... params) { String message = String.format(format, params); - System.out.println(message); + + if (messageLoggedCallback != null) { + messageLoggedCallback.accept(message); + } else { + System.out.println(message); + } + if (generalLog != null) { try { generalLog.writeLine(message); @@ -259,7 +302,13 @@ private void logInfo(String format, Object... params) { private void logError(String format, Object... params) { String message = String.format(format, params); - System.out.println(message); + + if (messageLoggedCallback != null) { + messageLoggedCallback.accept("ERROR: " + message); + } else { + System.err.println(message); + } + if (errorLog != null) { try { errorLog.writeLine(message); @@ -298,12 +347,7 @@ public String evaluate(Item item) { } } if (!hasGuid) { - exportProfile = exportProfile.addMetadata("GUID", new ItemExpression() { - @Override - public String evaluate(Item item) { - return item.getGuid(); - } - }); + exportProfile = exportProfile.addMetadata("GUID", Item::getGuid); } } return exportProfile; @@ -421,7 +465,6 @@ public void exportItems(Case nuixCase, File exportDirectory, List items) t exporter.addProduct("tiff", productSettings); } - exportDirectory.mkdirs(); File tempDatFile = new File(exportTempDirectory, "loadfile.dat"); File finalDatFile = new File(exportDirectory, "loadfile.dat"); @@ -448,6 +491,8 @@ public void itemProcessed(ItemEventInfo info) { logError("BatchExporter reports error while exporting item with GUID '%s':\n%s", info.getItem().getGuid(), FormatUtility.debugString(info.getFailure())); } + + fireProgressEvent("BatchExport: " + info.getStage(), info.getStageCount(), items.size()); } }); @@ -469,6 +514,9 @@ public void itemProcessed(ItemEventInfo info) { exporter.setStampingOptions(stampingSettings); } + // Forward setting regarding natives slipsheet generation + exporter.setSkipNativesSlipsheetedItems(skipNativesSlipsheetedItems); + logInfo("Beginning temp export using BatchExporter..."); exporter.exportItems(items); logInfo("Finished temp export using BatchExporter"); @@ -495,6 +543,8 @@ public void itemProcessed(ItemEventInfo info) { @Override public void accept(LinkedHashMap record) { + fireProgressEvent("Export Restructure", recordsProcessed, items.size()); + // Periodically log progress long diffMillis = System.currentTimeMillis() - restructureStartMillis; if (diffMillis > 2 * 1000 || recordsProcessed % 100 == 0) { diff --git a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/BoundedProgressInfo.java b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/BoundedProgressInfo.java new file mode 100644 index 0000000..7ac2b6b --- /dev/null +++ b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/BoundedProgressInfo.java @@ -0,0 +1,34 @@ +package com.nuix.superutilities.misc; + +import lombok.Getter; + +/*** + * Represents progress of an operation which has a bounded value, that is, we known what progress + * value is considered maximum/done. + */ +@Getter +public class BoundedProgressInfo extends ProgressInfo { + /*** + * The maximum progress value + */ + protected final long maximum; + + public BoundedProgressInfo(String stage, long current, long maximum) { + super(stage, current); + this.maximum = maximum; + } + + /*** + * Provides a percentage completed value by dividing double value of current by + * double value of maximum. + * @return A double value representing percentage complete + */ + public double percentageComplete() { + return ((double) current) / ((double) maximum); + } + + @Override + public String toString() { + return "BoundedProgressInfo [stage=" + stage + ", " + current + "/" + maximum + "]"; + } +} diff --git a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PeriodicGatedConsumer.java b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PeriodicGatedConsumer.java new file mode 100644 index 0000000..621fa80 --- /dev/null +++ b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PeriodicGatedConsumer.java @@ -0,0 +1,68 @@ +package com.nuix.superutilities.misc; + +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +/*** + * A wrapper for a {@link Consumer} instance which will only periodically forward call to accept + * method of wrapped instance. Created for taking per item events that Nuix publishes and turning them + * into periodic progress reporters. + * @param The type of the {@link Consumer} being wrapped + */ +public class PeriodicGatedConsumer implements Consumer { + private final Consumer wrappedConsumer; + private long lastPassThru = 0; + + @Getter + @Setter + private long intervalMillis = 1000; + + public PeriodicGatedConsumer(@NotNull Consumer wrappedConsumer) { + this.wrappedConsumer = wrappedConsumer; + } + + public PeriodicGatedConsumer(@NotNull Consumer wrappedConsumer, long intervalMillis) { + this.wrappedConsumer = wrappedConsumer; + this.intervalMillis = intervalMillis; + } + + /*** + * Convenience method for setting millis interval to a certain number of seconds + * @param intervalSeconds The number of seconds + */ + public void setIntervalSeconds(long intervalSeconds) { + this.intervalMillis = intervalSeconds * 1000; + } + + /*** + * Convenience method for setting millis interval to a certain number of minutes + * @param intervalMinutes The number of minutes + */ + public void setIntervalMinutes(long intervalMinutes) { + this.intervalMillis = intervalMinutes * 60 * 1000; + } + + /*** + * Will periodically forward call to wrapped Consumer and then reset current interval + * @param t the input argument + */ + @Override + public void accept(T t) { + if (intervalMillis < 1 || System.currentTimeMillis() - lastPassThru > intervalMillis) { + wrappedConsumer.accept(t); + lastPassThru = System.currentTimeMillis(); + } + } + + /*** + * Will immediately forward call to wrapped Consumer and then reset current interval + * @param t the input argument + */ + public void acceptImmediately(T t) { + wrappedConsumer.accept(t); + lastPassThru = System.currentTimeMillis(); + } +} diff --git a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PlaceholderResolver.java b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PlaceholderResolver.java index 6cc1b3b..c920dbf 100644 --- a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PlaceholderResolver.java +++ b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/PlaceholderResolver.java @@ -135,7 +135,7 @@ public void setFromItem(Item item) { * {nuix_version} - The Nuix version as defined in NUIX_VERSION
*/ public void setStandardValues(DateTime now) { - if(now == null) { + if (now == null) { now = DateTime.now(); } set("date_short", now.toString("YYYYMMdd")); @@ -191,7 +191,7 @@ public String get(String key) { } /*** - * Clears all currently associated place holders (keys and values) + * Clears all currently associated placeholders (keys and values) */ public void clear() { placeholderData.clear(); @@ -264,6 +264,11 @@ public String resolveTemplatePath(String template) { } result = p.matcher(result).replaceAll(value); } + + // Attempt to prevent double assigned extension. Happens in scenario where item name already + // contains the extension and therefore {extension} in template effectively adds it a second time + result = result.replaceAll("([a-zA-Z0-9]+)\\.\\1$", "$1"); + return result; } diff --git a/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/ProgressInfo.java b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/ProgressInfo.java new file mode 100644 index 0000000..763f25f --- /dev/null +++ b/Java/SuperUtilities/src/main/java/com/nuix/superutilities/misc/ProgressInfo.java @@ -0,0 +1,31 @@ +package com.nuix.superutilities.misc; + +import lombok.Getter; + +/*** + * Represents progress of an indeterminate operation where the maximum progress value + * is not known until completion. + */ +@Getter +public class ProgressInfo { + /*** + * A title representing the current stage, may be null or blank if progress + * publisher does not populate it! + */ + protected final String stage; + + /*** + * The current progress value + */ + protected final long current; + + public ProgressInfo(String stage, long current) { + this.stage = stage; + this.current = current; + } + + @Override + public String toString() { + return "ProgressInfo [stage=" + stage + ", current=" + current + "]"; + } +}