58 lines
1.6 KiB
Python
58 lines
1.6 KiB
Python
"""Layout signature extraction для Issue #113 (Phase 2.1 minimal).
|
|
|
|
Без `layout_type` / `balcony_count` — ждут B2B Объектив (#52).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Literal
|
|
|
|
RoomBucket = Literal["studio", "1", "2", "3", "4+"]
|
|
AreaBin = Literal["<25", "25-40", "40-60", "60-80", "80-100", "100+"]
|
|
|
|
|
|
def room_bucket_from_flat(
|
|
rooms: int | None,
|
|
flat_type: str | None,
|
|
is_studio: bool | None,
|
|
) -> RoomBucket:
|
|
"""Determine room_bucket из kn_flats полей.
|
|
|
|
Priority:
|
|
1. is_studio=True OR flat_type='Квартира-студия' → "studio"
|
|
2. rooms IN (1, 2, 3) → str(rooms)
|
|
3. rooms >= 4 → "4+"
|
|
4. rooms = 0 (без is_studio) → "studio"
|
|
5. fallback на "1" (rooms is None и не studio)
|
|
"""
|
|
if is_studio is True or flat_type == "Квартира-студия":
|
|
return "studio"
|
|
if rooms is None:
|
|
return "1"
|
|
if rooms == 0:
|
|
return "studio"
|
|
if rooms >= 4:
|
|
return "4+"
|
|
if rooms in (1, 2, 3):
|
|
return str(rooms) # type: ignore[return-value]
|
|
return "1"
|
|
|
|
|
|
def area_bin(area_m2: float) -> AreaBin:
|
|
"""Bucket площади per ARN-buckets (НСПД)."""
|
|
if area_m2 < 25.0:
|
|
return "<25"
|
|
if area_m2 < 40.0:
|
|
return "25-40"
|
|
if area_m2 < 60.0:
|
|
return "40-60"
|
|
if area_m2 < 80.0:
|
|
return "60-80"
|
|
if area_m2 < 100.0:
|
|
return "80-100"
|
|
return "100+"
|
|
|
|
|
|
def layout_signature(room_bucket_val: RoomBucket, area_bin_val: AreaBin) -> str:
|
|
"""Deterministic string-signature для группировки."""
|
|
return f"{room_bucket_val}__{area_bin_val}"
|