| 1325 | |
| 1326 | |
| 1327 | def _print_audit_report( |
| 1328 | run_dir: Path, |
| 1329 | summary: dict[str, Any], |
| 1330 | rules: list[tuple[str, AuditScanner, str]], |
| 1331 | violations: dict[str, list[tuple[str, int, list[str]]]], |
| 1332 | ) -> None: |
| 1333 | print(f"audit: {run_dir}") |
| 1334 | print(f"pages scanned: {summary.get('page_count', len(summary.get('pages', [])))}") |
| 1335 | print() |
| 1336 | |
| 1337 | rules_with_violations = 0 |
| 1338 | total_occurrences = 0 |
| 1339 | page_cap = 10 |
| 1340 | for rule_id, _, _ in rules: |
| 1341 | per_page = violations[rule_id] |
| 1342 | if not per_page: |
| 1343 | print(f"{rule_id}: ✓ none") |
| 1344 | print() |
| 1345 | continue |
| 1346 | rules_with_violations += 1 |
| 1347 | total_pages = len(per_page) |
| 1348 | total_hits = sum(count for _, count, _ in per_page) |
| 1349 | total_occurrences += total_hits |
| 1350 | print(f"{rule_id}: {total_pages} pages, {total_hits} occurrences") |
| 1351 | per_page_sorted = sorted(per_page, key=lambda item: (-item[1], item[0])) |
| 1352 | for path, count, _ in per_page_sorted[:page_cap]: |
| 1353 | print(f" {path:40s} {count}") |
| 1354 | if len(per_page_sorted) > page_cap: |
| 1355 | print(f" ... +{len(per_page_sorted) - page_cap} pages") |
| 1356 | first_example = next( |
| 1357 | (example for _, _, examples in per_page_sorted for example in examples), |
| 1358 | None, |
| 1359 | ) |
| 1360 | if first_example: |
| 1361 | print(f" example: {first_example!r}") |
| 1362 | print() |
| 1363 | |
| 1364 | print( |
| 1365 | f"summary: {rules_with_violations} of {len(rules)} rules with violations " |
| 1366 | f"({total_occurrences} total occurrences)" |
| 1367 | ) |
| 1368 | |
| 1369 | |
| 1370 | def audit_run(args: argparse.Namespace) -> int: |