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

This commit is contained in:
2026-05-17 00:14:55 +03:00
parent b93fd88e81
commit f695846b7b
3 changed files with 66 additions and 1 deletions
@@ -323,6 +323,7 @@ def render_html5_object_context(
access: object | None = None, access: object | None = None,
ui: object | None = None, ui: object | None = None,
runtime: Iterable[object] | None = None, runtime: Iterable[object] | None = None,
knowledge: Iterable[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"""
@@ -343,6 +344,7 @@ def render_html5_object_context(
grants = getattr(access, "grants", []) if access is not None else [] grants = getattr(access, "grants", []) if access is not None else []
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 [])
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>
@@ -358,6 +360,7 @@ def render_html5_object_context(
{_metric("Forms", len(ui_forms) or len(forms))} {_metric("Forms", len(ui_forms) or len(forms))}
{_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))}
</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>'}
@@ -365,6 +368,7 @@ def render_html5_object_context(
{''.join(_ui_form_item(item) for item in ui_forms[:4])} {''.join(_ui_form_item(item) for item in ui_forms[:4])}
{''.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(_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>
@@ -1442,6 +1446,19 @@ def _runtime_summary_item(item: object) -> str:
""" """
def _knowledge_record_item(record: object) -> str:
title = str(getattr(record, "title", "knowledge"))
scope = _enum_text(getattr(record, "scope", ""))
body = str(getattr(record, "body", "") or "")
record_id = str(getattr(record, "record_id", ""))
return f"""
<article class="object-context-item" data-html5-object-context-item="knowledge">
<strong>{escape(title)}</strong>
<small>{escape(scope)} · {escape(record_id)} · {escape(body[:120])}</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", ""))
+36 -1
View File
@@ -1748,8 +1748,9 @@ async def html5_project_object_context(project_id: str, object_name: str) -> Res
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)
runtime = _runtime_for_object_context(project_id, impact) runtime = _runtime_for_object_context(project_id, impact)
knowledge = _knowledge_for_object_context(schema, impact, ui)
return Response( return Response(
render_html5_object_context(project_id, schema, impact, access, ui, runtime), render_html5_object_context(project_id, schema, impact, access, ui, runtime, knowledge),
media_type="text/html; charset=utf-8", media_type="text/html; charset=utf-8",
) )
@@ -8142,6 +8143,40 @@ def _runtime_for_object_context(project_id: str, impact: ObjectImpactResponse) -
] ]
def _knowledge_for_object_context(
schema: ObjectSchemaResponse,
impact: ObjectImpactResponse,
ui: ObjectUiResponse,
) -> list[KnowledgeRecord]:
lineages: set[str] = {schema.object.lineage_id, impact.object.lineage_id}
lineages.update(item.lineage_id for item in schema.attributes)
for section in schema.tabular_sections:
lineages.add(section.tabular_section.lineage_id)
lineages.update(column.lineage_id for column in section.columns)
for group in [
impact.modules,
impact.routines,
impact.forms,
impact.commands,
impact.roles,
impact.jobs,
impact.writes,
impact.query_tables,
]:
lineages.update(item.lineage_id for item in group)
for form in ui.forms:
lineages.add(form.form.lineage_id)
lineages.update(command.lineage_id for command in form.commands)
lineages.update(element.lineage_id for element in form.elements)
lineages.update(handler.lineage_id for handler in form.command_handlers.values())
records = [
record
for record in _knowledge.list_records()
if lineages.intersection(record.related_lineages)
]
return sorted(records, key=lambda item: item.title.lower())[:12]
def _current_import_source(project_id: str) -> ImportSourceKind: def _current_import_source(project_id: str) -> ImportSourceKind:
setup = _project_setup_response(project_id) setup = _project_setup_response(project_id)
if setup.current_source is not None: if setup.current_source is not None:
+13
View File
@@ -268,6 +268,17 @@ def test_html5_object_context_fragment(tmp_path: Path):
}, },
) )
assert signal.status_code == 200 assert signal.status_code == 200
knowledge = client.post(
"/knowledge",
json={
"record_id": f"knowledge.html5.object.{uuid4()}",
"scope": "PROJECT",
"title": "Правила проведения HTML5",
"body": "Контекст проведения заказа для HTML5 inspector.",
"related_lineages": [handler["lineage_id"]],
},
)
assert knowledge.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
@@ -288,6 +299,8 @@ def test_html5_object_context_fragment(tmp_path: Path):
assert "1 signals" in context.text assert "1 signals" in context.text
assert "1 errors" in context.text assert "1 errors" in context.text
assert "125.0 ms" in context.text assert "125.0 ms" in context.text
assert "Правила проведения HTML5" 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