#7: Historien-Tab mit Liste, Detail, Filter, Status-Reset und Eintrag-Loeschen
Implementiert den vollstaendigen Historien-Tab (Verlauf) als vierten Tab der GUI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+159
-4
@@ -87,7 +87,18 @@ import de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog.AiModelCatal
|
||||
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.adapter.in.gui.history.GuiDeleteDocumentHistoryPort;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.in.gui.history.GuiHistoryDetailsPort;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.in.gui.history.GuiHistoryOverviewPort;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.in.gui.history.GuiHistoryResetDocumentStatusPort;
|
||||
import de.gecheckt.pdf.umbenenner.adapter.out.sqlite.SqliteHistoryQueryAdapter;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.history.HistoryQuery;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.history.HistoryQueryPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultBatchRunProcessingUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultDeleteDocumentHistoryUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultHistoryDetailsUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultHistoryOverviewUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultHistoryResetDocumentStatusUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultManualFileCopyUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultManualFileRenameUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.usecase.DefaultPromptEditorUseCase;
|
||||
@@ -808,6 +819,10 @@ public class BootstrapRunner {
|
||||
GuiManualFileRenamePort manualRenamePort = this::performGuiManualFileRename;
|
||||
GuiManualFileCopyPort manualCopyPort = this::performGuiManualFileCopy;
|
||||
GuiHistoricalDocumentContextPort historicalDocumentContextPort = this::resolveHistoricalDocumentContextForGui;
|
||||
GuiHistoryOverviewPort historyOverviewPort = this::loadHistoryOverviewForGui;
|
||||
GuiHistoryDetailsPort historyDetailsPort = this::loadHistoryDetailsForGui;
|
||||
GuiHistoryResetDocumentStatusPort historyResetPort = this::resetHistoryDocumentStatusForGui;
|
||||
GuiDeleteDocumentHistoryPort deleteHistoryPort = this::deleteDocumentHistoryForGui;
|
||||
// Versionsnummer aus dem MANIFEST.MF des gepackten JARs lesen; Fallback "dev" bei IDE-Start
|
||||
String applicationVersion = ApplicationVersionProvider.resolveVersion();
|
||||
|
||||
@@ -830,7 +845,11 @@ public class BootstrapRunner {
|
||||
manualCopyPort,
|
||||
historicalDocumentContextPort,
|
||||
applicationVersion,
|
||||
noOpGuiPromptEditorPort());
|
||||
noOpGuiPromptEditorPort(),
|
||||
historyOverviewPort,
|
||||
historyDetailsPort,
|
||||
historyResetPort,
|
||||
deleteHistoryPort);
|
||||
}
|
||||
|
||||
Path configPath = Paths.get(configPathOverride.get());
|
||||
@@ -856,7 +875,11 @@ public class BootstrapRunner {
|
||||
manualCopyPort,
|
||||
historicalDocumentContextPort,
|
||||
applicationVersion,
|
||||
noOpGuiPromptEditorPort());
|
||||
noOpGuiPromptEditorPort(),
|
||||
historyOverviewPort,
|
||||
historyDetailsPort,
|
||||
historyResetPort,
|
||||
deleteHistoryPort);
|
||||
}
|
||||
|
||||
LOG.info("GUI startup: configuration file confirmed at: {}", configPath.toAbsolutePath());
|
||||
@@ -868,7 +891,8 @@ public class BootstrapRunner {
|
||||
modelCatalogPort, apiKeyResolutionPort, providerTechnicalTestService, pathCheckPort,
|
||||
technicalTestOrchestrator, correctionExecutionService, batchRunLauncher,
|
||||
miniRunLauncher, resetPort, manualRenamePort, manualCopyPort,
|
||||
historicalDocumentContextPort, applicationVersion, promptEditorPort);
|
||||
historicalDocumentContextPort, applicationVersion, promptEditorPort,
|
||||
historyOverviewPort, historyDetailsPort, historyResetPort, deleteHistoryPort);
|
||||
} catch (GuiConfigurationLoadException e) {
|
||||
LOG.error("GUI startup: configuration could not be loaded, starting without it: {}",
|
||||
e.getMessage(), e);
|
||||
@@ -890,7 +914,11 @@ public class BootstrapRunner {
|
||||
manualCopyPort,
|
||||
historicalDocumentContextPort,
|
||||
applicationVersion,
|
||||
noOpGuiPromptEditorPort());
|
||||
noOpGuiPromptEditorPort(),
|
||||
historyOverviewPort,
|
||||
historyDetailsPort,
|
||||
historyResetPort,
|
||||
deleteHistoryPort);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1421,6 +1449,133 @@ public class BootstrapRunner {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt die gefilterte Dokumentenübersicht für den Historien-Tab.
|
||||
* <p>
|
||||
* Verdrahtet {@link SqliteHistoryQueryAdapter} und {@link DefaultHistoryOverviewUseCase}
|
||||
* frisch pro Aufruf, da keine persistente Verbindung über GUI-Interaktionen hinweg
|
||||
* gehalten wird.
|
||||
*
|
||||
* @param configFilePath Pfad zur geladenen Konfigurationsdatei; darf nicht {@code null} sein
|
||||
* @param query Abfrageparameter; darf nicht {@code null} sein
|
||||
* @return Ergebnis mit gefundenen Zeilen und hasMore-Flag; nie {@code null}
|
||||
*/
|
||||
DefaultHistoryOverviewUseCase.HistoryOverviewResult loadHistoryOverviewForGui(
|
||||
Path configFilePath,
|
||||
de.gecheckt.pdf.umbenenner.application.port.out.history.HistoryQuery query) {
|
||||
Objects.requireNonNull(configFilePath, "configFilePath must not be null");
|
||||
Objects.requireNonNull(query, "query must not be null");
|
||||
try {
|
||||
migrateConfigurationIfNeeded(configFilePath);
|
||||
StartConfiguration config = loadAndValidateConfiguration(configFilePath);
|
||||
initializeSchema(config);
|
||||
String jdbcUrl = buildJdbcUrl(config);
|
||||
HistoryQueryPort historyQueryPort = new SqliteHistoryQueryAdapter(jdbcUrl);
|
||||
DefaultHistoryOverviewUseCase useCase = new DefaultHistoryOverviewUseCase(historyQueryPort);
|
||||
return useCase.loadOverview(query);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Historienübersicht konnte nicht geladen werden: {}", e.getMessage(), e);
|
||||
throw new DocumentPersistenceException(
|
||||
"Historienübersicht konnte nicht geladen werden: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt Stammsatz und alle Verarbeitungsversuche für den angegebenen Fingerprint.
|
||||
* <p>
|
||||
* Verdrahtet {@link SqliteHistoryQueryAdapter} und {@link DefaultHistoryDetailsUseCase}
|
||||
* frisch pro Aufruf.
|
||||
*
|
||||
* @param configFilePath Pfad zur geladenen Konfigurationsdatei; darf nicht {@code null} sein
|
||||
* @param fingerprint der Dokumentbezeichner; darf nicht {@code null} sein
|
||||
* @return Optional mit den Detaildaten, oder leer wenn kein Eintrag gefunden wurde
|
||||
*/
|
||||
Optional<DefaultHistoryDetailsUseCase.HistoryDetailsResult> loadHistoryDetailsForGui(
|
||||
Path configFilePath,
|
||||
DocumentFingerprint fingerprint) {
|
||||
Objects.requireNonNull(configFilePath, "configFilePath must not be null");
|
||||
Objects.requireNonNull(fingerprint, "fingerprint must not be null");
|
||||
try {
|
||||
migrateConfigurationIfNeeded(configFilePath);
|
||||
StartConfiguration config = loadAndValidateConfiguration(configFilePath);
|
||||
initializeSchema(config);
|
||||
String jdbcUrl = buildJdbcUrl(config);
|
||||
HistoryQueryPort historyQueryPort = new SqliteHistoryQueryAdapter(jdbcUrl);
|
||||
DefaultHistoryDetailsUseCase useCase = new DefaultHistoryDetailsUseCase(historyQueryPort);
|
||||
return useCase.loadDetails(fingerprint);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Historiendetails für {} konnten nicht geladen werden: {}",
|
||||
fingerprint.sha256Hex(), e.getMessage(), e);
|
||||
throw new DocumentPersistenceException(
|
||||
"Historiendetails konnten nicht geladen werden: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Führt den feldgenauen Status-Reset für das angegebene Dokument durch.
|
||||
* <p>
|
||||
* Setzt {@code overall_status}, {@code content_error_count}, {@code transient_error_count}
|
||||
* und {@code last_failure_instant} zurück. Die Versuchshistorie bleibt erhalten.
|
||||
*
|
||||
* @param configFilePath Pfad zur geladenen Konfigurationsdatei; darf nicht {@code null} sein
|
||||
* @param fingerprint der Dokumentbezeichner; darf nicht {@code null} sein
|
||||
*/
|
||||
void resetHistoryDocumentStatusForGui(
|
||||
Path configFilePath,
|
||||
DocumentFingerprint fingerprint) {
|
||||
Objects.requireNonNull(configFilePath, "configFilePath must not be null");
|
||||
Objects.requireNonNull(fingerprint, "fingerprint must not be null");
|
||||
LOG.info("Historien-Status-Reset für Fingerprint: {}", fingerprint.sha256Hex());
|
||||
try {
|
||||
migrateConfigurationIfNeeded(configFilePath);
|
||||
StartConfiguration config = loadAndValidateConfiguration(configFilePath);
|
||||
initializeSchema(config);
|
||||
String jdbcUrl = buildJdbcUrl(config);
|
||||
UnitOfWorkPort unitOfWorkPort = new SqliteUnitOfWorkAdapter(jdbcUrl);
|
||||
DefaultHistoryResetDocumentStatusUseCase useCase =
|
||||
new DefaultHistoryResetDocumentStatusUseCase(unitOfWorkPort);
|
||||
useCase.resetStatus(fingerprint);
|
||||
LOG.info("Historien-Status-Reset abgeschlossen für Fingerprint: {}", fingerprint.sha256Hex());
|
||||
} catch (Exception e) {
|
||||
LOG.error("Historien-Status-Reset fehlgeschlagen für {}: {}",
|
||||
fingerprint.sha256Hex(), e.getMessage(), e);
|
||||
throw new DocumentPersistenceException(
|
||||
"Status-Reset fehlgeschlagen: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht den Stammsatz und alle Verarbeitungsversuche für das angegebene Dokument.
|
||||
* <p>
|
||||
* Die Löschung ist destruktiv und nicht rückgängig zu machen.
|
||||
*
|
||||
* @param configFilePath Pfad zur geladenen Konfigurationsdatei; darf nicht {@code null} sein
|
||||
* @param fingerprint der Dokumentbezeichner; darf nicht {@code null} sein
|
||||
*/
|
||||
void deleteDocumentHistoryForGui(
|
||||
Path configFilePath,
|
||||
DocumentFingerprint fingerprint) {
|
||||
Objects.requireNonNull(configFilePath, "configFilePath must not be null");
|
||||
Objects.requireNonNull(fingerprint, "fingerprint must not be null");
|
||||
LOG.info("Historien-Löschen für Fingerprint: {}", fingerprint.sha256Hex());
|
||||
try {
|
||||
migrateConfigurationIfNeeded(configFilePath);
|
||||
StartConfiguration config = loadAndValidateConfiguration(configFilePath);
|
||||
initializeSchema(config);
|
||||
String jdbcUrl = buildJdbcUrl(config);
|
||||
UnitOfWorkPort unitOfWorkPort = new SqliteUnitOfWorkAdapter(jdbcUrl);
|
||||
DefaultDeleteDocumentHistoryUseCase useCase =
|
||||
new DefaultDeleteDocumentHistoryUseCase(unitOfWorkPort);
|
||||
useCase.deleteHistory(fingerprint);
|
||||
LOG.info("Historien-Löschen abgeschlossen für Fingerprint: {}", fingerprint.sha256Hex());
|
||||
} catch (Exception e) {
|
||||
LOG.error("Historien-Löschen fehlgeschlagen für {}: {}",
|
||||
fingerprint.sha256Hex(), e.getMessage(), e);
|
||||
throw new DocumentPersistenceException(
|
||||
"Löschen fehlgeschlagen: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link ResetDocumentStatusResult} where every requested fingerprint is
|
||||
* recorded as a failure with the given error message.
|
||||
|
||||
Reference in New Issue
Block a user