Initial SQL-only 1C adapter baseline
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build an inventory that maps _ExtensionsInfo rows to DBNames-Ext files.
|
||||
|
||||
The script keeps the byte conversion explicit. SQL _ExtensionsInfo._IDRRef is
|
||||
stored as 16 bytes. Observed DBNames-Ext suffixes match this rearrangement:
|
||||
|
||||
b[12:16] b[10:12] b[8:10] b[0:2] b[2:8]
|
||||
|
||||
This is recorded as an observed conversion and validated against exported
|
||||
DBNames-Ext file names.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import uuid
|
||||
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-sig"))
|
||||
|
||||
|
||||
def dbnames_ext_guid_from_idrref(data: bytes) -> str:
|
||||
if len(data) != 16:
|
||||
raise ValueError(f"_IDRRef must contain 16 bytes, got {len(data)}")
|
||||
reordered = data[12:16] + data[10:12] + data[8:10] + data[0:2] + data[2:8]
|
||||
return str(uuid.UUID(bytes=reordered))
|
||||
|
||||
|
||||
def binary_info(value: Any) -> dict[str, Any] | None:
|
||||
if isinstance(value, dict) and value.get("type") == "binary":
|
||||
return value
|
||||
return None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Map _ExtensionsInfo rows to DBNames-Ext files.")
|
||||
parser.add_argument("--extensions-info", type=Path, required=True)
|
||||
parser.add_argument("--params-dir", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
report = load_json(args.extensions_info)
|
||||
dbnames_ext_files = {
|
||||
path.name.removeprefix("DBNames-Ext-").lower(): path
|
||||
for path in args.params_dir.iterdir()
|
||||
if path.is_file() and path.name.startswith("DBNames-Ext-")
|
||||
}
|
||||
|
||||
rows = []
|
||||
matched = 0
|
||||
for row in report.get("rows") or []:
|
||||
columns = row.get("columns") or {}
|
||||
idrref = binary_info(columns.get("_IDRRef"))
|
||||
if not idrref:
|
||||
continue
|
||||
idrref_bytes = Path(idrref["path"]).read_bytes()
|
||||
dbnames_guid = dbnames_ext_guid_from_idrref(idrref_bytes)
|
||||
dbnames_file = dbnames_ext_files.get(dbnames_guid)
|
||||
if dbnames_file:
|
||||
matched += 1
|
||||
rows.append(
|
||||
{
|
||||
"row_index": row.get("row_index"),
|
||||
"extension_name": columns.get("_ExtName"),
|
||||
"extension_order": columns.get("_ExtensionOrder"),
|
||||
"update_time": columns.get("_UpdateTime"),
|
||||
"use_purpose": columns.get("_ExtensionUsePurpose"),
|
||||
"scope": columns.get("_ExtensionScope"),
|
||||
"idrref_hex": idrref_bytes.hex(),
|
||||
"dbnames_ext_guid": dbnames_guid,
|
||||
"dbnames_ext_file": str(dbnames_file) if dbnames_file else None,
|
||||
"dbnames_ext_file_name": dbnames_file.name if dbnames_file else None,
|
||||
"dbnames_ext_file_bytes": dbnames_file.stat().st_size if dbnames_file else None,
|
||||
"extension_zipped_info": binary_info(columns.get("_ExtensionZippedInfo")),
|
||||
}
|
||||
)
|
||||
|
||||
known_from_rows = {row["dbnames_ext_guid"] for row in rows}
|
||||
orphan_dbnames_files = [
|
||||
{
|
||||
"file_name": path.name,
|
||||
"guid_or_marker": guid,
|
||||
"bytes": path.stat().st_size,
|
||||
}
|
||||
for guid, path in sorted(dbnames_ext_files.items())
|
||||
if guid not in known_from_rows
|
||||
]
|
||||
|
||||
result = {
|
||||
"schema": "onec_extension_inventory.v1",
|
||||
"extensions_info": str(args.extensions_info),
|
||||
"params_dir": str(args.params_dir),
|
||||
"observed_idrref_to_dbnames_ext_guid": "b[12:16] + b[10:12] + b[8:10] + b[0:2] + b[2:8]",
|
||||
"extension_row_count": len(rows),
|
||||
"matched_dbnames_ext_count": matched,
|
||||
"orphan_dbnames_ext_count": len(orphan_dbnames_files),
|
||||
"extensions": rows,
|
||||
"orphan_dbnames_ext_files": orphan_dbnames_files,
|
||||
}
|
||||
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(
|
||||
{
|
||||
"output": str(args.output),
|
||||
"extensions": len(rows),
|
||||
"matched_dbnames_ext": matched,
|
||||
"orphan_dbnames_ext": len(orphan_dbnames_files),
|
||||
},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user