Extract managed form elements from XML
CI / python (push) Has been cancelled
CI / rust (push) Has been cancelled

This commit is contained in:
2026-05-21 06:10:05 +03:00
parent 5bd188fe6f
commit af900e4e34
9 changed files with 186 additions and 46 deletions
@@ -156,8 +156,7 @@ def html5_setup_job(
async def html5_setup_reindex(
*,
project_id: str,
reindex: Callable[[str], Any],
setup_response: Callable[[str], object],
start_reindex_job: Callable[[str], Any],
) -> str:
await reindex(project_id)
return render_html5_setup_summary(project_id, setup_response(project_id))
job = await start_reindex_job(project_id)
return render_html5_import_job(project_id, job)
+69 -30
View File
@@ -488,7 +488,7 @@ def _load_persisted_state() -> None:
_collaboration.ownership[_collaboration._ownership_key(ownership)] = ownership
for payload in _storage.list_documents("operations_jobs"):
job = OperationJob.model_validate(payload)
if job.kind == "SERVER_IMPORT" and job.status in {OperationJobStatus.QUEUED, OperationJobStatus.RUNNING}:
if job.kind in {"SERVER_IMPORT", "REINDEX"} and job.status in {OperationJobStatus.QUEUED, OperationJobStatus.RUNNING}:
job.status = OperationJobStatus.FAILED
job.error = "Операция была прервана перезапуском сервера."
from datetime import datetime, timezone
@@ -1738,8 +1738,7 @@ async def html5_project_setup_reindex(project_id: str) -> Response:
return _html5_response(
await _html5_setup_reindex(
project_id=project_id,
reindex=reindex_project,
setup_response=_project_setup_response,
start_reindex_job=start_project_reindex_job,
)
)
@@ -2100,6 +2099,33 @@ async def check_project_import(
@app.post("/projects/{project_id}/reindex", response_model=ImportSummary)
async def reindex_project(project_id: str) -> ImportSummary:
return _execute_reindex_project(project_id)
@app.post("/projects/{project_id}/reindex/jobs", response_model=OperationJob)
async def start_project_reindex_job(project_id: str) -> OperationJob:
active_job = _active_project_operation_job(project_id, {"REINDEX"})
if active_job is not None:
return active_job
job = OperationJob(
job_id=f"reindex-{uuid4()}",
kind="REINDEX",
status=OperationJobStatus.QUEUED,
payload={
"project_id": project_id,
"stage": "queued",
"message": "Переиндексация поставлена в очередь.",
"logs": ["Переиндексация поставлена в очередь."],
"started_at": None,
"finished_at": None,
},
)
_persist_job(_operations.enqueue(job))
threading.Thread(target=_run_reindex_job, args=(job.job_id, project_id), daemon=True).start()
return _operations.jobs[job.job_id]
def _execute_reindex_project(project_id: str) -> ImportSummary:
state = _project_setup.get(project_id, {})
current_source = ImportSourceKind(state.get("current_source") or ImportSourceKind.XML_DUMP.value)
last_import = state.get("last_import") or {}
@@ -2128,6 +2154,7 @@ async def reindex_project(project_id: str) -> ImportSummary:
state["last_import"] = summary.model_dump(mode="json")
_append_import_history(project_id, summary)
state["status"] = ProjectSetupStatus.INDEXED.value
_storage.write_document("project_settings", project_id, state)
return summary
@@ -7850,32 +7877,11 @@ def _form_semantics_response(item) -> FormSemanticsResponse:
def _form_semantics_for_lineages(snapshot: SirSnapshot, form_lineages: set[str]) -> list[FormSemanticsResponse]:
if not form_lineages:
return []
nodes = {node.lineage_id: node for node in snapshot.nodes}
forms = {
lineage_id: FormSemanticsResponse(form=_named_node(node), commands=[], elements=[], command_handlers={})
for lineage_id, node in nodes.items()
if lineage_id in form_lineages and node.kind == NodeKind.FORM
}
command_to_form: dict[str, FormSemanticsResponse] = {}
for edge in snapshot.edges:
form = forms.get(edge.source_lineage)
target = nodes.get(edge.target_lineage)
if form is None or target is None:
continue
if edge.kind == EdgeKind.HAS_COMMAND:
named_target = _named_node(target)
form.commands.append(named_target)
command_to_form[target.lineage_id] = form
elif edge.kind == EdgeKind.HAS_ELEMENT:
form.elements.append(_named_node(target))
for edge in snapshot.edges:
if edge.kind != EdgeKind.HANDLES:
continue
form = command_to_form.get(edge.source_lineage)
handler = nodes.get(edge.target_lineage)
if form is not None and handler is not None:
form.command_handlers[edge.source_lineage] = _named_node(handler)
return sorted(forms.values(), key=lambda item: item.form.qualified_name)
return [
_form_semantics_response(item)
for item in form_semantics(snapshot)
if item.form.lineage_id in form_lineages
]
def _persist_job(job: OperationJob) -> OperationJob:
@@ -7916,6 +7922,35 @@ def _run_server_import_job(job_id: str, project_id: str, request: ImportRequest)
)
def _run_reindex_job(job_id: str, project_id: str) -> None:
if job_id not in _operations.jobs:
return
_update_import_job_progress(job_id, "Переиндексация запущена.", stage="running", status=OperationJobStatus.RUNNING)
try:
_update_import_job_progress(job_id, "Пересборка snapshot и UI-семантики.", stage="indexing")
summary = _execute_reindex_project(project_id)
except Exception as error:
_update_import_job_progress(
job_id,
f"Переиндексация завершилась ошибкой: {error}",
stage="failed",
status=OperationJobStatus.FAILED,
error=str(error),
finished_at=_current_timestamp(),
)
return
status = OperationJobStatus.SUCCEEDED if summary.applied is not False and not summary.errors else OperationJobStatus.FAILED
_update_import_job_progress(
job_id,
"Переиндексация завершена." if status == OperationJobStatus.SUCCEEDED else "Переиндексация остановлена. Проверьте ошибки.",
stage="done" if status == OperationJobStatus.SUCCEEDED else "failed",
status=status,
result={"import_summary": summary.model_dump(mode="json")},
error="; ".join(summary.errors) if summary.errors else None,
finished_at=_current_timestamp(),
)
def _create_agent_server_import_job(agent_job: AgentImportJob, request: ImportRequest) -> OperationJob:
operation_job = OperationJob(
job_id=f"server-import-{uuid4()}",
@@ -8091,11 +8126,15 @@ def _run_agent_uploaded_zip_job(job_id: str, upload_path_raw: str, import_root_r
def _active_server_import_job(project_id: str) -> OperationJob | None:
return _active_project_operation_job(project_id, {"SERVER_IMPORT"})
def _active_project_operation_job(project_id: str, kinds: set[str]) -> OperationJob | None:
active_statuses = {OperationJobStatus.QUEUED, OperationJobStatus.RUNNING}
candidates = [
job
for job in _operations.jobs.values()
if job.kind == "SERVER_IMPORT"
if job.kind in kinds
and job.payload.get("project_id") == project_id
and job.status in active_statuses
]