feat(tradein): add data_quality flag to /sales-vs-listings (#721)

ADR variant 3 (human-decided): house-level ДКП↔listing join is physically
impossible (deals carry only a street centroid). Instead of null-silence at
linkage=0, expose an honest enum so the frontend can tell 'no house linkage'
(street_only) from 'no data' (no_data) from 'linked' (house_linked).

- SalesVsListingsResponse: new required field data_quality.
- _empty() → 'no_data'; main return → 'house_linked' if deals_with_listings>0
  else 'street_only' (main return guarded by 'if not rows' → total_deals>0).

Does not touch SQL linkage / cadastre / geo ingest (out of scope per ADR).

Refs #721
This commit is contained in:
bot-backend 2026-05-30 16:18:01 +03:00
parent a79cf7d5e0
commit 1917b6dbf7
2 changed files with 4 additions and 0 deletions

View file

@ -1581,6 +1581,7 @@ def get_sales_vs_listings(
deals_with_listings=0,
linkage_rate_pct=0.0,
median_discount_pct=None,
data_quality="no_data", # #721 ADR v3: нет сделок / улица не извлеклась
pairs=[],
)
@ -1686,5 +1687,7 @@ def get_sales_vs_listings(
deals_with_listings=deals_with_listings,
linkage_rate_pct=linkage_rate_pct,
median_discount_pct=median_discount,
# #721 ADR v3: total_deals>0 здесь (guard `if not rows` выше → _empty).
data_quality="house_linked" if deals_with_listings > 0 else "street_only",
pairs=pairs,
)

View file

@ -457,6 +457,7 @@ class SalesVsListingsResponse(BaseModel):
deals_with_listings: int # сколько имеют связанный listing
linkage_rate_pct: float # deals_with_listings / total_deals * 100
median_discount_pct: float | None # медиана по парам с listing
data_quality: str # "house_linked" | "street_only" | "no_data" (#721, ADR v3)
pairs: list[SalesListingPair] # все пары, sorted by deal_date DESC