"""Pydantic models for FINN ads and Eiendom.no units.""" from datetime import UTC, datetime from pydantic import BaseModel, ConfigDict, Field class FinnSearchCard(BaseModel): """FINN search result card (minimal fields from search listing).""" finnkode: str url: str title: str | None = None address: str | None = None area_m2: int | None = None asking_price: int | None = None total_price: int | None = None common_costs: int | None = None property_type: str | None = None ownership_type: str | None = None bedrooms: int | None = None floor: str | None = None broker_company: str | None = None class FinnAd(BaseModel): """FINN listing detail with all available fields.""" finnkode: str url: str title: str | None = None address: str | None = None postal_area: str | None = None district: str | None = None property_type: str | None = None ownership_type: str | None = None asking_price: int | None = None total_price: int | None = None shared_debt: int | None = None common_costs: int | None = None municipal_fee: int | None = None other_fees: int | None = None area_m2: int | None = None rooms: int | None = None bedrooms: int | None = None floor: str | None = None construction_year: int | None = None energy_rating: str | None = None heating: str | None = None has_balcony: bool | None = None has_terrace: bool | None = None has_elevator: bool | None = None has_parking: bool | None = None has_garage: bool | None = None listing_description: str | None = None broker_name: str | None = None broker_company: str | None = None first_seen_at: datetime = Field(default_factory=lambda: datetime.now(UTC)) last_seen_at: datetime = Field(default_factory=lambda: datetime.now(UTC)) detail_fetched_at: datetime | None = None eiendom_unit_code: str | None = None model_config = ConfigDict(serializers={datetime: lambda v: v.isoformat()}) class EiendomUnit(BaseModel): """Eiendom.no unit detail with market data.""" unit_code: str address: str | None = None lat: float | None = None lng: float | None = None property_type: str | None = None floor: int | None = None rooms: int | None = None construction_year: int | None = None usable_area: int | None = None estimated_selling_price: int | None = None estimated_selling_price_lower: int | None = None estimated_selling_price_upper: int | None = None listing_price: int | None = None listing_sqm_price: int | None = None common_costs: int | None = None days_on_market: int | None = None sale_status: str | None = None market_placement_score: str | None = None unit_vector: str | None = None fetched_at: datetime = Field(default_factory=lambda: datetime.now(UTC)) model_config = ConfigDict(serializers={datetime: lambda v: v.isoformat()}) class SimilarUnit(BaseModel): """Eiendom.no similar unit (comp) result.""" unit_code: str address: str | None = None lat: float | None = None lng: float | None = None property_type: str | None = None floor: int | None = None rooms: int | None = None construction_year: int | None = None usable_area: int | None = None listing_price: int | None = None selling_price: int | None = None shared_debt: int | None = None common_costs: int | None = None sqm_price: int | None = None days_on_market: int | None = None sale_status: str | None = None finalized_at: datetime | None = None listing_status: str = Field(default="RECENTLY_SOLD") model_config = ConfigDict(serializers={datetime: lambda v: v.isoformat() if v else None}) class UnitVector(BaseModel): """Unit vector payload for similar-units API.""" lon: float lat: float ptype: str # property type: APARTMENT, HOUSE, etc. floor: int | None = None rooms: int | None = None built: int | None = None # construction year area: int | None = None # usable area price: int | None = None # listing or estimated price