Add HTML5 object privacy context
CI / python (push) Has been cancelled
CI / rust (push) Has been cancelled

This commit is contained in:
2026-05-17 00:24:29 +03:00
parent f695846b7b
commit 79ae2b3023
3 changed files with 30 additions and 1 deletions
@@ -324,6 +324,7 @@ def render_html5_object_context(
ui: object | None = None, ui: object | None = None,
runtime: Iterable[object] | None = None, runtime: Iterable[object] | None = None,
knowledge: Iterable[object] | None = None, knowledge: Iterable[object] | None = None,
privacy: object | None = None,
) -> str: ) -> str:
if schema is None or impact is None: if schema is None or impact is None:
return f""" return f"""
@@ -345,6 +346,7 @@ def render_html5_object_context(
ui_forms = getattr(ui, "forms", []) if ui is not None else [] ui_forms = getattr(ui, "forms", []) if ui is not None else []
runtime_items = list(runtime or []) runtime_items = list(runtime or [])
knowledge_items = list(knowledge or []) knowledge_items = list(knowledge or [])
privacy_markers = getattr(privacy, "markers", []) if privacy is not None else []
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))}">
<div class="panel-title">Object context</div> <div class="panel-title">Object context</div>
@@ -361,6 +363,7 @@ def render_html5_object_context(
{_metric("Roles", len(grants) or len(roles))} {_metric("Roles", len(grants) or len(roles))}
{_metric("Runtime", len(runtime_items))} {_metric("Runtime", len(runtime_items))}
{_metric("Knowledge", len(knowledge_items))} {_metric("Knowledge", len(knowledge_items))}
{_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>'} {''.join(_named_node_item("attr", item) for item in attributes[:6]) or '<p class="muted padded">Реквизиты не найдены</p>'}
@@ -369,6 +372,7 @@ def render_html5_object_context(
{''.join(_role_access_item(item) for item in grants[:6])} {''.join(_role_access_item(item) for item in grants[:6])}
{''.join(_runtime_summary_item(item) for item in runtime_items[:6])} {''.join(_runtime_summary_item(item) for item in runtime_items[:6])}
{''.join(_knowledge_record_item(item) for item in knowledge_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("routine", item) for item in routines[:6])}
{''.join(_named_node_item("job", item) for item in jobs[:4])} {''.join(_named_node_item("job", item) for item in jobs[:4])}
</div> </div>
@@ -1459,6 +1463,18 @@ def _knowledge_record_item(record: object) -> str:
""" """
def _privacy_marker_item(marker: object) -> str:
classification = _enum_text(getattr(marker, "classification", ""))
reason = str(getattr(marker, "reason", "") or "")
target_id = str(getattr(marker, "target_id", "") or "target unavailable")
return f"""
<article class="object-context-item" data-html5-object-context-item="privacy">
<strong>{escape(classification or "privacy")}</strong>
<small>{escape(reason or target_id)}</small>
</article>
"""
def _authoring_diff_item(line: object) -> str: def _authoring_diff_item(line: object) -> str:
kind = str(getattr(line, "kind", "")) kind = str(getattr(line, "kind", ""))
text = str(getattr(line, "text", "")) text = str(getattr(line, "text", ""))
+2 -1
View File
@@ -1747,10 +1747,11 @@ async def html5_project_object_context(project_id: str, object_name: str) -> Res
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)
ui = await get_object_ui(project_id, object_name) ui = await get_object_ui(project_id, object_name)
privacy = await object_privacy(project_id, object_name)
runtime = _runtime_for_object_context(project_id, impact) runtime = _runtime_for_object_context(project_id, impact)
knowledge = _knowledge_for_object_context(schema, impact, ui) knowledge = _knowledge_for_object_context(schema, impact, ui)
return Response( return Response(
render_html5_object_context(project_id, schema, impact, access, ui, runtime, knowledge), render_html5_object_context(project_id, schema, impact, access, ui, runtime, knowledge, privacy),
media_type="text/html; charset=utf-8", media_type="text/html; charset=utf-8",
) )
+12
View File
@@ -256,6 +256,7 @@ def test_html5_object_context_fragment(tmp_path: Path):
assert indexed.status_code == 200 assert indexed.status_code == 200
snapshot = client.get(f"/projects/{project_id}/snapshot/export").json() snapshot = client.get(f"/projects/{project_id}/snapshot/export").json()
handler = next(node for node in snapshot["nodes"] if node["name"] == "ПровестиКоманда") handler = next(node for node in snapshot["nodes"] if node["name"] == "ПровестиКоманда")
attribute = next(node for node in snapshot["nodes"] if node["name"] == "Контрагент")
signal = client.post( signal = client.post(
f"/projects/{project_id}/runtime/signals", f"/projects/{project_id}/runtime/signals",
json={ json={
@@ -279,6 +280,15 @@ def test_html5_object_context_fragment(tmp_path: Path):
}, },
) )
assert knowledge.status_code == 200 assert knowledge.status_code == 200
marker = client.post(
f"/projects/{project_id}/privacy/markers",
json={
"target_id": attribute["lineage_id"],
"classification": "PERSONAL_DATA",
"reason": "Контрагент содержит персональные данные",
},
)
assert marker.status_code == 200
editor = client.get(f"/html5/projects/{project_id}/editor") editor = client.get(f"/html5/projects/{project_id}/editor")
assert editor.status_code == 200 assert editor.status_code == 200
@@ -301,6 +311,8 @@ def test_html5_object_context_fragment(tmp_path: Path):
assert "125.0 ms" in context.text assert "125.0 ms" in context.text
assert "Правила проведения HTML5" in context.text assert "Правила проведения HTML5" in context.text
assert "Контекст проведения заказа" in context.text assert "Контекст проведения заказа" in context.text
assert "PERSONAL_DATA" in context.text
assert "Контрагент содержит персональные данные" in context.text
assert "Роль.Менеджер" in context.text assert "Роль.Менеджер" in context.text
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