Split HTML5 authoring renderer
This commit is contained in:
@@ -5,6 +5,11 @@ from html import escape
|
|||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
from api_server.html5_authoring import (
|
||||||
|
render_html5_authoring_changes,
|
||||||
|
render_html5_authoring_preview,
|
||||||
|
render_html5_metadata_authoring,
|
||||||
|
)
|
||||||
from sir import NodeKind, SirSnapshot
|
from sir import NodeKind, SirSnapshot
|
||||||
|
|
||||||
_HTML5_OBJECT_CONTEXT_KINDS = {
|
_HTML5_OBJECT_CONTEXT_KINDS = {
|
||||||
@@ -580,388 +585,6 @@ def render_html5_object_context(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def render_html5_authoring_changes(project_id: str, changes: Iterable[object] | None) -> str:
|
|
||||||
if changes is None:
|
|
||||||
return f"""
|
|
||||||
<div
|
|
||||||
class="authoring-panel"
|
|
||||||
data-html5-authoring-changes
|
|
||||||
hx-get="/html5/projects/{quote(project_id)}/authoring/changes"
|
|
||||||
hx-trigger="load"
|
|
||||||
sse-swap="authoring-changes"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
>
|
|
||||||
<div class="panel-title">Authoring</div>
|
|
||||||
<p class="muted padded">Сервер загружает историю рабочих изменений.</p>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
change_list = list(changes)
|
|
||||||
if not change_list:
|
|
||||||
body = '<p class="muted padded">Изменений пока нет</p>'
|
|
||||||
else:
|
|
||||||
body = "".join(_authoring_change_item(change) for change in change_list[:12])
|
|
||||||
return f"""
|
|
||||||
<div
|
|
||||||
class="authoring-panel"
|
|
||||||
data-html5-authoring-changes
|
|
||||||
hx-get="/html5/projects/{quote(project_id)}/authoring/changes"
|
|
||||||
sse-swap="authoring-changes"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
>
|
|
||||||
<div class="panel-title">Authoring · {len(change_list)}</div>
|
|
||||||
{_authoring_changes_summary(change_list)}
|
|
||||||
{_authoring_recent_change(change_list)}
|
|
||||||
<div class="review-list">{body}</div>
|
|
||||||
{render_html5_authoring_change_detail(project_id, None)}
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def render_html5_authoring_preview(project_id: str, preview: object | None, error: str | None = None) -> str:
|
|
||||||
if preview is None and error is None:
|
|
||||||
return f"""
|
|
||||||
<div class="authoring-preview" data-html5-authoring-preview>
|
|
||||||
<div class="panel-title">Authoring preview</div>
|
|
||||||
<form
|
|
||||||
class="authoring-preview-form"
|
|
||||||
data-html5-authoring-preview-form
|
|
||||||
method="post"
|
|
||||||
action="/html5/projects/{quote(project_id)}/authoring/completion-preview"
|
|
||||||
hx-post="/html5/projects/{quote(project_id)}/authoring/completion-preview"
|
|
||||||
hx-target="[data-html5-authoring-preview-result]"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
>
|
|
||||||
<input name="object_name" placeholder="object_name" />
|
|
||||||
<input name="routine_name" placeholder="routine_name" />
|
|
||||||
<input name="cursor_line" placeholder="line" />
|
|
||||||
<input name="intent" value="fill-check" />
|
|
||||||
<input name="user_id" placeholder="user_id" />
|
|
||||||
<textarea name="source_text" placeholder="BSL source"></textarea>
|
|
||||||
<button type="submit">Preview</button>
|
|
||||||
</form>
|
|
||||||
{render_html5_authoring_preview_result(project_id)}
|
|
||||||
<form
|
|
||||||
class="authoring-preview-form"
|
|
||||||
data-html5-authoring-diff-form
|
|
||||||
method="post"
|
|
||||||
action="/html5/projects/{quote(project_id)}/authoring/semantic-diff-preview"
|
|
||||||
hx-post="/html5/projects/{quote(project_id)}/authoring/semantic-diff-preview"
|
|
||||||
hx-target="[data-html5-authoring-diff-result]"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
>
|
|
||||||
<input name="routine_name" placeholder="routine_name" />
|
|
||||||
<input name="source_path" placeholder="source_path" />
|
|
||||||
<input name="task_id" placeholder="task_id" />
|
|
||||||
<input name="session_id" placeholder="session_id" />
|
|
||||||
<input name="user_id" placeholder="user_id" />
|
|
||||||
<textarea name="original_text" placeholder="Original BSL"></textarea>
|
|
||||||
<textarea name="proposed_text" placeholder="Proposed BSL"></textarea>
|
|
||||||
<button type="submit">Diff preview</button>
|
|
||||||
</form>
|
|
||||||
{render_html5_authoring_diff_result(project_id)}
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
return render_html5_authoring_preview_result(project_id, preview, error)
|
|
||||||
|
|
||||||
|
|
||||||
def render_html5_authoring_preview_result(project_id: str, preview: object | None = None, error: str | None = None) -> str:
|
|
||||||
if preview is None and error is None:
|
|
||||||
return '<div class="authoring-preview-result" data-html5-authoring-preview-result></div>'
|
|
||||||
if error:
|
|
||||||
return f"""
|
|
||||||
<div class="authoring-preview-result" data-html5-authoring-preview-result>
|
|
||||||
<div class="panel-title">Preview result</div>
|
|
||||||
<p class="muted padded">{escape(error)}</p>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
allowed = bool(getattr(preview, "allowed", False))
|
|
||||||
insert_text = str(getattr(preview, "insert_text", ""))
|
|
||||||
checks = getattr(preview, "checks", []) or []
|
|
||||||
diff = getattr(preview, "semantic_diff", []) or []
|
|
||||||
context = getattr(preview, "context", None)
|
|
||||||
object_node = getattr(context, "object", None)
|
|
||||||
routine_node = getattr(context, "routine", None)
|
|
||||||
object_name = getattr(object_node, "qualified_name", None) or getattr(object_node, "name", None) or "object unavailable"
|
|
||||||
routine_name = getattr(routine_node, "name", None) or "routine unavailable"
|
|
||||||
check_rows = "".join(_authoring_check_item(check) for check in checks[:8])
|
|
||||||
diff_rows = "".join(_authoring_diff_item(line) for line in diff[:8]) or '<p class="muted padded">Diff пустой</p>'
|
|
||||||
return f"""
|
|
||||||
<div class="authoring-preview-result" data-html5-authoring-preview-result data-html5-project-id="{escape(project_id)}">
|
|
||||||
<div class="panel-title">Preview result · {'allowed' if allowed else 'blocked'}</div>
|
|
||||||
<article class="authoring-change">
|
|
||||||
<strong>{escape(str(object_name))}</strong>
|
|
||||||
<span>{escape(str(routine_name))}</span>
|
|
||||||
<small>{escape(insert_text[:180] or "insert text unavailable")}</small>
|
|
||||||
</article>
|
|
||||||
{_authoring_result_summary("allowed" if allowed else "blocked", diff, checks)}
|
|
||||||
<div class="check-list">{check_rows}</div>
|
|
||||||
<div class="diff-list">{diff_rows}</div>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def render_html5_authoring_diff_result(
|
|
||||||
project_id: str,
|
|
||||||
preview: object | None = None,
|
|
||||||
error: str | None = None,
|
|
||||||
request_payload: dict | None = None,
|
|
||||||
) -> str:
|
|
||||||
if preview is None and error is None:
|
|
||||||
return '<div class="authoring-diff-result" data-html5-authoring-diff-result></div>'
|
|
||||||
if error:
|
|
||||||
return f"""
|
|
||||||
<div class="authoring-diff-result" data-html5-authoring-diff-result>
|
|
||||||
<div class="panel-title">Diff preview</div>
|
|
||||||
<p class="muted padded">{escape(error)}</p>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
changed = bool(getattr(preview, "changed", False))
|
|
||||||
added = getattr(preview, "added_lines", 0)
|
|
||||||
removed = getattr(preview, "removed_lines", 0)
|
|
||||||
target = getattr(preview, "target", None)
|
|
||||||
target_name = getattr(target, "qualified_name", None) or getattr(target, "name", None) or "target unavailable"
|
|
||||||
checks = getattr(preview, "checks", []) or []
|
|
||||||
diff = getattr(preview, "semantic_diff", []) or []
|
|
||||||
version_preview = getattr(preview, "version_preview", None)
|
|
||||||
next_version_id = str(getattr(version_preview, "next_version_id", ""))
|
|
||||||
check_rows = "".join(_authoring_check_item(check) for check in checks[:8])
|
|
||||||
diff_rows = "".join(_authoring_diff_item(line) for line in diff[:12]) or '<p class="muted padded">Diff пустой</p>'
|
|
||||||
apply_form = (
|
|
||||||
_authoring_apply_change_set_form(project_id, request_payload or {}, next_version_id)
|
|
||||||
if changed and next_version_id
|
|
||||||
else ""
|
|
||||||
)
|
|
||||||
return f"""
|
|
||||||
<div class="authoring-diff-result" data-html5-authoring-diff-result data-html5-project-id="{escape(project_id)}">
|
|
||||||
<div class="panel-title">Diff preview · {'changed' if changed else 'unchanged'}</div>
|
|
||||||
<article class="authoring-change">
|
|
||||||
<strong>{escape(str(target_name))}</strong>
|
|
||||||
<span>+{escape(str(added))} / -{escape(str(removed))}</span>
|
|
||||||
<small>{escape(next_version_id or "version preview unavailable")}</small>
|
|
||||||
</article>
|
|
||||||
{_authoring_result_summary("changed" if changed else "unchanged", diff, checks)}
|
|
||||||
<div class="check-list">{check_rows}</div>
|
|
||||||
<div class="diff-list">{diff_rows}</div>
|
|
||||||
{apply_form}
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def render_html5_authoring_apply_result(project_id: str, result: object | None = None, error: str | None = None) -> str:
|
|
||||||
if result is None and error is None:
|
|
||||||
return '<div class="authoring-apply-result" data-html5-authoring-apply-result></div>'
|
|
||||||
if error:
|
|
||||||
return f"""
|
|
||||||
<div class="authoring-apply-result" data-html5-authoring-apply-result>
|
|
||||||
<div class="panel-title">Apply change-set</div>
|
|
||||||
<p class="muted padded">{escape(error)}</p>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
status = str(getattr(result, "status", "UNKNOWN"))
|
|
||||||
change_id = str(getattr(result, "change_id", ""))
|
|
||||||
version = getattr(result, "version", None)
|
|
||||||
version_id = str(getattr(version, "version_id", ""))
|
|
||||||
return f"""
|
|
||||||
<div
|
|
||||||
class="authoring-apply-result"
|
|
||||||
data-html5-authoring-apply-result
|
|
||||||
data-html5-authoring-change="{escape(change_id)}"
|
|
||||||
data-html5-version-id="{escape(version_id)}"
|
|
||||||
>
|
|
||||||
<div class="panel-title">Apply change-set</div>
|
|
||||||
<article class="authoring-change">
|
|
||||||
<strong>{escape(status)}</strong>
|
|
||||||
<span>{escape(change_id)}</span>
|
|
||||||
<small>{escape(version_id)}</small>
|
|
||||||
</article>
|
|
||||||
{_authoring_apply_summary("change-set", status, change_id, version_id)}
|
|
||||||
<p class="muted padded">Change-set применен в workspace для проекта {escape(project_id)}.</p>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def render_html5_metadata_authoring(project_id: str) -> str:
|
|
||||||
return f"""
|
|
||||||
<div class="authoring-preview" data-html5-metadata-authoring>
|
|
||||||
<div class="panel-title">Metadata draft</div>
|
|
||||||
<form
|
|
||||||
class="authoring-preview-form"
|
|
||||||
data-html5-metadata-preview-form
|
|
||||||
method="post"
|
|
||||||
action="/html5/projects/{quote(project_id)}/authoring/metadata-object-preview"
|
|
||||||
hx-post="/html5/projects/{quote(project_id)}/authoring/metadata-object-preview"
|
|
||||||
hx-target="[data-html5-metadata-preview-result]"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
>
|
|
||||||
<input name="object_kind" value="DOCUMENT" />
|
|
||||||
<input name="name" placeholder="Имя объекта" required />
|
|
||||||
<input name="synonym" placeholder="Синоним" />
|
|
||||||
<input name="attributes" placeholder="Реквизиты: Имя:Тип, ..." />
|
|
||||||
<input name="tabular_sections" placeholder="ТЧ: Товары[Номенклатура:Строка;Количество:Число]" />
|
|
||||||
<input name="forms" placeholder="Формы через запятую" />
|
|
||||||
<input name="commands" placeholder="Команды: Имя:Обработчик" />
|
|
||||||
<input name="task_id" placeholder="task_id" />
|
|
||||||
<input name="session_id" placeholder="session_id" />
|
|
||||||
<input name="user_id" placeholder="user_id" />
|
|
||||||
<button type="submit">Metadata preview</button>
|
|
||||||
</form>
|
|
||||||
{render_html5_metadata_preview_result(project_id)}
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def render_html5_metadata_preview_result(
|
|
||||||
project_id: str,
|
|
||||||
preview: object | None = None,
|
|
||||||
error: str | None = None,
|
|
||||||
request_payload: dict | None = None,
|
|
||||||
) -> str:
|
|
||||||
if preview is None and error is None:
|
|
||||||
return '<div class="metadata-preview-result" data-html5-metadata-preview-result></div>'
|
|
||||||
if error:
|
|
||||||
return f"""
|
|
||||||
<div class="metadata-preview-result" data-html5-metadata-preview-result>
|
|
||||||
<div class="panel-title">Metadata preview</div>
|
|
||||||
<p class="muted padded">{escape(error)}</p>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
changed = bool(getattr(preview, "changed", False))
|
|
||||||
added = getattr(preview, "added_lines", 0)
|
|
||||||
target = getattr(preview, "target", None)
|
|
||||||
target_name = getattr(target, "qualified_name", None) or getattr(target, "name", None) or "target unavailable"
|
|
||||||
checks = getattr(preview, "checks", []) or []
|
|
||||||
diff = getattr(preview, "semantic_diff", []) or []
|
|
||||||
version_preview = getattr(preview, "version_preview", None)
|
|
||||||
next_version_id = str(getattr(version_preview, "next_version_id", ""))
|
|
||||||
check_rows = "".join(_authoring_check_item(check) for check in checks[:8])
|
|
||||||
diff_rows = "".join(_authoring_diff_item(line) for line in diff[:12]) or '<p class="muted padded">Diff пустой</p>'
|
|
||||||
apply_form = (
|
|
||||||
_metadata_apply_form(project_id, request_payload or {}, next_version_id)
|
|
||||||
if changed and next_version_id
|
|
||||||
else ""
|
|
||||||
)
|
|
||||||
return f"""
|
|
||||||
<div class="metadata-preview-result" data-html5-metadata-preview-result data-html5-project-id="{escape(project_id)}">
|
|
||||||
<div class="panel-title">Metadata preview · {'changed' if changed else 'unchanged'}</div>
|
|
||||||
<article class="authoring-change">
|
|
||||||
<strong>{escape(str(target_name))}</strong>
|
|
||||||
<span>+{escape(str(added))} / -0</span>
|
|
||||||
<small>{escape(next_version_id or "version preview unavailable")}</small>
|
|
||||||
</article>
|
|
||||||
{_authoring_result_summary("changed" if changed else "unchanged", diff, checks)}
|
|
||||||
<div class="check-list">{check_rows}</div>
|
|
||||||
<div class="diff-list">{diff_rows}</div>
|
|
||||||
{apply_form}
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def render_html5_metadata_apply_result(project_id: str, result: object | None = None, error: str | None = None) -> str:
|
|
||||||
if result is None and error is None:
|
|
||||||
return '<div class="metadata-apply-result" data-html5-metadata-apply-result></div>'
|
|
||||||
if error:
|
|
||||||
return f"""
|
|
||||||
<div class="metadata-apply-result" data-html5-metadata-apply-result>
|
|
||||||
<div class="panel-title">Metadata apply</div>
|
|
||||||
<p class="muted padded">{escape(error)}</p>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
status = str(getattr(result, "status", "UNKNOWN"))
|
|
||||||
change_id = str(getattr(result, "change_id", ""))
|
|
||||||
version = getattr(result, "version", None)
|
|
||||||
version_id = str(getattr(version, "version_id", ""))
|
|
||||||
return f"""
|
|
||||||
<div
|
|
||||||
class="metadata-apply-result"
|
|
||||||
data-html5-metadata-apply-result
|
|
||||||
data-html5-authoring-change="{escape(change_id)}"
|
|
||||||
data-html5-version-id="{escape(version_id)}"
|
|
||||||
>
|
|
||||||
<div class="panel-title">Metadata apply</div>
|
|
||||||
<article class="authoring-change">
|
|
||||||
<strong>{escape(status)}</strong>
|
|
||||||
<span>{escape(change_id)}</span>
|
|
||||||
<small>{escape(version_id)}</small>
|
|
||||||
</article>
|
|
||||||
{_authoring_apply_summary("metadata", status, change_id, version_id)}
|
|
||||||
<p class="muted padded">Metadata draft применен в workspace для проекта {escape(project_id)}.</p>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def render_html5_authoring_change_detail(project_id: str, preview: object | None) -> str:
|
|
||||||
if preview is None:
|
|
||||||
return f"""
|
|
||||||
<div class="authoring-detail" data-html5-authoring-detail>
|
|
||||||
<div class="panel-title">Rollback preview</div>
|
|
||||||
<p class="muted padded">Выберите изменение, чтобы сервер рассчитал rollback diff для проекта {escape(project_id)}.</p>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
change_id = str(getattr(preview, "change_id", ""))
|
|
||||||
original_version_id = str(getattr(preview, "original_version_id", ""))
|
|
||||||
rollback_version_id = str(getattr(preview, "rollback_version_id", ""))
|
|
||||||
target = getattr(preview, "target", None)
|
|
||||||
target_name = getattr(target, "qualified_name", None) or getattr(target, "name", None) or "target unavailable"
|
|
||||||
diff = getattr(preview, "semantic_diff", []) or []
|
|
||||||
checks = getattr(preview, "checks", []) or []
|
|
||||||
apply_available = bool(getattr(preview, "apply_available", False))
|
|
||||||
diff_rows = "".join(_authoring_diff_item(line) for line in diff[:12]) or '<p class="muted padded">Diff пустой</p>'
|
|
||||||
check_rows = "".join(_authoring_check_item(check) for check in checks[:8])
|
|
||||||
apply_form = _authoring_rollback_form(project_id, change_id, rollback_version_id) if apply_available else ""
|
|
||||||
return f"""
|
|
||||||
<div
|
|
||||||
class="authoring-detail"
|
|
||||||
data-html5-authoring-detail
|
|
||||||
data-html5-authoring-change="{escape(change_id)}"
|
|
||||||
>
|
|
||||||
<div class="panel-title">Rollback preview · {'ready' if apply_available else 'blocked'}</div>
|
|
||||||
<article class="authoring-change">
|
|
||||||
<strong>{escape(str(target_name))}</strong>
|
|
||||||
<span>{escape(original_version_id)} -> {escape(rollback_version_id)}</span>
|
|
||||||
<small>{escape(change_id)}</small>
|
|
||||||
</article>
|
|
||||||
{_authoring_detail_summary(diff, checks, apply_available)}
|
|
||||||
<div class="check-list">{check_rows}</div>
|
|
||||||
<div class="diff-list">{diff_rows}</div>
|
|
||||||
{apply_form}
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def render_html5_authoring_rollback_result(project_id: str, result: object | None = None, error: str | None = None) -> str:
|
|
||||||
if result is None and error is None:
|
|
||||||
return '<div class="authoring-result" data-html5-authoring-result></div>'
|
|
||||||
if error:
|
|
||||||
return f"""
|
|
||||||
<div class="authoring-result" data-html5-authoring-result>
|
|
||||||
<div class="panel-title">Rollback apply</div>
|
|
||||||
<p class="muted padded">{escape(error)}</p>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
status = str(getattr(result, "status", "UNKNOWN"))
|
|
||||||
change_id = str(getattr(result, "change_id", ""))
|
|
||||||
rollback_change_id = str(getattr(result, "rollback_change_id", ""))
|
|
||||||
version = getattr(result, "version", None)
|
|
||||||
version_id = str(getattr(version, "version_id", ""))
|
|
||||||
return f"""
|
|
||||||
<div
|
|
||||||
class="authoring-result"
|
|
||||||
data-html5-authoring-result
|
|
||||||
data-html5-authoring-change="{escape(change_id)}"
|
|
||||||
data-html5-version-id="{escape(version_id)}"
|
|
||||||
>
|
|
||||||
<div class="panel-title">Rollback apply</div>
|
|
||||||
<article class="authoring-change">
|
|
||||||
<strong>{escape(status)}</strong>
|
|
||||||
<span>{escape(rollback_change_id)}</span>
|
|
||||||
<small>{escape(version_id)}</small>
|
|
||||||
</article>
|
|
||||||
{_authoring_apply_summary("rollback", status, rollback_change_id or change_id, version_id)}
|
|
||||||
<p class="muted padded">Rollback применен в workspace для проекта {escape(project_id)}.</p>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def render_html5_status(project_id: str, snapshot: SirSnapshot) -> str:
|
def render_html5_status(project_id: str, snapshot: SirSnapshot) -> str:
|
||||||
return (
|
return (
|
||||||
f'<span>project: {escape(project_id)}</span>'
|
f'<span>project: {escape(project_id)}</span>'
|
||||||
@@ -1297,113 +920,6 @@ def _symbol_reference_item(project_id: str, reference: object) -> str:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def _authoring_change_item(change: object) -> str:
|
|
||||||
change_id = str(getattr(change, "change_id", ""))
|
|
||||||
status = str(getattr(change, "status", ""))
|
|
||||||
target = getattr(change, "target", None)
|
|
||||||
target_name = getattr(target, "qualified_name", None) or getattr(target, "name", None) or "target unavailable"
|
|
||||||
approved_by = str(getattr(change, "approved_by", "") or "not approved")
|
|
||||||
task_id = str(getattr(change, "task_id", "") or "no task")
|
|
||||||
added = getattr(change, "added_lines", 0)
|
|
||||||
removed = getattr(change, "removed_lines", 0)
|
|
||||||
production = "production" if bool(getattr(change, "production_applied", False)) else "workspace"
|
|
||||||
project_id = str(getattr(change, "project_id", ""))
|
|
||||||
detail_attrs = (
|
|
||||||
f'hx-get="/html5/projects/{quote(project_id)}/authoring/changes/{quote(change_id, safe="")}" '
|
|
||||||
'hx-target="[data-html5-authoring-detail]" hx-swap="outerHTML"'
|
|
||||||
if change_id and project_id
|
|
||||||
else ""
|
|
||||||
)
|
|
||||||
return f"""
|
|
||||||
<article class="authoring-change" data-html5-authoring-change="{escape(change_id)}" {detail_attrs}>
|
|
||||||
<strong>{escape(str(target_name))}</strong>
|
|
||||||
<span>{escape(status)} · +{escape(str(added))} / -{escape(str(removed))} · {escape(production)}</span>
|
|
||||||
<small>{escape(task_id)} · {escape(approved_by)} · {escape(change_id)}</small>
|
|
||||||
</article>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _authoring_changes_summary(changes: Iterable[object]) -> str:
|
|
||||||
change_list = list(changes)
|
|
||||||
statuses = Counter(str(getattr(change, "status", "") or "UNKNOWN") for change in change_list)
|
|
||||||
production = sum(1 for change in change_list if bool(getattr(change, "production_applied", False)))
|
|
||||||
workspace = len(change_list) - production
|
|
||||||
added = sum(int(getattr(change, "added_lines", 0) or 0) for change in change_list)
|
|
||||||
removed = sum(int(getattr(change, "removed_lines", 0) or 0) for change in change_list)
|
|
||||||
status_text = ", ".join(f"{name}: {count}" for name, count in sorted(statuses.items())) or "no changes"
|
|
||||||
return f"""
|
|
||||||
<p class="authoring-summary" data-html5-authoring-summary>
|
|
||||||
{escape(str(len(change_list)))} changes · {escape(status_text)} · {escape(str(workspace))} workspace · {escape(str(production))} production · +{escape(str(added))} / -{escape(str(removed))}
|
|
||||||
</p>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _authoring_recent_change(changes: Iterable[object]) -> str:
|
|
||||||
change_list = list(changes)
|
|
||||||
if not change_list:
|
|
||||||
return ""
|
|
||||||
latest = change_list[0]
|
|
||||||
change_id = str(getattr(latest, "change_id", ""))
|
|
||||||
status = str(getattr(latest, "status", "") or "UNKNOWN")
|
|
||||||
target = getattr(latest, "target", None)
|
|
||||||
target_name = getattr(target, "qualified_name", None) or getattr(target, "name", None) or "target unavailable"
|
|
||||||
version = getattr(latest, "version", None)
|
|
||||||
version_id = str(getattr(latest, "version_id", "") or getattr(version, "version_id", "") or "version unavailable")
|
|
||||||
approved_by = str(getattr(latest, "approved_by", "") or "not approved")
|
|
||||||
return f"""
|
|
||||||
<article class="authoring-change" data-html5-authoring-recent-change="{escape(change_id)}">
|
|
||||||
<strong>{escape(status)} · {escape(str(target_name))}</strong>
|
|
||||||
<span>{escape(version_id)}</span>
|
|
||||||
<small>{escape(approved_by)} · {escape(change_id)}</small>
|
|
||||||
</article>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _authoring_result_summary(state: str, diff: Iterable[object], checks: Iterable[object]) -> str:
|
|
||||||
diff_list = list(diff)
|
|
||||||
check_list = list(checks)
|
|
||||||
diff_kinds = Counter(str(getattr(line, "kind", "") or "CHANGE") for line in diff_list)
|
|
||||||
check_statuses = Counter(str(getattr(check, "status", "") or "UNKNOWN") for check in check_list)
|
|
||||||
added = sum(1 for line in diff_list if str(getattr(line, "kind", "")) == "ADD")
|
|
||||||
removed = sum(1 for line in diff_list if str(getattr(line, "kind", "")) == "REMOVE")
|
|
||||||
diff_text = ", ".join(f"{name}: {count}" for name, count in sorted(diff_kinds.items())) or "empty diff"
|
|
||||||
check_text = ", ".join(f"{name}: {count}" for name, count in sorted(check_statuses.items())) or "no checks"
|
|
||||||
return f"""
|
|
||||||
<p class="authoring-summary" data-html5-authoring-result-summary>
|
|
||||||
{escape(state)} · {escape(str(len(diff_list)))} diff lines · +{escape(str(added))} / -{escape(str(removed))} · {escape(diff_text)} · {escape(check_text)}
|
|
||||||
</p>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _authoring_detail_summary(diff: Iterable[object], checks: Iterable[object], apply_available: bool) -> str:
|
|
||||||
diff_list = list(diff)
|
|
||||||
check_list = list(checks)
|
|
||||||
diff_kinds = Counter(str(getattr(line, "kind", "") or "CHANGE") for line in diff_list)
|
|
||||||
check_statuses = Counter(str(getattr(check, "status", "") or "UNKNOWN") for check in check_list)
|
|
||||||
added = sum(1 for line in diff_list if str(getattr(line, "kind", "")) == "ADD")
|
|
||||||
removed = sum(1 for line in diff_list if str(getattr(line, "kind", "")) == "REMOVE")
|
|
||||||
diff_text = ", ".join(f"{name}: {count}" for name, count in sorted(diff_kinds.items())) or "empty diff"
|
|
||||||
check_text = ", ".join(f"{name}: {count}" for name, count in sorted(check_statuses.items())) or "no checks"
|
|
||||||
state = "rollback ready" if apply_available else "rollback blocked"
|
|
||||||
return f"""
|
|
||||||
<p class="authoring-summary" data-html5-authoring-detail-summary>
|
|
||||||
{escape(state)} · {escape(str(len(diff_list)))} diff lines · +{escape(str(added))} / -{escape(str(removed))} · {escape(diff_text)} · {escape(check_text)}
|
|
||||||
</p>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _authoring_apply_summary(kind: str, status: str, change_id: str, version_id: str) -> str:
|
|
||||||
return f"""
|
|
||||||
<p
|
|
||||||
class="authoring-summary"
|
|
||||||
data-html5-authoring-apply-summary
|
|
||||||
data-html5-authoring-apply-kind="{escape(kind)}"
|
|
||||||
>
|
|
||||||
{escape(kind)} · {escape(status or "UNKNOWN")} · {escape(change_id or "change unavailable")} · {escape(version_id or "version unavailable")}
|
|
||||||
</p>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _named_node_item(label: str, node: object) -> str:
|
def _named_node_item(label: str, node: object) -> str:
|
||||||
name = getattr(node, "qualified_name", None) or getattr(node, "name", "")
|
name = getattr(node, "qualified_name", None) or getattr(node, "name", "")
|
||||||
kind = getattr(node, "kind", label)
|
kind = getattr(node, "kind", label)
|
||||||
@@ -1759,109 +1275,6 @@ def _flowchart_node_item(project_id: str, node: object, depth: int) -> str:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def _authoring_diff_item(line: object) -> str:
|
|
||||||
kind = str(getattr(line, "kind", ""))
|
|
||||||
text = str(getattr(line, "text", ""))
|
|
||||||
return f"""
|
|
||||||
<article class="diff-item" data-html5-authoring-diff="{escape(kind)}">
|
|
||||||
<span>{escape(kind)}</span>
|
|
||||||
<code>{escape(text)}</code>
|
|
||||||
</article>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _authoring_check_item(check: object) -> str:
|
|
||||||
name = str(getattr(check, "name", "check"))
|
|
||||||
status = str(getattr(check, "status", "UNKNOWN"))
|
|
||||||
message = str(getattr(check, "message", ""))
|
|
||||||
return f"""
|
|
||||||
<article class="check-item" data-html5-authoring-check="{escape(status)}">
|
|
||||||
<strong>{escape(name)}</strong>
|
|
||||||
<span>{escape(status)}</span>
|
|
||||||
<small>{escape(message)}</small>
|
|
||||||
</article>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _authoring_rollback_form(project_id: str, change_id: str, rollback_version_id: str) -> str:
|
|
||||||
return f"""
|
|
||||||
<form
|
|
||||||
class="rollback-form"
|
|
||||||
data-html5-authoring-rollback-form
|
|
||||||
method="post"
|
|
||||||
action="/html5/projects/{quote(project_id)}/authoring/changes/{quote(change_id, safe="")}/apply-rollback"
|
|
||||||
hx-post="/html5/projects/{quote(project_id)}/authoring/changes/{quote(change_id, safe="")}/apply-rollback"
|
|
||||||
hx-target="[data-html5-authoring-result]"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
>
|
|
||||||
<input type="hidden" name="expected_rollback_version_id" value="{escape(rollback_version_id)}" />
|
|
||||||
<input name="approved_by" placeholder="approved_by" required />
|
|
||||||
<input name="task_id" placeholder="task_id" />
|
|
||||||
<input name="session_id" placeholder="session_id" />
|
|
||||||
<input name="approval_note" placeholder="Комментарий" />
|
|
||||||
<button type="submit">Apply rollback</button>
|
|
||||||
</form>
|
|
||||||
{render_html5_authoring_rollback_result(project_id)}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _authoring_apply_change_set_form(project_id: str, payload: dict, next_version_id: str) -> str:
|
|
||||||
return f"""
|
|
||||||
<form
|
|
||||||
class="authoring-preview-form"
|
|
||||||
data-html5-authoring-apply-form
|
|
||||||
method="post"
|
|
||||||
action="/html5/projects/{quote(project_id)}/authoring/apply-change-set"
|
|
||||||
hx-post="/html5/projects/{quote(project_id)}/authoring/apply-change-set"
|
|
||||||
hx-target="[data-html5-authoring-apply-result]"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
>
|
|
||||||
<input type="hidden" name="routine_name" value="{escape(str(payload.get("routine_name") or ""))}" />
|
|
||||||
<input type="hidden" name="source_path" value="{escape(str(payload.get("source_path") or ""))}" />
|
|
||||||
<textarea hidden name="original_text">{escape(str(payload.get("original_text") or ""))}</textarea>
|
|
||||||
<textarea hidden name="proposed_text">{escape(str(payload.get("proposed_text") or ""))}</textarea>
|
|
||||||
<input type="hidden" name="task_id" value="{escape(str(payload.get("task_id") or ""))}" />
|
|
||||||
<input type="hidden" name="session_id" value="{escape(str(payload.get("session_id") or ""))}" />
|
|
||||||
<input type="hidden" name="user_id" value="{escape(str(payload.get("user_id") or ""))}" />
|
|
||||||
<input type="hidden" name="expected_next_version_id" value="{escape(next_version_id)}" />
|
|
||||||
<input name="approved_by" placeholder="approved_by" required />
|
|
||||||
<input name="approval_note" placeholder="Комментарий" />
|
|
||||||
<button type="submit">Apply change-set</button>
|
|
||||||
</form>
|
|
||||||
{render_html5_authoring_apply_result(project_id)}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _metadata_apply_form(project_id: str, payload: dict, next_version_id: str) -> str:
|
|
||||||
return f"""
|
|
||||||
<form
|
|
||||||
class="authoring-preview-form"
|
|
||||||
data-html5-metadata-apply-form
|
|
||||||
method="post"
|
|
||||||
action="/html5/projects/{quote(project_id)}/authoring/apply-metadata-object"
|
|
||||||
hx-post="/html5/projects/{quote(project_id)}/authoring/apply-metadata-object"
|
|
||||||
hx-target="[data-html5-metadata-apply-result]"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
>
|
|
||||||
<input type="hidden" name="object_kind" value="{escape(str(payload.get("object_kind") or ""))}" />
|
|
||||||
<input type="hidden" name="name" value="{escape(str(payload.get("name") or ""))}" />
|
|
||||||
<input type="hidden" name="synonym" value="{escape(str(payload.get("synonym") or ""))}" />
|
|
||||||
<input type="hidden" name="attributes" value="{escape(str(payload.get("_raw_attributes") or ""))}" />
|
|
||||||
<input type="hidden" name="tabular_sections" value="{escape(str(payload.get("_raw_tabular_sections") or ""))}" />
|
|
||||||
<input type="hidden" name="forms" value="{escape(str(payload.get("_raw_forms") or ""))}" />
|
|
||||||
<input type="hidden" name="commands" value="{escape(str(payload.get("_raw_commands") or ""))}" />
|
|
||||||
<input type="hidden" name="task_id" value="{escape(str(payload.get("task_id") or ""))}" />
|
|
||||||
<input type="hidden" name="session_id" value="{escape(str(payload.get("session_id") or ""))}" />
|
|
||||||
<input type="hidden" name="user_id" value="{escape(str(payload.get("user_id") or ""))}" />
|
|
||||||
<input type="hidden" name="expected_next_version_id" value="{escape(next_version_id)}" />
|
|
||||||
<input name="approved_by" placeholder="approved_by" required />
|
|
||||||
<input name="approval_note" placeholder="Комментарий" />
|
|
||||||
<button type="submit">Apply metadata draft</button>
|
|
||||||
</form>
|
|
||||||
{render_html5_metadata_apply_result(project_id)}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _node_source_text(node: object | None) -> str:
|
def _node_source_text(node: object | None) -> str:
|
||||||
if node is None:
|
if node is None:
|
||||||
return "// Модуль не найден в snapshot.\n// HTML5 IDE показывает серверный fallback без клиентского JS."
|
return "// Модуль не найден в snapshot.\n// HTML5 IDE показывает серверный fallback без клиентского JS."
|
||||||
|
|||||||
@@ -0,0 +1,596 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections import Counter
|
||||||
|
from html import escape
|
||||||
|
from typing import Iterable
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
|
||||||
|
def render_html5_authoring_changes(project_id: str, changes: Iterable[object] | None) -> str:
|
||||||
|
if changes is None:
|
||||||
|
return f"""
|
||||||
|
<div
|
||||||
|
class="authoring-panel"
|
||||||
|
data-html5-authoring-changes
|
||||||
|
hx-get="/html5/projects/{quote(project_id)}/authoring/changes"
|
||||||
|
hx-trigger="load"
|
||||||
|
sse-swap="authoring-changes"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
<div class="panel-title">Authoring</div>
|
||||||
|
<p class="muted padded">Сервер загружает историю рабочих изменений.</p>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
change_list = list(changes)
|
||||||
|
if not change_list:
|
||||||
|
body = '<p class="muted padded">Изменений пока нет</p>'
|
||||||
|
else:
|
||||||
|
body = "".join(_authoring_change_item(change) for change in change_list[:12])
|
||||||
|
return f"""
|
||||||
|
<div
|
||||||
|
class="authoring-panel"
|
||||||
|
data-html5-authoring-changes
|
||||||
|
hx-get="/html5/projects/{quote(project_id)}/authoring/changes"
|
||||||
|
sse-swap="authoring-changes"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
<div class="panel-title">Authoring · {len(change_list)}</div>
|
||||||
|
{_authoring_changes_summary(change_list)}
|
||||||
|
{_authoring_recent_change(change_list)}
|
||||||
|
<div class="review-list">{body}</div>
|
||||||
|
{render_html5_authoring_change_detail(project_id, None)}
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def render_html5_authoring_preview(project_id: str, preview: object | None, error: str | None = None) -> str:
|
||||||
|
if preview is None and error is None:
|
||||||
|
return f"""
|
||||||
|
<div class="authoring-preview" data-html5-authoring-preview>
|
||||||
|
<div class="panel-title">Authoring preview</div>
|
||||||
|
<form
|
||||||
|
class="authoring-preview-form"
|
||||||
|
data-html5-authoring-preview-form
|
||||||
|
method="post"
|
||||||
|
action="/html5/projects/{quote(project_id)}/authoring/completion-preview"
|
||||||
|
hx-post="/html5/projects/{quote(project_id)}/authoring/completion-preview"
|
||||||
|
hx-target="[data-html5-authoring-preview-result]"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
<input name="object_name" placeholder="object_name" />
|
||||||
|
<input name="routine_name" placeholder="routine_name" />
|
||||||
|
<input name="cursor_line" placeholder="line" />
|
||||||
|
<input name="intent" value="fill-check" />
|
||||||
|
<input name="user_id" placeholder="user_id" />
|
||||||
|
<textarea name="source_text" placeholder="BSL source"></textarea>
|
||||||
|
<button type="submit">Preview</button>
|
||||||
|
</form>
|
||||||
|
{render_html5_authoring_preview_result(project_id)}
|
||||||
|
<form
|
||||||
|
class="authoring-preview-form"
|
||||||
|
data-html5-authoring-diff-form
|
||||||
|
method="post"
|
||||||
|
action="/html5/projects/{quote(project_id)}/authoring/semantic-diff-preview"
|
||||||
|
hx-post="/html5/projects/{quote(project_id)}/authoring/semantic-diff-preview"
|
||||||
|
hx-target="[data-html5-authoring-diff-result]"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
<input name="routine_name" placeholder="routine_name" />
|
||||||
|
<input name="source_path" placeholder="source_path" />
|
||||||
|
<input name="task_id" placeholder="task_id" />
|
||||||
|
<input name="session_id" placeholder="session_id" />
|
||||||
|
<input name="user_id" placeholder="user_id" />
|
||||||
|
<textarea name="original_text" placeholder="Original BSL"></textarea>
|
||||||
|
<textarea name="proposed_text" placeholder="Proposed BSL"></textarea>
|
||||||
|
<button type="submit">Diff preview</button>
|
||||||
|
</form>
|
||||||
|
{render_html5_authoring_diff_result(project_id)}
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
return render_html5_authoring_preview_result(project_id, preview, error)
|
||||||
|
|
||||||
|
|
||||||
|
def render_html5_authoring_preview_result(project_id: str, preview: object | None = None, error: str | None = None) -> str:
|
||||||
|
if preview is None and error is None:
|
||||||
|
return '<div class="authoring-preview-result" data-html5-authoring-preview-result></div>'
|
||||||
|
if error:
|
||||||
|
return f"""
|
||||||
|
<div class="authoring-preview-result" data-html5-authoring-preview-result>
|
||||||
|
<div class="panel-title">Preview result</div>
|
||||||
|
<p class="muted padded">{escape(error)}</p>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
allowed = bool(getattr(preview, "allowed", False))
|
||||||
|
insert_text = str(getattr(preview, "insert_text", ""))
|
||||||
|
checks = getattr(preview, "checks", []) or []
|
||||||
|
diff = getattr(preview, "semantic_diff", []) or []
|
||||||
|
context = getattr(preview, "context", None)
|
||||||
|
object_node = getattr(context, "object", None)
|
||||||
|
routine_node = getattr(context, "routine", None)
|
||||||
|
object_name = getattr(object_node, "qualified_name", None) or getattr(object_node, "name", None) or "object unavailable"
|
||||||
|
routine_name = getattr(routine_node, "name", None) or "routine unavailable"
|
||||||
|
check_rows = "".join(_authoring_check_item(check) for check in checks[:8])
|
||||||
|
diff_rows = "".join(_authoring_diff_item(line) for line in diff[:8]) or '<p class="muted padded">Diff пустой</p>'
|
||||||
|
return f"""
|
||||||
|
<div class="authoring-preview-result" data-html5-authoring-preview-result data-html5-project-id="{escape(project_id)}">
|
||||||
|
<div class="panel-title">Preview result · {'allowed' if allowed else 'blocked'}</div>
|
||||||
|
<article class="authoring-change">
|
||||||
|
<strong>{escape(str(object_name))}</strong>
|
||||||
|
<span>{escape(str(routine_name))}</span>
|
||||||
|
<small>{escape(insert_text[:180] or "insert text unavailable")}</small>
|
||||||
|
</article>
|
||||||
|
{_authoring_result_summary("allowed" if allowed else "blocked", diff, checks)}
|
||||||
|
<div class="check-list">{check_rows}</div>
|
||||||
|
<div class="diff-list">{diff_rows}</div>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def render_html5_authoring_diff_result(
|
||||||
|
project_id: str,
|
||||||
|
preview: object | None = None,
|
||||||
|
error: str | None = None,
|
||||||
|
request_payload: dict | None = None,
|
||||||
|
) -> str:
|
||||||
|
if preview is None and error is None:
|
||||||
|
return '<div class="authoring-diff-result" data-html5-authoring-diff-result></div>'
|
||||||
|
if error:
|
||||||
|
return f"""
|
||||||
|
<div class="authoring-diff-result" data-html5-authoring-diff-result>
|
||||||
|
<div class="panel-title">Diff preview</div>
|
||||||
|
<p class="muted padded">{escape(error)}</p>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
changed = bool(getattr(preview, "changed", False))
|
||||||
|
added = getattr(preview, "added_lines", 0)
|
||||||
|
removed = getattr(preview, "removed_lines", 0)
|
||||||
|
target = getattr(preview, "target", None)
|
||||||
|
target_name = getattr(target, "qualified_name", None) or getattr(target, "name", None) or "target unavailable"
|
||||||
|
checks = getattr(preview, "checks", []) or []
|
||||||
|
diff = getattr(preview, "semantic_diff", []) or []
|
||||||
|
version_preview = getattr(preview, "version_preview", None)
|
||||||
|
next_version_id = str(getattr(version_preview, "next_version_id", ""))
|
||||||
|
check_rows = "".join(_authoring_check_item(check) for check in checks[:8])
|
||||||
|
diff_rows = "".join(_authoring_diff_item(line) for line in diff[:12]) or '<p class="muted padded">Diff пустой</p>'
|
||||||
|
apply_form = (
|
||||||
|
_authoring_apply_change_set_form(project_id, request_payload or {}, next_version_id)
|
||||||
|
if changed and next_version_id
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
return f"""
|
||||||
|
<div class="authoring-diff-result" data-html5-authoring-diff-result data-html5-project-id="{escape(project_id)}">
|
||||||
|
<div class="panel-title">Diff preview · {'changed' if changed else 'unchanged'}</div>
|
||||||
|
<article class="authoring-change">
|
||||||
|
<strong>{escape(str(target_name))}</strong>
|
||||||
|
<span>+{escape(str(added))} / -{escape(str(removed))}</span>
|
||||||
|
<small>{escape(next_version_id or "version preview unavailable")}</small>
|
||||||
|
</article>
|
||||||
|
{_authoring_result_summary("changed" if changed else "unchanged", diff, checks)}
|
||||||
|
<div class="check-list">{check_rows}</div>
|
||||||
|
<div class="diff-list">{diff_rows}</div>
|
||||||
|
{apply_form}
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def render_html5_authoring_apply_result(project_id: str, result: object | None = None, error: str | None = None) -> str:
|
||||||
|
if result is None and error is None:
|
||||||
|
return '<div class="authoring-apply-result" data-html5-authoring-apply-result></div>'
|
||||||
|
if error:
|
||||||
|
return f"""
|
||||||
|
<div class="authoring-apply-result" data-html5-authoring-apply-result>
|
||||||
|
<div class="panel-title">Apply change-set</div>
|
||||||
|
<p class="muted padded">{escape(error)}</p>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
status = str(getattr(result, "status", "UNKNOWN"))
|
||||||
|
change_id = str(getattr(result, "change_id", ""))
|
||||||
|
version = getattr(result, "version", None)
|
||||||
|
version_id = str(getattr(version, "version_id", ""))
|
||||||
|
return f"""
|
||||||
|
<div
|
||||||
|
class="authoring-apply-result"
|
||||||
|
data-html5-authoring-apply-result
|
||||||
|
data-html5-authoring-change="{escape(change_id)}"
|
||||||
|
data-html5-version-id="{escape(version_id)}"
|
||||||
|
>
|
||||||
|
<div class="panel-title">Apply change-set</div>
|
||||||
|
<article class="authoring-change">
|
||||||
|
<strong>{escape(status)}</strong>
|
||||||
|
<span>{escape(change_id)}</span>
|
||||||
|
<small>{escape(version_id)}</small>
|
||||||
|
</article>
|
||||||
|
{_authoring_apply_summary("change-set", status, change_id, version_id)}
|
||||||
|
<p class="muted padded">Change-set применен в workspace для проекта {escape(project_id)}.</p>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def render_html5_metadata_authoring(project_id: str) -> str:
|
||||||
|
return f"""
|
||||||
|
<div class="authoring-preview" data-html5-metadata-authoring>
|
||||||
|
<div class="panel-title">Metadata draft</div>
|
||||||
|
<form
|
||||||
|
class="authoring-preview-form"
|
||||||
|
data-html5-metadata-preview-form
|
||||||
|
method="post"
|
||||||
|
action="/html5/projects/{quote(project_id)}/authoring/metadata-object-preview"
|
||||||
|
hx-post="/html5/projects/{quote(project_id)}/authoring/metadata-object-preview"
|
||||||
|
hx-target="[data-html5-metadata-preview-result]"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
<input name="object_kind" value="DOCUMENT" />
|
||||||
|
<input name="name" placeholder="Имя объекта" required />
|
||||||
|
<input name="synonym" placeholder="Синоним" />
|
||||||
|
<input name="attributes" placeholder="Реквизиты: Имя:Тип, ..." />
|
||||||
|
<input name="tabular_sections" placeholder="ТЧ: Товары[Номенклатура:Строка;Количество:Число]" />
|
||||||
|
<input name="forms" placeholder="Формы через запятую" />
|
||||||
|
<input name="commands" placeholder="Команды: Имя:Обработчик" />
|
||||||
|
<input name="task_id" placeholder="task_id" />
|
||||||
|
<input name="session_id" placeholder="session_id" />
|
||||||
|
<input name="user_id" placeholder="user_id" />
|
||||||
|
<button type="submit">Metadata preview</button>
|
||||||
|
</form>
|
||||||
|
{render_html5_metadata_preview_result(project_id)}
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def render_html5_metadata_preview_result(
|
||||||
|
project_id: str,
|
||||||
|
preview: object | None = None,
|
||||||
|
error: str | None = None,
|
||||||
|
request_payload: dict | None = None,
|
||||||
|
) -> str:
|
||||||
|
if preview is None and error is None:
|
||||||
|
return '<div class="metadata-preview-result" data-html5-metadata-preview-result></div>'
|
||||||
|
if error:
|
||||||
|
return f"""
|
||||||
|
<div class="metadata-preview-result" data-html5-metadata-preview-result>
|
||||||
|
<div class="panel-title">Metadata preview</div>
|
||||||
|
<p class="muted padded">{escape(error)}</p>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
changed = bool(getattr(preview, "changed", False))
|
||||||
|
added = getattr(preview, "added_lines", 0)
|
||||||
|
target = getattr(preview, "target", None)
|
||||||
|
target_name = getattr(target, "qualified_name", None) or getattr(target, "name", None) or "target unavailable"
|
||||||
|
checks = getattr(preview, "checks", []) or []
|
||||||
|
diff = getattr(preview, "semantic_diff", []) or []
|
||||||
|
version_preview = getattr(preview, "version_preview", None)
|
||||||
|
next_version_id = str(getattr(version_preview, "next_version_id", ""))
|
||||||
|
check_rows = "".join(_authoring_check_item(check) for check in checks[:8])
|
||||||
|
diff_rows = "".join(_authoring_diff_item(line) for line in diff[:12]) or '<p class="muted padded">Diff пустой</p>'
|
||||||
|
apply_form = (
|
||||||
|
_metadata_apply_form(project_id, request_payload or {}, next_version_id)
|
||||||
|
if changed and next_version_id
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
return f"""
|
||||||
|
<div class="metadata-preview-result" data-html5-metadata-preview-result data-html5-project-id="{escape(project_id)}">
|
||||||
|
<div class="panel-title">Metadata preview · {'changed' if changed else 'unchanged'}</div>
|
||||||
|
<article class="authoring-change">
|
||||||
|
<strong>{escape(str(target_name))}</strong>
|
||||||
|
<span>+{escape(str(added))} / -0</span>
|
||||||
|
<small>{escape(next_version_id or "version preview unavailable")}</small>
|
||||||
|
</article>
|
||||||
|
{_authoring_result_summary("changed" if changed else "unchanged", diff, checks)}
|
||||||
|
<div class="check-list">{check_rows}</div>
|
||||||
|
<div class="diff-list">{diff_rows}</div>
|
||||||
|
{apply_form}
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def render_html5_metadata_apply_result(project_id: str, result: object | None = None, error: str | None = None) -> str:
|
||||||
|
if result is None and error is None:
|
||||||
|
return '<div class="metadata-apply-result" data-html5-metadata-apply-result></div>'
|
||||||
|
if error:
|
||||||
|
return f"""
|
||||||
|
<div class="metadata-apply-result" data-html5-metadata-apply-result>
|
||||||
|
<div class="panel-title">Metadata apply</div>
|
||||||
|
<p class="muted padded">{escape(error)}</p>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
status = str(getattr(result, "status", "UNKNOWN"))
|
||||||
|
change_id = str(getattr(result, "change_id", ""))
|
||||||
|
version = getattr(result, "version", None)
|
||||||
|
version_id = str(getattr(version, "version_id", ""))
|
||||||
|
return f"""
|
||||||
|
<div
|
||||||
|
class="metadata-apply-result"
|
||||||
|
data-html5-metadata-apply-result
|
||||||
|
data-html5-authoring-change="{escape(change_id)}"
|
||||||
|
data-html5-version-id="{escape(version_id)}"
|
||||||
|
>
|
||||||
|
<div class="panel-title">Metadata apply</div>
|
||||||
|
<article class="authoring-change">
|
||||||
|
<strong>{escape(status)}</strong>
|
||||||
|
<span>{escape(change_id)}</span>
|
||||||
|
<small>{escape(version_id)}</small>
|
||||||
|
</article>
|
||||||
|
{_authoring_apply_summary("metadata", status, change_id, version_id)}
|
||||||
|
<p class="muted padded">Metadata draft применен в workspace для проекта {escape(project_id)}.</p>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def render_html5_authoring_change_detail(project_id: str, preview: object | None) -> str:
|
||||||
|
if preview is None:
|
||||||
|
return f"""
|
||||||
|
<div class="authoring-detail" data-html5-authoring-detail>
|
||||||
|
<div class="panel-title">Rollback preview</div>
|
||||||
|
<p class="muted padded">Выберите изменение, чтобы сервер рассчитал rollback diff для проекта {escape(project_id)}.</p>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
change_id = str(getattr(preview, "change_id", ""))
|
||||||
|
original_version_id = str(getattr(preview, "original_version_id", ""))
|
||||||
|
rollback_version_id = str(getattr(preview, "rollback_version_id", ""))
|
||||||
|
target = getattr(preview, "target", None)
|
||||||
|
target_name = getattr(target, "qualified_name", None) or getattr(target, "name", None) or "target unavailable"
|
||||||
|
diff = getattr(preview, "semantic_diff", []) or []
|
||||||
|
checks = getattr(preview, "checks", []) or []
|
||||||
|
apply_available = bool(getattr(preview, "apply_available", False))
|
||||||
|
diff_rows = "".join(_authoring_diff_item(line) for line in diff[:12]) or '<p class="muted padded">Diff пустой</p>'
|
||||||
|
check_rows = "".join(_authoring_check_item(check) for check in checks[:8])
|
||||||
|
apply_form = _authoring_rollback_form(project_id, change_id, rollback_version_id) if apply_available else ""
|
||||||
|
return f"""
|
||||||
|
<div
|
||||||
|
class="authoring-detail"
|
||||||
|
data-html5-authoring-detail
|
||||||
|
data-html5-authoring-change="{escape(change_id)}"
|
||||||
|
>
|
||||||
|
<div class="panel-title">Rollback preview · {'ready' if apply_available else 'blocked'}</div>
|
||||||
|
<article class="authoring-change">
|
||||||
|
<strong>{escape(str(target_name))}</strong>
|
||||||
|
<span>{escape(original_version_id)} -> {escape(rollback_version_id)}</span>
|
||||||
|
<small>{escape(change_id)}</small>
|
||||||
|
</article>
|
||||||
|
{_authoring_detail_summary(diff, checks, apply_available)}
|
||||||
|
<div class="check-list">{check_rows}</div>
|
||||||
|
<div class="diff-list">{diff_rows}</div>
|
||||||
|
{apply_form}
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def render_html5_authoring_rollback_result(project_id: str, result: object | None = None, error: str | None = None) -> str:
|
||||||
|
if result is None and error is None:
|
||||||
|
return '<div class="authoring-result" data-html5-authoring-result></div>'
|
||||||
|
if error:
|
||||||
|
return f"""
|
||||||
|
<div class="authoring-result" data-html5-authoring-result>
|
||||||
|
<div class="panel-title">Rollback apply</div>
|
||||||
|
<p class="muted padded">{escape(error)}</p>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
status = str(getattr(result, "status", "UNKNOWN"))
|
||||||
|
change_id = str(getattr(result, "change_id", ""))
|
||||||
|
rollback_change_id = str(getattr(result, "rollback_change_id", ""))
|
||||||
|
version = getattr(result, "version", None)
|
||||||
|
version_id = str(getattr(version, "version_id", ""))
|
||||||
|
return f"""
|
||||||
|
<div
|
||||||
|
class="authoring-result"
|
||||||
|
data-html5-authoring-result
|
||||||
|
data-html5-authoring-change="{escape(change_id)}"
|
||||||
|
data-html5-version-id="{escape(version_id)}"
|
||||||
|
>
|
||||||
|
<div class="panel-title">Rollback apply</div>
|
||||||
|
<article class="authoring-change">
|
||||||
|
<strong>{escape(status)}</strong>
|
||||||
|
<span>{escape(rollback_change_id)}</span>
|
||||||
|
<small>{escape(version_id)}</small>
|
||||||
|
</article>
|
||||||
|
{_authoring_apply_summary("rollback", status, rollback_change_id or change_id, version_id)}
|
||||||
|
<p class="muted padded">Rollback применен в workspace для проекта {escape(project_id)}.</p>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _authoring_change_item(change: object) -> str:
|
||||||
|
change_id = str(getattr(change, "change_id", ""))
|
||||||
|
status = str(getattr(change, "status", ""))
|
||||||
|
target = getattr(change, "target", None)
|
||||||
|
target_name = getattr(target, "qualified_name", None) or getattr(target, "name", None) or "target unavailable"
|
||||||
|
approved_by = str(getattr(change, "approved_by", "") or "not approved")
|
||||||
|
task_id = str(getattr(change, "task_id", "") or "no task")
|
||||||
|
added = getattr(change, "added_lines", 0)
|
||||||
|
removed = getattr(change, "removed_lines", 0)
|
||||||
|
production = "production" if bool(getattr(change, "production_applied", False)) else "workspace"
|
||||||
|
project_id = str(getattr(change, "project_id", ""))
|
||||||
|
detail_attrs = (
|
||||||
|
f'hx-get="/html5/projects/{quote(project_id)}/authoring/changes/{quote(change_id, safe="")}" '
|
||||||
|
'hx-target="[data-html5-authoring-detail]" hx-swap="outerHTML"'
|
||||||
|
if change_id and project_id
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
return f"""
|
||||||
|
<article class="authoring-change" data-html5-authoring-change="{escape(change_id)}" {detail_attrs}>
|
||||||
|
<strong>{escape(str(target_name))}</strong>
|
||||||
|
<span>{escape(status)} · +{escape(str(added))} / -{escape(str(removed))} · {escape(production)}</span>
|
||||||
|
<small>{escape(task_id)} · {escape(approved_by)} · {escape(change_id)}</small>
|
||||||
|
</article>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _authoring_changes_summary(changes: Iterable[object]) -> str:
|
||||||
|
change_list = list(changes)
|
||||||
|
statuses = Counter(str(getattr(change, "status", "") or "UNKNOWN") for change in change_list)
|
||||||
|
production = sum(1 for change in change_list if bool(getattr(change, "production_applied", False)))
|
||||||
|
workspace = len(change_list) - production
|
||||||
|
added = sum(int(getattr(change, "added_lines", 0) or 0) for change in change_list)
|
||||||
|
removed = sum(int(getattr(change, "removed_lines", 0) or 0) for change in change_list)
|
||||||
|
status_text = ", ".join(f"{name}: {count}" for name, count in sorted(statuses.items())) or "no changes"
|
||||||
|
return f"""
|
||||||
|
<p class="authoring-summary" data-html5-authoring-summary>
|
||||||
|
{escape(str(len(change_list)))} changes · {escape(status_text)} · {escape(str(workspace))} workspace · {escape(str(production))} production · +{escape(str(added))} / -{escape(str(removed))}
|
||||||
|
</p>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _authoring_recent_change(changes: Iterable[object]) -> str:
|
||||||
|
change_list = list(changes)
|
||||||
|
if not change_list:
|
||||||
|
return ""
|
||||||
|
latest = change_list[0]
|
||||||
|
change_id = str(getattr(latest, "change_id", ""))
|
||||||
|
status = str(getattr(latest, "status", "") or "UNKNOWN")
|
||||||
|
target = getattr(latest, "target", None)
|
||||||
|
target_name = getattr(target, "qualified_name", None) or getattr(target, "name", None) or "target unavailable"
|
||||||
|
version = getattr(latest, "version", None)
|
||||||
|
version_id = str(getattr(latest, "version_id", "") or getattr(version, "version_id", "") or "version unavailable")
|
||||||
|
approved_by = str(getattr(latest, "approved_by", "") or "not approved")
|
||||||
|
return f"""
|
||||||
|
<article class="authoring-change" data-html5-authoring-recent-change="{escape(change_id)}">
|
||||||
|
<strong>{escape(status)} · {escape(str(target_name))}</strong>
|
||||||
|
<span>{escape(version_id)}</span>
|
||||||
|
<small>{escape(approved_by)} · {escape(change_id)}</small>
|
||||||
|
</article>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _authoring_result_summary(state: str, diff: Iterable[object], checks: Iterable[object]) -> str:
|
||||||
|
diff_list = list(diff)
|
||||||
|
check_list = list(checks)
|
||||||
|
diff_kinds = Counter(str(getattr(line, "kind", "") or "CHANGE") for line in diff_list)
|
||||||
|
check_statuses = Counter(str(getattr(check, "status", "") or "UNKNOWN") for check in check_list)
|
||||||
|
added = sum(1 for line in diff_list if str(getattr(line, "kind", "")) == "ADD")
|
||||||
|
removed = sum(1 for line in diff_list if str(getattr(line, "kind", "")) == "REMOVE")
|
||||||
|
diff_text = ", ".join(f"{name}: {count}" for name, count in sorted(diff_kinds.items())) or "empty diff"
|
||||||
|
check_text = ", ".join(f"{name}: {count}" for name, count in sorted(check_statuses.items())) or "no checks"
|
||||||
|
return f"""
|
||||||
|
<p class="authoring-summary" data-html5-authoring-result-summary>
|
||||||
|
{escape(state)} · {escape(str(len(diff_list)))} diff lines · +{escape(str(added))} / -{escape(str(removed))} · {escape(diff_text)} · {escape(check_text)}
|
||||||
|
</p>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _authoring_detail_summary(diff: Iterable[object], checks: Iterable[object], apply_available: bool) -> str:
|
||||||
|
diff_list = list(diff)
|
||||||
|
check_list = list(checks)
|
||||||
|
diff_kinds = Counter(str(getattr(line, "kind", "") or "CHANGE") for line in diff_list)
|
||||||
|
check_statuses = Counter(str(getattr(check, "status", "") or "UNKNOWN") for check in check_list)
|
||||||
|
added = sum(1 for line in diff_list if str(getattr(line, "kind", "")) == "ADD")
|
||||||
|
removed = sum(1 for line in diff_list if str(getattr(line, "kind", "")) == "REMOVE")
|
||||||
|
diff_text = ", ".join(f"{name}: {count}" for name, count in sorted(diff_kinds.items())) or "empty diff"
|
||||||
|
check_text = ", ".join(f"{name}: {count}" for name, count in sorted(check_statuses.items())) or "no checks"
|
||||||
|
state = "rollback ready" if apply_available else "rollback blocked"
|
||||||
|
return f"""
|
||||||
|
<p class="authoring-summary" data-html5-authoring-detail-summary>
|
||||||
|
{escape(state)} · {escape(str(len(diff_list)))} diff lines · +{escape(str(added))} / -{escape(str(removed))} · {escape(diff_text)} · {escape(check_text)}
|
||||||
|
</p>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _authoring_apply_summary(kind: str, status: str, change_id: str, version_id: str) -> str:
|
||||||
|
return f"""
|
||||||
|
<p
|
||||||
|
class="authoring-summary"
|
||||||
|
data-html5-authoring-apply-summary
|
||||||
|
data-html5-authoring-apply-kind="{escape(kind)}"
|
||||||
|
>
|
||||||
|
{escape(kind)} · {escape(status or "UNKNOWN")} · {escape(change_id or "change unavailable")} · {escape(version_id or "version unavailable")}
|
||||||
|
</p>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _authoring_diff_item(line: object) -> str:
|
||||||
|
kind = str(getattr(line, "kind", ""))
|
||||||
|
text = str(getattr(line, "text", ""))
|
||||||
|
return f"""
|
||||||
|
<article class="diff-item" data-html5-authoring-diff="{escape(kind)}">
|
||||||
|
<span>{escape(kind)}</span>
|
||||||
|
<code>{escape(text)}</code>
|
||||||
|
</article>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _authoring_check_item(check: object) -> str:
|
||||||
|
name = str(getattr(check, "name", "check"))
|
||||||
|
status = str(getattr(check, "status", "UNKNOWN"))
|
||||||
|
message = str(getattr(check, "message", ""))
|
||||||
|
return f"""
|
||||||
|
<article class="check-item" data-html5-authoring-check="{escape(status)}">
|
||||||
|
<strong>{escape(name)}</strong>
|
||||||
|
<span>{escape(status)}</span>
|
||||||
|
<small>{escape(message)}</small>
|
||||||
|
</article>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _authoring_rollback_form(project_id: str, change_id: str, rollback_version_id: str) -> str:
|
||||||
|
return f"""
|
||||||
|
<form
|
||||||
|
class="rollback-form"
|
||||||
|
data-html5-authoring-rollback-form
|
||||||
|
method="post"
|
||||||
|
action="/html5/projects/{quote(project_id)}/authoring/changes/{quote(change_id, safe="")}/apply-rollback"
|
||||||
|
hx-post="/html5/projects/{quote(project_id)}/authoring/changes/{quote(change_id, safe="")}/apply-rollback"
|
||||||
|
hx-target="[data-html5-authoring-result]"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
<input type="hidden" name="expected_rollback_version_id" value="{escape(rollback_version_id)}" />
|
||||||
|
<input name="approved_by" placeholder="approved_by" required />
|
||||||
|
<input name="task_id" placeholder="task_id" />
|
||||||
|
<input name="session_id" placeholder="session_id" />
|
||||||
|
<input name="approval_note" placeholder="Комментарий" />
|
||||||
|
<button type="submit">Apply rollback</button>
|
||||||
|
</form>
|
||||||
|
{render_html5_authoring_rollback_result(project_id)}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _authoring_apply_change_set_form(project_id: str, payload: dict, next_version_id: str) -> str:
|
||||||
|
return f"""
|
||||||
|
<form
|
||||||
|
class="authoring-preview-form"
|
||||||
|
data-html5-authoring-apply-form
|
||||||
|
method="post"
|
||||||
|
action="/html5/projects/{quote(project_id)}/authoring/apply-change-set"
|
||||||
|
hx-post="/html5/projects/{quote(project_id)}/authoring/apply-change-set"
|
||||||
|
hx-target="[data-html5-authoring-apply-result]"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
<input type="hidden" name="routine_name" value="{escape(str(payload.get("routine_name") or ""))}" />
|
||||||
|
<input type="hidden" name="source_path" value="{escape(str(payload.get("source_path") or ""))}" />
|
||||||
|
<textarea hidden name="original_text">{escape(str(payload.get("original_text") or ""))}</textarea>
|
||||||
|
<textarea hidden name="proposed_text">{escape(str(payload.get("proposed_text") or ""))}</textarea>
|
||||||
|
<input type="hidden" name="task_id" value="{escape(str(payload.get("task_id") or ""))}" />
|
||||||
|
<input type="hidden" name="session_id" value="{escape(str(payload.get("session_id") or ""))}" />
|
||||||
|
<input type="hidden" name="user_id" value="{escape(str(payload.get("user_id") or ""))}" />
|
||||||
|
<input type="hidden" name="expected_next_version_id" value="{escape(next_version_id)}" />
|
||||||
|
<input name="approved_by" placeholder="approved_by" required />
|
||||||
|
<input name="approval_note" placeholder="Комментарий" />
|
||||||
|
<button type="submit">Apply change-set</button>
|
||||||
|
</form>
|
||||||
|
{render_html5_authoring_apply_result(project_id)}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _metadata_apply_form(project_id: str, payload: dict, next_version_id: str) -> str:
|
||||||
|
return f"""
|
||||||
|
<form
|
||||||
|
class="authoring-preview-form"
|
||||||
|
data-html5-metadata-apply-form
|
||||||
|
method="post"
|
||||||
|
action="/html5/projects/{quote(project_id)}/authoring/apply-metadata-object"
|
||||||
|
hx-post="/html5/projects/{quote(project_id)}/authoring/apply-metadata-object"
|
||||||
|
hx-target="[data-html5-metadata-apply-result]"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
>
|
||||||
|
<input type="hidden" name="object_kind" value="{escape(str(payload.get("object_kind") or ""))}" />
|
||||||
|
<input type="hidden" name="name" value="{escape(str(payload.get("name") or ""))}" />
|
||||||
|
<input type="hidden" name="synonym" value="{escape(str(payload.get("synonym") or ""))}" />
|
||||||
|
<input type="hidden" name="attributes" value="{escape(str(payload.get("_raw_attributes") or ""))}" />
|
||||||
|
<input type="hidden" name="tabular_sections" value="{escape(str(payload.get("_raw_tabular_sections") or ""))}" />
|
||||||
|
<input type="hidden" name="forms" value="{escape(str(payload.get("_raw_forms") or ""))}" />
|
||||||
|
<input type="hidden" name="commands" value="{escape(str(payload.get("_raw_commands") or ""))}" />
|
||||||
|
<input type="hidden" name="task_id" value="{escape(str(payload.get("task_id") or ""))}" />
|
||||||
|
<input type="hidden" name="session_id" value="{escape(str(payload.get("session_id") or ""))}" />
|
||||||
|
<input type="hidden" name="user_id" value="{escape(str(payload.get("user_id") or ""))}" />
|
||||||
|
<input type="hidden" name="expected_next_version_id" value="{escape(next_version_id)}" />
|
||||||
|
<input name="approved_by" placeholder="approved_by" required />
|
||||||
|
<input name="approval_note" placeholder="Комментарий" />
|
||||||
|
<button type="submit">Apply metadata draft</button>
|
||||||
|
</form>
|
||||||
|
{render_html5_metadata_apply_result(project_id)}
|
||||||
|
"""
|
||||||
@@ -38,17 +38,9 @@ from neo4j import AsyncGraphDatabase
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from api_server.html5 import (
|
from api_server.html5 import (
|
||||||
render_html5_authoring_apply_result,
|
|
||||||
render_html5_authoring_changes,
|
|
||||||
render_html5_authoring_change_detail,
|
|
||||||
render_html5_authoring_diff_result,
|
|
||||||
render_html5_authoring_preview_result,
|
|
||||||
render_html5_authoring_rollback_result,
|
|
||||||
render_html5_editor,
|
render_html5_editor,
|
||||||
render_html5_flowchart,
|
render_html5_flowchart,
|
||||||
render_html5_index,
|
render_html5_index,
|
||||||
render_html5_metadata_apply_result,
|
|
||||||
render_html5_metadata_preview_result,
|
|
||||||
render_html5_object_context,
|
render_html5_object_context,
|
||||||
render_html5_object_report,
|
render_html5_object_report,
|
||||||
render_html5_project_rows,
|
render_html5_project_rows,
|
||||||
@@ -59,6 +51,16 @@ from api_server.html5 import (
|
|||||||
render_html5_status,
|
render_html5_status,
|
||||||
render_html5_symbols,
|
render_html5_symbols,
|
||||||
)
|
)
|
||||||
|
from api_server.html5_authoring import (
|
||||||
|
render_html5_authoring_apply_result,
|
||||||
|
render_html5_authoring_changes,
|
||||||
|
render_html5_authoring_change_detail,
|
||||||
|
render_html5_authoring_diff_result,
|
||||||
|
render_html5_authoring_preview_result,
|
||||||
|
render_html5_authoring_rollback_result,
|
||||||
|
render_html5_metadata_apply_result,
|
||||||
|
render_html5_metadata_preview_result,
|
||||||
|
)
|
||||||
from api_server.html5_operations import (
|
from api_server.html5_operations import (
|
||||||
render_html5_operation_detail,
|
render_html5_operation_detail,
|
||||||
render_html5_operation_rows,
|
render_html5_operation_rows,
|
||||||
|
|||||||
Reference in New Issue
Block a user