#!/usr/bin/env python3 """Build enum order -> presentation map from 1C XML routes.""" from __future__ import annotations import argparse import json import xml.etree.ElementTree as ET from pathlib import Path from typing import Any NS = { "md": "http://v8.3/MDClasses", "v8": "http://v8.1c.ru/8.1/data/core", } def load_json(path: Path) -> dict[str, Any]: return json.loads(path.read_text(encoding="utf-8-sig")) def local_name(tag: str) -> str: return tag.rsplit("}", 1)[-1] def child_text(parent: ET.Element, name: str) -> str | None: for child in list(parent): if local_name(child.tag) == name: return child.text or "" return None def properties(element: ET.Element) -> ET.Element | None: for child in list(element): if local_name(child.tag) == "Properties": return child return None def synonym(props: ET.Element | None) -> str | None: if props is None: return None for syn in list(props): if local_name(syn.tag) != "Synonym": continue for item in list(syn): lang = None content = None for child in list(item): if local_name(child.tag) == "lang": lang = child.text elif local_name(child.tag) == "content": content = child.text if lang == "ru" and content: return content return None def enum_values(path: Path) -> list[dict[str, Any]]: root = ET.parse(path).getroot() enum = next((node for node in root.iter() if local_name(node.tag) == "Enum"), None) if enum is None: return [] child_objects = next((node for node in list(enum) if local_name(node.tag) == "ChildObjects"), None) if child_objects is None: return [] values = [] order = 0 for node in list(child_objects): if local_name(node.tag) != "EnumValue": continue props = properties(node) name = child_text(props, "Name") if props is not None else None values.append( { "order": order, "uuid": (node.attrib.get("uuid") or "").lower() or None, "name": name, "synonym": synonym(props), } ) order += 1 return values def is_base_config(top: dict[str, Any]) -> bool: relative = str(top.get("relative_path") or "") return relative.startswith("Enums\\") def route_score(row: dict[str, Any]) -> tuple[int, str]: return (1 if str(row.get("relative_path") or "").startswith("Enums\\") else 0, str(row.get("relative_path") or "")) def main() -> int: parser = argparse.ArgumentParser(description="Build 1C enum presentation map.") parser.add_argument("--index", type=Path, required=True) parser.add_argument("--output", type=Path, required=True) args = parser.parse_args() index = load_json(args.index) enums: dict[str, dict[str, Any]] = {} for guid, item in (index.get("objects") or {}).items(): tops = [ top for top in item.get("xml_top_objects") or [] if top.get("xml_kind") == "Enum" and top.get("name") and top.get("path") ] if not tops: continue tops.sort(key=lambda top: (not is_base_config(top), top.get("relative_path") or "")) top = tops[0] path = Path(top["path"]) if not path.is_file(): continue try: values = enum_values(path) except ET.ParseError as error: values = [] parse_error = str(error) else: parse_error = None key = str(top["name"]) candidate = { "guid": guid, "name": top.get("name"), "synonym": top.get("synonym"), "relative_path": top.get("relative_path"), "path": top.get("path"), "parse_error": parse_error, "values": values, "by_order": {str(value["order"]): value for value in values}, "by_uuid": {value["uuid"]: value for value in values if value.get("uuid")}, } existing = enums.get(key) if existing is None or route_score(candidate) > route_score(existing): enums[key] = candidate result = { "schema": "onec_enum_presentation_map.v1", "index": str(args.index), "enum_count": len(enums), "enums": dict(sorted(enums.items())), } args.output.parent.mkdir(parents=True, exist_ok=True) args.output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") print(json.dumps({"output": str(args.output), "enum_count": len(enums)}, ensure_ascii=False)) return 0 if __name__ == "__main__": raise SystemExit(main())