gendesign/backend/app/schemas/recommend.py

63 lines
2.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""IO contracts for the rule-based квартирография recommender.
POST /api/v1/analytics/recommend/mix
"""
from typing import Any, Literal
from pydantic import BaseModel, Field
ClassLiteral = Literal["Comfort", "Comfort+", "Business", "Elite"]
class RecommendMixInput(BaseModel):
district_name: str = Field(..., min_length=2, max_length=80)
area_total_m2: float | None = Field(default=None, ge=100, le=500_000)
target_class: ClassLiteral | None = None
months_window: int = Field(default=12, ge=3, le=24)
# Velocity / pricing scenario knobs (live-tuned client-side; backend just
# ships base coefficients so frontend can recompute without round-trips).
# 0.01..3.0 = -99%..+200% к рынку. min=0.01 (а не 0) чтобы избежать
# деления на 0 / pf^elasticity = ∞ при «бесплатной» цене.
price_factor: float = Field(default=1.0, ge=0.01, le=3.0)
target_months: int | None = Field(default=None, ge=3, le=120)
class RecommendBucket(BaseModel):
bucket: str
share_pct: float
deal_count: int
area_avg_m2: float
area_median_m2: float
price_median_per_m2: float
price_p25_per_m2: float
price_p75_per_m2: float
units_planned: int | None = None
revenue_planned_rub: float | None = None
# Velocity baseline (units/month for THIS project allocated to this bucket
# at price_factor=1.0). Frontend scales by price_factor^elasticity for live
# what-if recompute.
velocity_per_month: float | None = None
months_to_sellout: float | None = None
# Success-driven mix flag (issue #25): bucket has top success_score in district
is_top_success: bool = False
class RecommendComparable(BaseModel):
obj_id: int
comm_name: str | None = None
dev_name: str | None = None
obj_class: str | None = None
flat_count: int | None = None
sold_perc: float | None = None
cad_quarter: str | None = None
lat: float | None = None
lon: float | None = None
buildings_n: int | None = None
class RecommendMixOutput(BaseModel):
scope: dict[str, Any]
buckets: list[RecommendBucket]
summary: dict[str, Any]
comparables: list[RecommendComparable]