P3+P4 app: persist audit gate (extraction_meta) + digital-text endpoint
_run_service_extract_merge now records extraction_meta {engine, slug, audit
(cover-total reconciliation), counts} on the template (migration 76) — a durable
trust signal the setup UI shows. New GET /templates/{id}/digital-text proxies the
service's /api/replica for the paper (resolved via extraction_meta.slug), returning
the digital-replica markdown. exam_extract.get_replica client added.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
@@ -793,9 +793,17 @@ def _run_service_extract_merge(ctx: ExamContext, template_id: str, pdf_bytes: by
|
||||
contract = exam_extract.extract_suggestions(slug, pdf_bytes)
|
||||
rows = _map_service_contract_to_rows(template_id, contract, pdf_bytes)
|
||||
_refresh_ai_rows(ctx, template_id, rows)
|
||||
n_pages = (contract.get("meta") or {}).get("n_pages") or (contract.get("meta") or {}).get("pages")
|
||||
meta = contract.get("meta") or {}
|
||||
# P3: record provenance + the audit cover-reconciliation gate so the setup UI can flag under-reads,
|
||||
# and store the slug so the digital-text view can resolve this paper's replica.
|
||||
updates: Dict[str, Any] = {"extraction_meta": {
|
||||
"engine": "extract-service", "slug": slug, "audit": meta.get("audit") or {},
|
||||
"counts": {k: len(v) for k, v in rows.items()},
|
||||
}}
|
||||
n_pages = meta.get("n_pages") or meta.get("pages")
|
||||
if n_pages:
|
||||
ctx.supabase.table("exam_templates").update({"page_count": n_pages}).eq("id", template_id).execute()
|
||||
updates["page_count"] = n_pages
|
||||
ctx.supabase.table("exam_templates").update(updates).eq("id", template_id).execute()
|
||||
return rows
|
||||
|
||||
|
||||
@@ -1034,6 +1042,24 @@ async def auto_map_status(
|
||||
return body
|
||||
|
||||
|
||||
@router.get("/templates/{template_id}/digital-text")
|
||||
async def template_digital_text(
|
||||
template_id: str,
|
||||
ctx: ExamContext = Depends(get_exam_context),
|
||||
) -> Dict[str, Any]:
|
||||
"""P4: the digital-replica markdown for this template's paper (stem text, parts, marks, answer-space
|
||||
placeholders, spec refs). Resolves the paper via the slug the extraction used."""
|
||||
template = _fetch_template_or_404(ctx, template_id)
|
||||
_require_source_visibility_or_404(ctx, template)
|
||||
if not exam_extract.is_enabled():
|
||||
raise HTTPException(status_code=503, detail="Extraction service not configured")
|
||||
slug = (template.get("extraction_meta") or {}).get("slug") or _extract_slug(ctx, template)
|
||||
try:
|
||||
return exam_extract.get_replica(slug)
|
||||
except exam_extract.ExtractError as exc:
|
||||
raise HTTPException(status_code=404, detail=f"No digital text yet — run auto-map first ({exc})")
|
||||
|
||||
|
||||
@router.put("/templates/{template_id}")
|
||||
async def replace_template(
|
||||
template_id: str,
|
||||
|
||||
Reference in New Issue
Block a user