feat(pipeline): carry the report-card inspection's own date; pick newest MI file in discovery

The MI file's report-card grade columns belong to the latest FULL
inspection (col 'Inspection start date'), but inspection_date maps to the
legacy OEIF graded/ungraded dates — so report cards were being dated with
pre-Nov-2025 inspections. Also discover_csv_url() returned matches[0],
the oldest (2017) link on the GOV.UK page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
This commit is contained in:
Tudor
2026-07-16 14:44:51 +01:00
co-authored by Claude Fable 5
parent 026a7ab6aa
commit e00a1b38a8
4 changed files with 59 additions and 6 deletions
@@ -89,6 +89,10 @@ COLUMN_PRIORITY = {
"rc_leadership_governance": ["Leadership and governance"],
"rc_early_years": ["Early years (where applicable)"],
"rc_sixth_form": ["Post-16 provision (where applicable)"],
# Date of the latest FULL inspection — in the renewed framework this is
# the report-card inspection's own start date (col "Inspection start
# date"), distinct from the legacy OEIF graded/ungraded dates above.
"rc_inspection_date": ["Inspection start date"],
"report_url": [
"Web Link (opens in new window)",
"Web link to Ofsted provider page",
@@ -98,16 +102,44 @@ COLUMN_PRIORITY = {
def discover_csv_url() -> str | None:
"""Scrape GOV.UK page to find the latest MI CSV download link."""
"""Scrape GOV.UK page to find the latest MI CSV download link.
The page lists a decade of monthly files, oldest first — take the
newest 'latest inspections as at <date>' link by parsing its date,
never matches[0] (that is a 2017 file).
"""
resp = requests.get(GOV_UK_PAGE, timeout=30)
resp.raise_for_status()
# Look for CSV attachment links
matches = re.findall(
csv_links = re.findall(
r'href="(https://assets\.publishing\.service\.gov\.uk/[^"]+\.csv)"',
resp.text,
)
if matches:
return matches[0]
months = {
'january': 1, 'february': 2, 'march': 3, 'april': 4, 'may': 5, 'june': 6,
'july': 7, 'august': 8, 'september': 9, 'october': 10, 'november': 11, 'december': 12,
'jan': 1, 'feb': 2, 'mar': 3, 'apr': 4, 'jun': 6,
'jul': 7, 'aug': 8, 'sep': 9, 'oct': 10, 'nov': 11, 'dec': 12,
}
parsed_links = []
for link in csv_links:
normalized = link.lower().replace('-', '_')
if 'latest_inspections_as_at' not in normalized:
continue
match = re.search(r'as_at_(\d{1,2})_([a-z]+)_(\d{4})', normalized)
if match:
day, month_str, year = match.groups()
month = months.get(month_str)
if month:
try:
parsed_links.append((datetime(int(year), month, int(day)), link))
except ValueError:
continue
parsed_links.sort(reverse=True)
if parsed_links:
return parsed_links[0][1]
if csv_links:
return csv_links[-1]
# Fall back to ODS
matches = re.findall(
r'href="(https://assets\.publishing\.service\.gov\.uk/[^"]+\.ods)"',
@@ -191,6 +223,7 @@ class OfstedInspectionsStream(Stream):
th.Property("rc_leadership_governance", th.StringType),
th.Property("rc_early_years", th.StringType),
th.Property("rc_sixth_form", th.StringType),
th.Property("rc_inspection_date", th.StringType),
th.Property("report_url", th.StringType),
).to_dict()