Initial SQL-only 1C adapter baseline
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env python3
|
||||
"""List timestamped 1C saved-state watch runs."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import importlib.util
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def load_json(path: Path) -> dict[str, Any]:
|
||||
return json.loads(path.read_text(encoding="utf-8-sig"))
|
||||
|
||||
|
||||
def write_json(path: Path, data: dict[str, Any]) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def render_markdown(data: dict[str, Any]) -> str:
|
||||
module_path = REPO_ROOT / "scripts" / "render_1c_saved_state_watch_run_list_markdown.py"
|
||||
spec = importlib.util.spec_from_file_location("saved_state_watch_run_list_markdown", module_path)
|
||||
if spec is None or spec.loader is None:
|
||||
raise RuntimeError(f"Cannot load watch run list renderer: {module_path}")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module.render(data)
|
||||
|
||||
|
||||
def check_run_list(list_path: Path, check_output: Path) -> None:
|
||||
module_path = REPO_ROOT / "scripts" / "check_1c_saved_state_watch_run_list.py"
|
||||
spec = importlib.util.spec_from_file_location("saved_state_watch_run_list_check", module_path)
|
||||
if spec is None or spec.loader is None:
|
||||
raise RuntimeError(f"Cannot load watch run list checker: {module_path}")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
result = module.check_list(list_path)
|
||||
module.write_json(check_output, result)
|
||||
if not result.get("passed"):
|
||||
raise RuntimeError(f"Saved-state watch run list check failed: {check_output}")
|
||||
|
||||
|
||||
def manifest_path(run_dir: Path) -> Path:
|
||||
return run_dir / "saved-state-watch-run.json"
|
||||
|
||||
|
||||
def check_passed(path: str | None) -> bool | None:
|
||||
if not path:
|
||||
return None
|
||||
check_path = Path(path)
|
||||
if not check_path.exists():
|
||||
return None
|
||||
try:
|
||||
return load_json(check_path).get("passed")
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def compact_run(path: Path, manifest: dict[str, Any]) -> dict[str, Any]:
|
||||
counts = manifest.get("counts") or {}
|
||||
return {
|
||||
"run": path.name,
|
||||
"run_dir": str(path.resolve()),
|
||||
"database": manifest.get("database"),
|
||||
"previous_report": manifest.get("previous_report"),
|
||||
"report": manifest.get("report"),
|
||||
"markdown": manifest.get("markdown"),
|
||||
"manifest_markdown": manifest.get("manifest_markdown"),
|
||||
"manifest_check": manifest.get("manifest_check"),
|
||||
"manifest_check_passed": check_passed(manifest.get("manifest_check")),
|
||||
"delta": manifest.get("delta"),
|
||||
"delta_markdown": manifest.get("delta_markdown"),
|
||||
"delta_check": manifest.get("delta_check"),
|
||||
"delta_check_passed": check_passed(manifest.get("delta_check")),
|
||||
"counts": {
|
||||
"object_changes": counts.get("object_changes"),
|
||||
"system_changes": counts.get("system_changes"),
|
||||
"delta_objects_added": counts.get("delta_objects_added"),
|
||||
"delta_objects_removed": counts.get("delta_objects_removed"),
|
||||
"delta_objects_changed": counts.get("delta_objects_changed"),
|
||||
"delta_objects_unchanged": counts.get("delta_objects_unchanged"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def has_delta_changes(run: dict[str, Any]) -> bool:
|
||||
counts = run.get("counts") or {}
|
||||
return any(int(counts.get(key) or 0) > 0 for key in ("delta_objects_added", "delta_objects_removed", "delta_objects_changed"))
|
||||
|
||||
|
||||
def list_runs(root: Path, *, limit: int | None, only_with_delta: bool, only_changed: bool) -> dict[str, Any]:
|
||||
root = root.resolve()
|
||||
runs: list[dict[str, Any]] = []
|
||||
if root.exists():
|
||||
for child in sorted((item for item in root.iterdir() if item.is_dir()), key=lambda item: item.name, reverse=True):
|
||||
path = manifest_path(child)
|
||||
if not path.exists():
|
||||
continue
|
||||
try:
|
||||
run = compact_run(child, load_json(path))
|
||||
except Exception as exc:
|
||||
run = {
|
||||
"run": child.name,
|
||||
"run_dir": str(child),
|
||||
"error": str(exc),
|
||||
}
|
||||
if only_with_delta and not run.get("delta"):
|
||||
continue
|
||||
if only_changed and not has_delta_changes(run):
|
||||
continue
|
||||
runs.append(run)
|
||||
if limit is not None and len(runs) >= limit:
|
||||
break
|
||||
return {
|
||||
"schema": "onec_saved_state_watch_run_list.v1",
|
||||
"output_root": str(root),
|
||||
"filters": {
|
||||
"limit": limit,
|
||||
"only_with_delta": only_with_delta,
|
||||
"only_changed": only_changed,
|
||||
},
|
||||
"runs": runs,
|
||||
"latest": runs[0] if runs else None,
|
||||
"counts": {
|
||||
"runs": len(runs),
|
||||
"with_delta": sum(1 for run in runs if run.get("delta")),
|
||||
"with_delta_changes": sum(1 for run in runs if has_delta_changes(run)),
|
||||
},
|
||||
"safety": {
|
||||
"read_only": True,
|
||||
"sql_write_performed": False,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="List timestamped 1C saved-state watch runs.")
|
||||
parser.add_argument("--root", type=Path, required=True)
|
||||
parser.add_argument("--limit", type=int)
|
||||
parser.add_argument("--only-with-delta", action="store_true")
|
||||
parser.add_argument("--only-changed", action="store_true")
|
||||
parser.add_argument("--output", type=Path)
|
||||
parser.add_argument("--markdown-output", type=Path)
|
||||
parser.add_argument("--skip-markdown", action="store_true")
|
||||
parser.add_argument("--check-output", type=Path)
|
||||
parser.add_argument("--skip-check", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
result = list_runs(args.root, limit=args.limit, only_with_delta=args.only_with_delta, only_changed=args.only_changed)
|
||||
markdown_output = args.markdown_output
|
||||
if markdown_output is None and args.output and not args.skip_markdown:
|
||||
markdown_output = args.output.with_suffix(".md")
|
||||
if markdown_output and not args.skip_markdown:
|
||||
markdown_output = markdown_output.resolve()
|
||||
result["markdown"] = str(markdown_output)
|
||||
check_output = args.check_output
|
||||
if check_output is None and args.output and not args.skip_check:
|
||||
check_output = args.output.with_name(f"{args.output.stem}-check.json")
|
||||
if check_output and not args.skip_check:
|
||||
check_output = check_output.resolve()
|
||||
result["check"] = str(check_output)
|
||||
if args.output:
|
||||
write_json(args.output, result)
|
||||
if markdown_output and not args.skip_markdown:
|
||||
markdown_output.parent.mkdir(parents=True, exist_ok=True)
|
||||
markdown_output.write_text(render_markdown(result), encoding="utf-8")
|
||||
if args.output:
|
||||
write_json(args.output, result)
|
||||
if check_output and not args.skip_check:
|
||||
if not args.output:
|
||||
raise SystemExit("Use --output when watch run list check output is enabled.")
|
||||
check_run_list(args.output, check_output)
|
||||
print(json.dumps({"output": str(args.output) if args.output else None, "schema": result["schema"], "counts": result["counts"]}, ensure_ascii=False))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user