Initial SQL-only 1C adapter baseline

This commit is contained in:
2026-07-22 03:03:47 +03:00
commit e2503b77e7
545 changed files with 184711 additions and 0 deletions
+197
View File
@@ -0,0 +1,197 @@
#!/usr/bin/env python3
"""List forms, templates, commands, and module files related to a 1C object."""
from __future__ import annotations
import argparse
import base64
import json
import sys
from pathlib import Path
from typing import Any
sys.path.insert(0, str(Path(__file__).resolve().parent))
from resolve_1c_object import resolve_object # noqa: E402
def load_json(path: Path) -> dict[str, Any]:
return json.loads(path.read_text(encoding="utf-8-sig"))
def decode_arg(value: str | None, encoded: str | None) -> str | None:
if encoded:
return base64.b64decode(encoded).decode("utf-8")
return value
def normalize(value: str) -> str:
return value.casefold().replace(" ", "")
def top_objects(item: dict[str, Any]) -> list[dict[str, Any]]:
return item.get("xml_top_objects") or []
def find_owner(index: dict[str, Any], *, kind: str, name: str) -> tuple[str, dict[str, Any], dict[str, Any], dict[str, Any]]:
resolution = resolve_object(index, kind=kind, name=name, limit=50)
canonical = resolution.get("canonical")
if not canonical:
raise SystemExit(f"Object not found: {kind}.{name}")
guid = canonical["guid"]
item = (index.get("objects") or {}).get(guid)
if not item:
raise SystemExit(f"Resolved object is missing from index: {guid}")
wanted_path = canonical.get("relative_path")
owner_top = next((top for top in top_objects(item) if top.get("relative_path") == wanted_path), None)
if not owner_top:
owner_top = next((top for top in top_objects(item) if top.get("name") == canonical.get("name")), None)
if not owner_top:
raise SystemExit(f"Resolved object XML route is missing from index: {guid}")
return guid, item, owner_top, resolution
def owner_prefix(relative_path: str) -> str:
if relative_path.lower().endswith(".xml"):
return relative_path[:-4]
return relative_path
def classify_child(owner: str, top: dict[str, Any]) -> str | None:
relative = str(top.get("relative_path") or "")
if not relative.startswith(owner + "\\"):
return None
rest = relative[len(owner) + 1 :]
first = rest.split("\\", 1)[0]
if first in {"Forms", "Templates", "Commands"}:
return first[:-1].lower() if first.endswith("s") else first.lower()
return "other"
def collect_xml_children(index: dict[str, Any], owner: str) -> dict[str, list[dict[str, Any]]]:
result: dict[str, list[dict[str, Any]]] = {"form": [], "template": [], "command": [], "other": []}
for guid, item in (index.get("objects") or {}).items():
for top in top_objects(item):
category = classify_child(owner, top)
if not category:
continue
result.setdefault(category, []).append(
{
"guid": guid,
"xml_kind": top.get("xml_kind"),
"name": top.get("name"),
"synonym": top.get("synonym"),
"relative_path": top.get("relative_path"),
"path": top.get("path"),
"route_kind": item.get("route_kind") or [],
"dbnames": item.get("dbnames") or [],
"extension_routes": item.get("extension_routes") or [],
}
)
for values in result.values():
values.sort(key=lambda row: row.get("relative_path") or "")
return result
def file_record(path: Path, *, root: Path) -> dict[str, Any]:
try:
relative = str(path.relative_to(root))
except ValueError:
relative = str(path)
return {
"path": str(path),
"relative_to_object_dir": relative,
"size": path.stat().st_size,
"suffix": path.suffix.lower(),
}
def collect_files(owner_top: dict[str, Any]) -> dict[str, list[dict[str, Any]]]:
owner_xml = Path(str(owner_top.get("path") or ""))
owner_dir = owner_xml.with_suffix("")
result = {"object_modules": [], "form_modules": [], "form_xml": [], "template_xml": [], "command_xml": [], "command_modules": [], "other": []}
if not owner_dir.exists():
return result
for path in sorted(item for item in owner_dir.rglob("*") if item.is_file()):
relative = str(path.relative_to(owner_dir))
record = file_record(path, root=owner_dir)
if relative in {"Ext\\ObjectModule.bsl", "Ext\\ManagerModule.bsl"}:
result["object_modules"].append(record)
elif relative.endswith("\\Ext\\Form\\Module.bsl"):
result["form_modules"].append(record)
elif relative.startswith("Forms\\") and path.suffix.lower() == ".xml":
result["form_xml"].append(record)
elif relative.startswith("Templates\\") and path.suffix.lower() == ".xml":
result["template_xml"].append(record)
elif relative.startswith("Commands\\") and path.suffix.lower() == ".xml":
result["command_xml"].append(record)
elif relative.startswith("Commands\\") and path.suffix.lower() == ".bsl":
result["command_modules"].append(record)
else:
result["other"].append(record)
return result
def main() -> int:
parser = argparse.ArgumentParser(description="Get related 1C object artifacts.")
parser.add_argument("--index", type=Path, required=True)
parser.add_argument("--kind")
parser.add_argument("--name")
parser.add_argument("--kind-b64")
parser.add_argument("--name-b64")
parser.add_argument("--output", type=Path)
args = parser.parse_args()
kind = decode_arg(args.kind, args.kind_b64)
name = decode_arg(args.name, args.name_b64)
if not kind or not name:
raise SystemExit("Use --kind/--name or --kind-b64/--name-b64.")
index = load_json(args.index)
guid, item, owner_top, resolution = find_owner(index, kind=kind, name=name)
owner = owner_prefix(str(owner_top.get("relative_path") or ""))
children = collect_xml_children(index, owner)
files = collect_files(owner_top)
result = {
"schema": "onec_object_artifacts.v1",
"query": {"kind": kind, "name": name},
"resolution": {
"schema": resolution.get("schema"),
"canonical": resolution.get("canonical"),
"summary": resolution.get("summary"),
},
"owner": {
"guid": guid,
"xml_kind": owner_top.get("xml_kind"),
"name": owner_top.get("name"),
"synonym": owner_top.get("synonym"),
"relative_path": owner_top.get("relative_path"),
"path": owner_top.get("path"),
"route_kind": item.get("route_kind") or [],
"dbnames": item.get("dbnames") or [],
"config_routes": item.get("config_routes") or [],
"extension_routes": item.get("extension_routes") or [],
},
"children": children,
"files": files,
"counts": {
"forms": len(children.get("form") or []),
"templates": len(children.get("template") or []),
"commands": len(children.get("command") or []),
"command_xml_files": len(files.get("command_xml") or []),
"command_modules": len(files.get("command_modules") or []),
"object_modules": len(files.get("object_modules") or []),
"form_modules": len(files.get("form_modules") or []),
},
}
text = json.dumps(result, ensure_ascii=False, indent=2) + "\n"
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(text, encoding="utf-8")
print(json.dumps({"output": str(args.output), "counts": result["counts"]}, ensure_ascii=True))
else:
print(json.dumps(result, ensure_ascii=True, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())