Update job detail page to track revisions and display document image next to transcription text.

This commit is contained in:
Jim Lancaster
2026-06-28 18:02:19 -05:00
parent e2e421835f
commit 761765636a
19 changed files with 744 additions and 111 deletions
+50 -19
View File
@@ -6,9 +6,9 @@ import pytest
from sqlmodel import select
from transcription.config import Settings
from transcription.models import Job, JobStatus, Transcript
from transcription.models import Job, JobStatus, Transcript, TranscriptRevision
from transcription.providers.base import TranscriptionResult
from transcription.services.upload import create_upload_job
from transcription.services.store import create_upload_job
from transcription.worker import process_next_queued_job
@@ -16,24 +16,37 @@ from transcription.worker import process_next_queued_job
class TestPipelineSuccessFlow:
"""Verify end-to-end success lifecycle behavior."""
def test_upload_then_worker_persists_transcribed_terminal_state(self, session, tmp_path: Path, monkeypatch):
@pytest.mark.asyncio
async def test_upload_then_worker_persists_transcribed_terminal_state(self, async_session, tmp_path: Path, monkeypatch):
"""Upload followed by worker processing persists transcript and transcribed status."""
settings = Settings(openrouter_api_key="test-key", upload_dir=tmp_path)
upload_result = create_upload_job(
upload_result = await create_upload_job(
filename="pipeline.jpg",
file_bytes=b"pipeline-bytes",
session=session,
session=async_session,
settings=settings,
)
def _fake_transcribe(_path: str) -> TranscriptionResult:
return TranscriptionResult(text="Pipeline transcript", provider="openrouter", model="test-model")
async def _fake_transcribe(_path: str) -> TranscriptionResult:
return TranscriptionResult(
text="Pipeline transcript",
provider="openrouter",
prompt_name="transcribe_document.md",
model="test-model",
)
monkeypatch.setattr("transcription.worker.transcribe_document_image", _fake_transcribe)
monkeypatch.setattr("transcription.services.workflows.transcribe_document_image", _fake_transcribe)
processed = process_next_queued_job(session=session)
job = session.get(Job, upload_result.job_id)
transcript = session.exec(select(Transcript).where(Transcript.job_id == upload_result.job_id)).first()
processed = await process_next_queued_job(session=async_session)
job = await async_session.get(Job, upload_result.job_id)
transcript = (await async_session.exec(select(Transcript).where(Transcript.job_id == upload_result.job_id))).first()
revisions = (
await async_session.exec(
select(TranscriptRevision)
.where(TranscriptRevision.job_id == upload_result.job_id)
.order_by(TranscriptRevision.version_number)
)
).all()
assert processed is True
assert job is not None
@@ -41,30 +54,43 @@ class TestPipelineSuccessFlow:
assert transcript is not None
assert transcript.text == "Pipeline transcript"
assert transcript.error_detail is None
assert transcript.model == "test-model"
assert len(revisions) == 1
assert revisions[0].version_number == 1
assert revisions[0].source == "ai"
assert revisions[0].text == "Pipeline transcript"
@pytest.mark.integration
class TestPipelineFailureFlow:
"""Verify end-to-end failure lifecycle behavior."""
def test_upload_then_worker_persists_failed_terminal_state(self, session, tmp_path: Path, monkeypatch):
@pytest.mark.asyncio
async def test_upload_then_worker_persists_failed_terminal_state(self, async_session, tmp_path: Path, monkeypatch):
"""Upload followed by worker processing persists error detail and failed status."""
settings = Settings(openrouter_api_key="test-key", upload_dir=tmp_path)
upload_result = create_upload_job(
upload_result = await create_upload_job(
filename="pipeline.jpg",
file_bytes=b"pipeline-bytes",
session=session,
session=async_session,
settings=settings,
)
def _fake_transcribe(_path: str) -> TranscriptionResult:
async def _fake_transcribe(_path: str) -> TranscriptionResult:
raise RuntimeError("pipeline provider failure")
monkeypatch.setattr("transcription.worker.transcribe_document_image", _fake_transcribe)
monkeypatch.setattr("transcription.services.workflows.transcribe_document_image", _fake_transcribe)
processed = process_next_queued_job(session=session)
job = session.get(Job, upload_result.job_id)
transcript = session.exec(select(Transcript).where(Transcript.job_id == upload_result.job_id)).first()
processed = await process_next_queued_job(session=async_session)
job = await async_session.get(Job, upload_result.job_id)
transcript = (await async_session.exec(select(Transcript).where(Transcript.job_id == upload_result.job_id))).first()
revisions = (
await async_session.exec(
select(TranscriptRevision)
.where(TranscriptRevision.job_id == upload_result.job_id)
.order_by(TranscriptRevision.version_number)
)
).all()
assert processed is True
assert job is not None
@@ -74,3 +100,8 @@ class TestPipelineFailureFlow:
assert "pipeline provider failure" in transcript.error_detail
assert "[internal_unexpected_error]" in transcript.error_detail
assert "error_id=" in transcript.error_detail
assert len(revisions) == 1
assert revisions[0].version_number == 1
assert revisions[0].source == "ai"
assert revisions[0].text is None
assert "pipeline provider failure" in (revisions[0].error_detail or "")