#!/usr/bin/env python3 """Run the full 1C SQL read-view pipeline for one metadata object.""" from __future__ import annotations import argparse import base64 import json import os import hashlib import subprocess import sys from pathlib import Path from typing import Any sys.path.insert(0, str(Path(__file__).resolve().parent)) from build_1c_metadata_from_resolved_object import build_metadata # noqa: E402 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 find_metadata(summary_path: Path, *, kind: str, name: str) -> Path | None: summary = load_json(summary_path) matches = [ row for row in summary.get("outputs") or [] if str(row.get("kind") or "").casefold() == kind.casefold() and str(row.get("name") or "").casefold() == name.casefold() ] if not matches: return None if len(matches) > 1: raise SystemExit(f"Ambiguous metadata object in {summary_path}: {kind}.{name}") return Path(matches[0]["output"]) def safe_name(value: str) -> str: keep = [] for char in value: if char.isascii() and (char.isalnum() or char in ("-", "_")): keep.append(char) else: keep.append(f"u{ord(char):04x}") result = "".join(keep).strip("-") digest = hashlib.sha1(value.encode("utf-8")).hexdigest()[:8] if len(result) > 48: result = result[:48].rstrip("-_") return f"{result or 'object'}-{digest}" def run(command: list[str], *, env: dict[str, str] | None = None) -> None: printable = " ".join(command) print(printable, flush=True) completed = subprocess.run(command, env=env) if completed.returncode != 0: raise SystemExit(completed.returncode) def powershell_exe() -> str: return os.environ.get("POWERSHELL_EXE") or "powershell" def main() -> int: parser = argparse.ArgumentParser(description="Build a 1C SQL read view by kind/name.") parser.add_argument("--kind") parser.add_argument("--name") parser.add_argument("--kind-b64", help="UTF-8 base64 encoded kind.") parser.add_argument("--name-b64", help="UTF-8 base64 encoded name.") parser.add_argument("--top", type=int, default=5) parser.add_argument("--view", choices=["effective", "base", "extension"], default="effective") parser.add_argument("--extension") parser.add_argument("--summary", type=Path, required=True) parser.add_argument("--validation", type=Path, required=True) parser.add_argument("--route-index", type=Path, required=True) parser.add_argument("--enum-presentation-map", type=Path) parser.add_argument("--output-dir", type=Path, required=True) parser.add_argument("--server", default=os.environ.get("ONEC_SQL_SERVER")) parser.add_argument("--database", default=os.environ.get("ONEC_SQL_DATABASE")) parser.add_argument("--user", default=os.environ.get("ONEC_SQL_USER")) parser.add_argument("--password", default=os.environ.get("ONEC_SQL_PASSWORD")) args = parser.parse_args() if args.kind_b64: args.kind = base64.b64decode(args.kind_b64).decode("utf-8") if args.name_b64: args.name = base64.b64decode(args.name_b64).decode("utf-8") if not args.kind or not args.name: raise SystemExit("Use --kind/--name or --kind-b64/--name-b64.") if args.view == "extension" and not args.extension: raise SystemExit("Use --extension with --view extension.") missing = [name for name in ("server", "database", "user", "password") if not getattr(args, name)] if missing: raise SystemExit(f"Missing SQL connection settings: {', '.join(missing)}") route_index = load_json(args.route_index) resolution = resolve_object(route_index, kind=args.kind, name=args.name, limit=50) canonical = resolution.get("canonical") if not canonical: raise SystemExit(f"Object not found: {args.kind}.{args.name}") args.kind = str(canonical["kind"]) args.name = str(canonical["name"]) prefix = f"{safe_name(args.kind)}-{safe_name(args.name)}" args.output_dir.mkdir(parents=True, exist_ok=True) metadata_path = args.output_dir / f"metadata-{prefix}.json" metadata = build_metadata(route_index, kind=args.kind, name=args.name) metadata_path.write_text(json.dumps(metadata, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") projection_path = args.output_dir / f"sql-read-projection-{prefix}.json" read_result_path = args.output_dir / f"sql-read-result-{prefix}.json" reference_path = args.output_dir / f"sql-reference-resolution-{prefix}.json" composite_path = args.output_dir / f"sql-composite-reference-resolution-{prefix}.json" composite_value_path = args.output_dir / f"sql-composite-value-resolution-{prefix}.json" enum_map_path = args.enum_presentation_map or args.output_dir / "enum-presentation-map.json" view_path = args.output_dir / f"sql-read-view-{prefix}.json" python = sys.executable ps_env = os.environ.copy() ps_env.update( { "ONEC_SQL_SERVER": args.server, "ONEC_SQL_DATABASE": args.database, "ONEC_SQL_USER": args.user, "ONEC_SQL_PASSWORD": args.password, } ) run( [ python, "plugins/1c/tools/build_sql_read_projection.py", "--metadata", str(metadata_path), "--validation", str(args.validation), "--output", str(projection_path), "--top", str(args.top), "--view", args.view, ] + (["--extension", args.extension] if args.extension else []) ) run( [ powershell_exe(), "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "scripts/execute_1c_sql_read_projection.ps1", "-ProjectionPath", str(projection_path), "-OutputPath", str(read_result_path), ], env=ps_env, ) run( [ powershell_exe(), "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "scripts/resolve_1c_sql_read_references.ps1", "-ReadResultPath", str(read_result_path), "-RouteIndexPath", str(args.route_index), "-OutputPath", str(reference_path), ], env=ps_env, ) run( [ powershell_exe(), "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "scripts/resolve_1c_sql_composite_references.ps1", "-ReadResultPath", str(read_result_path), "-RouteIndexPath", str(args.route_index), "-OutputPath", str(composite_path), ], env=ps_env, ) run( [ python, "scripts/resolve_1c_sql_composite_values.py", "--read-result", str(read_result_path), "--output", str(composite_value_path), ] ) if not enum_map_path.is_file(): run( [ python, "scripts/build_1c_enum_presentation_map.py", "--index", str(args.route_index), "--output", str(enum_map_path), ] ) run( [ python, "scripts/build_1c_sql_read_view.py", "--read-result", str(read_result_path), "--reference-resolution", str(reference_path), "--composite-reference-resolution", str(composite_path), "--composite-value-resolution", str(composite_value_path), "--enum-presentation-map", str(enum_map_path), "--output", str(view_path), ] ) view = load_json(view_path) print( json.dumps( { "schema": "onec_sql_read_object_view_run.v1", "kind": args.kind, "name": args.name, "requested_view": args.view, "extension": args.extension, "resolution": { "schema": resolution.get("schema"), "canonical": canonical, "summary": resolution.get("summary"), }, "metadata": str(metadata_path), "projection": str(projection_path), "read_result": str(read_result_path), "reference_resolution": str(reference_path), "composite_reference_resolution": str(composite_path), "composite_value_resolution": str(composite_value_path), "enum_presentation_map": str(enum_map_path), "view_path": str(view_path), "summary": view.get("summary"), }, ensure_ascii=True, indent=2, ), flush=True, ) return 0 if __name__ == "__main__": raise SystemExit(main())