Expose SCD XML structure outline

This commit is contained in:
2026-08-14 11:59:52 +03:00
parent 03fed309c6
commit 832d159f37
2 changed files with 40 additions and 6 deletions
+27 -6
View File
@@ -75,22 +75,20 @@ def query_without_line_comments(text: str) -> str:
def node_path(root: ET.Element, target: ET.Element) -> str:
"""Produce a stable, human-readable evidence path without XML prefixes."""
def visit(node: ET.Element, prefix: str) -> str | None:
name = local_name(node.tag)
current = f"{prefix}/{name}" if prefix else f"/{name}"
def visit(node: ET.Element, current: str) -> str | None:
if node is target:
return current
positions: dict[str, int] = {}
for child in node:
child_name = local_name(child.tag)
positions[child_name] = positions.get(child_name, 0) + 1
child_prefix = f"{current}[{positions[child_name]}]"
found = visit(child, child_prefix)
suffix = f"[{positions[child_name]}]" if positions[child_name] > 1 else ""
found = visit(child, f"{current}/{child_name}{suffix}")
if found:
return found
return None
return visit(root, "") or "/"
return visit(root, f"/{local_name(root.tag)}[1]") or "/"
def xml_from_scd_payload(data: bytes) -> tuple[ET.Element | None, dict[str, Any]]:
@@ -184,6 +182,21 @@ def inspect_scd_payload(data: bytes, *, sections: list[str] | None = None) -> di
"container": {**container, "code": "SCD_SCHEMA_NODE_NOT_FOUND"},
"sections": {name: [] for name in requested},
}
# Keep a complete, evidence-backed map of the XML shape alongside the
# curated semantic sections below. 1C releases and SCD variants may add
# nodes that this adapter does not yet assign a business meaning to. A
# tag/count/path outline makes those nodes visible to callers without
# guessing that (for example) a form property is a dataset or layout.
top_level_counts: dict[str, int] = {}
top_level_paths: dict[str, list[str]] = {}
for child in schema:
tag = local_name(child.tag)
top_level_counts[tag] = top_level_counts.get(tag, 0) + 1
top_level_paths.setdefault(tag, []).append(node_path(schema, child))
xml_tag_counts: dict[str, int] = {}
for node in schema.iter():
tag = local_name(node.tag)
xml_tag_counts[tag] = xml_tag_counts.get(tag, 0) + 1
node_names = {
"parameters": {"parameter"},
"datasets": {"dataSet"},
@@ -220,6 +233,14 @@ def inspect_scd_payload(data: bytes, *, sections: list[str] | None = None) -> di
query_references.append({"dataset": dataset.get("name"), "parameters": references})
analysis = {
"kind": "raw_query_parameter_token_scan",
"schema_outline": {
"kind": "exact_xml_tag_inventory",
"top_level": [
{"tag": tag, "count": top_level_counts[tag], "paths": top_level_paths[tag]}
for tag in sorted(top_level_counts, key=str.casefold)
],
"all_tag_counts": dict(sorted(xml_tag_counts.items(), key=lambda item: item[0].casefold())),
},
"declared_parameters": declared,
"query_parameter_references": query_references,
"referenced_not_declared_in_schema": sorted(