Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### v1.17.0
- Added `has_pool` parameter to `property.search.retrieve` and `property_v2.search.retrieve` to filter properties by pool availability.

### v1.16.2
- Deprecate `volatility` endpoints.

Expand Down
2 changes: 1 addition & 1 deletion parcllabs/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = "1.16.2"
VERSION = "1.17.0"
10 changes: 10 additions & 0 deletions parcllabs/schemas/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ class PropertyV2RetrieveParams(BaseModel):
current_investor_owned_flag: bool | None = Field(
default=None, description="Whether to filter by current investor owned flag"
)

# Property features
has_pool: bool | None = Field(
default=None,
description=(
"Whether to filter by pool. True returns properties with a pool. "
"False returns properties where pool data is not available."
),
)

current_entity_owner_name: str | None = Field(
default=None, description="Current entity owner name to filter by"
)
Expand Down
6 changes: 6 additions & 0 deletions parcllabs/services/properties/property_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def _prepare_params(
record_added_date_start: str | None = None,
record_added_date_end: str | None = None,
current_on_market_flag: bool | None = None,
has_pool: bool | None = None,
) -> dict:
params = {}

Expand All @@ -89,6 +90,7 @@ def _prepare_params(
"current_owner_occupied_flag": current_owner_occupied_flag,
"current_investor_owned_flag": current_investor_owned_flag,
"current_on_market_flag": current_on_market_flag,
"has_pool": has_pool,
}

for param_name, param_value in bool_flags.items():
Expand Down Expand Up @@ -140,6 +142,7 @@ def retrieve(
record_added_date_start: str | None = None,
record_added_date_end: str | None = None,
current_on_market_flag: bool | None = None,
has_pool: bool | None = None,
) -> pd.DataFrame:
"""
Retrieve parcl_property_id for geographic markets based on specified criteria.
Expand Down Expand Up @@ -180,6 +183,8 @@ def retrieve(
this date (YYYY-MM-DD). Defaults to None.
current_on_market_flag (bool, optional): Filter properties currently on the
market. Defaults to None.
has_pool (bool, optional): Filter properties by pool availability.
True returns properties with a pool. Defaults to None.

Returns:
pd.DataFrame: A DataFrame containing the parcl_property_id and other detail
Expand All @@ -206,6 +211,7 @@ def retrieve(
record_added_date_start=record_added_date_start,
record_added_date_end=record_added_date_end,
current_on_market_flag=current_on_market_flag,
has_pool=has_pool,
)

output_data = deque()
Expand Down
5 changes: 5 additions & 0 deletions parcllabs/services/properties/property_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ def _build_boolean_filters(self, params: PropertyV2RetrieveParams) -> dict[str,
filters["current_investor_owned_flag"] = self.simple_bool_validator(
params.current_investor_owned_flag
)
if params.has_pool is not None:
filters["has_pool"] = self.simple_bool_validator(params.has_pool)

return filters

Expand Down Expand Up @@ -488,6 +490,7 @@ def retrieve(
current_new_construction_flag: bool | None = None,
current_owner_occupied_flag: bool | None = None,
current_investor_owned_flag: bool | None = None,
has_pool: bool | None = None,
current_entity_owner_name: str | None = None,
include_events: bool | None = None,
include_full_event_history: bool | None = None,
Expand Down Expand Up @@ -532,6 +535,7 @@ def retrieve(
current_new_construction_flag: Whether to filter by current_new_construction flag.
current_owner_occupied_flag: Whether to filter by current_owner_occupied flag.
current_investor_owned_flag: Whether to filter by current_investor_owned flag.
has_pool: Whether to filter by pool availability.
current_entity_owner_name: Current entity owner name to filter by.
include_events: Whether to include events in the response.
include_full_event_history: Whether to include full event history in the response.
Expand Down Expand Up @@ -577,6 +581,7 @@ def retrieve(
current_new_construction_flag=current_new_construction_flag,
current_owner_occupied_flag=current_owner_occupied_flag,
current_investor_owned_flag=current_investor_owned_flag,
has_pool=has_pool,
current_entity_owner_name=current_entity_owner_name,
include_events=include_events,
include_full_event_history=include_full_event_history,
Expand Down
46 changes: 46 additions & 0 deletions tests/test_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,49 @@ def test_retrieve_with_on_market_flag(

# Check that .json() was called on the response mock twice
assert mock_response.json.call_count == 2


@patch("parcllabs.services.properties.property_search.PropertySearch._get")
def test_retrieve_with_has_pool(mock_get: Mock, property_search_service: PropertySearch) -> None:
"""Test retrieve method with has_pool parameter."""
mock_response = MagicMock()
mock_response.json.return_value = json.loads(sample_search_response)
mock_get.return_value = mock_response

parcl_ids_to_test = [5503877]
property_type_to_test = "single_family"

# Test with flag = True
property_search_service.retrieve(
parcl_ids=parcl_ids_to_test,
property_type=property_type_to_test,
has_pool=True,
)

# Test with flag = False
property_search_service.retrieve(
parcl_ids=parcl_ids_to_test,
property_type=property_type_to_test,
has_pool=False,
)

expected_params_on = {
"property_type": property_type_to_test.upper(),
"has_pool": "true",
"parcl_id": parcl_ids_to_test[0],
}
expected_params_off = {
"property_type": property_type_to_test.upper(),
"has_pool": "false",
"parcl_id": parcl_ids_to_test[0],
}

assert len(mock_get.call_args_list) == 2

# Check first call (has_pool=True)
_, call_on_kwargs = mock_get.call_args_list[0]
assert call_on_kwargs["params"] == expected_params_on

# Check second call (has_pool=False)
_, call_off_kwargs = mock_get.call_args_list[1]
assert call_off_kwargs["params"] == expected_params_off
20 changes: 20 additions & 0 deletions tests/test_property_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def test_build_property_filters_from_schema(property_v2_service: PropertyV2Servi
include_property_details=True,
min_record_added_date="2023-01-01",
max_record_added_date="2023-12-31",
has_pool=True,
)

filters = property_v2_service._build_property_filters(params)
Expand All @@ -102,6 +103,7 @@ def test_build_property_filters_from_schema(property_v2_service: PropertyV2Servi
"include_property_details": "true",
"min_record_added_date": "2023-01-01",
"max_record_added_date": "2023-12-31",
"has_pool": "true",
}


Expand Down Expand Up @@ -374,3 +376,21 @@ def test_retrieve_with_schema_validation_errors(
parcl_ids=[123],
min_event_date="2023/01/01",
)


def test_build_boolean_filters_has_pool(property_v2_service: PropertyV2Service) -> None:
"""Test has_pool boolean filter."""
# True
params = PropertyV2RetrieveParams(has_pool=True)
filters = property_v2_service._build_boolean_filters(params)
assert filters["has_pool"] == "true"

# False
params = PropertyV2RetrieveParams(has_pool=False)
filters = property_v2_service._build_boolean_filters(params)
assert filters["has_pool"] == "false"

# None (omitted)
params = PropertyV2RetrieveParams()
filters = property_v2_service._build_boolean_filters(params)
assert "has_pool" not in filters
Loading