V1 mostly complete except for some testing. Linting in the last step changed nearly every file which is why this commit is so larger.

This commit is contained in:
Jim Lancaster
2026-07-29 17:27:21 -05:00
parent bc21a97019
commit a3b3bab571
37 changed files with 1383 additions and 410 deletions
+1 -2
View File
@@ -2,12 +2,12 @@
from __future__ import annotations
import logging
from contextlib import AsyncExitStack
from contextlib import asynccontextmanager
from datetime import UTC
from datetime import datetime
from datetime import timedelta
import logging
from fastapi import FastAPI
from fastapi import status
@@ -26,7 +26,6 @@ from .services.jobs import JobService
from .ui import register_pages
from .worker import worker_consumer_lifespan
logger = logging.getLogger(__name__)
+10 -2
View File
@@ -67,5 +67,13 @@ def _ensure_sqlite_compat_columns(connection: Connection) -> None:
has_unique_source = True
break
if not has_unique_source:
connection.execute(text("CREATE UNIQUE INDEX IF NOT EXISTS ux_revision_source_id ON revision(source_id)"))
logger.warning("Applied SQLite compatibility schema patch table=revision unique_index=ux_revision_source_id")
connection.execute(
text(
"CREATE UNIQUE INDEX IF NOT EXISTS "
"ux_revision_source_id ON revision(source_id)"
)
)
logger.warning(
"Applied SQLite compatibility schema patch "
"table=revision unique_index=ux_revision_source_id"
)
+1 -1
View File
@@ -85,7 +85,7 @@ async def process_queued_job(
source.id,
result.provider,
)
except TimeoutError as exc:
except TimeoutError:
error = AppError(
f"Provider call timed out after {runtime_settings.worker_provider_timeout_seconds:.1f}s",
category=ErrorCategory.EXTERNAL_PROVIDER,
+43 -5
View File
@@ -23,7 +23,7 @@ from ..components.transcript import render_original_transcription_card
from ..components.transcript import render_revision_row
def register_page() -> None:
def register_page() -> None: # noqa: PLR0915
"""Register jobs list and detail routes."""
@ui.page("/jobs")
@@ -51,7 +51,7 @@ def register_page() -> None:
await render_table()
@ui.page("/jobs/{job_id}")
async def job_detail_page(job_id: str, request: Request) -> None:
async def job_detail_page(job_id: str, request: Request) -> None: # noqa: PLR0915
session_factory = resolve_session_factory(request.app.state)
jobs_service = JobService(session_factory=session_factory)
transcription_service = TranscriptionService(session_factory=session_factory)
@@ -105,14 +105,52 @@ def register_page() -> None:
async def render_revision_panel() -> None:
refreshed_job = await jobs_service.read_job(job_id=parsed_job_id)
refreshed_source = _resolve_primary_source(refreshed_job)
if refreshed_source is None or refreshed_source.revision is None:
if refreshed_source is None:
ui.label("No source is available for revision editing.").classes("text-body2 text-grey-3")
return
current_revision = refreshed_source.revision
default_revision_text = (
current_revision.text if current_revision is not None else (refreshed_job.text or "")
)
ui.label("Revision Editor").classes("text-subtitle1 text-weight-medium")
editor = ui.textarea(label="Revision text", value=default_revision_text).props("autogrow outlined")
editor.classes("w-full")
async def save_revision() -> None:
candidate = (editor.value or "").strip()
if not candidate:
ui.notify("Revision text is required.", type="warning")
return
try:
await transcription_service.upsert_revision_for_source(
source_id=refreshed_source.id,
text=candidate,
)
except Exception as exc: # noqa: BLE001
show_error(exc, title="Save failed", operation="jobs.save_revision")
return
ui.notify("Revision saved", type="positive")
await render_revision_panel.refresh()
with ui.row().classes("w-full justify-end"):
ui.button(
"Create revision" if current_revision is None else "Update revision",
on_click=save_revision,
icon="save",
).props('unelevated color="primary"')
if current_revision is None:
ui.label("No revision exists for this source.").classes("text-body2 text-grey-3")
return
render_revision_row(
revision=refreshed_source.revision,
revision=current_revision,
initially_expanded=True,
on_delete=lambda _revision, rid=refreshed_source.revision.id: delete_revision_by_id(rid),
on_delete=lambda _revision, rid=current_revision.id: delete_revision_by_id(rid),
)
await render_revision_panel()