pipeline_miami/discover_srs.py
Served verbatim from the project repository. Internal working document conventions apply: documents may reference file paths, branch names, and findings-ledger anchors from the repo.
"""Discover FLDOE State Report of Schools (SRS) assessment files for a results
year and register the spring administrations in docs/cities/miami/sources.yaml.
The per-year results page (fldoe.org/accountability/assessments/k-12-student-
assessment/results/<year>.stml) links one .xls per subject × grade ×
administration; the file-name prefixes drift by year (findings.md F.5), so
the registry is built from the page rather than templated.
Usage: python -m pipeline_miami.discover_srs 2025 [2024 ...]
Then: python -m pipeline_miami.discover --all
"""
from __future__ import annotations
import re
import sys
import requests
import yaml
from pipeline_miami.discover import SOURCES_YAML, UA, fetch_url_for
PAGE = "https://www.fldoe.org/accountability/assessments/k-12-student-assessment/results/{year}.stml"
# file-name fragment → (metric family, grade or subject)
PATTERNS = [
(re.compile(r"ELA(\d{2})SRS", re.I), lambda m: ("fast_ela", f"g{int(m.group(1))}")),
(re.compile(r"Math(\d{2})SRS", re.I), lambda m: ("fast_math", f"g{int(m.group(1))}")),
(re.compile(r"Alg1SRS", re.I), lambda m: ("eoc", "alg1")),
(re.compile(r"(?<![A-Za-z])G(\d)SRS", re.I), lambda m: ("science", f"g{m.group(1)}")),
(re.compile(r"GeoSRS", re.I), lambda m: ("eoc", "geometry")),
(re.compile(r"Bio1SRS", re.I), lambda m: ("eoc", "biology")),
(re.compile(r"CivSRS", re.I), lambda m: ("eoc", "civics")),
(re.compile(r"USHSRS", re.I), lambda m: ("eoc", "ushistory")),
]
def classify(fname: str):
if not re.search(r"spring", fname, re.I):
return None # fall / winter / summer EOC administrations and non-SRS files
for pat, fn in PATTERNS:
m = pat.search(fname)
if m:
return fn(m)
return None
def main(years: list[str]) -> None:
sources = yaml.safe_load(open(SOURCES_YAML))
existing = {s["source_id"] for s in sources}
urls_known = {s.get("url") for s in sources}
new = []
for y in years:
url = PAGE.format(year=y)
r = requests.get(fetch_url_for(url), headers={"User-Agent": UA}, timeout=120)
r.raise_for_status()
links = set(re.findall(r'href="([^"]+\.xls[x]?)"', r.text, re.I))
sy = f"{int(y) - 1}-{y[2:]}"
for href in sorted(links):
fname = href.rsplit("/", 1)[-1]
c = classify(fname)
if not c:
continue
fam, part = c
sid = f"fldoe_srs_{fam}_{part}_spring{y}"
full = href if href.startswith("http") else "https://www.fldoe.org" + href
if sid in existing or full in urls_known:
continue
new.append({
"source_id": sid, "role": "ingested", "publisher": "Florida Department of Education, Bureau of K-12 Student Assessment",
"url": full, "format": "xls", "year_format": "spring_yy", "year": sy, "header_row": 5,
"join_key": ["District Number", "School Number"],
"metrics_backed": [f"{fam}_{part}_proficiency" if fam != "eoc" else f"eoc_{part}_proficiency"],
"notes": f"State Report of Schools, spring {y} administration; discovered from {url} by pipeline_miami/discover_srs.py. All students only; '*' n<10.",
})
existing.add(sid)
if not new:
print("nothing new")
return
text = open(SOURCES_YAML).read().rstrip("\n") + "\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Assessment SRS files discovered from the FLDOE results pages (discover_srs.py)\n# ─────────────────────────────────────────────────────────────────────────────\n\n" + yaml.safe_dump(new, sort_keys=False, allow_unicode=True, width=120)
open(SOURCES_YAML, "w").write(text)
for s in new:
print(f" + {s['source_id']:40s} {s['url']}")
print(f"registered {len(new)} SRS sources")
if __name__ == "__main__":
main(sys.argv[1:] or ["2025"])