File size: 3,398 Bytes
da6ee5e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
"""Export and dashboard API."""

from __future__ import annotations

from collections import Counter

from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import FileResponse
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.config import get_settings
from app.db import get_session
from app.models import Batch, Document
from app.schemas import DashboardStats, DocStatus, PublicSettings
from app.services.export import build_export

router = APIRouter(prefix="/api", tags=["export"])

_XLSX_MEDIA = (
    "application/vnd.openxmlformats-officedocument"
    ".spreadsheetml.sheet"
)


@router.get("/export")
async def export_documents(
    fmt: str = Query("xlsx", pattern="^(xlsx|excel|json|csv)$"),
    batch_id: str | None = None,
    session: AsyncSession = Depends(get_session),
) -> FileResponse:
    """Export documents as Excel, JSON, or CSV."""
    stmt = select(Document).where(
        Document.status.in_(
            [DocStatus.READY.value, DocStatus.VALIDATED.value]
        )
    )
    if batch_id:
        stmt = stmt.where(Document.batch_id == batch_id)
    result = await session.execute(stmt)
    docs = list(result.scalars().all())
    if not docs:
        raise HTTPException(404, "No documents to export")
    try:
        path = build_export(docs, fmt)
    except ValueError as exc:
        raise HTTPException(400, str(exc)) from exc

    media = {
        "json": "application/json",
        "csv": "text/csv",
        "xlsx": _XLSX_MEDIA,
        "excel": _XLSX_MEDIA,
    }
    return FileResponse(
        path,
        filename=path.name,
        media_type=media.get(fmt, "application/octet-stream"),
    )


@router.get("/dashboard", response_model=DashboardStats)
async def dashboard(
    session: AsyncSession = Depends(get_session),
) -> DashboardStats:
    """Aggregate processing stats."""
    docs = (
        await session.execute(select(Document))
    ).scalars().all()
    by_type = Counter(d.doc_type for d in docs)
    needs_review = 0
    conf_scores: list[float] = []
    duplicates = 0
    for doc in docs:
        if doc.is_duplicate:
            duplicates += 1
        if doc.avg_confidence is not None:
            conf_scores.append(doc.avg_confidence)
        conf = doc.confidence or {}
        if any(
            isinstance(v, dict) and v.get("needs_review")
            for v in conf.values()
        ):
            needs_review += 1

    batch_count = (
        await session.execute(select(func.count(Batch.id)))
    ).scalar() or 0

    avg = (
        sum(conf_scores) / len(conf_scores)
        if conf_scores
        else 0.0
    )
    return DashboardStats(
        total_documents=len(docs),
        by_type=dict(by_type),
        needs_review=needs_review,
        duplicates=duplicates,
        avg_confidence=round(avg, 3),
        batches=batch_count,
    )


@router.get("/settings/public", response_model=PublicSettings)
async def public_settings() -> PublicSettings:
    """Expose non-secret settings to the UI."""
    settings = get_settings()
    return PublicSettings(
        app_name=settings.app_name,
        confidence_threshold=settings.confidence_threshold,
        output_language=settings.output_language,
        openai=settings.has_openai,
        text_model=settings.openai_model,
        vision_model=settings.openai_vision_model,
    )