Download app/api/documents.py from nvikou/Sheetly: direct link, hf CLI and curl.
- Browser
- Download file 4.12 kB
-
https://huggingface.co/spaces/nvikou/Sheetly/resolve/main/app/api/documents.py
- Command line
-
hf download hf://spaces/nvikou/Sheetly/app/api/documents.py
-
curl -L -o documents.py https://huggingface.co/spaces/nvikou/Sheetly/resolve/main/app/api/documents.py
4.12 kB
| """Document CRUD and validation API.""" | |
| from __future__ import annotations | |
| from fastapi import APIRouter, Depends, HTTPException, Query | |
| from fastapi.responses import FileResponse | |
| from sqlalchemy import select | |
| from sqlalchemy.ext.asyncio import AsyncSession | |
| from app.api.serializers import document_out | |
| from app.db import get_session | |
| from app.models import Document | |
| from app.schemas import DocStatus, DocumentOut, DocumentUpdate | |
| from app.services.cleanup import cleanup_orphan_documents | |
| from app.services.paths import resolve_stored_path | |
| from app.services.validation import ( | |
| apply_duplicate_flags, | |
| apply_post_extract_checks, | |
| average_from_detail, | |
| mark_fields_confirmed, | |
| ) | |
| router = APIRouter(prefix="/api/documents", tags=["documents"]) | |
| async def list_documents( | |
| status: str | None = None, | |
| doc_type: str | None = None, | |
| limit: int = Query(100, ge=1, le=500), | |
| session: AsyncSession = Depends(get_session), | |
| ) -> list[DocumentOut]: | |
| """List processed documents (files that still exist).""" | |
| stmt = select(Document).order_by(Document.created_at.desc()) | |
| if status: | |
| stmt = stmt.where(Document.status == status) | |
| if doc_type: | |
| stmt = stmt.where(Document.doc_type == doc_type) | |
| stmt = stmt.limit(limit * 5) | |
| result = await session.execute(stmt) | |
| docs = [] | |
| for d in result.scalars().all(): | |
| if len(docs) >= limit: | |
| break | |
| try: | |
| if resolve_stored_path(d.file_path).is_file(): | |
| docs.append(document_out(d)) | |
| except FileNotFoundError: | |
| continue | |
| return docs | |
| async def cleanup_documents( | |
| session: AsyncSession = Depends(get_session), | |
| ) -> dict[str, int]: | |
| """Remove orphan DB rows whose files were deleted.""" | |
| return await cleanup_orphan_documents(session) | |
| async def get_document( | |
| doc_id: str, | |
| session: AsyncSession = Depends(get_session), | |
| ) -> DocumentOut: | |
| """Fetch one document.""" | |
| doc = await session.get(Document, doc_id) | |
| if not doc: | |
| raise HTTPException(404, "Document not found") | |
| return document_out(doc) | |
| async def get_document_file( | |
| doc_id: str, | |
| session: AsyncSession = Depends(get_session), | |
| ) -> FileResponse: | |
| """Serve original uploaded file for validation UI.""" | |
| doc = await session.get(Document, doc_id) | |
| if not doc: | |
| raise HTTPException(404, "Document not found") | |
| try: | |
| path = resolve_stored_path(doc.file_path) | |
| except FileNotFoundError as exc: | |
| raise HTTPException( | |
| 404, | |
| "Original file is no longer available", | |
| ) from exc | |
| if not path.is_file(): | |
| raise HTTPException( | |
| 404, | |
| "Original file is no longer available", | |
| ) | |
| return FileResponse( | |
| path, | |
| filename=doc.filename, | |
| media_type=doc.mime_type or "application/octet-stream", | |
| content_disposition_type="inline", | |
| ) | |
| async def update_document( | |
| doc_id: str, | |
| body: DocumentUpdate, | |
| session: AsyncSession = Depends(get_session), | |
| ) -> DocumentOut: | |
| """Apply manual corrections and re-run checks.""" | |
| doc = await session.get(Document, doc_id) | |
| if not doc: | |
| raise HTTPException(404, "Document not found") | |
| if body.doc_type: | |
| doc.doc_type = body.doc_type | |
| doc.extracted = body.extracted | |
| issues, is_dup, dup_of = await apply_post_extract_checks( | |
| session, | |
| document_id=doc.id, | |
| doc_type=doc.doc_type, | |
| data=body.extracted, | |
| ) | |
| apply_duplicate_flags( | |
| doc, | |
| is_duplicate=is_dup, | |
| duplicate_of=dup_of, | |
| ) | |
| conf = mark_fields_confirmed(body.extracted, doc.confidence) | |
| doc.confidence = conf | |
| doc.avg_confidence = average_from_detail(conf) or 1.0 | |
| doc.issues = issues | |
| doc.status = DocStatus.VALIDATED.value | |
| await session.commit() | |
| await session.refresh(doc) | |
| return document_out(doc) | |