From 86e9c05aaf0664e8c6d81037feea866ae2d1b60b Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 13 Apr 2026 15:14:27 +0200 Subject: [PATCH] feat(training): add Paraglide i18n to training UI components and wire SegmentationTrainingCard - Convert TrainingHistory, OcrTrainingCard, SegmentationTrainingCard, and TranscriptionBlock "Nur Segmentierung" badge to use Paraglide message keys - Add availableSegBlocks to TrainingInfoResponse to expose segmentation block count in the training info endpoint - Wire SegmentationTrainingCard into admin/system page below OCR training card - Update api.ts with availableSegBlocks field Co-Authored-By: Claude Sonnet 4.6 --- .../service/OcrTrainingService.java | 5 ++ .../controller/OcrControllerTest.java | 3 +- .../service/OcrTrainingServiceTest.java | 5 +- .../src/lib/components/OcrTrainingCard.svelte | 27 +++--- .../SegmentationTrainingCard.svelte | 86 +++++++++++++++++++ .../src/lib/components/TrainingHistory.svelte | 18 ++-- .../lib/components/TranscriptionBlock.svelte | 9 +- frontend/src/lib/generated/api.ts | 2 + frontend/src/routes/admin/system/+page.svelte | 14 ++- 9 files changed, 144 insertions(+), 25 deletions(-) create mode 100644 frontend/src/lib/components/SegmentationTrainingCard.svelte diff --git a/backend/src/main/java/org/raddatz/familienarchiv/service/OcrTrainingService.java b/backend/src/main/java/org/raddatz/familienarchiv/service/OcrTrainingService.java index 1315a2de..04844fd0 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/service/OcrTrainingService.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/service/OcrTrainingService.java @@ -27,6 +27,7 @@ public class OcrTrainingService { private final OcrTrainingRunRepository trainingRunRepository; private final TrainingDataExportService trainingDataExportService; + private final SegmentationTrainingExportService segmentationTrainingExportService; private final OcrClient ocrClient; private final OcrHealthClient ocrHealthClient; private final TranscriptionBlockRepository blockRepository; @@ -35,6 +36,7 @@ public class OcrTrainingService { int availableBlocks, int totalOcrBlocks, int availableDocuments, + int availableSegBlocks, boolean ocrServiceAvailable, OcrTrainingRun lastRun, List runs @@ -105,6 +107,7 @@ public class OcrTrainingService { .count(); int totalOcrBlocks = blockRepository.findAll().size(); + int availableSegBlocks = segmentationTrainingExportService.querySegmentationBlocks().size(); List recentRuns = trainingRunRepository.findTop5ByOrderByCreatedAtDesc(); OcrTrainingRun lastRun = recentRuns.isEmpty() ? null : recentRuns.get(0); @@ -113,6 +116,7 @@ public class OcrTrainingService { eligibleBlocks.size(), totalOcrBlocks, availableDocuments, + availableSegBlocks, ocrHealthClient.isHealthy(), lastRun, recentRuns @@ -139,6 +143,7 @@ public class OcrTrainingService { "availableBlocks", info.availableBlocks(), "totalOcrBlocks", info.totalOcrBlocks(), "availableDocuments", info.availableDocuments(), + "availableSegBlocks", info.availableSegBlocks(), "ocrServiceAvailable", info.ocrServiceAvailable(), "lastRun", info.lastRun() != null ? info.lastRun() : Map.of(), "runs", info.runs() diff --git a/backend/src/test/java/org/raddatz/familienarchiv/controller/OcrControllerTest.java b/backend/src/test/java/org/raddatz/familienarchiv/controller/OcrControllerTest.java index 075c18be..b65bb467 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/controller/OcrControllerTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/controller/OcrControllerTest.java @@ -44,6 +44,7 @@ class OcrControllerTest { @MockitoBean UserService userService; @MockitoBean CustomUserDetailsService customUserDetailsService; @MockitoBean TrainingDataExportService trainingDataExportService; + @MockitoBean SegmentationTrainingExportService segmentationTrainingExportService; @MockitoBean OcrTrainingService ocrTrainingService; @Test @@ -217,7 +218,7 @@ class OcrControllerTest { @WithMockUser(authorities = "ADMIN") void getTrainingInfo_returns200_withInfo() throws Exception { OcrTrainingService.TrainingInfoResponse info = - new OcrTrainingService.TrainingInfoResponse(5, 20, 2, true, null, List.of()); + new OcrTrainingService.TrainingInfoResponse(5, 20, 2, 3, true, null, List.of()); when(ocrTrainingService.getTrainingInfo()).thenReturn(info); mockMvc.perform(get("/api/ocr/training-info")) diff --git a/backend/src/test/java/org/raddatz/familienarchiv/service/OcrTrainingServiceTest.java b/backend/src/test/java/org/raddatz/familienarchiv/service/OcrTrainingServiceTest.java index d4aea29c..5dcf1fcb 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/service/OcrTrainingServiceTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/service/OcrTrainingServiceTest.java @@ -25,6 +25,7 @@ class OcrTrainingServiceTest { OcrTrainingRunRepository runRepository; TrainingDataExportService exportService; + SegmentationTrainingExportService segExportService; OcrClient ocrClient; OcrHealthClient healthClient; TranscriptionBlockRepository blockRepository; @@ -34,14 +35,16 @@ class OcrTrainingServiceTest { void setUp() { runRepository = mock(OcrTrainingRunRepository.class); exportService = mock(TrainingDataExportService.class); + segExportService = mock(SegmentationTrainingExportService.class); ocrClient = mock(OcrClient.class); healthClient = mock(OcrHealthClient.class); blockRepository = mock(TranscriptionBlockRepository.class); - service = new OcrTrainingService(runRepository, exportService, ocrClient, healthClient, blockRepository); + service = new OcrTrainingService(runRepository, exportService, segExportService, ocrClient, healthClient, blockRepository); when(blockRepository.findAll()).thenReturn(List.of()); when(runRepository.findTop5ByOrderByCreatedAtDesc()).thenReturn(List.of()); + when(segExportService.querySegmentationBlocks()).thenReturn(List.of()); } // ─── Concurrent guard ───────────────────────────────────────────────────── diff --git a/frontend/src/lib/components/OcrTrainingCard.svelte b/frontend/src/lib/components/OcrTrainingCard.svelte index 040a1b14..64f96f2f 100644 --- a/frontend/src/lib/components/OcrTrainingCard.svelte +++ b/frontend/src/lib/components/OcrTrainingCard.svelte @@ -1,5 +1,6 @@
-

Kurrent-Erkennung trainieren

-

- Starte ein neues Training mit den bisher geprüften OCR-Blöcken, um die Erkennungsgenauigkeit für - Kurrentschrift zu verbessern. -

+

{m.training_ocr_heading()}

+

{m.training_ocr_description()}

- {available} geprüfte Blöcke bereit / - {trainingInfo?.availableDocuments ?? 0} Dokumente - (von {trainingInfo?.totalOcrBlocks ?? 0} OCR-Blöcken gesamt) + {m.training_ocr_blocks_ready({ blocks: available, docs: trainingInfo?.availableDocuments ?? 0 })} + {m.training_ocr_blocks_total({ total: trainingInfo?.totalOcrBlocks ?? 0 })}

{#if tooFewBlocks}

- Mindestens 5 geprüfte Blöcke erforderlich (aktuell: {available}). + {m.training_too_few_blocks({ available })}

{:else if serviceDown} -

OCR-Dienst ist nicht erreichbar.

+

{m.training_service_down()}

{/if} {#if successMessage}

{successMessage}

{/if} -

Verlauf

+

+ {m.training_history_heading()} +

diff --git a/frontend/src/lib/components/SegmentationTrainingCard.svelte b/frontend/src/lib/components/SegmentationTrainingCard.svelte new file mode 100644 index 00000000..b1553c94 --- /dev/null +++ b/frontend/src/lib/components/SegmentationTrainingCard.svelte @@ -0,0 +1,86 @@ + + +
+

{m.training_seg_heading()}

+

{m.training_seg_description()}

+ +

+ {m.training_seg_blocks_ready({ blocks: available })} +

+ + + + {#if tooFewBlocks} +

+ {m.training_seg_too_few_blocks({ available })} +

+ {:else if serviceDown} +

{m.training_service_down()}

+ {/if} + + {#if successMessage} +

{successMessage}

+ {/if} + +

+ {m.training_history_heading()} +

+ r.modelName === 'blla')} /> +
diff --git a/frontend/src/lib/components/TrainingHistory.svelte b/frontend/src/lib/components/TrainingHistory.svelte index 86f1361c..1260c15d 100644 --- a/frontend/src/lib/components/TrainingHistory.svelte +++ b/frontend/src/lib/components/TrainingHistory.svelte @@ -1,4 +1,6 @@