Files
personal-portfolio/portfolio_app/toronto/schemas/amenities.py
lmiranda 053acf6436 feat: Implement Phase 3 neighbourhood data model
Add schemas, parsers, loaders, and models for Toronto neighbourhood-centric
data including census profiles, crime statistics, and amenities.

Schemas:
- NeighbourhoodRecord, CensusRecord, CrimeRecord, CrimeType
- AmenityType, AmenityRecord, AmenityCount

Models:
- BridgeCMHCNeighbourhood (zone-to-neighbourhood mapping with weights)
- FactCensus, FactCrime, FactAmenities

Parsers:
- TorontoOpenDataParser (CKAN API for neighbourhoods, census, amenities)
- TorontoPoliceParser (crime rates, MCI data)

Loaders:
- load_census_data, load_crime_data, load_amenities
- build_cmhc_neighbourhood_crosswalk (PostGIS area weights)

Also updates CLAUDE.md with projman plugin workflow documentation.

Closes #53, #54, #55, #56, #57, #58, #59

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 11:07:13 -05:00

61 lines
1.8 KiB
Python

"""Pydantic schemas for Toronto amenities data.
Includes schemas for parks, schools, childcare centres, and transit stops.
"""
from decimal import Decimal
from enum import Enum
from pydantic import BaseModel, Field
class AmenityType(str, Enum):
"""Types of amenities tracked in the neighbourhood dashboard."""
PARK = "park"
SCHOOL = "school"
CHILDCARE = "childcare"
TRANSIT_STOP = "transit_stop"
LIBRARY = "library"
COMMUNITY_CENTRE = "community_centre"
HOSPITAL = "hospital"
class AmenityRecord(BaseModel):
"""Amenity location record for a neighbourhood.
Represents a single amenity (park, school, etc.) with its location
and associated neighbourhood.
"""
neighbourhood_id: int = Field(
ge=1, le=200, description="Neighbourhood ID containing this amenity"
)
amenity_type: AmenityType = Field(description="Type of amenity")
amenity_name: str = Field(max_length=200, description="Name of the amenity")
address: str | None = Field(
default=None, max_length=300, description="Street address"
)
latitude: Decimal | None = Field(
default=None, ge=-90, le=90, description="Latitude (WGS84)"
)
longitude: Decimal | None = Field(
default=None, ge=-180, le=180, description="Longitude (WGS84)"
)
model_config = {"str_strip_whitespace": True}
class AmenityCount(BaseModel):
"""Aggregated amenity count for a neighbourhood.
Used for dashboard metrics showing amenity density per neighbourhood.
"""
neighbourhood_id: int = Field(ge=1, le=200, description="Neighbourhood ID")
amenity_type: AmenityType = Field(description="Type of amenity")
count: int = Field(ge=0, description="Number of amenities of this type")
year: int = Field(ge=2020, le=2030, description="Year of data snapshot")
model_config = {"str_strip_whitespace": True}