fix(ocr): resume polling on page reload + track single-doc job status
Some checks failed
CI / Unit & Component Tests (push) Failing after 1s
CI / Backend Unit Tests (push) Failing after 1s
CI / Unit & Component Tests (pull_request) Failing after 0s
CI / Backend Unit Tests (pull_request) Failing after 1s

Single-document OCR now creates an OcrJobDocument row so
GET /api/documents/{id}/ocr-status can find running jobs.
OcrAsyncRunner updates the job document status (RUNNING → DONE/FAILED).

Frontend checks OCR status when entering transcription mode —
if a job is running, resumes polling and shows the spinner.

Refs #226

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-12 23:16:59 +02:00
parent 2db1b73d5d
commit c1befd3fa3
4 changed files with 44 additions and 1 deletions

View File

@@ -39,16 +39,32 @@ public class OcrAsyncRunner {
job.setStatus(OcrJobStatus.RUNNING);
ocrJobRepository.save(job);
OcrJobDocument jobDoc = ocrJobDocumentRepository.findByJobIdAndDocumentId(jobId, documentId)
.orElse(null);
if (jobDoc != null) {
jobDoc.setStatus(OcrDocumentStatus.RUNNING);
ocrJobDocumentRepository.save(jobDoc);
}
Document doc = documentService.getDocumentById(documentId);
try {
processDocument(documentId, doc, userId);
job.setStatus(OcrJobStatus.DONE);
job.setProcessedDocuments(1);
if (jobDoc != null) {
jobDoc.setStatus(OcrDocumentStatus.DONE);
ocrJobDocumentRepository.save(jobDoc);
}
} catch (Exception e) {
log.error("OCR processing failed for document {}", documentId, e);
job.setStatus(OcrJobStatus.FAILED);
job.setErrorCount(1);
if (jobDoc != null) {
jobDoc.setStatus(OcrDocumentStatus.FAILED);
jobDoc.setErrorMessage(e.getMessage());
ocrJobDocumentRepository.save(jobDoc);
}
}
ocrJobRepository.save(job);

View File

@@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j;
import org.raddatz.familienarchiv.exception.DomainException;
import org.raddatz.familienarchiv.exception.ErrorCode;
import org.raddatz.familienarchiv.model.*;
import org.raddatz.familienarchiv.repository.OcrJobDocumentRepository;
import org.raddatz.familienarchiv.repository.OcrJobRepository;
import org.springframework.stereotype.Service;
@@ -18,6 +19,7 @@ public class OcrService {
private final OcrHealthClient ocrHealthClient;
private final DocumentService documentService;
private final OcrJobRepository ocrJobRepository;
private final OcrJobDocumentRepository ocrJobDocumentRepository;
private final OcrAsyncRunner ocrAsyncRunner;
public UUID startOcr(UUID documentId, ScriptType scriptTypeOverride, UUID userId) {
@@ -44,6 +46,13 @@ public class OcrService {
.build();
job = ocrJobRepository.save(job);
OcrJobDocument jobDoc = OcrJobDocument.builder()
.jobId(job.getId())
.documentId(documentId)
.status(OcrDocumentStatus.PENDING)
.build();
ocrJobDocumentRepository.save(jobDoc);
ocrAsyncRunner.runSingleDocument(job.getId(), documentId, userId);
return job.getId();
}

View File

@@ -8,6 +8,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import org.raddatz.familienarchiv.exception.DomainException;
import org.raddatz.familienarchiv.exception.ErrorCode;
import org.raddatz.familienarchiv.model.*;
import org.raddatz.familienarchiv.repository.OcrJobDocumentRepository;
import org.raddatz.familienarchiv.repository.OcrJobRepository;
import java.util.UUID;
@@ -24,6 +25,7 @@ class OcrServiceTest {
@Mock OcrHealthClient ocrHealthClient;
@Mock DocumentService documentService;
@Mock OcrJobRepository ocrJobRepository;
@Mock OcrJobDocumentRepository ocrJobDocumentRepository;
@Mock OcrAsyncRunner ocrAsyncRunner;
@InjectMocks OcrService ocrService;