Add HTML5 object context modes
This commit is contained in:
@@ -437,6 +437,7 @@ def render_html5_object_context(
|
|||||||
privacy: object | None = None,
|
privacy: object | None = None,
|
||||||
integrations: Iterable[object] | None = None,
|
integrations: Iterable[object] | None = None,
|
||||||
flowchart: object | None = None,
|
flowchart: object | None = None,
|
||||||
|
mode: str = "overview",
|
||||||
) -> str:
|
) -> str:
|
||||||
if schema is None or impact is None:
|
if schema is None or impact is None:
|
||||||
return f"""
|
return f"""
|
||||||
@@ -466,9 +467,50 @@ def render_html5_object_context(
|
|||||||
integration_items = list(integrations or [])
|
integration_items = list(integrations or [])
|
||||||
flow_nodes = getattr(flowchart, "nodes", []) if flowchart is not None else []
|
flow_nodes = getattr(flowchart, "nodes", []) if flowchart is not None else []
|
||||||
flow_edges = getattr(flowchart, "edges", []) if flowchart is not None else []
|
flow_edges = getattr(flowchart, "edges", []) if flowchart is not None else []
|
||||||
|
normalized_mode = mode if mode in {"overview", "schema", "impact", "privacy"} else "overview"
|
||||||
|
if normalized_mode == "schema":
|
||||||
|
compact_body = (
|
||||||
|
''.join(_named_node_item("attr", item) for item in attributes[:12])
|
||||||
|
or '<p class="muted padded">Реквизиты не найдены</p>'
|
||||||
|
)
|
||||||
|
compact_body += ''.join(_tabular_section_item(item) for item in sections[:8])
|
||||||
|
compact_body += ''.join(_ui_form_item(item) for item in ui_forms[:8])
|
||||||
|
elif normalized_mode == "impact":
|
||||||
|
compact_body = ''.join(_integration_endpoint_item(item) for item in integration_items[:8])
|
||||||
|
compact_body += ''.join(_named_node_item("command", item) for item in commands[:8])
|
||||||
|
compact_body += ''.join(_named_node_item("read", item) for item in query_tables[:8])
|
||||||
|
compact_body += ''.join(_named_node_item("write", item) for item in writes[:8])
|
||||||
|
compact_body += ''.join(_named_node_item("call", item) for item in callees[:8])
|
||||||
|
compact_body += ''.join(_flowchart_edge_item(item, flow_nodes) for item in flow_edges[:8])
|
||||||
|
compact_body += ''.join(_named_node_item("routine", item) for item in routines[:8])
|
||||||
|
compact_body += ''.join(_named_node_item("job", item) for item in jobs[:6])
|
||||||
|
compact_body = compact_body or '<p class="muted padded">Impact-связи не найдены</p>'
|
||||||
|
elif normalized_mode == "privacy":
|
||||||
|
compact_body = ''.join(_role_access_item(item) for item in grants[:12])
|
||||||
|
compact_body += ''.join(_privacy_marker_item(item) for item in privacy_markers[:12])
|
||||||
|
compact_body = compact_body or '<p class="muted padded">Доступы и privacy-маркеры не найдены</p>'
|
||||||
|
else:
|
||||||
|
compact_body = (
|
||||||
|
''.join(_named_node_item("attr", item) for item in attributes[:6])
|
||||||
|
or '<p class="muted padded">Реквизиты не найдены</p>'
|
||||||
|
)
|
||||||
|
compact_body += ''.join(_tabular_section_item(item) for item in sections[:4])
|
||||||
|
compact_body += ''.join(_ui_form_item(item) for item in ui_forms[:4])
|
||||||
|
compact_body += ''.join(_role_access_item(item) for item in grants[:6])
|
||||||
|
compact_body += ''.join(_integration_endpoint_item(item) for item in integration_items[:4])
|
||||||
|
compact_body += ''.join(_named_node_item("command", item) for item in commands[:6])
|
||||||
|
compact_body += ''.join(_named_node_item("read", item) for item in query_tables[:4])
|
||||||
|
compact_body += ''.join(_named_node_item("write", item) for item in writes[:4])
|
||||||
|
compact_body += ''.join(_named_node_item("call", item) for item in callees[:6])
|
||||||
|
compact_body += ''.join(_flowchart_edge_item(item, flow_nodes) for item in flow_edges[:8])
|
||||||
|
compact_body += ''.join(_runtime_summary_item(item) for item in runtime_items[:6])
|
||||||
|
compact_body += ''.join(_knowledge_record_item(item) for item in knowledge_items[:6])
|
||||||
|
compact_body += ''.join(_privacy_marker_item(item) for item in privacy_markers[:6])
|
||||||
|
compact_body += ''.join(_named_node_item("routine", item) for item in routines[:6])
|
||||||
|
compact_body += ''.join(_named_node_item("job", item) for item in jobs[:4])
|
||||||
return f"""
|
return f"""
|
||||||
<div class="object-context" data-html5-object-context data-html5-object-name="{escape(str(name))}">
|
<div class="object-context" data-html5-object-context data-html5-object-name="{escape(str(name))}" data-html5-object-mode="{escape(normalized_mode)}">
|
||||||
<div class="panel-title">Object context</div>
|
<div class="panel-title">Object context · {escape(normalized_mode)}</div>
|
||||||
<article class="object-focus">
|
<article class="object-focus">
|
||||||
<strong>{escape(str(name))}</strong>
|
<strong>{escape(str(name))}</strong>
|
||||||
<span>{escape(str(getattr(obj, "kind", "object")))}</span>
|
<span>{escape(str(getattr(obj, "kind", "object")))}</span>
|
||||||
@@ -493,21 +535,7 @@ def render_html5_object_context(
|
|||||||
{_metric("Privacy", len(privacy_markers))}
|
{_metric("Privacy", len(privacy_markers))}
|
||||||
</dl>
|
</dl>
|
||||||
<div class="compact-list">
|
<div class="compact-list">
|
||||||
{''.join(_named_node_item("attr", item) for item in attributes[:6]) or '<p class="muted padded">Реквизиты не найдены</p>'}
|
{compact_body}
|
||||||
{''.join(_tabular_section_item(item) for item in sections[:4])}
|
|
||||||
{''.join(_ui_form_item(item) for item in ui_forms[:4])}
|
|
||||||
{''.join(_role_access_item(item) for item in grants[:6])}
|
|
||||||
{''.join(_integration_endpoint_item(item) for item in integration_items[:4])}
|
|
||||||
{''.join(_named_node_item("command", item) for item in commands[:6])}
|
|
||||||
{''.join(_named_node_item("read", item) for item in query_tables[:4])}
|
|
||||||
{''.join(_named_node_item("write", item) for item in writes[:4])}
|
|
||||||
{''.join(_named_node_item("call", item) for item in callees[:6])}
|
|
||||||
{''.join(_flowchart_edge_item(item, flow_nodes) for item in flow_edges[:8])}
|
|
||||||
{''.join(_runtime_summary_item(item) for item in runtime_items[:6])}
|
|
||||||
{''.join(_knowledge_record_item(item) for item in knowledge_items[:6])}
|
|
||||||
{''.join(_privacy_marker_item(item) for item in privacy_markers[:6])}
|
|
||||||
{''.join(_named_node_item("routine", item) for item in routines[:6])}
|
|
||||||
{''.join(_named_node_item("job", item) for item in jobs[:4])}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
@@ -1539,9 +1567,27 @@ def _object_action_links(project_id: str, object_name: str, lineage_id: object,
|
|||||||
)
|
)
|
||||||
return f"""
|
return f"""
|
||||||
<nav class="object-actions" data-html5-object-actions>
|
<nav class="object-actions" data-html5-object-actions>
|
||||||
<a class="button" href="/projects/{quoted_project}/objects/schema/{quoted_object}">Schema</a>
|
<a
|
||||||
<a class="button" href="/projects/{quoted_project}/objects/impact/{quoted_object}">Impact</a>
|
class="button"
|
||||||
<a class="button" href="/projects/{quoted_project}/objects/privacy/{quoted_object}">Privacy</a>
|
href="/projects/{quoted_project}/objects/schema/{quoted_object}"
|
||||||
|
hx-get="/html5/projects/{quoted_project}/objects/context/{quoted_object}?mode=schema"
|
||||||
|
hx-target="[data-html5-object-context]"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>Schema</a>
|
||||||
|
<a
|
||||||
|
class="button"
|
||||||
|
href="/projects/{quoted_project}/objects/impact/{quoted_object}"
|
||||||
|
hx-get="/html5/projects/{quoted_project}/objects/context/{quoted_object}?mode=impact"
|
||||||
|
hx-target="[data-html5-object-context]"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>Impact</a>
|
||||||
|
<a
|
||||||
|
class="button"
|
||||||
|
href="/projects/{quoted_project}/objects/privacy/{quoted_object}"
|
||||||
|
hx-get="/html5/projects/{quoted_project}/objects/context/{quoted_object}?mode=privacy"
|
||||||
|
hx-target="[data-html5-object-context]"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>Privacy</a>
|
||||||
<a
|
<a
|
||||||
class="button"
|
class="button"
|
||||||
href="/html5/projects/{quoted_project}/flowchart?focus={quoted_object}"
|
href="/html5/projects/{quoted_project}/flowchart?focus={quoted_object}"
|
||||||
|
|||||||
@@ -1758,7 +1758,7 @@ async def html5_project_flowchart(
|
|||||||
|
|
||||||
|
|
||||||
@app.get("/html5/projects/{project_id}/objects/context/{object_name}")
|
@app.get("/html5/projects/{project_id}/objects/context/{object_name}")
|
||||||
async def html5_project_object_context(project_id: str, object_name: str) -> Response:
|
async def html5_project_object_context(project_id: str, object_name: str, mode: str = "overview") -> Response:
|
||||||
schema = await get_object_schema(project_id, object_name)
|
schema = await get_object_schema(project_id, object_name)
|
||||||
impact = await get_object_impact(project_id, object_name)
|
impact = await get_object_impact(project_id, object_name)
|
||||||
access = await get_object_access(project_id, object_name)
|
access = await get_object_access(project_id, object_name)
|
||||||
@@ -1782,6 +1782,7 @@ async def html5_project_object_context(project_id: str, object_name: str) -> Res
|
|||||||
privacy,
|
privacy,
|
||||||
integrations,
|
integrations,
|
||||||
flowchart,
|
flowchart,
|
||||||
|
mode,
|
||||||
)
|
)
|
||||||
flowchart_context = render_html5_flowchart(project_id, flowchart, focus=object_name, oob=True)
|
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 ""
|
source_context = render_html5_source(source_node, oob=True) if source_node is not None else ""
|
||||||
|
|||||||
@@ -318,6 +318,10 @@ def test_html5_object_context_fragment(tmp_path: Path):
|
|||||||
assert "data-html5-object-actions" in context.text
|
assert "data-html5-object-actions" in context.text
|
||||||
assert f"/projects/{project_id}/objects/schema/%D0%94%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82.%D0%97%D0%B0%D0%BA%D0%B0%D0%B7%D0%9F%D0%BE%D0%BA%D1%83%D0%BF%D0%B0%D1%82%D0%B5%D0%BB%D1%8F" in context.text
|
assert f"/projects/{project_id}/objects/schema/%D0%94%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82.%D0%97%D0%B0%D0%BA%D0%B0%D0%B7%D0%9F%D0%BE%D0%BA%D1%83%D0%BF%D0%B0%D1%82%D0%B5%D0%BB%D1%8F" in context.text
|
||||||
assert f"/projects/{project_id}/objects/impact/%D0%94%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82.%D0%97%D0%B0%D0%BA%D0%B0%D0%B7%D0%9F%D0%BE%D0%BA%D1%83%D0%BF%D0%B0%D1%82%D0%B5%D0%BB%D1%8F" in context.text
|
assert f"/projects/{project_id}/objects/impact/%D0%94%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82.%D0%97%D0%B0%D0%BA%D0%B0%D0%B7%D0%9F%D0%BE%D0%BA%D1%83%D0%BF%D0%B0%D1%82%D0%B5%D0%BB%D1%8F" in context.text
|
||||||
|
assert "mode=schema" in context.text
|
||||||
|
assert "mode=impact" in context.text
|
||||||
|
assert "mode=privacy" in context.text
|
||||||
|
assert 'hx-target="[data-html5-object-context]"' in context.text
|
||||||
assert 'hx-target="[data-html5-flowchart]"' in context.text
|
assert 'hx-target="[data-html5-flowchart]"' in context.text
|
||||||
assert 'hx-target="[data-html5-source]"' in context.text
|
assert 'hx-target="[data-html5-source]"' in context.text
|
||||||
assert 'hx-target="[data-html5-symbol-detail]"' in context.text
|
assert 'hx-target="[data-html5-symbol-detail]"' in context.text
|
||||||
@@ -357,6 +361,33 @@ def test_html5_object_context_fragment(tmp_path: Path):
|
|||||||
assert "read, write, post" in context.text or "post, read, write" in context.text
|
assert "read, write, post" in context.text or "post, read, write" in context.text
|
||||||
assert "<html" not in context.text
|
assert "<html" not in context.text
|
||||||
|
|
||||||
|
schema_context = client.get(
|
||||||
|
f"/html5/projects/{project_id}/objects/context/Документ.ЗаказПокупателя",
|
||||||
|
params={"mode": "schema"},
|
||||||
|
)
|
||||||
|
assert schema_context.status_code == 200
|
||||||
|
assert 'data-html5-object-mode="schema"' in schema_context.text
|
||||||
|
assert "Object context · schema" in schema_context.text
|
||||||
|
assert "Контрагент" in schema_context.text
|
||||||
|
|
||||||
|
impact_context = client.get(
|
||||||
|
f"/html5/projects/{project_id}/objects/context/Документ.ЗаказПокупателя",
|
||||||
|
params={"mode": "impact"},
|
||||||
|
)
|
||||||
|
assert impact_context.status_code == 200
|
||||||
|
assert 'data-html5-object-mode="impact"' in impact_context.text
|
||||||
|
assert "Object context · impact" in impact_context.text
|
||||||
|
assert "HTTPConnection" in impact_context.text
|
||||||
|
|
||||||
|
privacy_context = client.get(
|
||||||
|
f"/html5/projects/{project_id}/objects/context/Документ.ЗаказПокупателя",
|
||||||
|
params={"mode": "privacy"},
|
||||||
|
)
|
||||||
|
assert privacy_context.status_code == 200
|
||||||
|
assert 'data-html5-object-mode="privacy"' in privacy_context.text
|
||||||
|
assert "Object context · privacy" in privacy_context.text
|
||||||
|
assert "PERSONAL_DATA" in privacy_context.text
|
||||||
|
|
||||||
|
|
||||||
def test_html5_flowchart_fragment(tmp_path: Path):
|
def test_html5_flowchart_fragment(tmp_path: Path):
|
||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
|
|||||||
Reference in New Issue
Block a user