from __future__ import annotations import asyncio from collections.abc import AsyncIterator, Callable, Iterable from typing import Any from fastapi import HTTPException from api_server.html5_authoring import render_html5_authoring_changes from api_server.html5_editor import ( render_html5_source, render_html5_status, render_html5_symbol_detail, render_html5_symbols, ) from api_server.html5_forms import form_value from api_server.html5_inspector import render_html5_flowchart, render_html5_project_report, render_html5_review from api_server.html5_projects import render_html5_index, render_html5_project_rows from api_server.html5_sse import html5_sse_comment, html5_sse_if_changed from sir import SirSnapshot def html5_index_page(projects: Iterable[object]) -> str: return render_html5_index(projects) async def html5_create_project_rows( *, form: dict[str, list[str]], create_project: Callable[[object], Any], create_request: Callable[..., object], project_summaries: Callable[[], Iterable[object]], ) -> str: project_id = form_value(form, "project_id") if not project_id: raise HTTPException(status_code=400, detail="project_id is required.") await create_project(create_request(project_id=project_id, name=form_value(form, "name"))) return render_html5_project_rows(project_summaries()) async def html5_delete_project_rows( *, project_id: str, form: dict[str, list[str]], delete_project: Callable[[str, object], Any], delete_request: Callable[..., object], project_summaries: Callable[[], Iterable[object]], ) -> str: await delete_project(project_id, delete_request(confirmation=form_value(form, "confirmation") or "")) return render_html5_project_rows(project_summaries()) async def html5_project_event_stream( *, project_id: str, project_snapshot: Callable[[str], SirSnapshot], project_report: Callable[[str], Any], review: Callable[[str], Any], flowchart: Callable[..., Any], authoring_changes: Callable[[str], Iterable[object]], once: bool = False, ) -> AsyncIterator[str]: last_fragments: dict[str, str] = {} while True: yield html5_sse_comment(f"project {project_id} heartbeat") try: snapshot = project_snapshot(project_id) status = render_html5_status(project_id, snapshot) report = await project_report(project_id) findings = await review(project_id) graph = await flowchart(project_id, focus=None, depth=1, limit=80) except HTTPException as error: status = f'project: {project_id}error: {error.detail}' report = None findings = None graph = None for event_text in html5_sse_if_changed(last_fragments, "status", status): yield event_text for event_text in html5_sse_if_changed( last_fragments, "authoring-changes", render_html5_authoring_changes(project_id, authoring_changes(project_id)), ): yield event_text if report is not None: for event_text in html5_sse_if_changed(last_fragments, "project-report", render_html5_project_report(project_id, report)): yield event_text if findings is not None: for event_text in html5_sse_if_changed(last_fragments, "project-review", render_html5_review(project_id, findings)): yield event_text if graph is not None: for event_text in html5_sse_if_changed(last_fragments, "project-flowchart", render_html5_flowchart(project_id, graph)): yield event_text if once: break await asyncio.sleep(5) def html5_project_symbols(*, snapshot: SirSnapshot, q: str, project_id: str) -> str: return render_html5_symbols(snapshot, q, project_id) def html5_project_symbol_detail(*, project_id: str, references: object) -> str: return render_html5_symbol_detail(project_id, references) def html5_project_source_by_path(*, snapshot: SirSnapshot, path: str) -> str: node = next( ( item for item in snapshot.nodes if item.source_ref is not None and item.source_ref.source_path == path ), None, ) if node is None: raise HTTPException(status_code=404, detail=f"Source not found: {path}") return render_html5_source(node) def html5_project_source_by_lineage(*, node: object | None, lineage_id: str) -> str: if node is None: raise HTTPException(status_code=404, detail=f"Lineage not found: {lineage_id}") return render_html5_source(node) def html5_project_report_fragment(*, project_id: str, report: object) -> str: return render_html5_project_report(project_id, report) def html5_project_review_fragment(*, project_id: str, findings: object) -> str: return render_html5_review(project_id, findings) def html5_project_flowchart_fragment(*, project_id: str, flowchart: object, focus: str | None, depth: int) -> str: return render_html5_flowchart(project_id, flowchart, focus=focus, depth=depth)