feat(tradein): insufficient_data flag on AggregatedEstimate (#697) #734

Merged
bot-reviewer merged 1 commit from fix/697-insufficient-data into main 2026-05-30 14:40:05 +00:00
2 changed files with 62 additions and 1 deletions

View file

@ -9,7 +9,7 @@ from datetime import date, datetime
from typing import Any, Literal
from uuid import UUID
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, computed_field
class TradeInEstimateInput(BaseModel):
@ -132,6 +132,18 @@ class AggregatedEstimate(BaseModel):
confidence: Literal["low", "medium", "high"]
confidence_explanation: str | None = None # «Найдено 15 аналогов, разброс ±7%»
n_analogs: int
@computed_field # type: ignore[prop-decorator]
@property
def insufficient_data(self) -> bool:
"""#697: нет пригодной оценки — комплов/якоря не нашлось → headline median=0.
Фронт по этому флагу рендерит явное «недостаточно данных», а не буквальный
«0 ». Производное от median_price_rub (==0 нет оценки), поэтому корректно
во всех конструкторах AggregatedEstimate автоматически (вкл. rehydrate на GET).
"""
return self.median_price_rub <= 0
period_months: int # 24
analogs: list[AnalogLot] # top 5-10 listings
actual_deals: list[AnalogLot] # реальные продажи last 12 mo

View file

@ -0,0 +1,49 @@
"""#697 — AggregatedEstimate.insufficient_data computed flag.
median=0 (нет комплов/якоря) insufficient_data=True, чтобы фронт показал
«недостаточно данных», а не «0 ». Производное от median_price_rub.
"""
import os
from datetime import UTC, datetime
from uuid import uuid4
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost/test_db")
from app.schemas.trade_in import AggregatedEstimate
def _estimate(median: int) -> AggregatedEstimate:
return AggregatedEstimate(
estimate_id=uuid4(),
median_price_rub=median,
range_low_rub=0 if median == 0 else median - 100,
range_high_rub=0 if median == 0 else median + 100,
median_price_per_m2=0 if median == 0 else 150_000,
confidence="low" if median == 0 else "medium",
n_analogs=0 if median == 0 else 12,
period_months=12,
analogs=[],
actual_deals=[],
expires_at=datetime(2026, 6, 1, tzinfo=UTC),
)
def test_insufficient_when_median_zero() -> None:
assert _estimate(0).insufficient_data is True
def test_sufficient_when_median_positive() -> None:
assert _estimate(6_900_000).insufficient_data is False
def test_flag_serialized_in_model_dump() -> None:
dump = _estimate(0).model_dump()
assert dump["insufficient_data"] is True
dump2 = _estimate(5_000_000).model_dump()
assert dump2["insufficient_data"] is False
def test_negative_median_is_insufficient() -> None:
# защита от случайного отрицательного headline.
assert _estimate(-1).insufficient_data is True