Skip to content

Commit

Permalink
Merge pull request #4744 from freelawproject/4711-filter-parent-court
Browse files Browse the repository at this point in the history
  • Loading branch information
mlissner authored Nov 27, 2024
2 parents b30275e + 61cc4b6 commit 25624cb
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 3 deletions.
197 changes: 194 additions & 3 deletions cl/api/tests.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import json
from datetime import date, timedelta
from datetime import date, datetime, timedelta, timezone
from http import HTTPStatus
from typing import Any, Dict
from unittest import mock
Expand Down Expand Up @@ -86,7 +86,7 @@
TagViewSet,
)
from cl.search.factories import CourtFactory, DocketFactory
from cl.search.models import SOURCES, Docket, Opinion
from cl.search.models import SOURCES, Court, Docket, Opinion
from cl.stats.models import Event
from cl.tests.cases import SimpleTestCase, TestCase, TransactionTestCase
from cl.tests.utils import MockResponse, make_client
Expand Down Expand Up @@ -666,11 +666,202 @@ async def assertCountInResults(self, expected_count):
f"the JSON: \n{r.json()}",
)
got = len(r.data["results"])
try:
path = r.request.get("path")
query_string = r.request.get("query_string")
url = f"{path}?{query_string}"
except AttributeError:
url = self.path
self.assertEqual(
got,
expected_count,
msg=f"Expected {expected_count}, but got {got}.\n\nr.data was: {r.data}",
msg=f"Expected {expected_count}, but got {got} in {url}\n\nr.data was: {r.data}",
)
return r


class DRFCourtApiFilterTests(TestCase, FilteringCountTestCase):
@classmethod
def setUpTestData(cls):
Court.objects.all().delete()

cls.parent_court = CourtFactory(
id="parent1",
full_name="Parent Court",
short_name="PC",
citation_string="PC",
in_use=True,
has_opinion_scraper=True,
has_oral_argument_scraper=False,
position=1,
start_date=date(2000, 1, 1),
end_date=None,
jurisdiction=Court.FEDERAL_APPELLATE,
date_modified=datetime(2021, 1, 1, tzinfo=timezone.utc),
)

cls.child_court1 = CourtFactory(
id="child1",
parent_court=cls.parent_court,
full_name="Child Court 1",
short_name="CC1",
citation_string="CC1",
in_use=False,
has_opinion_scraper=False,
has_oral_argument_scraper=True,
position=2,
start_date=date(2010, 6, 15),
end_date=date(2020, 12, 31),
jurisdiction=Court.STATE_SUPREME,
date_modified=datetime(2022, 6, 15, tzinfo=timezone.utc),
)
cls.child_court2 = CourtFactory(
id="child2",
parent_court=cls.parent_court,
full_name="Child Court 2",
short_name="CC2",
citation_string="CC2",
in_use=True,
has_opinion_scraper=False,
has_oral_argument_scraper=False,
position=3,
start_date=date(2015, 5, 20),
end_date=None,
jurisdiction=Court.STATE_TRIAL,
date_modified=datetime(2023, 3, 10, tzinfo=timezone.utc),
)

cls.orphan_court = CourtFactory(
id="orphan",
full_name="Orphan Court",
short_name="OC",
citation_string="OC",
in_use=True,
has_opinion_scraper=False,
has_oral_argument_scraper=False,
position=4,
start_date=date(2012, 8, 25),
end_date=None,
jurisdiction=Court.FEDERAL_DISTRICT,
date_modified=datetime(2023, 5, 5, tzinfo=timezone.utc),
)

@async_to_sync
async def setUp(self):
self.path = reverse("court-list", kwargs={"version": "v4"})
self.q: Dict[str, Any] = {}

async def test_parent_court_filter(self):
"""Can we filter courts by parent_court id?"""
self.q["parent_court"] = "parent1"
# Should return child1 and child2:
response = await self.assertCountInResults(2)

# Verify the returned court IDs
court_ids = [court["id"] for court in response.data["results"]]
self.assertEqual(set(court_ids), {"child1", "child2"})

# Filter for courts with parent_court id='orphan' (none should match)
self.q = {"parent_court": "orphan"}
await self.assertCountInResults(0)

