feat(api): group GIAS school types and religions for parents
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3e2fa4a419
commit
354244f755
3 files changed
+214
-15
No files matched your search
@@ -0,0 +1,104 @@
|
||||
"""Parent-facing groups over GIAS establishment types and religious characters.
|
||||
|
||||
The 34 GIAS establishment types describe governance and funding, which for a
|
||||
mainstream state school barely changes what a parent experiences. The search
|
||||
filter offers six groups a parent recognises instead, and a faith filter in
|
||||
place of the faith signal that "Voluntary aided" and "Voluntary controlled"
|
||||
only half carry. See
|
||||
docs/superpowers/specs/2026-10-02-school-type-groups-and-faith-filter-design.md.
|
||||
|
||||
Groups are defined over GIAS codes and looked up by the translated name,
|
||||
because the DataFrame the API filters carries names only: the codes are
|
||||
replaced at load (data_loader.translate_gias_code_columns), the legacy-name
|
||||
mart fallback never had them, and test fixtures are written in names.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from .gias_codes import RELIGIOUS_CHARACTER, SCHOOL_TYPE
|
||||
|
||||
# (key, label, GIAS TypeOfEstablishment codes), in the order shown.
|
||||
TYPE_GROUPS: tuple[tuple[str, str, frozenset[int]], ...] = (
|
||||
# Academy sponsor led, Academy converter, Free schools, University technical
|
||||
# college, Studio schools, City technology college. UTCs and studio schools
|
||||
# are legally academies, and a family considering one searches by name.
|
||||
("academy", "State school: academy or free school", frozenset({28, 34, 35, 40, 41, 6})),
|
||||
# Community, Voluntary aided, Voluntary controlled, Foundation, LA nursery.
|
||||
("council", "State school: council-run", frozenset({1, 2, 3, 5, 15})),
|
||||
("independent", "Independent (fee-paying)", frozenset({11})),
|
||||
# Every special type, independent ones included (usually funded by the
|
||||
# council through an EHCP, so SEND provision to a parent, not private
|
||||
# school), and Special post 16 institutions.
|
||||
("special", "Special school (SEND)", frozenset({7, 8, 10, 12, 32, 33, 36, 44})),
|
||||
# Further education, Sixth form centres, the 16-19 academies and free schools.
|
||||
("post16", "Sixth form or college", frozenset({18, 31, 39, 45, 46})),
|
||||
# Pupil referral units and AP academies and free schools. Last: parents do
|
||||
# not apply to these; the local authority places children there.
|
||||
("alternative", "Alternative provision", frozenset({14, 38, 42, 43})),
|
||||
)
|
||||
|
||||
# In no group, so reachable only under "Any school type": Secure units,
|
||||
# Miscellaneous, Higher education institutions, Online provider, Institution
|
||||
# funded by other government department, Academy secure 16 to 19.
|
||||
UNOFFERED_TYPE_CODES: frozenset[int] = frozenset({24, 27, 29, 49, 56, 57})
|
||||
|
||||
# (key, label, GIAS ReligiousCharacter codes), in the order shown. A joint
|
||||
# school is in every faith its label names; a generic "Christian" beside a
|
||||
# named church adds nothing.
|
||||
FAITH_GROUPS: tuple[tuple[str, str, frozenset[int]], ...] = (
|
||||
# Does not apply, None, and 99 (a blank label).
|
||||
("none", "No religious character", frozenset({0, 6, 99})),
|
||||
("church_of_england", "Church of England",
|
||||
frozenset({2, 9, 10, 11, 12, 13, 19, 20, 30, 31, 32, 33, 34, 41, 48})),
|
||||
("roman_catholic", "Roman Catholic", frozenset({3, 11, 13, 35, 48})),
|
||||
# 28 "Inter- / non- denominational" is how GIAS files Christian schools
|
||||
# tied to no one church.
|
||||
("other_christian", "Other Christian",
|
||||
frozenset({4, 8, 9, 10, 12, 14, 15, 16, 17, 18, 19, 22, 26, 28, 30, 33,
|
||||
37, 38, 39, 40, 41, 44, 45, 46, 47})),
|
||||
("jewish", "Jewish", frozenset({5, 36, 43})),
|
||||
("muslim", "Muslim", frozenset({7, 42, 49})),
|
||||
("other_faith", "Other faith", frozenset({21, 24, 25, 29})),
|
||||
)
|
||||
|
||||
TYPE_GROUP_KEYS: frozenset[str] = frozenset(k for k, _, _ in TYPE_GROUPS)
|
||||
FAITH_KEYS: frozenset[str] = frozenset(k for k, _, _ in FAITH_GROUPS)
|
||||
|
||||
|
||||
def _key(name: str) -> str:
|
||||
return name.strip().lower()
|
||||
|
||||
|
||||
_TYPE_GROUP_BY_NAME: dict[str, str] = {
|
||||
_key(SCHOOL_TYPE[code]): key
|
||||
for key, _, codes in TYPE_GROUPS
|
||||
for code in codes
|
||||
if code in SCHOOL_TYPE
|
||||
}
|
||||
|
||||
_FAITHS_BY_NAME: dict[str, tuple[str, ...]] = {}
|
||||
for _faith, _, _codes in FAITH_GROUPS:
|
||||
for _code in sorted(_codes):
|
||||
if _code in RELIGIOUS_CHARACTER:
|
||||
_name = _key(RELIGIOUS_CHARACTER[_code])
|
||||
_FAITHS_BY_NAME[_name] = _FAITHS_BY_NAME.get(_name, ()) + (_faith,)
|
||||
|
||||
|
||||
def type_group_for(name: object) -> Optional[str]:
|
||||
"""The type group of a GIAS establishment type name, or None."""
|
||||
if not isinstance(name, str):
|
||||
return None
|
||||
return _TYPE_GROUP_BY_NAME.get(_key(name))
|
||||
|
||||
|
||||
def faith_groups_for(name: object) -> tuple[str, ...]:
|
||||
"""The faith groups of a GIAS religious character name.
|
||||
|
||||
A missing or blank name is "No religious character". A name the
|
||||
dictionary does not know has no faith, so it matches no faith option.
|
||||
"""
|
||||
if not isinstance(name, str):
|
||||
return ("none",) if name is None or pd.isna(name) else ()
|
||||
return _FAITHS_BY_NAME.get(_key(name), ())
|
||||
Reference in new issue
Block a user