mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
64 lines
2.1 KiB
Python
64 lines
2.1 KiB
Python
from dataclasses import dataclass
|
|
from typing import List, Literal, Optional, Sequence, Tuple
|
|
|
|
PhoneKind = Literal["mobile", "telephone"]
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class PhoneEntry:
|
|
"""One phone number as Abri lists it for a tenant, in document order."""
|
|
|
|
kind: PhoneKind
|
|
number: str
|
|
priority: Optional[str] = None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class SelectedPhoneNumbers:
|
|
"""The numbers a contact carries: best of each kind plus the runner-up."""
|
|
|
|
mobile: Optional[str]
|
|
telephone: Optional[str]
|
|
secondary: Optional[str]
|
|
|
|
|
|
# TODO(#1474): confirm the priority ordering with Abri — this assumes the
|
|
# lowest numeric priority marks the preferred number, and that entries with
|
|
# a missing or non-numeric priority rank last (ties keep document order).
|
|
def _priority_sort_key(entry: PhoneEntry) -> Tuple[int, int]:
|
|
"""Sort key: lowest numeric priority first; unusable priorities last."""
|
|
try:
|
|
return (0, int((entry.priority or "").strip()))
|
|
except ValueError:
|
|
return (1, 0)
|
|
|
|
|
|
def _best(entries: List[PhoneEntry]) -> Optional[PhoneEntry]:
|
|
ranked = sorted(entries, key=_priority_sort_key) # stable: ties keep document order
|
|
return ranked[0] if ranked else None
|
|
|
|
|
|
def _number(entry: Optional[PhoneEntry]) -> Optional[str]:
|
|
return entry.number.strip() if entry is not None else None
|
|
|
|
|
|
def select_phone_numbers(entries: Sequence[PhoneEntry]) -> SelectedPhoneNumbers:
|
|
"""Best mobile, best landline, then the best of the leftovers of both kinds.
|
|
|
|
All three picks use the same rule: lowest numeric priority wins, entries
|
|
without a usable priority sort last, ties break by document order, blank
|
|
numbers are ignored.
|
|
"""
|
|
usable = [entry for entry in entries if entry.number.strip()]
|
|
|
|
best_mobile = _best([e for e in usable if e.kind == "mobile"])
|
|
best_telephone = _best([e for e in usable if e.kind == "telephone"])
|
|
runner_up = _best(
|
|
[e for e in usable if e is not best_mobile and e is not best_telephone]
|
|
)
|
|
|
|
return SelectedPhoneNumbers(
|
|
mobile=_number(best_mobile),
|
|
telephone=_number(best_telephone),
|
|
secondary=_number(runner_up),
|
|
)
|