"""SQLAlchemy models for dimension tables.""" from datetime import date from geoalchemy2 import Geometry from sqlalchemy import Boolean, Date, Integer, Numeric, String, Text from sqlalchemy.orm import Mapped, mapped_column from .base import Base class DimTime(Base): """Time dimension table.""" __tablename__ = "dim_time" date_key: Mapped[int] = mapped_column(Integer, primary_key=True) full_date: Mapped[date] = mapped_column(Date, nullable=False, unique=True) year: Mapped[int] = mapped_column(Integer, nullable=False) month: Mapped[int] = mapped_column(Integer, nullable=False) quarter: Mapped[int] = mapped_column(Integer, nullable=False) month_name: Mapped[str] = mapped_column(String(20), nullable=False) is_month_start: Mapped[bool] = mapped_column(Boolean, default=True) class DimTRREBDistrict(Base): """TRREB district dimension table with PostGIS geometry.""" __tablename__ = "dim_trreb_district" district_key: Mapped[int] = mapped_column( Integer, primary_key=True, autoincrement=True ) district_code: Mapped[str] = mapped_column(String(3), nullable=False, unique=True) district_name: Mapped[str] = mapped_column(String(100), nullable=False) area_type: Mapped[str] = mapped_column(String(10), nullable=False) geometry = mapped_column(Geometry("POLYGON", srid=4326), nullable=True) class DimCMHCZone(Base): """CMHC zone dimension table with PostGIS geometry.""" __tablename__ = "dim_cmhc_zone" zone_key: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) zone_code: Mapped[str] = mapped_column(String(10), nullable=False, unique=True) zone_name: Mapped[str] = mapped_column(String(100), nullable=False) geometry = mapped_column(Geometry("POLYGON", srid=4326), nullable=True) class DimNeighbourhood(Base): """City of Toronto neighbourhood dimension. Note: No FK to fact tables in V1 - reference overlay only. """ __tablename__ = "dim_neighbourhood" neighbourhood_id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String(100), nullable=False) geometry = mapped_column(Geometry("POLYGON", srid=4326), nullable=True) population: Mapped[int | None] = mapped_column(Integer, nullable=True) land_area_sqkm: Mapped[float | None] = mapped_column(Numeric(10, 4), nullable=True) pop_density_per_sqkm: Mapped[float | None] = mapped_column( Numeric(10, 2), nullable=True ) pct_bachelors_or_higher: Mapped[float | None] = mapped_column( Numeric(5, 2), nullable=True ) median_household_income: Mapped[float | None] = mapped_column( Numeric(12, 2), nullable=True ) pct_owner_occupied: Mapped[float | None] = mapped_column( Numeric(5, 2), nullable=True ) pct_renter_occupied: Mapped[float | None] = mapped_column( Numeric(5, 2), nullable=True ) census_year: Mapped[int] = mapped_column(Integer, default=2021) class DimPolicyEvent(Base): """Policy event dimension for time-series annotation.""" __tablename__ = "dim_policy_event" event_id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) event_date: Mapped[date] = mapped_column(Date, nullable=False) effective_date: Mapped[date | None] = mapped_column(Date, nullable=True) level: Mapped[str] = mapped_column( String(20), nullable=False ) # federal/provincial/municipal category: Mapped[str] = mapped_column( String(20), nullable=False ) # monetary/tax/regulatory/supply/economic title: Mapped[str] = mapped_column(String(200), nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) expected_direction: Mapped[str] = mapped_column( String(10), nullable=False ) # bearish/bullish/neutral source_url: Mapped[str | None] = mapped_column(String(500), nullable=True) confidence: Mapped[str] = mapped_column( String(10), default="medium" ) # high/medium/low