Skip to content

Commit f974536

Browse files
authored
Allow partial matches for officer search terms (#522)
* Allow partial matches for officer search terms * Flake8 fix * Fix tests Removed the extraneous slashes in the test URLs.
1 parent c8e43d2 commit f974536

3 files changed

Lines changed: 31 additions & 29 deletions

File tree

backend/database/models/officer.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,19 +234,20 @@ def search(
234234
params["active_before"] = active_before
235235

236236
if agency:
237-
where_clauses.append("a.name IN $agency")
237+
where_clauses.append("ANY(n IN $agency WHERE a.name CONTAINS n)")
238238
params["agency"] = agency
239239

240240
if badge_number:
241-
where_clauses.append("e.badge_number IN $badge_number")
241+
where_clauses.append(
242+
"ANY(n IN $badge_number WHERE e.badge_number CONTAINS n)")
242243
params["badge_number"] = badge_number
243244

244245
if ethnicity:
245246
where_clauses.append("o.ethnicity IN $ethnicity")
246247
params["ethnicity"] = ethnicity
247248

248249
if unit:
249-
where_clauses.append("u.name IN $unit")
250+
where_clauses.append("ANY(n IN $unit WHERE u.name CONTAINS n)")
250251
params["unit"] = unit
251252

252253
# Combine query
@@ -260,6 +261,7 @@ def search(
260261
cypher_query += "\nRETURN count(*) as c"
261262
logging.warning("Cypher count query:\n%s", cypher_query)
262263
logging.warning("Params: %s", params)
264+
logging.warning("Query: %s", cypher_query)
263265
count_results, _ = db.cypher_query(cypher_query, params)
264266
return count_results[0][0] if count_results else 0
265267
else:

backend/routes/officers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@
345345

346346

347347
# Create an officer profile
348-
@bp.route("/", methods=["POST"])
348+
@bp.route("", methods=["POST"])
349349
@jwt_required()
350350
@min_role_required(UserRole.CONTRIBUTOR)
351351
@validate_request(CreateOfficer)
@@ -441,7 +441,7 @@ def get_officer(officer_uid: str):
441441

442442

443443
# Get all officers
444-
@bp.route("/", methods=["GET"])
444+
@bp.route("", methods=["GET"])
445445
@jwt_required()
446446
@min_role_required(UserRole.PUBLIC)
447447
def get_all_officers():

backend/tests/test_officers.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def test_create_officer(
148148
"gender": "Male"
149149
}
150150
res = client.post(
151-
"/api/v1/officers/",
151+
"/api/v1/officers",
152152
json=request,
153153
headers={
154154
"Authorization": "Bearer {0}".format(contributor_access_token)
@@ -241,7 +241,7 @@ def officer_name(officer):
241241
def test_get_officers(client, db_session, access_token, example_officers):
242242
all_officers = Officer.nodes.all()
243243
res = client.get(
244-
"/api/v1/officers/",
244+
"/api/v1/officers",
245245
headers={"Authorization ": "Bearer {0}".format(access_token)},
246246
)
247247

@@ -260,7 +260,7 @@ def test_officer_pagination(client, db_session, access_token, example_officers):
260260

261261
for page in range(1, expected_total_pages + 1):
262262
res = client.get(
263-
"/api/v1/officers/",
263+
"/api/v1/officers",
264264
query_string={"per_page": per_page, "page": page},
265265
headers={"Authorization": "Bearer {0}".format(access_token)},
266266
)
@@ -271,7 +271,7 @@ def test_officer_pagination(client, db_session, access_token, example_officers):
271271
assert len(res.json["results"]) == per_page
272272

273273
res = client.get(
274-
"/api/v1/officers/",
274+
"/api/v1/officers",
275275
query_string={"per_page": per_page, "page": expected_total_pages + 1},
276276
headers={"Authorization": "Bearer {0}".format(access_token)},
277277
)
@@ -293,7 +293,7 @@ def test_officer_pagination2(client, db_session, access_token):
293293

294294
# page 1
295295
res = client.get(
296-
"/api/v1/officers/",
296+
"/api/v1/officers",
297297
query_string={"per_page": 20, "page": 1},
298298
headers={"Authorization": "Bearer {0}".format(access_token)},
299299
)
@@ -305,7 +305,7 @@ def test_officer_pagination2(client, db_session, access_token):
305305

306306
# page 2
307307
res = client.get(
308-
"/api/v1/officers/",
308+
"/api/v1/officers",
309309
query_string={"per_page": 20, "page": 2},
310310
headers={"Authorization": "Bearer {0}".format(access_token)},
311311
)
@@ -317,7 +317,7 @@ def test_officer_pagination2(client, db_session, access_token):
317317

318318
# page 3 (should return empty)
319319
res = client.get(
320-
"/api/v1/officers/",
320+
"/api/v1/officers",
321321
query_string={"per_page": 20, "page": 3},
322322
headers={"Authorization": "Bearer {0}".format(access_token)},
323323
)
@@ -550,7 +550,7 @@ def test_get_officers_with_unit(client, db_session, access_token,
550550
officers_with_unit = [Officer.inflate(row[0]) for row in results]
551551

552552
res = client.get(
553-
"/api/v1/officers/?unit=Unit Alpha",
553+
"/api/v1/officers?unit=Unit Alpha",
554554
headers={"Authorization ": "Bearer {0}".format(access_token)},
555555
)
556556
print(officers_with_unit)
@@ -571,7 +571,7 @@ def test_get_officers_with_unit(client, db_session, access_token,
571571

572572
# API request with multiple units
573573
res = client.get(
574-
"/api/v1/officers/?unit=Unit Alpha&unit=Unit Bravo",
574+
"/api/v1/officers?unit=Unit Alpha&unit=Unit Bravo",
575575
headers={"Authorization": f"Bearer {access_token}"},
576576
)
577577
print(f"len results is {len(res.json['results'])}")
@@ -594,7 +594,7 @@ def test_get_officers_with_unit_and_agency(client, db_session, access_token,
594594
officers_with_unit = [Officer.inflate(row[0]) for row in results]
595595

596596
res = client.get(
597-
"/api/v1/officers/?agency=Chicago Police Department",
597+
"/api/v1/officers?agency=Chicago Police Department",
598598
headers={"Authorization ": "Bearer {0}".format(access_token)},
599599
)
600600
assert officers_with_unit is not None
@@ -615,7 +615,7 @@ def test_get_officers_with_unit_and_agency(client, db_session, access_token,
615615
])
616616
def test_get_officers_dates(client, access_token, query, expect_results,
617617
create_officers_units_agencies):
618-
res = client.get(f"/api/v1/officers/?{query}",
618+
res = client.get(f"/api/v1/officers?{query}",
619619
headers={"Authorization": f"Bearer {access_token}"})
620620
assert res.status_code == 200
621621

@@ -634,7 +634,7 @@ def test_get_officers_dates(client, access_token, query, expect_results,
634634
def test_get_officers_with_rank(client, db_session, access_token,
635635
create_officers_units_agencies):
636636
res = client.get(
637-
"/api/v1/officers/?rank=Officer",
637+
"/api/v1/officers?rank=Officer",
638638
headers={"Authorization ": "Bearer {0}".format(access_token)},
639639
)
640640
assert res.json != []
@@ -643,7 +643,7 @@ def test_get_officers_with_rank(client, db_session, access_token,
643643
assert res.json["results"][0]["last_name"] is not None
644644

645645
res = client.get(
646-
"/api/v1/officers/?rank=Officer&agency=Chicago Police Department",
646+
"/api/v1/officers?rank=Officer&agency=Chicago Police Department",
647647
headers={"Authorization ": "Bearer {0}".format(access_token)},
648648
)
649649
assert res.json != []
@@ -652,7 +652,7 @@ def test_get_officers_with_rank(client, db_session, access_token,
652652
assert res.json["results"][0]["last_name"] is not None
653653

654654
res = client.get(
655-
"/api/v1/officers/?rank=Lieutenant",
655+
"/api/v1/officers?rank=Lieutenant",
656656
headers={"Authorization ": "Bearer {0}".format(access_token)},
657657
)
658658
assert res.json != []
@@ -664,7 +664,7 @@ def test_get_officers_with_rank(client, db_session, access_token,
664664
def test_filter_by_ethnicity(client, db_session, access_token,
665665
create_officers_units_agencies):
666666
res = client.get(
667-
"/api/v1/officers/?ethnicity=White",
667+
"/api/v1/officers?ethnicity=White",
668668
headers={"Authorization": f"Bearer {access_token}"}
669669
)
670670

@@ -675,7 +675,7 @@ def test_filter_by_ethnicity(client, db_session, access_token,
675675

676676
# Multiple ethnicities
677677
res = client.get(
678-
"/api/v1/officers/?ethnicity=White&ethnicity=Hispanic",
678+
"/api/v1/officers?ethnicity=White&ethnicity=Hispanic",
679679
headers={"Authorization": f"Bearer {access_token}"}
680680
)
681681
assert res.status_code == 200
@@ -698,7 +698,7 @@ def test_filter_by_officer_name(client, db_session, access_token,
698698
first_name, last_name, expect_results):
699699
# Test first_name and last_name params
700700
res = client.get(
701-
f"/api/v1/officers/?first_name={first_name}&last_name={last_name}",
701+
f"/api/v1/officers?first_name={first_name}&last_name={last_name}",
702702
headers={"Authorization": f"Bearer {access_token}"}
703703
)
704704

@@ -711,7 +711,7 @@ def test_filter_by_officer_name(client, db_session, access_token,
711711

712712
# Test flexible full name param
713713
res = client.get(
714-
f"/api/v1/officers/?query={first_name} {last_name}",
714+
f"/api/v1/officers?query={first_name} {last_name}",
715715
headers={"Authorization": f"Bearer {access_token}"}
716716
)
717717
assert res.status_code == 200
@@ -721,7 +721,7 @@ def test_filter_by_officer_name(client, db_session, access_token,
721721
assert res.json == {"message": "No results found matching the query"}
722722

723723
res = client.get(
724-
f"/api/v1/officers/?query={last_name}",
724+
f"/api/v1/officers?query={last_name}",
725725
headers={"Authorization": f"Bearer {access_token}"}
726726
)
727727
assert res.status_code == 200
@@ -738,8 +738,8 @@ def test_get_search_result(client, db_session, access_token, example_officer,
738738
)
739739

740740
res = client.get(
741-
"/api/v1/officers/?first_name=John&searchResult=true",
742-
headers={"Authorization ": "Bearer {0}".format(access_token)},
741+
"/api/v1/officers?first_name=John&searchResult=true",
742+
headers={"Authorization": "Bearer {0}".format(access_token)},
743743
)
744744

745745
assert res.status_code == 200
@@ -758,8 +758,8 @@ def test_get_search_result_no_results(
758758
example_unit
759759
):
760760
res = client.get(
761-
"/api/v1/officers/?first_name=Nonexistent&searchResult=true",
762-
headers={"Authorization ": "Bearer {0}".format(access_token)},
761+
"/api/v1/officers?first_name=Nonexistent&searchResult=true",
762+
headers={"Authorization": "Bearer {0}".format(access_token)},
763763
)
764764

765765
assert res.status_code == 200
@@ -774,7 +774,7 @@ def test_invalid_query_params(
774774
example_unit
775775
):
776776
res = client.get(
777-
"/api/v1/officers/?abc=123",
777+
"/api/v1/officers?abc=123",
778778
headers={"Authorization": "Bearer {0}".format(access_token)},
779779
)
780780
body = res.get_data(as_text=True)

0 commit comments

Comments
 (0)