multivendor-cli-configurator / scripts /audit_data_quality.py
gesh75's picture
Upload folder using huggingface_hub
1bd64c1 verified
Raw History Blame Contribute Delete
4.68 kB
#!/usr/bin/env python3
"""Audit commands.json for data-quality defects flagged in the UI audit report.
Detects records where descriptive prose has leaked into the `title` or `cmd`
fields (a known artifact of chunking tutorial text from the Juniper source) and
records with implausibly long titles.
Usage:
python3 scripts/audit_data_quality.py # report only (safe)
python3 scripts/audit_data_quality.py --report out.json
python3 scripts/audit_data_quality.py --quarantine # write cleaned file + backup
Quarantine removes records whose `cmd` field itself contains prose (these are
unrecoverable — the command is garbage). Title-only prose is reported but NOT
removed, because the command may still be valid; fix those upstream in the
parse_*.py generators instead. A timestamped .bak is always written first.
"""
from __future__ import annotations
import argparse
import json
import re
import shutil
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
DATA = ROOT / "commands.json"
# Phrases that only appear in sentences, never in a CLI command or a terse title.
PROSE_RE = re.compile(
r"\b(this is|it means|for example|note that|in order to|you can|which is|"
r"such as|we'll see|by contrast|imagine that|halfway through)\b",
re.IGNORECASE,
)
# A sentence boundary (". X") mid-field is a strong prose signal.
SENTENCE_BREAK_RE = re.compile(r"\.\s+[A-Z]")
MAX_TITLE_LEN = 90
def cmd_is_prose(cmd: str) -> bool:
return bool(PROSE_RE.search(cmd) or SENTENCE_BREAK_RE.search(cmd))
def title_is_prose(title: str) -> bool:
return bool(PROSE_RE.search(title) or (len(title) > 60 and SENTENCE_BREAK_RE.search(title)))
def audit(records: list[dict]) -> dict:
cmd_prose, title_prose, long_title = [], [], []
for i, d in enumerate(records):
title = (d.get("title") or "").strip()
cmd = (d.get("cmd") or "").strip()
if cmd_is_prose(cmd):
cmd_prose.append(i)
if title_is_prose(title):
title_prose.append(i)
if len(title) > MAX_TITLE_LEN:
long_title.append(i)
return {"cmd_prose": cmd_prose, "title_prose": title_prose, "long_title": long_title}
def by_vendor(records: list[dict], idxs: list[int]) -> dict:
counts: dict[str, int] = {}
for i in idxs:
v = records[i].get("vendor", "?")
counts[v] = counts.get(v, 0) + 1
return dict(sorted(counts.items(), key=lambda kv: -kv[1]))
def main() -> int:
ap = argparse.ArgumentParser(description="Audit commands.json data quality.")
ap.add_argument("--report", metavar="FILE", help="write full flagged-index report to FILE (JSON)")
ap.add_argument("--quarantine", action="store_true",
help="remove records whose cmd field is prose; writes a .bak first")
args = ap.parse_args()
records = json.loads(DATA.read_text(encoding="utf-8"))
flags = audit(records)
print(f"Total records: {len(records):,}")
print(f" cmd-field prose (unrecoverable): {len(flags['cmd_prose']):,}")
print(f" title-field prose (fix upstream): {len(flags['title_prose']):,}")
print(f" over-long titles (>{MAX_TITLE_LEN} chars): {len(flags['long_title']):,}")
print(f"\ncmd-prose by vendor: {by_vendor(records, flags['cmd_prose'])}")
print(f"title-prose by vendor: {by_vendor(records, flags['title_prose'])}")
print("\nSample cmd-prose records:")
for i in flags["cmd_prose"][:8]:
d = records[i]
print(f" [{d.get('vendor')}/{d.get('cat')}] cmd={d.get('cmd','')[:70]!r}")
if args.report:
Path(args.report).write_text(json.dumps({
"total": len(records),
"flags": flags,
"samples": {k: [records[i] for i in v[:25]] for k, v in flags.items()},
}, ensure_ascii=False, indent=2), encoding="utf-8")
print(f"\nFull report written to {args.report}")
if args.quarantine:
drop = set(flags["cmd_prose"])
if not drop:
print("\nNothing to quarantine.")
return 0
# Non-clobbering backup: never overwrite an existing .bak.
bak = DATA.with_suffix(".prequarantine.json")
n = 1
while bak.exists():
bak = DATA.with_suffix(f".prequarantine.{n}.json")
n += 1
shutil.copy2(DATA, bak)
cleaned = [d for i, d in enumerate(records) if i not in drop]
DATA.write_text(json.dumps(cleaned, ensure_ascii=False), encoding="utf-8")
print(f"\nBackup: {bak}")
print(f"Quarantined {len(drop):,} cmd-prose records → {len(cleaned):,} remain.")
return 0
if __name__ == "__main__":
sys.exit(main())