From 6e89cdcd84f60ce57605e4d1c2b9ef40ec11ad30 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Sun, 17 May 2026 00:50:56 +0300 Subject: [PATCH] Add HTML5 focused object report --- services/api-server/src/api_server/html5.py | 46 +++++++++++++++++++++ services/api-server/src/api_server/main.py | 12 +++++- services/api-server/tests/test_api.py | 3 ++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/services/api-server/src/api_server/html5.py b/services/api-server/src/api_server/html5.py index 22ce8f4..4decd8b 100644 --- a/services/api-server/src/api_server/html5.py +++ b/services/api-server/src/api_server/html5.py @@ -339,6 +339,52 @@ def render_html5_project_report(project_id: str, report: dict | None) -> str: """ +def render_html5_object_report( + project_id: str, + impact: object, + *, + access: object | None = None, + privacy: object | None = None, + runtime: Iterable[object] | None = None, + integrations: Iterable[object] | None = None, + oob: bool = False, +) -> str: + obj = getattr(impact, "object", None) + name = getattr(obj, "qualified_name", None) or getattr(obj, "name", None) or getattr(impact, "object_name", "object") + grants = getattr(access, "grants", []) if access is not None else [] + markers = getattr(privacy, "markers", []) if privacy is not None else [] + runtime_items = list(runtime or []) + integration_items = list(integrations or []) + metrics = [ + ("Routines", len(getattr(impact, "routines", []) or [])), + ("Commands", len(getattr(impact, "commands", []) or [])), + ("Reads", len(getattr(impact, "query_tables", []) or [])), + ("Writes", len(getattr(impact, "writes", []) or [])), + ("Roles", len(grants) or len(getattr(impact, "roles", []) or [])), + ("Runtime", len(runtime_items)), + ("Privacy", len(markers)), + ("Integrations", len(integration_items)), + ] + oob_attr = ' hx-swap-oob="outerHTML"' if oob else "" + return f""" +
+
Отчет объекта
+
+ {escape(str(name))} + server focused summary +
+
{''.join(_metric(label, value) for label, value in metrics)}
+
+ """ + + def render_html5_review(project_id: str, findings: list[dict] | None) -> str: if findings is None: return f""" diff --git a/services/api-server/src/api_server/main.py b/services/api-server/src/api_server/main.py index 3183db3..d5a8d0c 100644 --- a/services/api-server/src/api_server/main.py +++ b/services/api-server/src/api_server/main.py @@ -48,6 +48,7 @@ from api_server.html5 import ( render_html5_metadata_apply_result, render_html5_metadata_preview_result, render_html5_object_context, + render_html5_object_report, render_html5_project_setup, render_html5_project_rows, render_html5_project_report, @@ -1784,8 +1785,17 @@ async def html5_project_object_context(project_id: str, object_name: str) -> Res flowchart_context = render_html5_flowchart(project_id, flowchart, focus=object_name, oob=True) source_context = render_html5_source(source_node, oob=True) if source_node is not None else "" symbol_context = render_html5_symbol_detail(project_id, symbol_references, oob=True) + report_context = render_html5_object_report( + project_id, + impact, + access=access, + privacy=privacy, + runtime=runtime, + integrations=integrations, + oob=True, + ) return Response( - object_context + flowchart_context + source_context + symbol_context, + object_context + flowchart_context + source_context + symbol_context + report_context, media_type="text/html; charset=utf-8", ) diff --git a/services/api-server/tests/test_api.py b/services/api-server/tests/test_api.py index fda668d..b89c7be 100644 --- a/services/api-server/tests/test_api.py +++ b/services/api-server/tests/test_api.py @@ -334,6 +334,9 @@ def test_html5_object_context_fragment(tmp_path: Path): assert "data-html5-symbol-detail" in context.text assert "Символ · DOCUMENT" in context.text assert "HAS_ATTRIBUTE" in context.text + assert "data-html5-project-report" in context.text + assert "Отчет объекта" in context.text + assert "server focused summary" in context.text assert "1 signals" in context.text assert "1 errors" in context.text assert "125.0 ms" in context.text