Merge pull request 'fix(seo): a school is publishable on any year's results, not the latest' (#112) from fix/sitemap-any-year-data into main
Stage (build -> staging -> E2E gate) / Build Backend (FastAPI) (push) Successful in 18s
Stage (build -> staging -> E2E gate) / Build Frontend (Next.js) (push) Successful in 49s
Stage (build -> staging -> E2E gate) / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 12s
Stage (build -> staging -> E2E gate) / Deploy to Staging (push) Successful in 0s
Stage (build -> staging -> E2E gate) / E2E Journeys against Staging (push) Failing after 1m24s
Stage (build -> staging -> E2E gate) / Build Backend (FastAPI) (push) Successful in 18s
Stage (build -> staging -> E2E gate) / Build Frontend (Next.js) (push) Successful in 49s
Stage (build -> staging -> E2E gate) / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 12s
Stage (build -> staging -> E2E gate) / Deploy to Staging (push) Successful in 0s
Stage (build -> staging -> E2E gate) / E2E Journeys against Staging (push) Failing after 1m24s
Reviewed-on: #112
This commit was merged in pull request #112.
This commit is contained in:
commit
4a3928df9f
2 files changed
+64
-2
No files matched your search
+23
-2
@@ -79,6 +79,12 @@ def _school_url(urn: int, school_name: str) -> str:
|
|||||||
STATIC_SITEMAP_PATHS = ("/", "/rankings", "/compare", "/admissions")
|
STATIC_SITEMAP_PATHS = ("/", "/rankings", "/compare", "/admissions")
|
||||||
|
|
||||||
|
|
||||||
|
# A page has something a search result could state if any of these is present
|
||||||
|
# in any year. Shared by _has_publishable_data and the per-school check in
|
||||||
|
# _school_sitemap_rows so the two can never drift.
|
||||||
|
_PUBLISHABLE_FIELDS = ("rwm_expected_pct", "attainment_8_score", "ofsted_grade")
|
||||||
|
|
||||||
|
|
||||||
def _has_publishable_data(row) -> bool:
|
def _has_publishable_data(row) -> bool:
|
||||||
"""True when a school page has something a search result could state.
|
"""True when a school page has something a search result could state.
|
||||||
|
|
||||||
@@ -87,7 +93,7 @@ def _has_publishable_data(row) -> bool:
|
|||||||
signal down, so it stays out of the sitemap. The page itself still resolves
|
signal down, so it stays out of the sitemap. The page itself still resolves
|
||||||
for anyone who has the URL.
|
for anyone who has the URL.
|
||||||
"""
|
"""
|
||||||
for field in ("rwm_expected_pct", "attainment_8_score", "ofsted_grade"):
|
for field in _PUBLISHABLE_FIELDS:
|
||||||
value = row.get(field)
|
value = row.get(field)
|
||||||
if value is not None and not pd.isna(value):
|
if value is not None and not pd.isna(value):
|
||||||
return True
|
return True
|
||||||
@@ -115,6 +121,21 @@ def _school_sitemap_rows(df) -> list[str]:
|
|||||||
rows: list[str] = []
|
rows: list[str] = []
|
||||||
seen: set[int] = set()
|
seen: set[int] = set()
|
||||||
|
|
||||||
|
# Publishable is a property of the SCHOOL, not of its latest row.
|
||||||
|
#
|
||||||
|
# The first cut tested the latest year's row alone, which quietly dropped
|
||||||
|
# every school that has results in its history but a null row for the most
|
||||||
|
# recent year — a school that stopped reporting, or whose figures were
|
||||||
|
# suppressed for small-cohort disclosure. The Mallard Academy (150367) is
|
||||||
|
# the case that caught it: real KS2 results for 2015-16 through 2018-19,
|
||||||
|
# then null rows for 2022-23 onward. Its page shows all four years; the
|
||||||
|
# sitemap omitted it. Roughly 220 schools were affected.
|
||||||
|
publishable_cols = [c for c in _PUBLISHABLE_FIELDS if c in df.columns]
|
||||||
|
publishable: set[int] = (
|
||||||
|
set(df.loc[df[publishable_cols].notna().any(axis=1), "urn"].astype(int))
|
||||||
|
if publishable_cols else set()
|
||||||
|
)
|
||||||
|
|
||||||
# Latest row per URN first, so a school's most recent Ofsted date wins.
|
# Latest row per URN first, so a school's most recent Ofsted date wins.
|
||||||
ordered = df.sort_values("year", ascending=False) if "year" in df.columns else df
|
ordered = df.sort_values("year", ascending=False) if "year" in df.columns else df
|
||||||
|
|
||||||
@@ -123,7 +144,7 @@ def _school_sitemap_rows(df) -> list[str]:
|
|||||||
if urn in seen:
|
if urn in seen:
|
||||||
continue
|
continue
|
||||||
seen.add(urn)
|
seen.add(urn)
|
||||||
if not _has_publishable_data(row):
|
if urn not in publishable:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
lastmod = None
|
lastmod = None
|
||||||
|
|||||||
@@ -176,3 +176,44 @@ def test_children_are_chunked_under_the_limit(monkeypatch):
|
|||||||
def test_build_sitemap_still_returns_the_index(sitemap):
|
def test_build_sitemap_still_returns_the_index(sitemap):
|
||||||
# lifespan and the admin endpoint call build_sitemap(); keep it working.
|
# lifespan and the admin endpoint call build_sitemap(); keep it working.
|
||||||
assert "<sitemapindex" in sitemap
|
assert "<sitemapindex" in sitemap
|
||||||
|
|
||||||
|
|
||||||
|
def test_school_with_results_in_an_earlier_year_is_still_listed(monkeypatch):
|
||||||
|
"""Regression: The Mallard Academy (150367).
|
||||||
|
|
||||||
|
Real KS2 results 2015-16 to 2018-19, then null rows from 2022-23 onward
|
||||||
|
because the school stopped reporting. The first cut tested the latest
|
||||||
|
year's row alone and dropped it, along with ~220 others, even though its
|
||||||
|
detail page shows all four years of results.
|
||||||
|
"""
|
||||||
|
from backend import app as app_module
|
||||||
|
import pandas as _pd
|
||||||
|
|
||||||
|
base = {"local_authority": "Testshire", "school_type": "Academy",
|
||||||
|
"phase": "Primary", "ofsted_date": None, "ofsted_grade": np.nan,
|
||||||
|
"attainment_8_score": np.nan, "urn": 150367,
|
||||||
|
"school_name": "Mallard Academy"}
|
||||||
|
df = _pd.DataFrame([
|
||||||
|
{**base, "year": 201819, "rwm_expected_pct": 67.0},
|
||||||
|
{**base, "year": 202324, "rwm_expected_pct": np.nan},
|
||||||
|
{**base, "year": 202425, "rwm_expected_pct": np.nan},
|
||||||
|
])
|
||||||
|
monkeypatch.setattr(app_module, "load_school_data", lambda: df)
|
||||||
|
|
||||||
|
xml = app_module.build_sitemaps()["schools-1.xml"]
|
||||||
|
assert "/school/150367-mallard-academy" in xml
|
||||||
|
|
||||||
|
|
||||||
|
def test_school_with_no_results_in_any_year_is_still_omitted(monkeypatch):
|
||||||
|
"""The fix must not turn into "list everything"."""
|
||||||
|
from backend import app as app_module
|
||||||
|
import pandas as _pd
|
||||||
|
|
||||||
|
base = {"local_authority": "Testshire", "school_type": "Academy",
|
||||||
|
"phase": "Primary", "ofsted_date": None, "ofsted_grade": np.nan,
|
||||||
|
"attainment_8_score": np.nan, "rwm_expected_pct": np.nan,
|
||||||
|
"urn": 100002, "school_name": "Ghost Primary"}
|
||||||
|
df = _pd.DataFrame([{**base, "year": y} for y in (202324, 202425)])
|
||||||
|
monkeypatch.setattr(app_module, "load_school_data", lambda: df)
|
||||||
|
|
||||||
|
assert "/school/100002" not in app_module.build_sitemaps()["schools-1.xml"]
|
||||||
Reference in new issue
Block a user