Skip to content

Models

Data models for household allocation.

This module contains the core dataclasses used throughout the household allocation system.

Note: Households are now represented as Venue objects with type="household". The Household class has been removed in favor of the generic Venue system.

Category dataclass

Generic category for classifying entities based on any attribute.

Supports: - Numerical attributes (e.g., age, income) with min/max ranges - Categorical attributes (future: education level, gender) with allowed values

Examples:

Age category

Category(name="Kids", symbol="K", attribute="age", type="numerical", min_value=0, max_value=17)

Income category

Category(name="Low Income", symbol="LI", attribute="income", type="numerical", min_value=0, max_value=30000)

Source code in may/residence/models.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
@dataclass
class Category:
    """
    Generic category for classifying entities based on any attribute.

    Supports:
    - Numerical attributes (e.g., age, income) with min/max ranges
    - Categorical attributes (future: education level, gender) with allowed values

    Examples:
        # Age category
        Category(name="Kids", symbol="K", attribute="age", type="numerical",
                min_value=0, max_value=17)

        # Income category
        Category(name="Low Income", symbol="LI", attribute="income", type="numerical",
                min_value=0, max_value=30000)
    """
    name: str
    symbol: str
    attribute: str                      # e.g., "age", "income", "education"
    type: str                           # "numerical" or "categorical"
    min_value: Optional[float] = None   # For numerical types
    max_value: Optional[float] = None   # For numerical types
    allowed_values: Optional[List[str]] = None  # For categorical types (future)

    def matches(self, entity: Any) -> bool:
        """
        Check if an entity matches this category.

        Args:
            entity: Object with the attribute to check (e.g., Person with .age)

        Returns:
            True if entity's attribute value falls within this category
        """
        # Get the attribute value from the entity
        attr_value = get_person_attribute(entity, self.attribute)
        if attr_value is None:
            return False

        if self.type == "numerical":
            if self.max_value is None:
                return attr_value >= self.min_value
            return self.min_value <= attr_value <= self.max_value
        elif self.type == "categorical":
            if self.allowed_values is None:
                raise ValueError(f"Category {self.name} is categorical but has no allowed_values")
            return attr_value in self.allowed_values
        else:
            raise ValueError(f"Unknown category type: {self.type}")

    def __repr__(self):
        if self.type == "numerical":
            max_str = f"{self.max_value}" if self.max_value is not None else "∞"
            return f"{self.name}({self.attribute}:{self.min_value}-{max_str})"
        else:
            return f"{self.name}({self.attribute}:{self.allowed_values})"

matches(entity)

Check if an entity matches this category.

Parameters:

Name Type Description Default
entity Any

Object with the attribute to check (e.g., Person with .age)

required

Returns:

Type Description
bool

True if entity's attribute value falls within this category

Source code in may/residence/models.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def matches(self, entity: Any) -> bool:
    """
    Check if an entity matches this category.

    Args:
        entity: Object with the attribute to check (e.g., Person with .age)

    Returns:
        True if entity's attribute value falls within this category
    """
    # Get the attribute value from the entity
    attr_value = get_person_attribute(entity, self.attribute)
    if attr_value is None:
        return False

    if self.type == "numerical":
        if self.max_value is None:
            return attr_value >= self.min_value
        return self.min_value <= attr_value <= self.max_value
    elif self.type == "categorical":
        if self.allowed_values is None:
            raise ValueError(f"Category {self.name} is categorical but has no allowed_values")
        return attr_value in self.allowed_values
    else:
        raise ValueError(f"Unknown category type: {self.type}")