Files
llm/scripts/build_1c_write_matrix_enum_registry.py

162 lines
7.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
from collections import Counter, defaultdict
from pathlib import Path
from typing import Any
KNOWN_ENUM_VALUES: dict[str, dict[str, str]] = {
"Вид": {
"1": "Поле надписи",
"2": "Поле ввода",
"4": "Страница",
"5": "Группа",
"9": "Командная панель",
"12": "Расширенная подсказка",
"31": "Кнопка командной панели",
"48": "Поле формы",
"55": "Динамический список",
"73": "Таблица формы",
},
"ПоложениеЗаголовка": {"0": "Авто", "1": "Верх", "2": "Нет"},
"ПоложениеВКоманднойПанели": {"0": "Авто", "1": "В командной панели", "2": "В дополнительном подменю"},
"Отображение": {"3": "Авто"},
"ЦветФона": {"3": "Авто"},
"ЦветТекста": {"3": "Авто"},
"ЦветРамки": {"3": "Авто"},
}
PROPERTY_ALIASES = {
"group": "Группа",
"id": "Идентификатор",
"name": "Имя",
"view": "Вид",
"title": "Заголовок",
"command_bar_location": "ПоложениеВКоманднойПанели",
}
def load_entries(report: dict[str, Any]) -> list[dict[str, Any]]:
matrix = report.get("matrix") if isinstance(report.get("matrix"), dict) else {}
entries = matrix.get("entries") if isinstance(matrix.get("entries"), list) else []
return [entry for entry in entries if isinstance(entry, dict)]
def property_key(entry: dict[str, Any]) -> tuple[str, str, str, str]:
prop = entry.get("property") if isinstance(entry.get("property"), dict) else {}
target = entry.get("requested_target") if isinstance(entry.get("requested_target"), dict) else {}
raw_name = str(prop.get("semantic_name") or prop.get("canonical_property") or prop.get("property") or "")
name = PROPERTY_ALIASES.get(raw_name, raw_name)
marker = str(target.get("marker") or "")
index = str(prop.get("parameter_index") if prop.get("parameter_index") is not None else "")
value_type = str(prop.get("value_type") or "")
return name, marker, index, value_type
def risk_class(name: str, value_type: str) -> str:
normalized = PROPERTY_ALIASES.get(name, name)
if normalized in {"Идентификатор", "Имя"} or "маркер" in normalized.casefold():
return "manual_only_identity_or_marker"
if name == "Вид":
return "structural_type_no_generic_write"
if normalized == "Группа":
return "reference_or_container_rule_required"
if normalized in KNOWN_ENUM_VALUES:
return "allowed_values_known_needs_smoke_rule"
if value_type in {"enum_atom", "bool_or_enum_atom", "color_or_enum_atom"}:
return "allowed_values_unknown"
return "scalar_semantics_unknown"
def main() -> int:
parser = argparse.ArgumentParser(description="Build enum/scalar learning registry from 1C write matrix gaps.")
parser.add_argument("--matrix-report", type=Path, required=True, help="Report produced by scripts/smoke_1c_write_matrix.py.")
parser.add_argument("--output", type=Path, required=True, help="Output enum registry JSON path.")
parser.add_argument("--sample-limit", type=int, default=8, help="Examples per enum/scalar group.")
args = parser.parse_args()
report = json.loads(args.matrix_report.read_text(encoding="utf-8"))
groups: dict[tuple[str, str, str, str], dict[str, Any]] = {}
for entry in load_entries(report):
prop = entry.get("property") if isinstance(entry.get("property"), dict) else {}
if entry.get("can_smoke"):
continue
if entry.get("reason") not in {"value_type_not_smoke_safe", "identity_or_binding_property"}:
continue
value_type = str(prop.get("value_type") or "")
if value_type not in {"enum_atom", "bool_or_enum_atom", "color_or_enum_atom", "integer_atom", "scalar"}:
continue
key = property_key(entry)
name, marker, index, _ = key
row = groups.setdefault(
key,
{
"property": name,
"marker": marker or None,
"parameter_index": index or None,
"value_type": value_type,
"risk": risk_class(name, value_type),
"observed_values": Counter(),
"reasons": Counter(),
"sections": Counter(),
"examples": [],
},
)
target = entry.get("requested_target") if isinstance(entry.get("requested_target"), dict) else {}
effective = entry.get("effective_target") if isinstance(entry.get("effective_target"), dict) else {}
old = "" if prop.get("old") is None else str(prop.get("old"))
row["observed_values"][old] += 1
row["reasons"][str(entry.get("reason") or "")] += 1
row["sections"][str(effective.get("section") or "")] += 1
if len(row["examples"]) < args.sample_limit:
row["examples"].append(
{
"target": target.get("name") or target.get("path"),
"requested_section": target.get("section"),
"effective_section": effective.get("section"),
"presentation": prop.get("presentation"),
"semantic_name": prop.get("semantic_name"),
"old": old,
"write_path": prop.get("write_path"),
"reason": entry.get("reason"),
}
)
properties = []
for row in groups.values():
known = KNOWN_ENUM_VALUES.get(str(row.get("property") or ""))
properties.append(
{
**{key: value for key, value in row.items() if key not in {"observed_values", "reasons", "sections"}},
"observed_values": dict(row["observed_values"].most_common()),
"known_values": known,
"reasons": dict(row["reasons"]),
"sections": dict(row["sections"]),
"counts": {"entries": sum(row["observed_values"].values()), "observed_values": len(row["observed_values"])},
}
)
properties.sort(key=lambda item: (-int(item["counts"]["entries"]), str(item.get("property")), str(item.get("marker")), str(item.get("parameter_index"))))
result = {
"schema": "onec_form_write_enum_registry.v1",
"status": "ok",
"source_report": str(args.matrix_report),
"counts": {
"groups": len(properties),
"entries": sum(int(item["counts"]["entries"]) for item in properties),
"by_risk": dict(Counter(str(item.get("risk")) for item in properties)),
},
"properties": properties,
}
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
print(json.dumps({"schema": result["schema"], "status": "ok", "counts": result["counts"], "path": str(args.output)}, ensure_ascii=False, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())