Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FCL-361] Add new types to utils to express NCNs and related concepts #191

Merged
merged 2 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
exclude: courts_schema_types_autogenerated\.py
exclude: courts_schema_autogen\.py
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
Expand Down
2 changes: 1 addition & 1 deletion jsonschema-gentypes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ generate:
- # JSON schema file path
source: src/ds_caselaw_utils/data/schema/courts.schema.json
# Python file path
destination: src/ds_caselaw_utils/courts_schema_types_autogenerated.py
destination: src/ds_caselaw_utils/types/courts_schema_autogen.py
# The name of the root element
root_name: RawCourtRepository
# Argument passed to the API
Expand Down
12 changes: 5 additions & 7 deletions src/ds_caselaw_utils/courts.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,17 @@

import pathlib
from datetime import date
from typing import NewType, Optional
from typing import Optional

from ruamel.yaml import YAML

from ds_caselaw_utils.courts_schema_types_autogenerated import (
from ds_caselaw_utils.types.courts_schema_autogen import (
RawCourt,
RawCourtRepository,
RawJurisdiction,
)

CourtCode = NewType("CourtCode", str)
CourtParam = NewType("CourtParam", str)
JurisdictionCode = NewType("JurisdictionCode", str)
from .types import CourtCode, CourtParam, JurisdictionCode, NeutralCitationString


class Jurisdiction:
Expand All @@ -36,7 +34,7 @@ def __init__(self, data: RawCourt) -> None:
self.name: str = data["name"]
self.grouped_name: str = data.get("grouped_name") or data["name"]
self.link: str = data["link"]
self.ncn: Optional[str] = data.get("ncn")
self.ncn: Optional[NeutralCitationString] = NeutralCitationString(data["ncn"]) if "ncn" in data else None
if "param" in data:
self.canonical_param = CourtParam(data["param"])
self.param_aliases = [CourtParam(data["param"])] + [
Expand Down Expand Up @@ -85,7 +83,7 @@ def link(self) -> str:
return self.court.link

@property
def ncn(self) -> Optional[str]:
def ncn(self) -> Optional[NeutralCitationString]:
return self.court.ncn

@property
Expand Down
2 changes: 1 addition & 1 deletion src/ds_caselaw_utils/factory.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import typing

from .courts import Court
from .courts_schema_types_autogenerated import RawCourt, RawCourtRepository
from .types.courts_schema_autogen import RawCourt, RawCourtRepository


class CourtFactory(Court):
Expand Down
6 changes: 4 additions & 2 deletions src/ds_caselaw_utils/neutral.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,21 @@

from ruamel.yaml import YAML

from .types import NCNBasedUriString, NeutralCitationString

yaml = YAML()
datafile = pathlib.Path(__file__).parent / "data/neutral_citation_regex.yaml"
with open(datafile) as f:
citation_data = yaml.load(f)


def neutral_url(citation: str) -> Optional[str]:
def neutral_url(citation: NeutralCitationString) -> Optional[NCNBasedUriString]:
"""Given a neutral citation such as `[2020] EAT 17`,
return a public-API URL like `/eat/2020/17`, or None
if no match is found.
"""
for regex, groups in citation_data:
if match := re.match(regex, citation):
url_components = "/".join([match.groups()[x - 1] for x in groups])
return f"/{url_components}".lower()
return NCNBasedUriString(f"/{url_components}".lower())
return None
47 changes: 24 additions & 23 deletions src/ds_caselaw_utils/test_neutral.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
import unittest

from .neutral import neutral_url
from .types import NeutralCitationString


class TestNeutralURL(unittest.TestCase):
def test_good_neutral_urls(self):
self.assertEqual(neutral_url("[2022] UKSC 1"), "/uksc/2022/1")
self.assertEqual(neutral_url("[1604] EWCA Crim 555"), "/ewca/crim/1604/555")
self.assertEqual(neutral_url("[2022] EWHC 1 (Comm)"), "/ewhc/comm/2022/1")
self.assertEqual(neutral_url("[1999] EWCOP 7"), "/ewcop/1999/7")
self.assertEqual(neutral_url("[2022] UKUT 1 (IAC)"), "/ukut/iac/2022/1")
self.assertEqual(neutral_url("[2022] EAT 1"), "/eat/2022/1")
self.assertEqual(neutral_url("[2022] UKFTT 1 (TC)"), "/ukftt/tc/2022/1")
self.assertEqual(neutral_url("[2022] UKFTT 1 (GRC)"), "/ukftt/grc/2022/1")
self.assertEqual(neutral_url("[2022] EWHC 1 (KB)"), "/ewhc/kb/2022/1")
self.assertEqual(neutral_url("[2023] UKAIT 1"), "/ukait/2023/1")
self.assertEqual(neutral_url("[2024] EWCOP 17 (T2)"), "/ewcop/t2/2024/17")
self.assertEqual(neutral_url("[2000] UKIPTrib 99"), "/ukiptrib/2000/99")
self.assertEqual(neutral_url("[2000] EWCR 99"), "/ewcr/2000/99")
self.assertEqual(neutral_url("[2000] EWCC 99"), "/ewcc/2000/99")
self.assertEqual(neutral_url(NeutralCitationString("[2022] UKSC 1")), "/uksc/2022/1")
self.assertEqual(neutral_url(NeutralCitationString("[1604] EWCA Crim 555")), "/ewca/crim/1604/555")
self.assertEqual(neutral_url(NeutralCitationString("[2022] EWHC 1 (Comm)")), "/ewhc/comm/2022/1")
self.assertEqual(neutral_url(NeutralCitationString("[1999] EWCOP 7")), "/ewcop/1999/7")
self.assertEqual(neutral_url(NeutralCitationString("[2022] UKUT 1 (IAC)")), "/ukut/iac/2022/1")
self.assertEqual(neutral_url(NeutralCitationString("[2022] EAT 1")), "/eat/2022/1")
self.assertEqual(neutral_url(NeutralCitationString("[2022] UKFTT 1 (TC)")), "/ukftt/tc/2022/1")
self.assertEqual(neutral_url(NeutralCitationString("[2022] UKFTT 1 (GRC)")), "/ukftt/grc/2022/1")
self.assertEqual(neutral_url(NeutralCitationString("[2022] EWHC 1 (KB)")), "/ewhc/kb/2022/1")
self.assertEqual(neutral_url(NeutralCitationString("[2023] UKAIT 1")), "/ukait/2023/1")
self.assertEqual(neutral_url(NeutralCitationString("[2024] EWCOP 17 (T2)")), "/ewcop/t2/2024/17")
self.assertEqual(neutral_url(NeutralCitationString("[2000] UKIPTrib 99")), "/ukiptrib/2000/99")
self.assertEqual(neutral_url(NeutralCitationString("[2000] EWCR 99")), "/ewcr/2000/99")
self.assertEqual(neutral_url(NeutralCitationString("[2000] EWCC 99")), "/ewcc/2000/99")

def test_bad_neutral_urls(self):
self.assertEqual(neutral_url(""), None)
self.assertEqual(neutral_url("1604] EWCA Crim 555"), None)
self.assertEqual(neutral_url("[2022 EWHC 1 Comm"), None)
self.assertEqual(neutral_url("[1999] EWCOP"), None)
self.assertEqual(neutral_url(NeutralCitationString("")), None)
self.assertEqual(neutral_url(NeutralCitationString("1604] EWCA Crim 555")), None)
self.assertEqual(neutral_url(NeutralCitationString("[2022 EWHC 1 Comm")), None)
self.assertEqual(neutral_url(NeutralCitationString("[1999] EWCOP")), None)
self.assertEqual(
neutral_url("[2022] UKUT B1 IAC"), None
neutral_url(NeutralCitationString("[2022] UKUT B1 IAC")), None
) # Could be a Bailii reference, might want to drop B in future.
self.assertEqual(neutral_url("[2022] EAT A"), None)
self.assertEqual(neutral_url("[2022] NOTACOURT 1 TC"), None)
self.assertEqual(neutral_url("[2022] EWHC 1 (T2)"), None)
self.assertEqual(neutral_url("[2000] EWCRC 99"), None)
self.assertEqual(neutral_url(NeutralCitationString("[2022] EAT A")), None)
self.assertEqual(neutral_url(NeutralCitationString("[2022] NOTACOURT 1 TC")), None)
self.assertEqual(neutral_url(NeutralCitationString("[2022] EWHC 1 (T2)")), None)
self.assertEqual(neutral_url(NeutralCitationString("[2000] EWCRC 99")), None)
10 changes: 10 additions & 0 deletions src/ds_caselaw_utils/types/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from typing import NewType

# Types which are used for identifying courts and their jurisdictions
CourtCode = NewType("CourtCode", str)
CourtParam = NewType("CourtParam", str)
JurisdictionCode = NewType("JurisdictionCode", str)

# Types which are used to identify a judgment
NeutralCitationString = NewType("NeutralCitationString", str)
NCNBasedUriString = NewType("NCNBasedUriString", str)