Skip to content

Commit beca3c2

Browse files
committed
feat: add housing support
- Add Housing model for ACS housing data - Add housing to GeocodioFields - Add unit and integration tests for housing
1 parent 25816de commit beca3c2

File tree

4 files changed

+90
-4
lines changed

4 files changed

+90
-4
lines changed

src/geocodio/client.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
GeocodingResponse, GeocodingResult, AddressComponents,
1515
Location, GeocodioFields, Timezone, CongressionalDistrict,
1616
CensusData, ACSSurveyData, StateLegislativeDistrict, SchoolDistrict,
17-
Demographics, Economics, Families
17+
Demographics, Economics, Families, Housing
1818
)
1919
from .exceptions import InvalidRequestError, AuthenticationError, GeocodioServerError
2020

@@ -239,6 +239,11 @@ def _parse_fields(self, fields_data: dict | None) -> GeocodioFields | None:
239239
if "acs-families" in fields_data else None
240240
)
241241

242+
housing = (
243+
Housing.from_api(fields_data["acs-housing"])
244+
if "acs-housing" in fields_data else None
245+
)
246+
242247
return GeocodioFields(
243248
timezone=timezone,
244249
congressional_districts=congressional_districts,
@@ -252,4 +257,5 @@ def _parse_fields(self, fields_data: dict | None) -> GeocodioFields | None:
252257
demographics=demographics,
253258
economics=economics,
254259
families=families,
260+
housing=housing,
255261
)

src/geocodio/models.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,21 @@ class Families(_HasExtras, ApiModelMixin):
179179
extras: Dict[str, Any] = field(default_factory=dict, repr=False)
180180

181181

182+
@dataclass(slots=True, frozen=True)
183+
class Housing(_HasExtras, ApiModelMixin):
184+
"""
185+
American Community Survey housing data.
186+
"""
187+
total_housing_units: Optional[int] = None
188+
occupied_housing_units: Optional[int] = None
189+
vacant_housing_units: Optional[int] = None
190+
owner_occupied_units: Optional[int] = None
191+
renter_occupied_units: Optional[int] = None
192+
median_home_value: Optional[int] = None
193+
median_rent: Optional[int] = None
194+
extras: Dict[str, Any] = field(default_factory=dict, repr=False)
195+
196+
182197
@dataclass(slots=True, frozen=True)
183198
class GeocodioFields:
184199
"""
@@ -197,6 +212,7 @@ class GeocodioFields:
197212
demographics: Optional[Demographics] = None
198213
economics: Optional[Economics] = None
199214
families: Optional[Families] = None
215+
housing: Optional[Housing] = None
200216

201217

202218
# ──────────────────────────────────────────────────────────────────────────────

tests/e2e/test_api.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,4 +397,42 @@ def test_integration_with_families(client):
397397
if fields.families.single_female_households is not None:
398398
assert isinstance(fields.families.single_female_households, int)
399399
if fields.families.average_household_size is not None:
400-
assert isinstance(fields.families.average_household_size, float)
400+
assert isinstance(fields.families.average_household_size, float)
401+
402+
403+
def test_integration_with_housing(client):
404+
"""Test real API call with housing field."""
405+
# Test address
406+
address = "1600 Pennsylvania Ave NW, Washington, DC"
407+
408+
# Request additional fields
409+
response = client.geocode(
410+
address,
411+
fields=["acs-housing"]
412+
)
413+
414+
# Verify response structure
415+
assert response is not None
416+
assert len(response.results) > 0
417+
result = response.results[0]
418+
419+
# Verify fields data
420+
fields = result.fields
421+
assert fields is not None
422+
423+
# Check housing data
424+
if fields.housing:
425+
if fields.housing.total_housing_units is not None:
426+
assert isinstance(fields.housing.total_housing_units, int)
427+
if fields.housing.occupied_housing_units is not None:
428+
assert isinstance(fields.housing.occupied_housing_units, int)
429+
if fields.housing.vacant_housing_units is not None:
430+
assert isinstance(fields.housing.vacant_housing_units, int)
431+
if fields.housing.owner_occupied_units is not None:
432+
assert isinstance(fields.housing.owner_occupied_units, int)
433+
if fields.housing.renter_occupied_units is not None:
434+
assert isinstance(fields.housing.renter_occupied_units, int)
435+
if fields.housing.median_home_value is not None:
436+
assert isinstance(fields.housing.median_home_value, int)
437+
if fields.housing.median_rent is not None:
438+
assert isinstance(fields.housing.median_rent, int)

tests/test_models.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22
from geocodio.models import (
33
AddressComponents, Timezone, CongressionalDistrict,
4-
GeocodioFields, GeocodingResult, GeocodingResponse, Location, StateLegislativeDistrict, SchoolDistrict, CensusData, Demographics, Economics, Families
4+
GeocodioFields, GeocodingResult, GeocodingResponse, Location, StateLegislativeDistrict, SchoolDistrict, CensusData, Demographics, Economics, Families, Housing
55
)
66

77

@@ -232,4 +232,30 @@ def test_families_extras():
232232
assert families.single_female_households == 100
233233
assert families.average_household_size == 2.5
234234
assert families.get_extra("extra_field") == "extra value"
235-
assert families.get_extra("nonexistent", "default") == "default"
235+
assert families.get_extra("nonexistent", "default") == "default"
236+
237+
238+
def test_housing_extras():
239+
# Test that extra fields are stored in extras
240+
data = {
241+
"total_housing_units": 1000,
242+
"occupied_housing_units": 800,
243+
"vacant_housing_units": 200,
244+
"owner_occupied_units": 500,
245+
"renter_occupied_units": 300,
246+
"median_home_value": 350000,
247+
"median_rent": 1500,
248+
"extra_field": "extra value"
249+
}
250+
251+
housing = Housing.from_api(data)
252+
253+
assert housing.total_housing_units == 1000
254+
assert housing.occupied_housing_units == 800
255+
assert housing.vacant_housing_units == 200
256+
assert housing.owner_occupied_units == 500
257+
assert housing.renter_occupied_units == 300
258+
assert housing.median_home_value == 350000
259+
assert housing.median_rent == 1500
260+
assert housing.get_extra("extra_field") == "extra value"
261+
assert housing.get_extra("nonexistent", "default") == "default"

0 commit comments

Comments
 (0)