Sprint 3 implementation: - Pydantic schemas for TRREB, CMHC, and dimension data validation - SQLAlchemy models with PostGIS geometry for fact and dimension tables - Parser structure (stubs) for TRREB PDF and CMHC CSV processing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
53 lines
1.8 KiB
Python
53 lines
1.8 KiB
Python
"""Pydantic schemas for TRREB monthly market data."""
|
|
|
|
from datetime import date
|
|
from decimal import Decimal
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class TRREBMonthlyRecord(BaseModel):
|
|
"""Schema for a single TRREB monthly summary record.
|
|
|
|
Represents aggregated sales data for one district in one month.
|
|
"""
|
|
|
|
report_date: date = Field(description="First of month (YYYY-MM-01)")
|
|
area_code: str = Field(
|
|
max_length=3, description="District code (W01, C01, E01, etc.)"
|
|
)
|
|
area_name: str = Field(max_length=100, description="District name")
|
|
area_type: str = Field(max_length=10, description="West / Central / East / North")
|
|
sales: int = Field(ge=0, description="Number of transactions")
|
|
dollar_volume: Decimal = Field(ge=0, description="Total sales volume ($)")
|
|
avg_price: Decimal = Field(ge=0, description="Average sale price ($)")
|
|
median_price: Decimal = Field(ge=0, description="Median sale price ($)")
|
|
new_listings: int = Field(ge=0, description="New listings count")
|
|
active_listings: int = Field(ge=0, description="Active listings at month end")
|
|
avg_sp_lp: Decimal = Field(
|
|
ge=0, le=200, description="Avg sale price / list price ratio (%)"
|
|
)
|
|
avg_dom: int = Field(ge=0, description="Average days on market")
|
|
|
|
model_config = {"str_strip_whitespace": True}
|
|
|
|
|
|
class TRREBMonthlyReport(BaseModel):
|
|
"""Schema for a complete TRREB monthly report.
|
|
|
|
Contains all district records for a single month.
|
|
"""
|
|
|
|
report_date: date
|
|
records: list[TRREBMonthlyRecord]
|
|
|
|
@property
|
|
def total_sales(self) -> int:
|
|
"""Total sales across all districts."""
|
|
return sum(r.sales for r in self.records)
|
|
|
|
@property
|
|
def district_count(self) -> int:
|
|
"""Number of districts in report."""
|
|
return len(self.records)
|