audit-labs/audit-tools

A collection of scripts, queries, and other goodies you can use in an audit.

clone: git clone https://gitbay.org/audit-labs/audit-tools.git

main: sampling/sampling_tool/reporting.py · raw

 1"""Human-readable reporting outputs."""
 2
 3from __future__ import annotations
 4
 5from pathlib import Path
 6
 7
 8class RunLogger:
 9    def __init__(self) -> None:
10        self.lines: list[str] = []
11
12    def log(self, message: str) -> None:
13        self.lines.append(message)
14
15    def warning(self, message: str) -> None:
16        self.log(f"WARNING: {message}")
17
18    def error(self, message: str) -> None:
19        self.log(f"ERROR: {message}")
20
21    def write(self, path: Path) -> None:
22        path.write_text("\n".join(self.lines) + "\n")
23
24
25def build_methodology(
26    *,
27    input_file: Path,
28    input_sheet: str | None,
29    input_sha256: str,
30    source_row_count: int,
31    options,
32    effective_id_column: str | None,
33    id_column_omitted: bool,
34    duplicate_id_count: int,
35    blank_id_count: int,
36    sample_size_actual: int,
37    random_seed: int | None,
38    strata_summary: list[dict[str, object]] | None,
39) -> str:
40    lines = [
41        "Audit Sampling Methodology",
42        "",
43        f"Source file: {input_file}",
44        f"Source sheet: {input_sheet or ''}",
45        f"Input SHA-256: {input_sha256}",
46        f"Source row count: {source_row_count}",
47    ]
48    if id_column_omitted:
49        lines.append(
50            "ID column: omitted; _audit_row_id was generated from _source_row_number."
51        )
52    else:
53        lines.append(f"ID column: {options.id_column}")
54    lines.extend(
55        [
56            f"Effective ID column: {effective_id_column or ''}",
57            f"Duplicate ID rows identified: {duplicate_id_count}",
58            f"Blank ID rows identified: {blank_id_count}",
59            f"Blank ID handling: {'excluded' if options.exclude_blank_id else 'retained'}",
60            f"Filters applied: {options.filters or {}}",
61            f"Sampling method: {options.method}",
62            f"Sample size requested: {options.sample_size or ''}",
63            f"Sample size selected: {sample_size_actual}",
64            f"Random seed used: {random_seed or ''}",
65        ]
66    )
67    if options.method == "stratified":
68        lines.append(f"Stratification column: {options.stratify_column}")
69        lines.append(f"Strata counts: {options.strata_counts or ''}")
70        lines.append(f"Strata proportions: {options.strata_proportions or ''}")
71        if strata_summary:
72            lines.append("Strata summary:")
73            for row in strata_summary:
74                lines.append(
75                    "  "
76                    f"{row['Stratum']}: population={row['Population Count']}, "
77                    f"requested={row['Requested Sample Count']}, "
78                    f"actual={row['Actual Sample Count']}, "
79                    f"shortfall={row['Shortfall']}"
80                )
81    lines.extend(
82        [
83            "Sampling was performed without replacement.",
84            "Selected rows retain source row numbers and original source fields.",
85        ]
86    )
87    return "\n".join(lines) + "\n"