gendesign/tradein-mvp/backend/app/schemas/buildings.py
lekss361 2a38561175
Some checks failed
Deploy Trade-In / build-backend (push) Blocked by required conditions
Deploy Trade-In / deploy (push) Blocked by required conditions
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Has been cancelled
feat(tradein): второй % «за 45 дней» + zhkh-знаменатель slot (146) (#2071)
2026-06-28 17:51:38 +00:00

90 lines
3.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.

"""Pydantic-схемы страницы «доля квартир дома в продаже» (мигр. 143).
Источник данных — view v_building_sale_share + базовая таблица listings.
Список домов вторичного рынка, где active_secondary / flat_count_effective >= threshold%.
"""
from __future__ import annotations
from datetime import date
from pydantic import BaseModel, ConfigDict
class BuildingSaleShare(BaseModel):
"""Один дом из v_building_sale_share для списка /buildings/sale-share."""
model_config = ConfigDict(from_attributes=True)
house_id: int
address: str | None = None
lat: float | None = None
lon: float | None = None
sale_share_pct: float | None = None
# 2-й процент: distinct vtorichka за последние 45д (активные ИЛИ недавно
# закрытые) / знаменатель. Дом проходит фильтр по ЛЮБОМУ из двух процентов.
sale_share_pct_45d: float | None = None
# implausible-матч (числитель > знаменателя по любому из %) — скорее всего
# коллизия адреса при ГАР-матче; не прячем, а маркируем для UI.
over_100: bool = False
active_secondary: int | None = None
# distinct vtorichka-листинги (активные ИЛИ закрытые) за окно 45д.
listings_45d: int | None = None
flat_count_effective: int | None = None
gar_match_method: str | None = None
median_price_rub: int | None = None
median_price_per_m2: int | None = None
avg_days_on_market: float | None = None
year_built: int | None = None
house_type: str | None = None
total_floors: int | None = None
series_name: str | None = None
is_emergency: bool | None = None
class BuildingListing(BaseModel):
"""Активное вторичное объявление в доме (click-through drawer)."""
model_config = ConfigDict(from_attributes=True)
listing_id: int
source: str
source_url: str | None = None
price_rub: int | None = None
price_per_m2: int | None = None
rooms: int | None = None
area_m2: float | None = None
floor: int | None = None
total_floors: int | None = None
days_on_market: int | None = None
listing_date: date | None = None
address: str | None = None
class HistogramBucket(BaseModel):
"""Корзина гистограммы распределения sale_share_pct."""
bucket: str
count: int
class SaleShareSummary(BaseModel):
"""Сводка для слайдера % + баннера покрытия (/buildings/sale-share/summary).
buildings_with_denominator/coverage считают дома с непустым ЛЮБЫМ из двух %.
max/p95 и histogram — отдельно для каждого процента (now + 45д).
"""
total_secondary_buildings: int
buildings_with_denominator: int
coverage_pct: float
max_pct: float | None = None
p95_pct: float | None = None
max_pct_45d: float | None = None
p95_pct_45d: float | None = None
histogram: list[HistogramBucket]
histogram_45d: list[HistogramBucket]