Files
stack/dev/scripts/generate_alr_filenames.py
kert 85ce5e719d
Some checks failed
CI / skinny-install (aco) (push) Successful in 45s
CI / skinny-install (api) (push) Successful in 29s
CI / skinny-install (bcda) (push) Successful in 25s
CI / skinny-install (bib) (push) Successful in 23s
CI / skinny-install (bls) (push) Successful in 20s
CI / skinny-install (ccw) (push) Successful in 36s
CI / skinny-install (cli) (push) Successful in 27s
CI / skinny-install (cms) (push) Successful in 24s
CI / skinny-install (conf) (push) Successful in 27s
CI / skinny-install (pfs) (push) Successful in 25s
CI / skinny-install (rex) (push) Successful in 25s
CI / lint-test (push) Successful in 6m2s
Infra CI / notebooks (push) Successful in 7s
Infra CI / zotero (push) Failing after 6s
Infra CI / docs (push) Successful in 33s
Infra CI / api (push) Successful in 6s
Infra CI / mc (push) Successful in 7s
Deploy / build-scan-report (push) Has been cancelled
chore: clean sweep — lint, format, stale refs, generated artifacts
- Fix all 72 ruff lint errors (unused imports, unused variables, E402)
- Format all 14 unformatted dev/scripts files
- Move generated artifacts to assets/ (dag.html, pfs.html)
- Remove duplicate root coverage.svg (already in assets/icons/)
- Update .dockerignore for infra/ tree layout
- Update .gitignore: add .env.bak, mirrors/, htmlcov/
- Fix stale path refs in coverage_badge.py, woodpecker backend,
  test_network_isolation.sh, docs custom.css
