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