Skip to content

Commit 5b3a89e

Browse files
committed
bump property_v2 limit + autopaginate
1 parent 1f1e391 commit 5b3a89e

File tree

6 files changed

+69
-9
lines changed

6 files changed

+69
-9
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
### v1.14.2
2+
- Add autopaginate support for `property_v2.search` endpoint, increase default limit to 100,000.
3+
14
### v1.14.1
25
- Update `property_v2.search` endpoint params.
36

7+
### v1.14.0
8+
- Simplification of parameter validation, reduce complexity of code.
9+
410
### v1.13.0
511
- Add support for `property_v2.search` endpoint.
612

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,11 +482,13 @@ addresses = client.property_address.search.retrieve(
482482

483483
### Property Search V2 <a id="property-search-v2"></a>
484484

485-
Gets a list of unique properties and their associated metadata and events based on a set of filters. Use one of three search methods:
485+
Gets a list of unique properties and their associated metadata and events based on a set of property, event, and owner filters. Use one of three search methods:
486486
1. `parcl_ids`
487487
2. `parcl_property_ids`
488488
3. `geo_coordinates` (must provide latitude, longitude, and radius)
489489

490+
Use limit to specify the number of matched properties to return. Set auto_paginate to `True` to retrieve all results, this will override the limit.
491+
490492
```python
491493
results, filter_data = client.property_v2.search.retrieve(
492494
# parcl_ids=[5495449],
@@ -511,6 +513,8 @@ results, filter_data = client.property_v2.search.retrieve(
511513
max_record_added_date="2024-12-31",
512514
min_record_added_date="2024-12-13",
513515
property_types=["SINGLE_FAMILY", "CONDO", "TOWNHOUSE"],
516+
limit=10,
517+
# auto_paginate=True,
514518
)
515519
```
516520

parcllabs/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VERSION = "1.14.1"
1+
VERSION = "1.14.2"

parcllabs/enums.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class RequestLimits(Enum):
1212
MAX_POST = 1000
1313
DEFAULT_SMALL = 1000
1414
DEFAULT_LARGE = 10000
15+
PROPERTY_V2_MAX = 100000
1516

1617

1718
class ResponseCodes(Enum):

parcllabs/services/properties/property_v2.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import pandas as pd
66

7+
from parcllabs.enums import RequestLimits
78
from parcllabs.services.parcllabs_service import ParclLabsService
89
from parcllabs.services.validators import Validators
910

@@ -12,7 +13,9 @@ class PropertyV2Service(ParclLabsService):
1213
def __init__(self, *args: object, **kwargs: object) -> None:
1314
super().__init__(*args, **kwargs)
1415

15-
def _fetch_post(self, params: dict[str, Any], data: dict[str, Any]) -> list[dict]:
16+
def _fetch_post(
17+
self, params: dict[str, Any], data: dict[str, Any], auto_paginate: bool
18+
) -> list[dict]:
1619
"""Fetch data using POST request with pagination support."""
1720
response = self._post(url=self.full_post_url, data=data, params=params)
1821
result = response.json()
@@ -22,7 +25,7 @@ def _fetch_post(self, params: dict[str, Any], data: dict[str, Any]) -> list[dict
2225
all_data = [result]
2326

2427
# If we need to paginate, use concurrent requests
25-
if pagination and pagination.get("has_more"):
28+
if auto_paginate and pagination and pagination.get("has_more"):
2629
limit = pagination.get("limit")
2730
offset = pagination.get("offset")
2831
total_count = metadata.get("results", {}).get("total_available", 0)
@@ -261,6 +264,26 @@ def _build_owner_filters(self, **kwargs: dict) -> dict[str, Any]:
261264

262265
return owner_filters
263266

267+
def _validate_limit(self, limit: int | None, auto_paginate: bool) -> int:
268+
"""Validate limit parameter."""
269+
max_limit = RequestLimits.PROPERTY_V2_MAX.value
270+
271+
# If auto-paginate is enabled or no limit is provided, use maximum limit
272+
if auto_paginate or limit is None:
273+
if auto_paginate and limit is not None:
274+
print(f"Auto-paginate is enabled. Setting limit to maximum value of {max_limit}.")
275+
return max_limit
276+
277+
# If limit exceeds maximum, cap it
278+
if limit > max_limit:
279+
print(
280+
f"Supplied limit value is too large for requested endpoint."
281+
f"Setting limit to maximum value of {max_limit}."
282+
)
283+
return max_limit
284+
285+
return limit
286+
264287
def retrieve(
265288
self,
266289
parcl_ids: list[int] | None = None,
@@ -290,7 +313,9 @@ def retrieve(
290313
owner_name: list[str] | None = None,
291314
is_investor_owned: bool | None = None,
292315
is_owner_occupied: bool | None = None,
293-
params: Mapping[str, Any] | None = None,
316+
limit: int | None = None,
317+
params: Mapping[str, Any] | None = {},
318+
auto_paginate: bool = False,
294319
) -> tuple[pd.DataFrame, dict[str, Any]]:
295320
"""
296321
Retrieve property data based on search criteria and filters.
@@ -353,8 +378,10 @@ def retrieve(
353378
data["event_filters"] = self._build_event_filters(**kwargs)
354379
data["owner_filters"] = self._build_owner_filters(**kwargs)
355380

381+
params["limit"] = self._validate_limit(limit, auto_paginate)
382+
356383
# Make request with pagination
357-
results = self._fetch_post(params=params or {}, data=data)
384+
results = self._fetch_post(params=params, data=data, auto_paginate=auto_paginate)
358385

359386
# Get metadata from results
360387
metadata = self._get_metadata(results)

tests/test_property_v2.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import pytest
44

5+
from parcllabs.enums import RequestLimits
56
from parcllabs.services.properties.property_v2 import PropertyV2Service
67

78

@@ -139,12 +140,32 @@ def test_build_owner_filters(property_v2_service: PropertyV2Service) -> None:
139140
}
140141

141142

143+
def test_validate_limit(property_v2_service: PropertyV2Service) -> None:
144+
assert (
145+
property_v2_service._validate_limit(limit=None, auto_paginate=True)
146+
== RequestLimits.PROPERTY_V2_MAX.value
147+
)
148+
assert (
149+
property_v2_service._validate_limit(limit=None, auto_paginate=False)
150+
== RequestLimits.PROPERTY_V2_MAX.value
151+
)
152+
assert (
153+
property_v2_service._validate_limit(limit=100, auto_paginate=True)
154+
== RequestLimits.PROPERTY_V2_MAX.value
155+
)
156+
assert property_v2_service._validate_limit(limit=100, auto_paginate=False) == 100
157+
assert (
158+
property_v2_service._validate_limit(limit=1000000000, auto_paginate=True)
159+
== RequestLimits.PROPERTY_V2_MAX.value
160+
)
161+
162+
142163
@patch.object(PropertyV2Service, "_post")
143164
def test_fetch_post_single_page(
144165
mock_post: Mock, property_v2_service: PropertyV2Service, mock_response: Mock
145166
) -> None:
146167
mock_post.return_value = mock_response
147-
result = property_v2_service._fetch_post(params={}, data={})
168+
result = property_v2_service._fetch_post(params={}, data={}, auto_paginate=False)
148169

149170
assert len(result) == 1
150171
assert result[0] == mock_response.json()
@@ -174,7 +195,7 @@ def test_fetch_post_pagination(mock_post: Mock, property_v2_service: PropertyV2S
174195
# Set up the mock to return different responses
175196
mock_post.side_effect = [first_response, second_response]
176197

177-
result = property_v2_service._fetch_post(params={}, data={})
198+
result = property_v2_service._fetch_post(params={"limit": 1}, data={}, auto_paginate=True)
178199

179200
assert len(result) == 2
180201
assert result[0]["data"][0]["parcl_id"] == 123
@@ -224,6 +245,7 @@ def test_retrieve(
224245
min_beds=2,
225246
max_beds=4,
226247
event_names=["LISTING"],
248+
limit=10,
227249
)
228250
# check that the dataframe has the expected data
229251
assert len(df) == 1
@@ -234,7 +256,7 @@ def test_retrieve(
234256

235257
# check that the correct data was passed to _fetch_post
236258
call_args = mock_fetch_post.call_args[1]
237-
assert call_args["params"] == {}
259+
assert call_args["params"] == {"limit": 10}
238260

239261
data = call_args["data"]
240262
assert data["parcl_ids"] == [123]

0 commit comments

Comments
 (0)