File size: 1,981 Bytes
c893230
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Live generation progress for ``GET /reports/{id}/status`` polling."""

from __future__ import annotations

from datetime import UTC, datetime

from sqlalchemy import case, func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.db.models import Report, ReportSection, ReportStatus


async def count_report_section_progress(
    db: AsyncSession,
    report_id: str,
) -> tuple[int, int]:
    """Return ``(sections_saved, sections_with_text)`` for a report."""
    row = await db.execute(
        select(
            func.count(ReportSection.id),
            func.coalesce(
                func.sum(
                    case(
                        (func.length(func.trim(ReportSection.text)) > 0, 1),
                        else_=0,
                    )
                ),
                0,
            ),
        ).where(ReportSection.report_id == report_id)
    )
    saved, with_text = row.one()
    return int(saved or 0), int(with_text or 0)


def generation_elapsed_seconds(report: Report) -> float | None:
    """Seconds since ``generation_started_at`` while the job is active."""
    if report.status != ReportStatus.generating or report.generation_started_at is None:
        return None
    started = report.generation_started_at
    if started.tzinfo is None:
        started = started.replace(tzinfo=UTC)
    return max(0.0, (datetime.now(UTC) - started).total_seconds())


async def build_status_progress_fields(
    db: AsyncSession,
    report: Report,
) -> dict[str, int | float | None]:
    """Fields merged into :class:`~app.models.schemas.ReportStatusResponse`."""
    saved, with_text = await count_report_section_progress(db, report.id)
    elapsed = generation_elapsed_seconds(report)
    return {
        "sections_saved": saved,
        "sections_with_text": with_text,
        "generation_section_total": report.generation_section_total,
        "generation_elapsed_seconds": round(elapsed, 1) if elapsed is not None else None,
    }