from __future__ import annotations import argparse import json from pathlib import Path from typing import Any from urllib import request def rpc(adapter_url: str, method: str, payload: dict[str, Any]) -> dict[str, Any]: body = json.dumps({"method": method, "payload": payload}, ensure_ascii=False).encode("utf-8") req = request.Request( f"{adapter_url.rstrip('/')}/rpc", data=body, headers={"Content-Type": "application/json; charset=utf-8"}, method="POST", ) with request.urlopen(req, timeout=240) as resp: return json.loads(resp.read().decode("utf-8", errors="replace")) def latest_configcas_rows(adapter_url: str, base_id: str, limit: int) -> list[dict[str, Any]]: result = rpc( adapter_url, "query.run", { "base_id": base_id, "diagnostic": True, "query": f"SELECT TOP {int(limit)} FileName, DATALENGTH(BinaryData) AS Bytes, PartNo, Creation, Modified FROM ConfigCAS ORDER BY Modified DESC", "timeout_seconds": 120, }, ) return result.get("rows") or [] def template_summary(adapter_url: str, base_id: str, file_name: str) -> dict[str, Any]: result = rpc( adapter_url, "templates.map", { "base_id": base_id, "table": "ConfigCAS", "file_name": file_name, "view": "summary", "sections": "cells,styles,named_areas,named_ranges,diagnostics", "max_cells": 200, "max_areas": 100, "timeout_seconds": 120, }, ) templates = result.get("templates") or [] if not templates: return {} return (templates[0] or {}).get("structure") or {} def compact_signature(item: dict[str, Any]) -> dict[str, Any]: data: dict[str, Any] = {} for key in ( "name", "kind", "text", "cell_id", "tree_position", "range", "range_candidate", "next_moxel_record", ): if key in item: data[key] = item.get(key) style = item.get("style_evidence") if isinstance(style, dict): compact_style: dict[str, Any] = {} for key in ("immediate_preceding_values", "last_7_preceding_values"): if key in style: compact_style[key] = style.get(key) if compact_style: data["style_evidence"] = compact_style return data def build_history( adapter_url: str, base_id: str, limit: int, track_name: str | None, track_text: str | None, ) -> list[dict[str, Any]]: rows = latest_configcas_rows(adapter_url, base_id, limit) history: list[dict[str, Any]] = [] for row in rows: file_name = str(row.get("FileName") or "") byte_count = int(row.get("Bytes") or 0) if not file_name or byte_count <= 0 or byte_count > 5000: continue structure = template_summary(adapter_url, base_id, file_name) if str(structure.get("format") or "") != "MOXCEL": continue item: dict[str, Any] = { "file_name": file_name, "bytes": byte_count, "modified": row.get("Modified"), "counts": structure.get("counts") or {}, } if track_name: matches = [] for candidate in structure.get("named_range_candidates") or []: if str((candidate or {}).get("name") or "") == track_name: matches.append(compact_signature(candidate)) item["named_range_matches"] = matches if track_text: matches = [] for candidate in structure.get("cell_style_candidates") or []: if str((candidate or {}).get("text") or "") == track_text: matches.append(compact_signature(candidate)) item["text_matches"] = matches history.append(item) return history def main() -> int: parser = argparse.ArgumentParser(description="Track recent MOXCEL template signatures from live ConfigCAS.") parser.add_argument("--adapter-url", default="http://docker.cin.su:8011") parser.add_argument("--base-id", default="upo_test") parser.add_argument("--limit", type=int, default=12) parser.add_argument("--track-name", default="R7C2_TEST") parser.add_argument("--track-text", default="Ячейка 7 - 2") parser.add_argument("--output", help="Optional JSON output path.") args = parser.parse_args() history = build_history(args.adapter_url, args.base_id, args.limit, args.track_name, args.track_text) payload = { "schema": "codex_1c_template_history.v1", "adapter_url": args.adapter_url, "base_id": args.base_id, "track_name": args.track_name, "track_text": args.track_text, "items": history, "counts": {"items": len(history)}, } rendered = json.dumps(payload, ensure_ascii=False, indent=2) if args.output: Path(args.output).write_text(rendered, encoding="utf-8") print(rendered) return 0 if __name__ == "__main__": raise SystemExit(main())