Matching

PropertyMatcher applies hard filters first and soft scoring second. A property that fails a hard requirement is never rescued by a high score.

class app.alba_core.matching.MatchingOutput(matches: list[MatchResult], rejected_reasons: dict[str, int])[source]

Collection of successful matches and aggregate rejection reasons.

to_dict() dict[str, object][source]

Return a JSON-friendly matching response.

class app.alba_core.matching.PropertyMatcher(top_n: int = 3)[source]

Hard filters first, soft scoring second.

The most important rule is that scoring never rescues a listing that failed a hard requirement such as city, budget, or minimum bedrooms.

match(requirements: RentalRequirements, properties: list[PropertyRecord]) MatchingOutput[source]

Filter, score, sort, and return the best matching properties.

Example

from app.alba_core.matching import PropertyMatcher
from app.alba_core.models import RentalRequirements
from app.propertyme.cache import load_property_cache

properties = load_property_cache("data/propertyme_property_seed_latest.json")
requirements = RentalRequirements(
    city="Queenstown",
    budget_max=5000,
    bedrooms_min=3,
    move_in_timing="asap",
    priority="location",
)

output = PropertyMatcher(top_n=3).match(requirements, properties)

print(output.rejected_reasons)
for match in output.matches:
    print(match.property.address, match.score, match.match_notes)

No-match handling

impossible = RentalRequirements(city="Auckland", budget_max=100, bedrooms_min=8)
output = PropertyMatcher().match(impossible, properties)

if not output.matches:
    print("No real listing fits the current hard filters.")
    print(output.rejected_reasons)