V6.1 more fixes
Quality Gate / gate (push) Failing after 2m35s

This commit is contained in:
Jim Lancaster
2026-09-01 12:36:59 -05:00
parent 4c877dd6a2
commit 9b6bb9ae66
3 changed files with 50 additions and 2 deletions
+32
View File
@@ -10,6 +10,8 @@ from pathlib import Path
from uuid import UUID
from sqlalchemy import update
from sqlalchemy.exc import SQLAlchemyError
from sqlmodel import SQLModel
from sqlmodel import col
from sqlmodel import func
from sqlmodel import select
@@ -48,6 +50,10 @@ class MaintenanceError(AppError):
class MaintenanceService(ServiceBase):
"""Persist and execute background maintenance runs."""
def __init__(self, session_factory=None, settings=None):
super().__init__(session_factory=session_factory, settings=settings)
self._maintenance_table_ready = False
async def list_runs(
self,
*,
@@ -55,6 +61,7 @@ class MaintenanceService(ServiceBase):
session: AsyncSession | None = None,
) -> list[MaintenanceRun]:
async with self._session_scope(session) as _session:
await self._ensure_runs_table(session=_session)
query = (
select(MaintenanceRun)
.order_by(col(MaintenanceRun.created_at).desc(), col(MaintenanceRun.id).desc())
@@ -75,12 +82,14 @@ class MaintenanceService(ServiceBase):
triggered_by=triggered_by,
)
async with self._session_scope(session) as _session:
await self._ensure_runs_table(session=_session)
_session.add(run)
await self._finalize(session=_session, caller_session=session, refresh=(run,))
return run
async def claim_next_queued_run(self, *, session: AsyncSession | None = None) -> MaintenanceRun | None:
async with self._session_scope(session) as _session:
await self._ensure_runs_table(session=_session)
now = _utc_now_naive()
queued_run_id = (
select(col(MaintenanceRun.id))
@@ -110,6 +119,29 @@ class MaintenanceService(ServiceBase):
await self._finalize(session=_session, caller_session=session, refresh=(run,))
return run
async def _ensure_runs_table(self, *, session: AsyncSession) -> None:
if self._maintenance_table_ready:
return
table = SQLModel.metadata.tables.get("maintenance_run")
if table is None:
raise MaintenanceError(
"Maintenance storage is unavailable.",
category=ErrorCategory.INTERNAL_UNEXPECTED,
suggestion="Check database schema registration, then retry.",
detail="maintenance_run table metadata is not registered.",
)
try:
connection = await session.connection()
await connection.run_sync(lambda sync_connection: table.create(sync_connection, checkfirst=True))
except SQLAlchemyError as exc:
raise MaintenanceError(
"Maintenance storage is unavailable.",
category=ErrorCategory.INFRA_PERSISTENT,
suggestion="Check database availability and schema permissions, then retry.",
detail=f"Failed to ensure maintenance_run table: {type(exc).__name__}: {exc}",
) from exc
self._maintenance_table_ready = True
async def process_next_queued_run(self, *, session: AsyncSession | None = None) -> bool:
run = await self.claim_next_queued_run(session=session)
if run is None:
+2 -2
View File
@@ -651,7 +651,7 @@ def _render_document_source_navigation(*, document: Document, active_source: Sou
def _render_document_detail_revision_zone(*, source: Source | None, sources_service: SourceService) -> None:
with ui.column().classes("col-span-12 lg:col-span-4 gap-4"), archival_card(title="Editable Revision"):
with ui.column().classes("col-span-12 lg:col-span-6 gap-4"), archival_card(title="Editable Revision"):
if source is None:
render_empty_state("No source pages are linked yet.", italic=True)
return
@@ -748,7 +748,7 @@ def _detail_document_date(exact: date | None, approximate: str | None) -> str:
def _render_bento_relations_zone(document: Document) -> None:
with ui.column().classes("col-span-12 lg:col-span-4 gap-4"):
with ui.column().classes("col-span-12 lg:col-span-2 gap-4"):
_render_related_people_card(document)
_render_document_processing_card(document)
@@ -5,6 +5,7 @@ from __future__ import annotations
from pathlib import Path
import pytest
from sqlalchemy import text
from transcription.config import Settings
from transcription.db.models import MaintenanceJobType
@@ -57,3 +58,18 @@ async def test_process_next_queued_run_persists_terminal_result(
assert updated.summary == "Synthetic success"
assert updated.log_path is not None
assert (settings.log_dir / Path(updated.log_path)).is_file()
@pytest.mark.asyncio
async def test_list_runs_recreates_missing_table(default_session_factory, default_settings):
service = MaintenanceService(session_factory=default_session_factory, settings=default_settings)
async with default_session_factory() as session:
await session.exec(text("DROP TABLE maintenance_run"))
await session.commit()
runs = await service.list_runs(limit=10)
assert runs == []
created = await service.enqueue_run(job_type=MaintenanceJobType.BACKUP, triggered_by="test")
listed = await service.list_runs(limit=10)
assert any(run.id == created.id for run in listed)