Download scripts/audit_data_quality.py from gesh75/multivendor-cli-configurator: direct link, hf CLI and curl.
- Browser
- Download file 4.68 kB
-
https://huggingface.co/spaces/gesh75/multivendor-cli-configurator/resolve/main/scripts/audit_data_quality.py
- Command line
-
hf download hf://spaces/gesh75/multivendor-cli-configurator/scripts/audit_data_quality.py
-
curl -L -o audit_data_quality.py https://huggingface.co/spaces/gesh75/multivendor-cli-configurator/resolve/main/scripts/audit_data_quality.py
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()) | |