"""get_supplementary_data_batch fetches one query per table for all URNs (not ~5 per school) and returns the same per-URN block shape as the single-URN function, picking the latest row per URN where relevant.""" import types from backend import data_loader from backend.data_loader import get_supplementary_data_batch class _FakeQuery: """Records that a query ran and serves canned rows filtered by an in-list.""" def __init__(self, recorder, model_name, rows): self._rec = recorder self._model = model_name self._rows = rows def filter(self, *args, **kwargs): return self def order_by(self, *args, **kwargs): return self def all(self): self._rec.append(self._model) return self._rows def first(self): self._rec.append(self._model) return self._rows[0] if self._rows else None class _FakeSession: def __init__(self, rows_by_model): self.rows_by_model = rows_by_model self.queries: list[str] = [] def query(self, model): name = model.__name__ return _FakeQuery(self.queries, name, self.rows_by_model.get(name, [])) def rollback(self): pass def _ofsted_row(urn, date, oe): base = {f: None for f in ( "framework", "inspection_type", "quality_of_education", "behaviour_attitudes", "personal_development", "leadership_management", "early_years_provision", "sixth_form_provision", "ungraded_outcome", "ungraded_grade", "rc_safeguarding_met", "rc_inclusion", "rc_curriculum_teaching", "rc_achievement", "rc_attendance_behaviour", "rc_personal_development", "rc_leadership_governance", "rc_early_years", "rc_sixth_form", "report_url", )} base.update(urn=urn, inspection_date=types.SimpleNamespace(isoformat=lambda: date), overall_effectiveness=oe, grade_source=None) return types.SimpleNamespace(**base) def _adm_row(urn, year): return types.SimpleNamespace( urn=urn, year=year, school_phase="Primary", places_offered=100, total_applications=200, first_preference_applications=150, first_preference_offers=140, first_preference_offer_pct=93.3, oversubscription_ratio=1.5, oversubscribed=True, total_offers=100, second_preference_offers=5, third_preference_offers=2, cross_la_applications=10, cross_la_offers=3, ) def test_one_query_per_table_and_latest_row_per_urn(): rows = { # URN 1 has two Ofsted rows; the batch must keep the most recent (2023). "FactOfstedInspection": [ _ofsted_row(1, "2023-01-01", 2), _ofsted_row(1, "2019-01-01", 3), _ofsted_row(2, "2021-06-01", 1), ], "FactAdmissions": [_adm_row(1, 202526), _adm_row(1, 202627), _adm_row(2, 202627)], "FactPupilCharacteristics": [], "FactDeprivation": [], "FactFinance": [], } session = _FakeSession(rows) out = get_supplementary_data_batch(session, [1, 2]) # Exactly one query per table — five total, regardless of two URNs. assert sorted(session.queries) == [ "FactAdmissions", "FactDeprivation", "FactFinance", "FactOfstedInspection", "FactPupilCharacteristics", ] # Latest Ofsted kept per URN assert out[1]["ofsted"]["overall_effectiveness"] == 2 assert out[2]["ofsted"]["overall_effectiveness"] == 1 # Admissions history grouped per URN, latest exposed as `admissions` assert [r["year"] for r in out[1]["admissions_history"]] == [202526, 202627] assert out[1]["admissions"]["year"] == 202627 assert out[2]["admissions_history"] == [{**out[2]["admissions_history"][0]}] # Empty tables degrade to the null block, not a crash assert out[1]["census"] is None and out[1]["deprivation"] is None def test_single_wrapper_matches_batch(monkeypatch): session = _FakeSession({"FactOfstedInspection": [_ofsted_row(5, "2022-01-01", 2)]}) single = data_loader.get_supplementary_data(session, 5) assert single["ofsted"]["overall_effectiveness"] == 2 assert single["admissions_history"] == []