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

This commit is contained in:
2026-05-17 00:07:30 +03:00
parent a8baa2aa49
commit 1da745c52e
3 changed files with 44 additions and 2 deletions
+33 -1
View File
@@ -321,6 +321,7 @@ def render_html5_object_context(
schema: object | None, schema: object | None,
impact: object | None, impact: object | None,
access: object | None = None, access: object | None = None,
ui: 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"""
@@ -339,6 +340,7 @@ def render_html5_object_context(
roles = getattr(impact, "roles", []) or [] roles = getattr(impact, "roles", []) or []
jobs = getattr(impact, "jobs", []) or [] jobs = getattr(impact, "jobs", []) or []
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 []
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>
@@ -351,12 +353,13 @@ def render_html5_object_context(
{_metric("Tables", len(sections))} {_metric("Tables", len(sections))}
{_metric("Modules", len(modules))} {_metric("Modules", len(modules))}
{_metric("Routines", len(routines))} {_metric("Routines", len(routines))}
{_metric("Forms", len(forms))} {_metric("Forms", len(ui_forms) or len(forms))}
{_metric("Roles", len(grants) or len(roles))} {_metric("Roles", len(grants) or len(roles))}
</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>'}
{''.join(_tabular_section_item(item) for item in sections[:4])} {''.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(_role_access_item(item) for item in grants[: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])}
@@ -1391,6 +1394,35 @@ def _role_access_item(grant: object) -> str:
""" """
def _ui_form_item(form_semantics: object) -> str:
form = getattr(form_semantics, "form", None)
commands = getattr(form_semantics, "commands", []) or []
elements = getattr(form_semantics, "elements", []) or []
handlers = getattr(form_semantics, "command_handlers", {}) or {}
form_name = getattr(form, "qualified_name", None) or getattr(form, "name", "form")
command_names = [
str(getattr(command, "name", getattr(command, "qualified_name", "")))
for command in commands[:3]
]
handler_names = [
str(getattr(handler, "name", getattr(handler, "qualified_name", "")))
for handler in list(handlers.values())[:3]
]
details = []
if command_names:
details.append("cmd: " + ", ".join(command_names))
if handler_names:
details.append("handler: " + ", ".join(handler_names))
if elements:
details.append(f"{len(elements)} elements")
return f"""
<article class="object-context-item" data-html5-object-context-item="ui-form">
<strong>{escape(str(form_name))}</strong>
<small>{escape(" · ".join(details) or "UI metadata")}</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
@@ -1746,8 +1746,9 @@ async def html5_project_object_context(project_id: str, object_name: str) -> Res
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)
ui = await get_object_ui(project_id, object_name)
return Response( return Response(
render_html5_object_context(project_id, schema, impact, access), render_html5_object_context(project_id, schema, impact, access, ui),
media_type="text/html; charset=utf-8", media_type="text/html; charset=utf-8",
) )
+9
View File
@@ -237,6 +237,9 @@ def test_html5_object_context_fragment(tmp_path: Path):
<TabularSection name="Товары" qualifiedName="Документ.ЗаказПокупателя.Товары"> <TabularSection name="Товары" qualifiedName="Документ.ЗаказПокупателя.Товары">
<Attribute name="Количество" qualifiedName="Документ.ЗаказПокупателя.Товары.Количество" /> <Attribute name="Количество" qualifiedName="Документ.ЗаказПокупателя.Товары.Количество" />
</TabularSection> </TabularSection>
<Form name="ФормаДокумента" qualifiedName="Документ.ЗаказПокупателя.ФормаДокумента">
<Command name="Провести" qualifiedName="Документ.ЗаказПокупателя.ФормаДокумента.Провести" action="ПровестиКоманда" />
</Form>
</Document> </Document>
<Role name="Менеджер" qualifiedName="Роль.Менеджер"> <Role name="Менеджер" qualifiedName="Роль.Менеджер">
<Right object="Документ.ЗаказПокупателя" read="true" write="true" post="true" /> <Right object="Документ.ЗаказПокупателя" read="true" write="true" post="true" />
@@ -245,6 +248,9 @@ def test_html5_object_context_fragment(tmp_path: Path):
""", """,
encoding="utf-8", encoding="utf-8",
) )
module = tmp_path / "Documents" / "ЗаказПокупателя" / "Ext" / "ObjectModule.bsl"
module.parent.mkdir(parents=True)
module.write_text("Процедура ПровестиКоманда()\nКонецПроцедуры\n", encoding="utf-8")
client = TestClient(app) client = TestClient(app)
indexed = client.post("/projects/index", json={"path": str(tmp_path), "project_id": project_id}) indexed = client.post("/projects/index", json={"path": str(tmp_path), "project_id": project_id})
assert indexed.status_code == 200 assert indexed.status_code == 200
@@ -262,6 +268,9 @@ def test_html5_object_context_fragment(tmp_path: Path):
assert "Документ.ЗаказПокупателя" in context.text assert "Документ.ЗаказПокупателя" in context.text
assert "Контрагент" in context.text assert "Контрагент" in context.text
assert "Товары" in context.text assert "Товары" in context.text
assert "ФормаДокумента" in context.text
assert "Провести" 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