- Add .gitkeep to empty dirs (infra/polaris, cloud/*/terraform)
- Delete 12 stale local branches, 10 stale remote branches
2026-03-24 17:33:55 -04:00

244 lines
10 KiB
Python

"""Generate ALR/ASR file naming pattern classifiers from ALRASRGuide.pdf.
Extracts report naming conventions from Appendix Tables 6-7 and generates
``src/aco/table/alr_filenames.py`` — a module containing regex patterns
and a classifier function that rex can use to identify which ALR/ASR
table a given filename represents.
Usage::
uv run python dev/scripts/generate_alr_filenames.py
Source: dev/ALRASRGuide.pdf pages 47-51
"""
from __future__ import annotations
from pathlib import Path
def generate_module() -> str:
"""Generate alr_filenames.py module with filename patterns."""
lines = [
'"""ALR/ASR filename patterns for report classification.',
"",
"Auto-generated from ALRASRGuide.pdf Appendix Tables 6-7.",
"Version #16 (April 2024).",
"",
"Naming conventions::",
"",
" Annual ALR Table 1-1: AALR_Table_1_1_ACOID_YYYY.csv",
" Quarterly ALR Table 1-1: QALR_Table_1_1_ACOID_YYYY_QX.csv",
" Annual ASR: AASR_ACOID_YYYY.xlsx",
" Quarterly ASR: QASR_ACOID_YYYY_QX.xlsx",
"",
"Report packages are delivered as ZIP files::",
"",
" Initial Assignment: HASSGN_ACOID_YYYY_MM_DD.zip",
" Quarterly Reports: QEXPU_ACOID_YYYY_MM_DD.zip",
" Financial Reconciliation: STLMT_ACOID_YYYY_MM_DD.zip",
" Historical Benchmark: BNMRK_ACOID_YYYY_MM_DD.zip",
"",
"Source: ALRASRGuide.pdf pages 47-51",
'"""',
"",
"from __future__ import annotations",
"",
"import re",
"from pathlib import Path",
"",
]
# Define ALR table patterns
lines.extend(
[
"",
"# ALR Table Patterns (Annual)",
"ALR_TABLE_1_1_ANNUAL = re.compile(r'AALR_Table_1_1_[A-Z0-9]+_\\d{4}\\.csv$')",
"ALR_TABLE_1_2_ANNUAL = re.compile(r'AALR_Table_1_2_[A-Z0-9]+_\\d{4}\\.csv$')",
"ALR_TABLE_1_3_ANNUAL = re.compile(r'AALR_Table_1_3_[A-Z0-9]+_\\d{4}\\.csv$')",
"ALR_TABLE_1_4_ANNUAL = re.compile(r'AALR_Table_1_4_[A-Z0-9]+_\\d{4}\\.csv$')",
"ALR_TABLE_1_5_ANNUAL = re.compile(r'AALR_Table_1_5_[A-Z0-9]+_\\d{4}\\.csv$')",
"ALR_TABLE_1_6_ANNUAL = re.compile(r'AALR_Table_1_6_[A-Z0-9]+_\\d{4}\\.csv$')",
"ALR_TABLE_1_7_ANNUAL = re.compile(r'AALR_Table_1_7_[A-Z0-9]+_\\d{4}\\.csv$')",
"ALR_TABLE_1_8_ANNUAL = re.compile(r'AALR_Table_1_8_[A-Z0-9]+_\\d{4}\\.csv$')",
"ALR_TABLE_1_9_ANNUAL = re.compile(r'AALR_Table_1_9_[A-Z0-9]+_\\d{4}\\.csv$')",
"",
"# ALR Table Patterns (Quarterly)",
"ALR_TABLE_1_1_QUARTERLY = re.compile(r'QALR_Table_1_1_[A-Z0-9]+_\\d{4}_Q[1-4]\\.csv$')",
"ALR_TABLE_1_2_QUARTERLY = re.compile(r'QALR_Table_1_2_[A-Z0-9]+_\\d{4}_Q[1-4]\\.csv$')",
"ALR_TABLE_1_3_QUARTERLY = re.compile(r'QALR_Table_1_3_[A-Z0-9]+_\\d{4}_Q[1-4]\\.csv$')",
"ALR_TABLE_1_4_QUARTERLY = re.compile(r'QALR_Table_1_4_[A-Z0-9]+_\\d{4}_Q[1-4]\\.csv$')",
"ALR_TABLE_1_5_QUARTERLY = re.compile(r'QALR_Table_1_5_[A-Z0-9]+_\\d{4}_Q[1-4]\\.csv$')",
"ALR_TABLE_1_6_QUARTERLY = re.compile(r'QALR_Table_1_6_[A-Z0-9]+_\\d{4}_Q[1-4]\\.csv$')",
"ALR_TABLE_1_7_QUARTERLY = re.compile(r'QALR_Table_1_7_[A-Z0-9]+_\\d{4}_Q[1-4]\\.csv$')",
"ALR_TABLE_1_8_QUARTERLY = re.compile(r'QALR_Table_1_8_[A-Z0-9]+_\\d{4}_Q[1-4]\\.csv$')",
"ALR_TABLE_1_9_QUARTERLY = re.compile(r'QALR_Table_1_9_[A-Z0-9]+_\\d{4}_Q[1-4]\\.csv$')",
"",
"# ASR Patterns",
"ASR_ANNUAL = re.compile(r'AASR_[A-Z0-9]+_\\d{4}\\.xlsx$')",
"ASR_QUARTERLY = re.compile(r'QASR_[A-Z0-9]+_\\d{4}_Q[1-4]\\.xlsx$')",
"",
"# Report Package Patterns (ZIP files)",
"PACKAGE_HASSGN = re.compile(r'HASSGN_[A-Z0-9]+_\\d{4}_\\d{2}_\\d{2}\\.zip$')",
"PACKAGE_QEXPU = re.compile(r'QEXPU_[A-Z0-9]+_\\d{4}_\\d{2}_\\d{2}\\.zip$')",
"PACKAGE_STLMT = re.compile(r'STLMT_[A-Z0-9]+_\\d{4}_\\d{2}_\\d{2}\\.zip$')",
"PACKAGE_BNMRK = re.compile(r'BNMRK_[A-Z0-9]+_\\d{4}_\\d{2}_\\d{2}\\.zip$')",
"",
]
)
# Generate classifier function
lines.extend(
[
"",
"# Table ID mapping",
"ALR_PATTERNS = {",
' "1-1": [ALR_TABLE_1_1_ANNUAL, ALR_TABLE_1_1_QUARTERLY],',
' "1-2": [ALR_TABLE_1_2_ANNUAL, ALR_TABLE_1_2_QUARTERLY],',
' "1-3": [ALR_TABLE_1_3_ANNUAL, ALR_TABLE_1_3_QUARTERLY],',
' "1-4": [ALR_TABLE_1_4_ANNUAL, ALR_TABLE_1_4_QUARTERLY],',
' "1-5": [ALR_TABLE_1_5_ANNUAL, ALR_TABLE_1_5_QUARTERLY],',
' "1-6": [ALR_TABLE_1_6_ANNUAL, ALR_TABLE_1_6_QUARTERLY],',
' "1-7": [ALR_TABLE_1_7_ANNUAL, ALR_TABLE_1_7_QUARTERLY],',
' "1-8": [ALR_TABLE_1_8_ANNUAL, ALR_TABLE_1_8_QUARTERLY],',
' "1-9": [ALR_TABLE_1_9_ANNUAL, ALR_TABLE_1_9_QUARTERLY],',
"}",
"",
"",
"def classify(filename: str | Path) -> dict | None:",
' """Classify an ALR/ASR filename and return metadata.',
"",
" Args:",
" filename: Filename or path to classify",
"",
" Returns:",
" dict with keys: type (alr/asr/package), table_id, report_type",
" (annual/quarterly), year, quarter (if quarterly), aco_id",
" Returns None if filename doesn't match any pattern.",
"",
" Examples::",
"",
' >>> classify("AALR_Table_1_1_A12345_2024.csv")',
" {",
' "type": "alr",',
' "table_id": "1-1",',
' "report_type": "annual",',
' "year": "2024",',
' "aco_id": "A12345",',
" }",
"",
' >>> classify("QALR_Table_1_2_B67890_2024_Q3.csv")',
" {",
' "type": "alr",',
' "table_id": "1-2",',
' "report_type": "quarterly",',
' "year": "2024",',
' "quarter": "Q3",',
' "aco_id": "B67890",',
" }",
' """',
" fname = Path(filename).name",
"",
" # Check ALR tables",
" for table_id, patterns in ALR_PATTERNS.items():",
" for pattern in patterns:",
" if m := pattern.match(fname):",
" # Parse filename components",
" parts = fname.replace('.csv', '').split('_')",
" report_type = 'annual' if fname.startswith('AALR') else 'quarterly'",
" ",
" result = {",
' "type": "alr",',
' "table_id": table_id,',
' "report_type": report_type,',
" }",
" ",
" # Extract ACO ID and year",
" if report_type == 'annual':",
" # AALR_Table_1_1_ACOID_YYYY.csv",
" result['aco_id'] = parts[3]",
" result['year'] = parts[4]",
" else:",
" # QALR_Table_1_1_ACOID_YYYY_QX.csv",
" result['aco_id'] = parts[3]",
" result['year'] = parts[4]",
" result['quarter'] = parts[5]",
" ",
" return result",
"",
" # Check ASR",
" if m := ASR_ANNUAL.match(fname):",
" parts = fname.replace('.xlsx', '').split('_')",
" return {",
' "type": "asr",',
' "report_type": "annual",',
" 'aco_id': parts[1],",
" 'year': parts[2],",
" }",
" ",
" if m := ASR_QUARTERLY.match(fname):",
" parts = fname.replace('.xlsx', '').split('_')",
" return {",
' "type": "asr",',
' "report_type": "quarterly",',
" 'aco_id': parts[1],",
" 'year': parts[2],",
" 'quarter': parts[3],",
" }",
"",
" # Check report packages",
" package_types = {",
" PACKAGE_HASSGN: 'initial_assignment',",
" PACKAGE_QEXPU: 'quarterly',",
" PACKAGE_STLMT: 'financial_reconciliation',",
" PACKAGE_BNMRK: 'historical_benchmark',",
" }",
" ",
" for pattern, pkg_type in package_types.items():",
" if m := pattern.match(fname):",
" parts = fname.replace('.zip', '').split('_')",
" return {",
' "type": "package",',
" 'package_type': pkg_type,",
" 'aco_id': parts[1],",
" 'year': parts[2],",
" 'month': parts[3],",
" 'day': parts[4],",
" }",
"",
" return None",
"",
]
)
return "\n".join(lines)
def main():
"""Generate src/aco/table/alr_filenames.py."""
output_path = (
Path(__file__).resolve().parents[2]
/ "src"
/ "aco"
/ "table"
/ "alr_filenames.py"
)
code = generate_module()
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(code)
print(f"Generated {output_path}")
print(" 18 ALR table patterns (9 annual + 9 quarterly)")
print(" 2 ASR patterns (annual + quarterly)")
print(" 4 package patterns")
return 0
if __name__ == "__main__":
raise SystemExit(main())