Initial SQL-only 1C adapter baseline
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare SQL DBNames GUIDs with a 1C XML GUID index.
|
||||
|
||||
This report is a bridge between SQL storage-role records and XML metadata
|
||||
objects. It keeps DBNames storage roles unchanged and only attaches XML facts
|
||||
when the same GUID is present in the XML dump.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from collections import Counter, defaultdict
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
def load_json(path: Path) -> dict[str, Any]:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def group_dbnames(dbnames_report: dict[str, Any]) -> dict[str, Any]:
|
||||
grouped: dict[str, Any] = defaultdict(lambda: {"records": [], "roles": Counter(), "sources": Counter()})
|
||||
for source in dbnames_report.get("dbnames") or []:
|
||||
file_name = source.get("file_name") or ""
|
||||
for record in source.get("records") or []:
|
||||
if record.get("status") != "parsed":
|
||||
continue
|
||||
guid = str(record.get("guid") or "").lower()
|
||||
if not guid:
|
||||
continue
|
||||
item = grouped[guid]
|
||||
compact = {
|
||||
"source_file": file_name,
|
||||
"storage_role": record.get("storage_role"),
|
||||
"sql_number": record.get("sql_number"),
|
||||
"index": record.get("index"),
|
||||
}
|
||||
item["records"].append(compact)
|
||||
item["roles"][compact["storage_role"]] += 1
|
||||
item["sources"][file_name] += 1
|
||||
result = {}
|
||||
for guid, item in grouped.items():
|
||||
result[guid] = {
|
||||
"records": sorted(item["records"], key=lambda row: (row["source_file"], str(row["storage_role"]), row["sql_number"])),
|
||||
"storage_roles": dict(sorted(item["roles"].items())),
|
||||
"source_files": dict(sorted(item["sources"].items())),
|
||||
}
|
||||
return dict(sorted(result.items()))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Compare SQL DBNames GUIDs with XML GUID index.")
|
||||
parser.add_argument("--dbnames", type=Path, required=True)
|
||||
parser.add_argument("--xml-index", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--max-objects", type=int, default=1000)
|
||||
args = parser.parse_args()
|
||||
|
||||
dbnames = load_json(args.dbnames)
|
||||
xml_index = load_json(args.xml_index)
|
||||
sql_by_guid = group_dbnames(dbnames)
|
||||
xml_map = xml_index.get("guid_map") or {}
|
||||
|
||||
matches = []
|
||||
top_object_matches = []
|
||||
occurrence_only_matches = []
|
||||
unmatched_sql = []
|
||||
matched_role_counts: Counter[str] = Counter()
|
||||
top_object_matched_role_counts: Counter[str] = Counter()
|
||||
occurrence_only_matched_role_counts: Counter[str] = Counter()
|
||||
unmatched_role_counts: Counter[str] = Counter()
|
||||
xml_kind_counts: Counter[str] = Counter()
|
||||
|
||||
for guid, sql_item in sql_by_guid.items():
|
||||
xml_item = xml_map.get(guid)
|
||||
roles = sql_item["storage_roles"]
|
||||
if not xml_item:
|
||||
unmatched_sql.append({"guid": guid, "storage_roles": roles, "records": sql_item["records"][:20]})
|
||||
unmatched_role_counts.update(roles)
|
||||
continue
|
||||
top_objects = xml_item.get("top_objects") or []
|
||||
for role, count in roles.items():
|
||||
matched_role_counts[role] += count
|
||||
if top_objects:
|
||||
top_object_matched_role_counts[role] += count
|
||||
else:
|
||||
occurrence_only_matched_role_counts[role] += count
|
||||
for top_object in top_objects:
|
||||
xml_kind_counts[top_object.get("xml_kind") or "<unknown>"] += 1
|
||||
match_item = {
|
||||
"guid": guid,
|
||||
"storage_roles": roles,
|
||||
"source_files": sql_item["source_files"],
|
||||
"xml_top_objects": top_objects,
|
||||
"xml_occurrences": xml_item.get("occurrences") or [],
|
||||
"records": sql_item["records"][:50],
|
||||
}
|
||||
matches.append(match_item)
|
||||
if top_objects:
|
||||
top_object_matches.append(match_item)
|
||||
else:
|
||||
occurrence_only_matches.append(match_item)
|
||||
|
||||
report = {
|
||||
"schema": "onec_sql_xml_guid_compare.v1",
|
||||
"dbnames": str(args.dbnames),
|
||||
"xml_index": str(args.xml_index),
|
||||
"sql_guid_count": len(sql_by_guid),
|
||||
"xml_guid_count": len(xml_map),
|
||||
"matched_guid_count": len(matches),
|
||||
"top_object_matched_guid_count": len(top_object_matches),
|
||||
"occurrence_only_matched_guid_count": len(occurrence_only_matches),
|
||||
"unmatched_sql_guid_count": len(unmatched_sql),
|
||||
"matched_guids": [item["guid"] for item in matches],
|
||||
"top_object_matched_guids": [item["guid"] for item in top_object_matches],
|
||||
"occurrence_only_matched_guids": [item["guid"] for item in occurrence_only_matches],
|
||||
"unmatched_sql_guids": [item["guid"] for item in unmatched_sql],
|
||||
"matched_storage_role_counts": dict(sorted(matched_role_counts.items(), key=lambda item: (-item[1], item[0]))),
|
||||
"top_object_matched_storage_role_counts": dict(
|
||||
sorted(top_object_matched_role_counts.items(), key=lambda item: (-item[1], item[0]))
|
||||
),
|
||||
"occurrence_only_matched_storage_role_counts": dict(
|
||||
sorted(occurrence_only_matched_role_counts.items(), key=lambda item: (-item[1], item[0]))
|
||||
),
|
||||
"unmatched_storage_role_counts": dict(sorted(unmatched_role_counts.items(), key=lambda item: (-item[1], item[0]))),
|
||||
"matched_xml_kind_counts": dict(sorted(xml_kind_counts.items(), key=lambda item: (-item[1], item[0]))),
|
||||
"matches": matches[: args.max_objects],
|
||||
"top_object_matches": top_object_matches[: args.max_objects],
|
||||
"occurrence_only_matches": occurrence_only_matches[: args.max_objects],
|
||||
"unmatched_sql": unmatched_sql[: args.max_objects],
|
||||
}
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"output": str(args.output),
|
||||
"sql_guids": len(sql_by_guid),
|
||||
"xml_guids": len(xml_map),
|
||||
"matched": len(matches),
|
||||
"top_object_matched": len(top_object_matches),
|
||||
"occurrence_only_matched": len(occurrence_only_matches),
|
||||
"unmatched_sql": len(unmatched_sql),
|
||||
},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user