Skip to content

Commit 4a3012a

Browse files
authored
Merge pull request #439 from mfoos/mfoos_agencies_filter
Added agency filters and tests
2 parents c4f0564 + 1c59104 commit 4a3012a

File tree

4 files changed

+115
-11
lines changed

4 files changed

+115
-11
lines changed

.github/actions/setup-frontend/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ runs:
99
using: "composite"
1010
steps:
1111
- name: Use Node.js ${{ inputs.node-version }}
12-
uses: actions/setup-node@v2
12+
uses: actions/setup-node@v4
1313
with:
1414
node-version: ${{ inputs.node-version }}
1515
cache: "npm"

backend/routes/agencies.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
NodeConflictException)
88
from backend.mixpanel.mix import track_to_mp
99
from backend.database.models.user import UserRole
10-
from backend.database.models.agency import Agency
10+
from backend.database.models.agency import Agency, State
1111
from .tmp.pydantic.agencies import CreateAgency, UpdateAgency
1212
from flask import Blueprint, abort, request
1313
from flask_jwt_extended.view_decorators import jwt_required
@@ -144,13 +144,40 @@ def get_all_agencies():
144144
Accepts Query Parameters for pagination:
145145
per_page: number of results per page
146146
page: page number
147+
name: filter on agency name
148+
hq_city: filter on agency city
149+
hq_state: filter on agency state
150+
hq_zip: filter on agency zipcode
151+
jurisdiction: filter on agency jurisdiction
147152
"""
148153
args = request.args
149154
q_page = args.get("page", 1, type=int)
150155
q_per_page = args.get("per_page", 20, type=int)
151156

152-
all_agencies = Agency.nodes.all()
153-
results = paginate_results(all_agencies, q_page, q_per_page)
157+
params = ["name", "hq_city", "hq_state", "hq_zip", "jurisdiction"]
158+
params_used = set(params).intersection(args.keys())
159+
params.extend(["page", "per_page"])
160+
161+
# includes unrecognized parameters
162+
if bool(set(args).difference(params)):
163+
abort(400)
164+
165+
# any valid filter parameters supplied
166+
if bool(params_used):
167+
filter_on = {}
168+
169+
for p in params_used:
170+
input_value = args.get(key=p, default=None, type=str)
171+
if p == "hq_state":
172+
if input_value not in State.choices():
173+
abort(400)
174+
filter_on[p] = input_value
175+
176+
agencies_filtered = Agency.nodes.filter(**filter_on)
177+
results = paginate_results(agencies_filtered, q_page, q_per_page)
178+
else:
179+
all_agencies = Agency.nodes.all()
180+
results = paginate_results(all_agencies, q_page, q_per_page)
154181

155182
return ordered_jsonify(results), 200
156183

backend/tests/test_agencies.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"hq_address": "3510 S Michigan Ave",
3838
"hq_city": "Chicago",
3939
"hq_zip": "60653",
40+
"hq_state": "IL",
4041
"jurisdiction": "MUNICIPAL"
4142
},
4243
"nypd": {
@@ -45,6 +46,16 @@
4546
"hq_address": "1 Police Plaza",
4647
"hq_city": "New York",
4748
"hq_zip": "10038",
49+
"hq_state": "NY",
50+
"jurisdiction": "MUNICIPAL"
51+
},
52+
"npd": {
53+
"name": "Newton Police Dept.",
54+
"website_url": "https://www.newtonma.gov/government/police-department",
55+
"hq_address": "1321 Washington St.",
56+
"hq_city": "Newton",
57+
"hq_zip": "02465",
58+
"hq_state": "MA",
4859
"jurisdiction": "MUNICIPAL"
4960
}
5061
}
@@ -125,6 +136,72 @@ def test_get_all_agencies(client, access_token, example_agencies):
125136
assert res.json["results"].__len__() == total_agencies
126137

127138

139+
def test_filter_agencies(client, access_token, example_agencies):
140+
# Test filtering
141+
expect_name_ct = Agency.nodes.filter(
142+
name="New York Police Department"
143+
).__len__()
144+
res = client.get(
145+
"/api/v1/agencies/?name=New York Police Department",
146+
headers={"Authorization": "Bearer {0}".format(access_token)}
147+
)
148+
assert res.status_code == 200
149+
assert res.json['results'].__len__() == expect_name_ct
150+
151+
expect_city_ct = Agency.nodes.filter(hq_city="Chicago").__len__()
152+
res = client.get(
153+
"/api/v1/agencies/?hq_city=Chicago",
154+
headers={"Authorization": "Bearer {0}".format(access_token)}
155+
)
156+
assert res.status_code == 200
157+
assert res.json['results'].__len__() == expect_city_ct
158+
159+
expect_state_ct = Agency.nodes.filter(hq_state="NY").__len__()
160+
res = client.get(
161+
"/api/v1/agencies/?hq_state=NY",
162+
headers={"Authorization": "Bearer {0}".format(access_token)}
163+
)
164+
assert res.status_code == 200
165+
assert res.json['results'].__len__() == expect_state_ct
166+
167+
# State name not abbreviated correctly
168+
res = client.get(
169+
"/api/v1/agencies/?hq_state=New York",
170+
headers={"Authorization": "Bearer {0}".format(access_token)}
171+
)
172+
assert res.status_code == 400
173+
174+
# No parameter "state"
175+
res = client.get(
176+
"/api/v1/agencies/?state=NY",
177+
headers={"Authorization": "Bearer {0}".format(access_token)}
178+
)
179+
assert res.status_code == 400
180+
181+
expect_zip_ct = Agency.nodes.filter(hq_zip="60653").__len__()
182+
res = client.get(
183+
"/api/v1/agencies/?hq_zip=60653",
184+
headers={"Authorization": "Bearer {0}".format(access_token)}
185+
)
186+
assert res.status_code == 200
187+
assert res.json['results'].__len__() == expect_zip_ct
188+
189+
# If leading zeroes get coerced anywhere, zip codes will break
190+
res = client.get(
191+
"/api/v1/agencies/?hq_zip=02465",
192+
headers={"Authorization": "Bearer {0}".format(access_token)}
193+
)
194+
assert res.status_code == 200
195+
196+
expect_juri_ct = Agency.nodes.filter(jurisdiction="MUNICIPAL").__len__()
197+
res = client.get(
198+
"/api/v1/agencies/?jurisdiction=MUNICIPAL",
199+
headers={"Authorization": "Bearer {0}".format(access_token)}
200+
)
201+
assert res.status_code == 200
202+
assert res.json['results'].__len__() == expect_juri_ct
203+
204+
128205
def test_agency_pagination(client, example_agencies, access_token):
129206
per_page = 1
130207
total_agencies = Agency.nodes.all().__len__()

oas/2.0/agencies.yaml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ paths:
3232
- $ref: '../common/pagination.yaml#/components/parameters/per_page'
3333
- $ref: '#/components/parameters/jurisdiction'
3434
- $ref: '#/components/parameters/name'
35-
- $ref: '#/components/parameters/zip_code'
36-
- $ref: '#/components/parameters/city'
37-
- $ref: '#/components/parameters/state'
35+
- $ref: '#/components/parameters/hq_zip'
36+
- $ref: '#/components/parameters/hq_city'
37+
- $ref: '#/components/parameters/hq_state'
3838
tags:
3939
- Agencies
4040
responses:
@@ -327,23 +327,23 @@ components:
327327
schema:
328328
type: "string"
329329
zip_code:
330-
name: "zip_code"
330+
name: "hq_zip"
331331
in: "query"
332332
description: "Filter by zip code"
333333
required: false
334334
schema:
335335
type: "string"
336336
city:
337-
name: "city"
337+
name: "hq_city"
338338
in: "query"
339339
description: "Filter by city"
340340
required: false
341341
schema:
342342
type: "string"
343343
state:
344-
name: "state"
344+
name: "hq_state"
345345
in: "query"
346-
description: "Filter by state"
346+
description: "Filter by state (two-letter abbreviation)"
347347
required: false
348348
schema:
349349
type: "string"

0 commit comments

Comments
 (0)