From de119c2106fd8d78c10c2206f313a2c1608e8582 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Sun, 17 May 2026 01:52:28 +0300 Subject: [PATCH] Link HTML5 review findings to source --- services/api-server/src/api_server/html5.py | 18 ++++++++++++++++-- services/api-server/src/api_server/main.py | 19 +++++++++++++++++++ services/api-server/tests/test_api.py | 11 +++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/services/api-server/src/api_server/html5.py b/services/api-server/src/api_server/html5.py index 4e57832..0aab37a 100644 --- a/services/api-server/src/api_server/html5.py +++ b/services/api-server/src/api_server/html5.py @@ -433,7 +433,7 @@ def render_html5_review( if not findings: body = '

Findings не найдены

' else: - body = "".join(_review_item(finding) for finding in findings[:12]) + body = "".join(_review_item(project_id, finding) for finding in findings[:12]) return f"""
str: """ -def _review_item(finding: dict) -> str: +def _review_item(project_id: str, finding: dict) -> str: title = str(finding.get("title") or finding.get("code") or "Finding") severity = str(finding.get("severity") or finding.get("level") or "INFO") message = str(finding.get("message") or finding.get("description") or "") source_path = str(finding.get("source_path") or finding.get("path") or "") line = finding.get("line_start") or finding.get("line") location = f"{source_path}:{line}" if source_path and line else source_path + source_link = ( + f""" + Source + """ + if source_path + else "" + ) return f"""
{escape(title)} {escape(severity)} {escape(message or location or "no details")} + {source_link}
""" diff --git a/services/api-server/src/api_server/main.py b/services/api-server/src/api_server/main.py index 2cf72f8..e98e2c3 100644 --- a/services/api-server/src/api_server/main.py +++ b/services/api-server/src/api_server/main.py @@ -1713,6 +1713,25 @@ async def html5_project_symbol_detail(project_id: str, lineage_id: str) -> Respo ) +@app.get("/html5/projects/{project_id}/source/by-path") +async def html5_project_source_by_path(project_id: str, path: str) -> Response: + snapshot = _project_snapshot_or_404(project_id) + 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 Response( + render_html5_source(node), + media_type="text/html; charset=utf-8", + ) + + @app.get("/html5/projects/{project_id}/source/{lineage_id}") async def html5_project_source(project_id: str, lineage_id: str) -> Response: snapshot = _project_snapshot_or_404(project_id) diff --git a/services/api-server/tests/test_api.py b/services/api-server/tests/test_api.py index 3d34870..a66b33c 100644 --- a/services/api-server/tests/test_api.py +++ b/services/api-server/tests/test_api.py @@ -171,6 +171,15 @@ def test_html5_server_rendered_project_editor(tmp_path: Path): assert "Проверить" in source.text assert "