coastwise / coastwise /source_registry.py
Stephen Lee
feat: add official source cache foundation
a3c85ad
Raw
History Blame Contribute Delete
7.79 kB
"""Official source registry for source-backed CoastWise answers."""
from __future__ import annotations
from coastwise.schemas import OfficialSource, OfficialSourceType, StatewideSourceCoverage, ValidationIssue
APPROVED_HOSTS = ("wildlife.ca.gov", "cdph.ca.gov", "www.cdph.ca.gov")
REGION_KEYS = (
"northern",
"mendocino",
"san_francisco",
"central",
"southern",
"san_francisco_bay",
)
def load_official_sources() -> list[OfficialSource]:
"""Return the curated CDFW/CDPH official source allowlist."""
return [
OfficialSource(
id="cdfw_ocean_regulations",
title="CDFW Ocean Sport Fishing Regulations",
source_type=OfficialSourceType.CDFW_OCEAN_REGULATIONS,
url="https://wildlife.ca.gov/Fishing/Ocean/Regulations",
scope="Statewide California ocean sport fishing regulations",
statewide_required=True,
required_for_mvp=True,
),
OfficialSource(
id="cdfw_inseason_changes",
title="CDFW Ocean Sport Fishing In-Season Regulation Changes",
source_type=OfficialSourceType.CDFW_OCEAN_REGULATIONS,
url="https://wildlife.ca.gov/Fishing/Ocean/Regulations/Inseason",
scope="Statewide California ocean in-season regulation changes",
statewide_required=True,
required_for_mvp=True,
),
OfficialSource(
id="cdfw_region_northern",
title="CDFW Northern Ocean Region",
source_type=OfficialSourceType.CDFW_OCEAN_REGION_MAP,
url="https://wildlife.ca.gov/Fishing/Ocean/Regulations/Fishing-Map/Northern",
scope="Northern California ocean region",
region_key="northern",
statewide_required=True,
required_for_mvp=True,
),
OfficialSource(
id="cdfw_region_mendocino",
title="CDFW Mendocino Ocean Region",
source_type=OfficialSourceType.CDFW_OCEAN_REGION_MAP,
url="https://wildlife.ca.gov/Fishing/Ocean/Regulations/Fishing-Map/Mendocino",
scope="Mendocino California ocean region",
region_key="mendocino",
statewide_required=True,
required_for_mvp=True,
),
OfficialSource(
id="cdfw_region_san_francisco",
title="CDFW San Francisco Ocean Region",
source_type=OfficialSourceType.CDFW_OCEAN_REGION_MAP,
url="https://wildlife.ca.gov/Fishing/Ocean/Regulations/Fishing-Map/San-Francisco",
scope="San Francisco California ocean region",
region_key="san_francisco",
statewide_required=True,
required_for_mvp=True,
),
OfficialSource(
id="cdfw_region_central",
title="CDFW Central Ocean Region",
source_type=OfficialSourceType.CDFW_OCEAN_REGION_MAP,
url="https://wildlife.ca.gov/Fishing/Ocean/Regulations/Fishing-Map/Central",
scope="Central California ocean region",
region_key="central",
statewide_required=True,
required_for_mvp=True,
),
OfficialSource(
id="cdfw_region_southern",
title="CDFW Southern Ocean Region",
source_type=OfficialSourceType.CDFW_OCEAN_REGION_MAP,
url="https://wildlife.ca.gov/Fishing/Ocean/Regulations/Fishing-Map/Southern",
scope="Southern California ocean region",
region_key="southern",
statewide_required=True,
required_for_mvp=True,
),
OfficialSource(
id="cdfw_region_san_francisco_bay",
title="CDFW San Francisco Bay Region",
source_type=OfficialSourceType.CDFW_REGIONAL_REGULATIONS,
url="https://wildlife.ca.gov/Fishing/Ocean/Regulations/Fishing-Map/SF-Bay",
scope="San Francisco Bay sport fishing region",
region_key="san_francisco_bay",
statewide_required=True,
required_for_mvp=True,
),
OfficialSource(
id="cdfw_groundfish_summary",
title="CDFW Groundfish Summary",
source_type=OfficialSourceType.CDFW_SPECIES_REGULATIONS,
url="https://wildlife.ca.gov/Fishing/Ocean/Regulations/Groundfish-Summary",
scope="California ocean groundfish summaries",
statewide_required=True,
required_for_mvp=True,
),
OfficialSource(
id="cdfw_crabs",
title="CDFW Crabs",
source_type=OfficialSourceType.CDFW_SPECIES_REGULATIONS,
url="https://wildlife.ca.gov/Conservation/Marine/Invertebrates/Crabs",
scope="California ocean crab regulations",
statewide_required=True,
required_for_mvp=True,
),
OfficialSource(
id="cdph_shellfish_advisories",
title="CDPH Shellfish Advisories",
source_type=OfficialSourceType.CDPH_SHELLFISH_ADVISORY,
url="https://www.cdph.ca.gov/Programs/OPA/Pages/Shellfish-Advisories.aspx",
scope="California shellfish advisories",
statewide_required=True,
required_for_mvp=True,
),
OfficialSource(
id="cdph_annual_mussel_quarantine",
title="CDPH Annual Mussel Quarantine",
source_type=OfficialSourceType.CDPH_ANNUAL_MUSSEL_QUARANTINE,
url="https://www.cdph.ca.gov/Programs/CEH/DRSEM/Pages/EMB/Shellfish/Annual-Mussel-Quarantine.aspx",
scope="California annual mussel quarantine",
statewide_required=True,
required_for_mvp=True,
),
]
def statewide_source_coverage() -> StatewideSourceCoverage:
"""Return the statewide California ocean source coverage guarantee."""
return StatewideSourceCoverage(
id="statewide_ca_ocean",
required_region_keys=REGION_KEYS,
required_source_ids=tuple(source.id for source in load_official_sources() if source.required_for_mvp),
general_source_ids=(
"cdfw_ocean_regulations",
"cdfw_inseason_changes",
"cdfw_groundfish_summary",
"cdfw_crabs",
),
cdph_source_ids=("cdph_shellfish_advisories", "cdph_annual_mussel_quarantine"),
)
def source_by_id(source_id: str) -> OfficialSource | None:
return {source.id: source for source in load_official_sources()}.get(source_id)
def validate_source_registry(sources: list[OfficialSource]) -> list[ValidationIssue]:
"""Validate official-only source coverage and URL policy."""
issues: list[ValidationIssue] = []
seen: set[str] = set()
for source in sources:
if source.id in seen:
issues.append(ValidationIssue("id", "error", f"Duplicate source id: {source.id}"))
seen.add(source.id)
if not source.url.startswith("https://"):
issues.append(ValidationIssue(source.id, "error", "Source URLs must use HTTPS."))
if not any(host in source.url for host in APPROVED_HOSTS):
issues.append(ValidationIssue(source.id, "error", "Source URL is not CDFW/CDPH official."))
by_region = {source.region_key for source in sources if source.region_key}
for region_key in REGION_KEYS:
if region_key not in by_region:
issues.append(ValidationIssue(region_key, "error", "Missing statewide region source."))
coverage = statewide_source_coverage()
source_ids = {source.id for source in sources}
for source_id in coverage.required_source_ids:
if source_id not in source_ids:
issues.append(ValidationIssue(source_id, "error", "Missing MVP-required source."))
return issues