""" Per-user filter matching. Each user has one row in user_filters. A flat matches the user if all of the user's non-null constraints are satisfied. Empty filters = matches all. """ import logging from typing import Iterable logger = logging.getLogger("web.matching") def flat_matches_filter(flat: dict, f: dict | None) -> bool: """f is a user_filters row converted to dict (or None = no filter set).""" if not f: return True rooms = flat.get("rooms") or 0.0 rent = flat.get("total_rent") or 0.0 size = flat.get("size") or 0.0 wbs_str = str(flat.get("wbs", "")).strip().lower() if f.get("rooms_min") is not None and rooms < float(f["rooms_min"]): return False if f.get("rooms_max") is not None and rooms > float(f["rooms_max"]): return False if f.get("max_rent") is not None and rent > float(f["max_rent"]): return False if f.get("min_size") is not None and size < float(f["min_size"]): return False wbs_req = (f.get("wbs_required") or "").strip().lower() if wbs_req == "yes": if not wbs_str or wbs_str in ("kein", "nein", "no", "ohne", "-"): return False elif wbs_req == "no": if wbs_str and wbs_str not in ("kein", "nein", "no", "ohne", "-"): return False # Berlin-Bezirk filter. Empty string = no filter = all Bezirke match. # When active, flats with an unknown/unmapped PLZ are excluded — if the # user bothered to narrow by district, we shouldn't sneak in flats we # couldn't place. districts_csv = (f.get("districts") or "").strip() if districts_csv: selected = {d.strip() for d in districts_csv.split(",") if d.strip()} flat_district = (flat.get("district") or "").strip() if not flat_district or flat_district not in selected: return False return True def row_to_dict(row) -> dict: if row is None: return {} try: return {k: row[k] for k in row.keys()} except Exception: return dict(row)