diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java index e79c2a5..d979435 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java @@ -43,6 +43,8 @@ import de.gecheckt.pdf.umbenenner.application.validation.editor.EditorValidation import de.gecheckt.pdf.umbenenner.application.validation.editor.EditorValidationSeverity; import de.gecheckt.pdf.umbenenner.application.validation.technicaltest.CorrectionExecutionService; import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; @@ -51,8 +53,6 @@ import javafx.scene.control.Alert; import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.CheckBox; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; import javafx.scene.control.ComboBox; import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; @@ -70,7 +70,6 @@ import javafx.scene.input.ClipboardContent; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; - import javafx.scene.layout.BorderPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunCoordinator.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunCoordinator.java index a0bfd4e..9639fcc 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunCoordinator.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunCoordinator.java @@ -268,6 +268,48 @@ public final class GuiBatchRunCoordinator { return startWorker(task); } + /** + * Starts a reprocessing operation: resets the database status of the specified + * fingerprints and immediately launches a targeted mini-run for them. + *

+ * This method is the preferred entry point for "Erneut verarbeiten" (reprocess) + * actions in the GUI. It ensures that documents marked as FAILED_FINAL or otherwise + * ineligible for processing are reset before the mini-run begins, so they are + * reprocessed rather than skipped. + *

+ * The reset executes synchronously on the caller's thread before the worker thread + * is started. This guarantees that the mini-run sees the documents in a + * reprocessable state. + * + * @param configFilePath the configuration file; must not be {@code null} + * @param fingerprintFilter the set of document fingerprints to reset and process; + * must not be {@code null} + * @return {@code true} when a new worker thread was started, {@code false} when a run + * was already in progress or when the reset failed for all fingerprints + * @throws NullPointerException if any argument is {@code null} + */ + public boolean startReprocessing(Path configFilePath, + Set fingerprintFilter) { + Objects.requireNonNull(configFilePath, "configFilePath must not be null"); + Objects.requireNonNull(fingerprintFilter, "fingerprintFilter must not be null"); + if (isRunning()) { + return false; + } + // Reset the database status synchronously before starting the mini-run. + // This ensures that documents are not skipped due to FAILED_FINAL or other + // terminal states. + ResetDocumentStatusResult resetResult = resetPort.reset(configFilePath, fingerprintFilter); + if (resetResult.successCount() == 0) { + LOG.warn("GUI-Reprocessing: Reset für alle {} Dokumente fehlgeschlagen; " + + "Mini-Lauf wird nicht gestartet.", fingerprintFilter.size()); + return false; + } + LOG.info("GUI-Reprocessing: {} von {} Dokumenten erfolgreich zurückgesetzt.", + resetResult.successCount(), resetResult.requestedCount()); + // Now start the mini-run with the reset fingerprints. + return startMiniRun(configFilePath, fingerprintFilter); + } + /** * Starts a reset-only operation for the supplied fingerprint set. *

diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTab.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTab.java index 3438cb3..ff1997c 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTab.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTab.java @@ -4,7 +4,6 @@ import java.nio.file.Path; import java.time.Duration; import java.time.format.DateTimeFormatter; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -707,7 +706,10 @@ public final class GuiBatchRunTab { // Mark selected rows as reset-pending immediately for visual feedback. markSelectedRowsAsResetPending(); - boolean started = coordinator.startMiniRun(configPath, snapshot); + // Reset database status and start mini-run. The reset executes synchronously + // before the mini-run worker thread is started, ensuring documents are not + // skipped due to FAILED_FINAL status. + boolean started = coordinator.startReprocessing(configPath, snapshot); if (!started) { showMessage(ALREADY_RUNNING_HINT); return; diff --git a/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunCoordinatorMiniRunTest.java b/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunCoordinatorMiniRunTest.java index 7e5d73b..b3f0c9a 100644 --- a/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunCoordinatorMiniRunTest.java +++ b/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunCoordinatorMiniRunTest.java @@ -18,8 +18,6 @@ import java.util.function.Function; import org.junit.jupiter.api.Test; -import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunCancellationToken; -import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunProgressObserver; import de.gecheckt.pdf.umbenenner.application.port.in.DocumentCompletionEvent; import de.gecheckt.pdf.umbenenner.application.port.in.DocumentCompletionStatus; import de.gecheckt.pdf.umbenenner.application.port.in.ResetDocumentStatusResult; diff --git a/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTabSelectionSmokeTest.java b/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTabSelectionSmokeTest.java index 1971302..a9b9789 100644 --- a/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTabSelectionSmokeTest.java +++ b/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTabSelectionSmokeTest.java @@ -196,7 +196,10 @@ class GuiBatchRunTabSelectionSmokeTest { GuiBatchRunLauncher noOpLauncher = (p, o, t) -> GuiBatchRunLaunchOutcome.rejected("not used"); GuiResetDocumentStatusPort noOpReset = (p, fps) -> - new ResetDocumentStatusResult(fps.size(), Set.of(), Map.of()); + // Return a successful reset for all fingerprints. The reset doesn't actually + // change anything in this test, but successCount() returns successfullyReset.size(), + // so we must return the fingerprints in the successfully reset set. + new ResetDocumentStatusResult(fps.size(), fps, Map.of()); CountDownLatch tabReady = new CountDownLatch(1); AtomicReferenceCapture tabRef = new AtomicReferenceCapture<>(); diff --git a/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTabSmokeTest.java b/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTabSmokeTest.java index f9d746c..6003a10 100644 --- a/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTabSmokeTest.java +++ b/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/batchrun/GuiBatchRunTabSmokeTest.java @@ -2,7 +2,6 @@ package de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.file.Path; diff --git a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/AiResponseValidator.java b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/AiResponseValidator.java index 901263c..95c850b 100644 --- a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/AiResponseValidator.java +++ b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/AiResponseValidator.java @@ -6,6 +6,7 @@ import java.util.Objects; import java.util.Set; import de.gecheckt.pdf.umbenenner.application.port.out.ClockPort; +import de.gecheckt.pdf.umbenenner.application.service.AiResponseValidator.AiValidationResult; import de.gecheckt.pdf.umbenenner.domain.model.AiErrorClassification; import de.gecheckt.pdf.umbenenner.domain.model.DateSource; import de.gecheckt.pdf.umbenenner.domain.model.NamingProposal; diff --git a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/DocumentProcessingCoordinator.java b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/DocumentProcessingCoordinator.java index 9b88475..aeb2e8f 100644 --- a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/DocumentProcessingCoordinator.java +++ b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/DocumentProcessingCoordinator.java @@ -7,6 +7,7 @@ import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; +import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunProgressObserver; import de.gecheckt.pdf.umbenenner.application.port.in.DocumentCompletionEvent; import de.gecheckt.pdf.umbenenner.application.port.in.DocumentCompletionStatus; import de.gecheckt.pdf.umbenenner.application.port.out.DocumentKnownProcessable; diff --git a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/usecase/DefaultBatchRunProcessingUseCase.java b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/usecase/DefaultBatchRunProcessingUseCase.java index e315572..b757318 100644 --- a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/usecase/DefaultBatchRunProcessingUseCase.java +++ b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/usecase/DefaultBatchRunProcessingUseCase.java @@ -12,14 +12,10 @@ import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunCancellationToken; import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunOutcome; import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunProcessingUseCase; import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunProgressObserver; -import de.gecheckt.pdf.umbenenner.application.port.in.DocumentCompletionEvent; -import de.gecheckt.pdf.umbenenner.application.port.in.DocumentCompletionStatus; -import de.gecheckt.pdf.umbenenner.application.port.in.RunSummary; import de.gecheckt.pdf.umbenenner.application.port.out.FingerprintPort; import de.gecheckt.pdf.umbenenner.application.port.out.FingerprintResult; import de.gecheckt.pdf.umbenenner.application.port.out.FingerprintSuccess; import de.gecheckt.pdf.umbenenner.application.port.out.FingerprintTechnicalError; -import de.gecheckt.pdf.umbenenner.domain.model.DocumentFingerprint; import de.gecheckt.pdf.umbenenner.application.port.out.PdfTextExtractionPort; import de.gecheckt.pdf.umbenenner.application.port.out.ProcessingLogger; import de.gecheckt.pdf.umbenenner.application.port.out.RunLockPort; diff --git a/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/usecase/BatchRunProgressObservationTest.java b/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/usecase/BatchRunProgressObservationTest.java index 4541bdb..9640758 100644 --- a/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/usecase/BatchRunProgressObservationTest.java +++ b/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/usecase/BatchRunProgressObservationTest.java @@ -2,14 +2,11 @@ package de.gecheckt.pdf.umbenenner.application.usecase; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; import java.time.Instant; @@ -17,7 +14,6 @@ import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import org.junit.jupiter.api.Test; diff --git a/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/usecase/DefaultResetDocumentStatusUseCaseTest.java b/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/usecase/DefaultResetDocumentStatusUseCaseTest.java index ebc4bab..a523702 100644 --- a/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/usecase/DefaultResetDocumentStatusUseCaseTest.java +++ b/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/usecase/DefaultResetDocumentStatusUseCaseTest.java @@ -6,7 +6,6 @@ import static org.assertj.core.api.Assertions.assertThatNullPointerException; import java.util.ArrayList; import java.util.List; import java.util.Set; -import java.util.function.Consumer; import org.junit.jupiter.api.Test; diff --git a/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunner.java b/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunner.java index d5c3d02..7a67763 100644 --- a/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunner.java +++ b/pdf-umbenenner-bootstrap/src/main/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunner.java @@ -24,8 +24,6 @@ import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiConfigurationLoadException; import de.gecheckt.pdf.umbenenner.adapter.in.gui.GuiStartupContext; import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiBatchRunLaunchOutcome; import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiBatchRunLauncher; -import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiMiniRunLauncher; -import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiResetDocumentStatusPort; import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiConfigurationEditorState; import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiConfigurationEditorStateFactory; import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiConfigurationFileSnapshot; @@ -56,7 +54,6 @@ import de.gecheckt.pdf.umbenenner.application.config.startup.StartConfiguration; import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunOutcome; import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunProcessingUseCase; import de.gecheckt.pdf.umbenenner.application.port.in.ResetDocumentStatusResult; -import de.gecheckt.pdf.umbenenner.application.usecase.DefaultResetDocumentStatusUseCase; import de.gecheckt.pdf.umbenenner.application.port.out.AiContentSensitivity; import de.gecheckt.pdf.umbenenner.application.port.out.AiInvocationPort; import de.gecheckt.pdf.umbenenner.application.port.out.ClockPort; @@ -77,6 +74,7 @@ import de.gecheckt.pdf.umbenenner.application.service.AiNamingService; import de.gecheckt.pdf.umbenenner.application.service.AiResponseValidator; import de.gecheckt.pdf.umbenenner.application.service.DocumentProcessingCoordinator; import de.gecheckt.pdf.umbenenner.application.usecase.DefaultBatchRunProcessingUseCase; +import de.gecheckt.pdf.umbenenner.application.usecase.DefaultResetDocumentStatusUseCase; import de.gecheckt.pdf.umbenenner.application.validation.editor.EditorConfigurationValidator; import de.gecheckt.pdf.umbenenner.application.validation.technicaltest.ProviderTechnicalTestService; import de.gecheckt.pdf.umbenenner.application.validation.technicaltest.TechnicalTestOrchestrator;