Add HTML5 flowchart fragment
CI / python (push) Has been cancelled
CI / rust (push) Has been cancelled

This commit is contained in:
2026-05-17 00:36:43 +03:00
parent 7ee6deb088
commit 7583851dc4
3 changed files with 105 additions and 0 deletions
@@ -231,6 +231,7 @@ def render_html5_editor(
{render_html5_symbols(snapshot, q, project_id)}
</div>
{render_html5_symbol_detail(project_id, None)}
{render_html5_flowchart(project_id, None)}
{render_html5_project_report(project_id, None)}
{render_html5_review(project_id, None)}
{render_html5_authoring_preview(project_id, None)}
@@ -246,6 +247,48 @@ def render_html5_editor(
return _page(f"SFERA HTML5 - {project_id}", content)
def render_html5_flowchart(project_id: str, flowchart: object | None) -> str:
if flowchart is None:
return f"""
<div
class="flowchart-panel"
data-html5-flowchart
hx-get="/html5/projects/{quote(project_id)}/flowchart"
hx-trigger="load"
hx-swap="outerHTML"
>
<div class="panel-title">Карта связей</div>
<p class="muted padded">Сервер собирает граф проекта.</p>
</div>
"""
nodes = getattr(flowchart, "nodes", []) or []
edges = getattr(flowchart, "edges", []) or []
mode = str(getattr(flowchart, "mode", "overview"))
body = "".join(_flowchart_edge_item(item, nodes) for item in edges[:10])
if not body:
body = "".join(_flowchart_node_item(item) for item in nodes[:10])
if not body:
body = '<p class="muted padded">Связи проекта не найдены</p>'
return f"""
<div
class="flowchart-panel"
data-html5-flowchart
hx-get="/html5/projects/{quote(project_id)}/flowchart"
hx-trigger="every 30s"
hx-swap="outerHTML"
>
<div class="panel-title">Карта связей · {escape(mode)}</div>
<dl class="report-grid">
{_metric("Nodes", len(nodes))}
{_metric("Edges", len(edges))}
{_metric("Total nodes", getattr(flowchart, "total_nodes", 0))}
{_metric("Total edges", getattr(flowchart, "total_edges", 0))}
</dl>
<div class="compact-list">{body}</div>
</div>
"""
def render_html5_project_report(project_id: str, report: dict | None) -> str:
if report is None:
return f"""
@@ -1527,6 +1570,19 @@ def _flowchart_edge_item(edge: object, nodes: Iterable[object]) -> str:
"""
def _flowchart_node_item(node: object) -> str:
name = str(getattr(node, "qualified_name", "") or getattr(node, "label", "") or "node")
kind = str(getattr(node, "kind", "") or "NODE")
level = getattr(node, "level", 0)
count = getattr(node, "count", 1)
return f"""
<article class="object-context-item" data-html5-flowchart-node="{escape(kind)}">
<strong>{escape(name)}</strong>
<small>{escape(kind)} · level {escape(str(level))} · count {escape(str(count))}</small>
</article>
"""
def _authoring_diff_item(line: object) -> str:
kind = str(getattr(line, "kind", ""))
text = str(getattr(line, "text", ""))
@@ -43,6 +43,7 @@ from api_server.html5 import (
render_html5_authoring_preview_result,
render_html5_authoring_rollback_result,
render_html5_editor,
render_html5_flowchart,
render_html5_index,
render_html5_metadata_apply_result,
render_html5_metadata_preview_result,
@@ -1741,6 +1742,20 @@ async def html5_project_review(project_id: str) -> Response:
)
@app.get("/html5/projects/{project_id}/flowchart")
async def html5_project_flowchart(
project_id: str,
focus: str | None = None,
depth: int = 1,
limit: int = 80,
) -> Response:
flowchart = await project_flowchart(project_id, focus=focus, depth=depth, limit=limit)
return Response(
render_html5_flowchart(project_id, flowchart),
media_type="text/html; charset=utf-8",
)
@app.get("/html5/projects/{project_id}/objects/context/{object_name}")
async def html5_project_object_context(project_id: str, object_name: str) -> Response:
schema = await get_object_schema(project_id, object_name)
+34
View File
@@ -108,6 +108,8 @@ def test_html5_server_rendered_project_editor(tmp_path: Path):
assert "data-html5-editor" in editor.text
assert "data-html5-symbol-results" in editor.text
assert "data-html5-symbol-detail" in editor.text
assert "data-html5-flowchart" in editor.text
assert f'hx-get="/html5/projects/{project_id}/flowchart"' in editor.text
assert "data-html5-project-report" in editor.text
assert f'hx-get="/html5/projects/{project_id}/report"' in editor.text
assert "data-html5-review" in editor.text
@@ -335,6 +337,38 @@ def test_html5_object_context_fragment(tmp_path: Path):
assert "<html" not in context.text
def test_html5_flowchart_fragment(tmp_path: Path):
client = TestClient(app)
project_id = f"html5-flowchart-{uuid4()}"
(tmp_path / "module.bsl").write_text(
"""
Процедура ПровестиЗаказ()
ПроверитьОстатки();
Движения.ОстаткиТоваров.Записать();
КонецПроцедуры
Процедура ПроверитьОстатки()
КонецПроцедуры
""",
encoding="utf-8",
)
indexed = client.post("/projects/index", json={"path": str(tmp_path), "project_id": project_id})
assert indexed.status_code == 200
flowchart = client.get(
f"/html5/projects/{project_id}/flowchart",
params={"focus": "ПровестиЗаказ"},
)
assert flowchart.status_code == 200
assert "text/html" in flowchart.headers["content-type"]
assert "data-html5-flowchart" in flowchart.text
assert "Карта связей" in flowchart.text
assert "Nodes" in flowchart.text
assert "Edges" in flowchart.text
assert "ПровестиЗаказ" in flowchart.text or "ПроверитьОстатки" in flowchart.text
assert "<html" not in flowchart.text
def test_html5_project_setup_renders_server_fragments():
client = TestClient(app)
project_id = f"html5-setup-{uuid4()}"