diff --git a/.gitignore b/.gitignore index e946e6d..33d9ca3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ census_area.egg-info/ census_area/__pycache__/ -docs/_build/ \ No newline at end of file +docs/_build/ +venv \ No newline at end of file diff --git a/census_area/core.py b/census_area/core.py index bdc87ff..e56da4c 100644 --- a/census_area/core.py +++ b/census_area/core.py @@ -128,7 +128,7 @@ def geo_tract(self, fields, geojson_geometry, year=None, **kwargs): within = 'state:{state} county:{county}'.format(**context) tract_id = tract['properties']['TRACT'] - result = self.get(fields, + result = self.get(tuple(fields), HashDict({'for': 'tract:{}'.format(tract_id), 'in': within}), year, @@ -171,7 +171,7 @@ def geo_blockgroup(self, fields, geojson_geometry, year=None, **kwargs): 'tract': block_group['properties']['TRACT']} within = 'state:{state} county:{county} tract:{tract}'.format(**context) - tract_blockgroups = self.get(fields, + tract_blockgroups = self.get(tuple(fields), HashDict({'for': 'block group:*', 'in': within}), year, @@ -302,7 +302,7 @@ def state_place_block(self, *args, **kwargs): ''' return self._state_place_area(self.geo_block, *args, **kwargs) - @supported_years(2020, 2010) + @supported_years(2020, 2010, 2000) def geo_block(self, fields, geojson_geometry, year): ''' Retrieve variable values for blocks intersecting with an arbitrary @@ -332,7 +332,7 @@ def geo_block(self, fields, geojson_geometry, year): 'tract': block['properties']['TRACT']} within = 'state:{state} county:{county} tract:{tract}'.format(**context) - tract_blocks = self.get(fields, + tract_blocks = self.get(tuple(fields), HashDict({'for': 'block:*', 'in': within}), year) @@ -343,7 +343,6 @@ def geo_block(self, fields, geojson_geometry, year): if result: result, = result else: - breakpoint() result = {} yield block, result, intersection_proportion diff --git a/test_geo_block_2000.py b/test_geo_block_2000.py new file mode 100644 index 0000000..20b4cb2 --- /dev/null +++ b/test_geo_block_2000.py @@ -0,0 +1,30 @@ +import os +from census_area import SF1Client + +API_KEY = os.environ.get('CENSUS_API_KEY') +if not API_KEY: + raise SystemExit('Set the CENSUS_API_KEY environment variable first.') + +# Small polygon covering a few blocks in Chicago's Loop +geometry = { + 'type': 'Polygon', + 'coordinates': [[ + [-87.6320, 41.8827], + [-87.6290, 41.8827], + [-87.6290, 41.8850], + [-87.6320, 41.8850], + [-87.6320, 41.8827], + ]] +} + +client = SF1Client(API_KEY) + +print('Querying geo_block for year 2000...') +results = list(client.geo_block(['P001001'], geometry, year=2000)) +print(f'Found {len(results)} blocks\n') + +for block, result, proportion in results: + props = block['properties'] + print(f"Block {props['STATE']}{props['COUNTY']}{props['TRACT']}{props['BLOCK']}" + f" population={result.get('P001001', 'N/A')}" + f" overlap={proportion:.1%}") diff --git a/tests/__pycache__/test_geo_block.cpython-314-pytest-9.0.3.pyc b/tests/__pycache__/test_geo_block.cpython-314-pytest-9.0.3.pyc new file mode 100644 index 0000000..c1c2345 Binary files /dev/null and b/tests/__pycache__/test_geo_block.cpython-314-pytest-9.0.3.pyc differ diff --git a/tests/test_geo_block.py b/tests/test_geo_block.py new file mode 100644 index 0000000..e17ae56 --- /dev/null +++ b/tests/test_geo_block.py @@ -0,0 +1,106 @@ +import pytest +from unittest.mock import patch, MagicMock + +from census_area import PLClient +from census_area.variables import GEO_URLS + + +TEST_GEOMETRY = { + 'type': 'Polygon', + 'coordinates': [[ + [-87.65, 41.85], + [-87.64, 41.85], + [-87.64, 41.86], + [-87.65, 41.86], + [-87.65, 41.85], + ]] +} + + +def make_block_feature(state='17', county='031', tract='839100', block='1001'): + return { + 'type': 'Feature', + 'geometry': { + 'type': 'Polygon', + 'coordinates': [[ + [-87.649, 41.850], + [-87.641, 41.850], + [-87.641, 41.859], + [-87.649, 41.859], + [-87.649, 41.850], + ]] + }, + 'properties': { + 'STATE': state, + 'COUNTY': county, + 'TRACT': tract, + 'BLOCK': block, + } + } + + +@pytest.fixture +def client(): + return PLClient('fake_api_key') + + +@pytest.mark.parametrize('year', [2000, 2010, 2020]) +def test_geo_block_uses_correct_url_for_year(client, year): + with patch('census_area.core.AreaFilter') as MockAreaFilter, \ + patch.object(client, 'get', return_value=[]): + MockAreaFilter.return_value = [] + list(client.geo_block(['P001001'], TEST_GEOMETRY, year=year)) + MockAreaFilter.assert_called_once_with(TEST_GEOMETRY, GEO_URLS['blocks'][year]) + + +def test_geo_block_yields_block_result_and_proportion(client): + block_feature = make_block_feature() + census_data = [{'state': '17', 'county': '031', 'tract': '839100', + 'block': '1001', 'P001001': '42'}] + + with patch('census_area.core.AreaFilter') as MockAreaFilter, \ + patch.object(client, 'get', return_value=census_data): + MockAreaFilter.return_value = [(block_feature, 0.75)] + results = list(client.geo_block(['P001001'], TEST_GEOMETRY, year=2020)) + + assert len(results) == 1 + block, result, proportion = results[0] + assert block == block_feature + assert result['P001001'] == '42' + assert proportion == 0.75 + + +def test_geo_block_2000(client): + block_feature = make_block_feature() + census_data = [{'state': '17', 'county': '031', 'tract': '839100', + 'block': '1001', 'P001001': '42'}] + + with patch('census_area.core.AreaFilter') as MockAreaFilter, \ + patch.object(client, 'get', return_value=census_data): + MockAreaFilter.return_value = [(block_feature, 1.0)] + results = list(client.geo_block(['P001001'], TEST_GEOMETRY, year=2000)) + + assert len(results) == 1 + _, result, proportion = results[0] + assert result['P001001'] == '42' + assert proportion == 1.0 + + +def test_geo_block_empty_result_when_block_not_in_census_response(client): + block_feature = make_block_feature(block='1001') + census_data = [{'state': '17', 'county': '031', 'tract': '839100', + 'block': '9999', 'P001001': '10'}] + + with patch('census_area.core.AreaFilter') as MockAreaFilter, \ + patch.object(client, 'get', return_value=census_data): + MockAreaFilter.return_value = [(block_feature, 1.0)] + results = list(client.geo_block(['P001001'], TEST_GEOMETRY, year=2020)) + + assert len(results) == 1 + _, result, _ = results[0] + assert result == {} + + +def test_geo_block_unsupported_year_raises(client): + with pytest.raises(Exception): + list(client.geo_block(['P001001'], TEST_GEOMETRY, year=1990))