async def test_no_parent_court_filter(self):
"""Do we get all courts when using no filters?"""
self.q = {}
await self.assertCountInResults(4) # Should return all four courts

async def test_invalid_parent_court_filter(self):
"""Do we handle invalid parent_court values correctly?"""
self.q["parent_court"] = "nonexistent"
await self.assertCountInResults(0)

async def test_id_filter(self):
"""Can we filter courts by id?"""
self.q["id"] = "child1"
response = await self.assertCountInResults(1)
self.assertEqual(response.data["results"][0]["id"], "child1")

async def test_in_use_filter(self):
"""Can we filter courts by in_use field?"""
self.q = {"in_use": "true"}
await self.assertCountInResults(3) # parent1, child2, orphan
self.q = {"in_use": "false"}
await self.assertCountInResults(1) # child1

async def test_has_opinion_scraper_filter(self):
"""Can we filter courts by has_opinion_scraper field?"""
self.q = {"has_opinion_scraper": "true"}
await self.assertCountInResults(1) # parent1
self.q = {"has_opinion_scraper": "false"}
await self.assertCountInResults(3) # child1, child2, orphan

async def test_has_oral_argument_scraper_filter(self):
"""Can we filter courts by has_oral_argument_scraper field?"""
self.q = {"has_oral_argument_scraper": "true"}
await self.assertCountInResults(1) # child1
self.q = {"has_oral_argument_scraper": "false"}
await self.assertCountInResults(3) # parent1, child2, orphan

async def test_position_filter(self):
"""Can we filter courts by position with integer lookups?"""
self.q = {"position__gt": "2"}
await self.assertCountInResults(2) # child2 (3), orphan (4)
self.q = {"position__lte": "2"}
await self.assertCountInResults(2) # parent1 (1), child1 (2)

async def test_start_date_filter(self):
"""Can we filter courts by start_date with date lookups?"""
self.q = {"start_date__year": "2015"}
await self.assertCountInResults(1) # child2 (2015-05-20)
self.q = {"start_date__gte": "2010-01-01"}
await self.assertCountInResults(3) # child1, child2, orphan

async def test_end_date_filter(self):
"""Can we filter courts by end_date with date lookups?"""
self.q = {"end_date__day": "31"}
await self.assertCountInResults(1) # parent1, child2, orphan
self.q = {"end_date__year": "2024"}
await self.assertCountInResults(0)

async def test_short_name_filter(self):
"""Can we filter courts by short_name with text lookups?"""
self.q = {"short_name__iexact": "Cc1"}
await self.assertCountInResults(1) # child1
self.q = {"short_name__icontains": "cc"}
await self.assertCountInResults(2) # child1, child2

async def test_full_name_filter(self):
"""Can we filter courts by full_name with text lookups?"""
self.q = {"full_name__istartswith": "Child"}
await self.assertCountInResults(2) # child1, child2
self.q = {"full_name__iendswith": "Court"}
await self.assertCountInResults(2) # parent1, orphan

async def test_citation_string_filter(self):
"""Can we filter courts by citation_string with text lookups?"""
self.q = {"citation_string": "OC"}
await self.assertCountInResults(1) # orphan
self.q = {"citation_string__icontains": "2"}
await self.assertCountInResults(1) # child2

async def test_jurisdiction_filter(self):
"""Can we filter courts by jurisdiction?"""
self.q = {
"jurisdiction": [
Court.FEDERAL_APPELLATE,
Court.FEDERAL_DISTRICT,
]
}
await self.assertCountInResults(2) # parent1 and orphan

async def test_combined_filters(self):
"""Can we filter courts with multiple filters applied?"""
self.q = {
"in_use": "true",
"has_opinion_scraper": "false",
"position__gt": "2",
}
await self.assertCountInResults(2) # child2 and orphan


class DRFJudgeApiFilterTests(
Expand Down
4 changes: 4 additions & 0 deletions cl/search/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ class CourtFilter(NoEmptyFilterSet):
"cl.search.filters.DocketFilter", queryset=Docket.objects.all()
)
jurisdiction = filters.MultipleChoiceFilter(choices=Court.JURISDICTIONS)
parent_court = filters.CharFilter(
field_name="parent_court__id",
lookup_expr="exact",
)

class Meta:
model = Court
Expand Down

0 comments on commit 25624cb

Please sign in to comment.