Initial SQL-only 1C adapter baseline
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user