Extract managed form elements from XML
This commit is contained in:
@@ -3338,15 +3338,33 @@ function buildIdeFormElements(form: ProjectWorkspaceData["forms"][number] | unde
|
|||||||
return explicitElements.map((element, index) => ({
|
return explicitElements.map((element, index) => ({
|
||||||
id: element.lineage_id || `element.${index}`,
|
id: element.lineage_id || `element.${index}`,
|
||||||
name: element.name,
|
name: element.name,
|
||||||
caption: element.name,
|
caption: formElementString(element.attributes, ["caption", "title", "synonym"]) ?? element.name,
|
||||||
controlKind: controlKindForFormNode(element.name, element.kind),
|
controlKind: controlKindForFormNode(element.name, formElementString(element.attributes, ["control_kind", "control", "type", "kind"]) ?? element.kind),
|
||||||
binding: element.qualified_name || element.name,
|
binding: formElementString(element.attributes, ["binding", "dataPath", "data_path", "path"]) ?? element.qualified_name ?? element.name,
|
||||||
width: "stretch"
|
width: formElementWidth(element.attributes, index)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formElementString(attributes: Record<string, unknown>, keys: string[]): string | null {
|
||||||
|
for (const key of keys) {
|
||||||
|
const value = attributes[key];
|
||||||
|
if (typeof value === "string" && value.trim()) {
|
||||||
|
return value.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formElementWidth(attributes: Record<string, unknown>, index: number): IdeFormElementDraft["width"] {
|
||||||
|
const raw = formElementString(attributes, ["width", "layout_width", "placement"]);
|
||||||
|
if (raw === "half" || raw === "third" || raw === "stretch") {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
return index < 2 ? "half" : "stretch";
|
||||||
|
}
|
||||||
|
|
||||||
function controlKindForFormNode(name: string, kind: string): IdeFormElementDraft["controlKind"] {
|
function controlKindForFormNode(name: string, kind: string): IdeFormElementDraft["controlKind"] {
|
||||||
const normalized = `${name} ${kind}`.toLowerCase();
|
const normalized = `${name} ${kind}`.toLowerCase();
|
||||||
if (normalized.includes("таб") || normalized.includes("table")) return "table";
|
if (normalized.includes("таб") || normalized.includes("table")) return "table";
|
||||||
|
|||||||
@@ -772,7 +772,10 @@ export function ProjectSetupClient({ initialSetup }: Readonly<{ initialSetup: Pr
|
|||||||
return;
|
return;
|
||||||
} else if (mode === "reindex") {
|
} else if (mode === "reindex") {
|
||||||
setLastSyncPreview(null);
|
setLastSyncPreview(null);
|
||||||
await postJson(`/api/sfera/projects/${setup.project_id}/reindex`, undefined);
|
const job = await postJson<OperationJob>(`/api/sfera/projects/${setup.project_id}/reindex/jobs`, undefined);
|
||||||
|
setServerImportJob(job);
|
||||||
|
keepImportRun = true;
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
setLastImportResult(null);
|
setLastImportResult(null);
|
||||||
setServerImportJob(null);
|
setServerImportJob(null);
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ export type NamedNode = {
|
|||||||
kind: string;
|
kind: string;
|
||||||
name: string;
|
name: string;
|
||||||
qualified_name: string;
|
qualified_name: string;
|
||||||
|
attributes: Record<string, unknown>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SourceLocation = {
|
export type SourceLocation = {
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ def parse_one_c_xml_file(path: str | Path) -> list[OneCXmlObject]:
|
|||||||
source_path = normalize_source_path(path)
|
source_path = normalize_source_path(path)
|
||||||
root = ET.fromstring(_read_text_file(Path(path)))
|
root = ET.fromstring(_read_text_file(Path(path)))
|
||||||
result: list[OneCXmlObject] = []
|
result: list[OneCXmlObject] = []
|
||||||
_walk_xml_objects(source_path, root, result, current_role=None, parent_qualified_name=None)
|
_walk_xml_objects(source_path, root, result, current_role=None, parent_qualified_name=None, parent_object_kind=None)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -351,10 +351,12 @@ def _walk_xml_objects(
|
|||||||
*,
|
*,
|
||||||
current_role: OneCXmlObject | None,
|
current_role: OneCXmlObject | None,
|
||||||
parent_qualified_name: str | None,
|
parent_qualified_name: str | None,
|
||||||
|
parent_object_kind: str | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
role_context = current_role
|
role_context = current_role
|
||||||
child_parent_qualified_name = parent_qualified_name
|
child_parent_qualified_name = parent_qualified_name
|
||||||
object_kind = _xml_object_kind(element)
|
child_parent_object_kind = parent_object_kind
|
||||||
|
object_kind = _xml_object_kind(element, parent_object_kind=parent_object_kind)
|
||||||
if object_kind == "RIGHT":
|
if object_kind == "RIGHT":
|
||||||
right = _xml_right_object(source_path, element, role_context)
|
right = _xml_right_object(source_path, element, role_context)
|
||||||
if right is not None:
|
if right is not None:
|
||||||
@@ -371,6 +373,7 @@ def _walk_xml_objects(
|
|||||||
)
|
)
|
||||||
result.append(xml_object)
|
result.append(xml_object)
|
||||||
child_parent_qualified_name = xml_object.qualified_name
|
child_parent_qualified_name = xml_object.qualified_name
|
||||||
|
child_parent_object_kind = object_kind
|
||||||
if object_kind == "ROLE":
|
if object_kind == "ROLE":
|
||||||
role_context = xml_object
|
role_context = xml_object
|
||||||
|
|
||||||
@@ -381,6 +384,7 @@ def _walk_xml_objects(
|
|||||||
result,
|
result,
|
||||||
current_role=role_context,
|
current_role=role_context,
|
||||||
parent_qualified_name=child_parent_qualified_name,
|
parent_qualified_name=child_parent_qualified_name,
|
||||||
|
parent_object_kind=child_parent_object_kind,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -695,6 +699,25 @@ _OBJECT_KIND_BY_TAG = {
|
|||||||
"предопределенный": "PREDEFINED",
|
"предопределенный": "PREDEFINED",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_FORM_ELEMENT_TAGS = {
|
||||||
|
"item",
|
||||||
|
"items",
|
||||||
|
"element",
|
||||||
|
"elements",
|
||||||
|
"formitem",
|
||||||
|
"formitems",
|
||||||
|
"field",
|
||||||
|
"fields",
|
||||||
|
"group",
|
||||||
|
"groups",
|
||||||
|
"table",
|
||||||
|
"tables",
|
||||||
|
"button",
|
||||||
|
"buttons",
|
||||||
|
"page",
|
||||||
|
"pages",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
_QUALIFIED_PREFIX_BY_KIND = {
|
_QUALIFIED_PREFIX_BY_KIND = {
|
||||||
"CATALOG": "Справочник",
|
"CATALOG": "Справочник",
|
||||||
@@ -1288,8 +1311,10 @@ _PATH_METADATA_ALIASES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _xml_object_kind(element: ET.Element) -> str | None:
|
def _xml_object_kind(element: ET.Element, *, parent_object_kind: str | None = None) -> str | None:
|
||||||
tag = _local_name(element.tag).lower()
|
tag = _local_name(element.tag).lower()
|
||||||
|
if parent_object_kind in {"FORM", "ELEMENT"} and tag in _FORM_ELEMENT_TAGS and _xml_name(element):
|
||||||
|
return "ELEMENT"
|
||||||
if tag in {"metadataobject", "object"}:
|
if tag in {"metadataobject", "object"}:
|
||||||
type_name = _xml_type_name(element)
|
type_name = _xml_type_name(element)
|
||||||
if type_name:
|
if type_name:
|
||||||
@@ -1384,6 +1409,11 @@ def _xml_qualified_name(
|
|||||||
|
|
||||||
def _xml_attributes(element: ET.Element) -> dict:
|
def _xml_attributes(element: ET.Element) -> dict:
|
||||||
attributes = dict(element.attrib)
|
attributes = dict(element.attrib)
|
||||||
|
for key, value in element.attrib.items():
|
||||||
|
local_key = _local_name(key)
|
||||||
|
attributes.setdefault(local_key, value)
|
||||||
|
if local_key.lower() == "type":
|
||||||
|
attributes.setdefault("control_kind", value.split(":")[-1].split(".")[-1])
|
||||||
attribute_role = _xml_attribute_role(element)
|
attribute_role = _xml_attribute_role(element)
|
||||||
if attribute_role:
|
if attribute_role:
|
||||||
attributes.setdefault("attribute_role", attribute_role)
|
attributes.setdefault("attribute_role", attribute_role)
|
||||||
|
|||||||
@@ -983,7 +983,9 @@ def _xml_edge_kind(kind: NodeKind) -> EdgeKind:
|
|||||||
return EdgeKind.HAS_TABULAR_SECTION
|
return EdgeKind.HAS_TABULAR_SECTION
|
||||||
if kind == NodeKind.ROLE:
|
if kind == NodeKind.ROLE:
|
||||||
return EdgeKind.HAS_ROLE
|
return EdgeKind.HAS_ROLE
|
||||||
return EdgeKind.HAS_ELEMENT
|
if kind == NodeKind.FORM_ELEMENT:
|
||||||
|
return EdgeKind.HAS_ELEMENT
|
||||||
|
return EdgeKind.CONTAINS
|
||||||
|
|
||||||
|
|
||||||
def _find_xml_parent(parents: dict[str, SemanticNode], qualified_name: str) -> SemanticNode | None:
|
def _find_xml_parent(parents: dict[str, SemanticNode], qualified_name: str) -> SemanticNode | None:
|
||||||
|
|||||||
@@ -346,6 +346,33 @@ def test_index_project_links_form_command_to_handler(tmp_path: Path):
|
|||||||
assert any(edge.kind == EdgeKind.HANDLES for edge in snapshot.edges)
|
assert any(edge.kind == EdgeKind.HANDLES for edge in snapshot.edges)
|
||||||
|
|
||||||
|
|
||||||
|
def test_index_project_extracts_managed_form_items_without_layouts(tmp_path: Path):
|
||||||
|
xml = tmp_path / "form.xml"
|
||||||
|
xml.write_text(
|
||||||
|
"""
|
||||||
|
<Form name="ФормаДокумента" qualifiedName="Документ.Заказ.ФормаДокумента">
|
||||||
|
<items xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="form:FormGroup" name="Основное">
|
||||||
|
<caption>Основное</caption>
|
||||||
|
<items xsi:type="form:FormField" name="Номер">
|
||||||
|
<caption>Номер</caption>
|
||||||
|
<dataPath>Объект.Номер</dataPath>
|
||||||
|
</items>
|
||||||
|
</items>
|
||||||
|
<Layout name="ПечатнаяФорма" qualifiedName="Документ.Заказ.ФормаДокумента.ПечатнаяФорма" />
|
||||||
|
</Form>
|
||||||
|
""",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
snapshot = index_project(tmp_path, project_id="ui-form-items")
|
||||||
|
|
||||||
|
form = form_semantics(snapshot)[0]
|
||||||
|
assert [element.name for element in form.elements] == ["Основное", "Номер"]
|
||||||
|
assert all(element.kind == NodeKind.FORM_ELEMENT for element in form.elements)
|
||||||
|
assert form.elements[1].attributes["dataPath"] == "Объект.Номер"
|
||||||
|
assert not any(element.name == "ПечатнаяФорма" for element in form.elements)
|
||||||
|
|
||||||
|
|
||||||
def test_index_project_links_form_events_to_handlers(tmp_path: Path):
|
def test_index_project_links_form_events_to_handlers(tmp_path: Path):
|
||||||
xml = tmp_path / "form.xml"
|
xml = tmp_path / "form.xml"
|
||||||
xml.write_text(
|
xml.write_text(
|
||||||
|
|||||||
@@ -19,15 +19,20 @@ def form_semantics(snapshot: SirSnapshot) -> list[FormSemantics]:
|
|||||||
for node in snapshot.nodes
|
for node in snapshot.nodes
|
||||||
if node.kind == NodeKind.FORM
|
if node.kind == NodeKind.FORM
|
||||||
}
|
}
|
||||||
|
element_children: dict[str, list[SemanticNode]] = {}
|
||||||
for edge in snapshot.edges:
|
for edge in snapshot.edges:
|
||||||
form = forms.get(edge.source_lineage)
|
form = forms.get(edge.source_lineage)
|
||||||
target = nodes.get(edge.target_lineage)
|
target = nodes.get(edge.target_lineage)
|
||||||
if form is None or target is None:
|
if target is None:
|
||||||
|
continue
|
||||||
|
if edge.kind == EdgeKind.HAS_ELEMENT and target.kind == NodeKind.FORM_ELEMENT:
|
||||||
|
element_children.setdefault(edge.source_lineage, []).append(target)
|
||||||
|
if form is None:
|
||||||
continue
|
continue
|
||||||
if edge.kind == EdgeKind.HAS_COMMAND:
|
if edge.kind == EdgeKind.HAS_COMMAND:
|
||||||
form.commands.append(target)
|
form.commands.append(target)
|
||||||
elif edge.kind == EdgeKind.HAS_ELEMENT:
|
for form in forms.values():
|
||||||
form.elements.append(target)
|
form.elements.extend(_flatten_form_elements(form.form.lineage_id, element_children))
|
||||||
command_to_form = {
|
command_to_form = {
|
||||||
command.lineage_id: form
|
command.lineage_id: form
|
||||||
for form in forms.values()
|
for form in forms.values()
|
||||||
@@ -43,4 +48,20 @@ def form_semantics(snapshot: SirSnapshot) -> list[FormSemantics]:
|
|||||||
return sorted(forms.values(), key=lambda item: item.form.qualified_name)
|
return sorted(forms.values(), key=lambda item: item.form.qualified_name)
|
||||||
|
|
||||||
|
|
||||||
|
def _flatten_form_elements(root_lineage: str, element_children: dict[str, list[SemanticNode]]) -> list[SemanticNode]:
|
||||||
|
result: list[SemanticNode] = []
|
||||||
|
seen: set[str] = set()
|
||||||
|
|
||||||
|
def visit(parent_lineage: str) -> None:
|
||||||
|
for element in element_children.get(parent_lineage, []):
|
||||||
|
if element.lineage_id in seen:
|
||||||
|
continue
|
||||||
|
seen.add(element.lineage_id)
|
||||||
|
result.append(element)
|
||||||
|
visit(element.lineage_id)
|
||||||
|
|
||||||
|
visit(root_lineage)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["FormSemantics", "form_semantics"]
|
__all__ = ["FormSemantics", "form_semantics"]
|
||||||
|
|||||||
@@ -156,8 +156,7 @@ def html5_setup_job(
|
|||||||
async def html5_setup_reindex(
|
async def html5_setup_reindex(
|
||||||
*,
|
*,
|
||||||
project_id: str,
|
project_id: str,
|
||||||
reindex: Callable[[str], Any],
|
start_reindex_job: Callable[[str], Any],
|
||||||
setup_response: Callable[[str], object],
|
|
||||||
) -> str:
|
) -> str:
|
||||||
await reindex(project_id)
|
job = await start_reindex_job(project_id)
|
||||||
return render_html5_setup_summary(project_id, setup_response(project_id))
|
return render_html5_import_job(project_id, job)
|
||||||
|
|||||||
@@ -488,7 +488,7 @@ def _load_persisted_state() -> None:
|
|||||||
_collaboration.ownership[_collaboration._ownership_key(ownership)] = ownership
|
_collaboration.ownership[_collaboration._ownership_key(ownership)] = ownership
|
||||||
for payload in _storage.list_documents("operations_jobs"):
|
for payload in _storage.list_documents("operations_jobs"):
|
||||||
job = OperationJob.model_validate(payload)
|
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.status = OperationJobStatus.FAILED
|
||||||
job.error = "Операция была прервана перезапуском сервера."
|
job.error = "Операция была прервана перезапуском сервера."
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
@@ -1738,8 +1738,7 @@ async def html5_project_setup_reindex(project_id: str) -> Response:
|
|||||||
return _html5_response(
|
return _html5_response(
|
||||||
await _html5_setup_reindex(
|
await _html5_setup_reindex(
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
reindex=reindex_project,
|
start_reindex_job=start_project_reindex_job,
|
||||||
setup_response=_project_setup_response,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2100,6 +2099,33 @@ async def check_project_import(
|
|||||||
|
|
||||||
@app.post("/projects/{project_id}/reindex", response_model=ImportSummary)
|
@app.post("/projects/{project_id}/reindex", response_model=ImportSummary)
|
||||||
async def reindex_project(project_id: str) -> 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, {})
|
state = _project_setup.get(project_id, {})
|
||||||
current_source = ImportSourceKind(state.get("current_source") or ImportSourceKind.XML_DUMP.value)
|
current_source = ImportSourceKind(state.get("current_source") or ImportSourceKind.XML_DUMP.value)
|
||||||
last_import = state.get("last_import") or {}
|
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")
|
state["last_import"] = summary.model_dump(mode="json")
|
||||||
_append_import_history(project_id, summary)
|
_append_import_history(project_id, summary)
|
||||||
state["status"] = ProjectSetupStatus.INDEXED.value
|
state["status"] = ProjectSetupStatus.INDEXED.value
|
||||||
|
_storage.write_document("project_settings", project_id, state)
|
||||||
return summary
|
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]:
|
def _form_semantics_for_lineages(snapshot: SirSnapshot, form_lineages: set[str]) -> list[FormSemanticsResponse]:
|
||||||
if not form_lineages:
|
if not form_lineages:
|
||||||
return []
|
return []
|
||||||
nodes = {node.lineage_id: node for node in snapshot.nodes}
|
return [
|
||||||
forms = {
|
_form_semantics_response(item)
|
||||||
lineage_id: FormSemanticsResponse(form=_named_node(node), commands=[], elements=[], command_handlers={})
|
for item in form_semantics(snapshot)
|
||||||
for lineage_id, node in nodes.items()
|
if item.form.lineage_id in form_lineages
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
def _persist_job(job: OperationJob) -> OperationJob:
|
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:
|
def _create_agent_server_import_job(agent_job: AgentImportJob, request: ImportRequest) -> OperationJob:
|
||||||
operation_job = OperationJob(
|
operation_job = OperationJob(
|
||||||
job_id=f"server-import-{uuid4()}",
|
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:
|
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}
|
active_statuses = {OperationJobStatus.QUEUED, OperationJobStatus.RUNNING}
|
||||||
candidates = [
|
candidates = [
|
||||||
job
|
job
|
||||||
for job in _operations.jobs.values()
|
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.payload.get("project_id") == project_id
|
||||||
and job.status in active_statuses
|
and job.status in active_statuses
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user