From 47f650d44821bd091c9d19b7a35f111613a2cd60 Mon Sep 17 00:00:00 2001 From: Nick Jackson Date: Wed, 2 Oct 2024 11:39:42 +0100 Subject: [PATCH] feat(Court): make it clearer what Court.ncn actually means `Court.ncn` is now `Court.ncn_pattern`, and is explicitly typed as being a subtype of a regular expression pattern. This is because it looked very similar to representing a Neutral Citation (ie the NCN of an individual judgment), and not (as it actually case) representing the pattern which valid NCNs for that court must match. BREAKING CHANGE: `Court.ncn` is now `Court.ncn_pattern`, and now returns a `NeutralCitationPattern` which is a subtype of `re.Pattern[str]`. --- src/ds_caselaw_utils/courts.py | 11 ++- src/ds_caselaw_utils/data/court_names.yaml | 84 +++++++++---------- .../data/schema/courts.schema.json | 4 +- src/ds_caselaw_utils/test_courts.py | 10 +-- src/ds_caselaw_utils/types/__init__.py | 4 + .../types/courts_schema_autogen.py | 8 +- 6 files changed, 68 insertions(+), 53 deletions(-) diff --git a/src/ds_caselaw_utils/courts.py b/src/ds_caselaw_utils/courts.py index bc8939a..e6e4ce2 100644 --- a/src/ds_caselaw_utils/courts.py +++ b/src/ds_caselaw_utils/courts.py @@ -6,6 +6,7 @@ import pathlib from datetime import date +from re import compile from typing import Optional from ruamel.yaml import YAML @@ -16,7 +17,7 @@ RawJurisdiction, ) -from .types import CourtCode, CourtParam, JurisdictionCode, NeutralCitationString +from .types import CourtCode, CourtParam, JurisdictionCode, NeutralCitationPattern class Jurisdiction: @@ -34,7 +35,9 @@ 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[NeutralCitationString] = NeutralCitationString(data["ncn"]) if "ncn" in data else None + self.ncn_pattern: Optional[NeutralCitationPattern] = ( + NeutralCitationPattern(compile(data["ncn_pattern"])) if "ncn_pattern" in data else None + ) if "param" in data: self.canonical_param = CourtParam(data["param"]) self.param_aliases = [CourtParam(data["param"])] + [ @@ -83,8 +86,8 @@ def link(self) -> str: return self.court.link @property - def ncn(self) -> Optional[NeutralCitationString]: - return self.court.ncn + def ncn_pattern(self) -> Optional[NeutralCitationPattern]: + return self.court.ncn_pattern @property def canonical_param(self) -> Optional[CourtParam]: diff --git a/src/ds_caselaw_utils/data/court_names.yaml b/src/ds_caselaw_utils/data/court_names.yaml index 3128035..beedf2a 100644 --- a/src/ds_caselaw_utils/data/court_names.yaml +++ b/src/ds_caselaw_utils/data/court_names.yaml @@ -5,7 +5,7 @@ - code: UKSC name: United Kingdom Supreme Court link: https://www.supremecourt.uk/ - ncn: \[(\d{4})\] (UKSC) (\d+) + ncn_pattern: \[(\d{4})\] (UKSC) (\d+) param: "uksc" start_year: 2014 selectable: true @@ -17,7 +17,7 @@ - code: UKPC name: Privy Council link: https://www.jcpc.uk/ - ncn: \[(\d{4})\] (UKPC) (\d+) + ncn_pattern: \[(\d{4})\] (UKPC) (\d+) param: "ukpc" start_year: 2014 selectable: true @@ -30,7 +30,7 @@ grouped_name: "Civil Division" name: "Court of Appeal (Civil Division)" link: https://www.gov.uk/courts-tribunals/court-of-appeal-civil-division - ncn: \[(\d{4})\] (EWCA) (Civ) (\d+) + ncn_pattern: \[(\d{4})\] (EWCA) (Civ) (\d+) param: "ewca/civ" start_year: 2003 selectable: true @@ -39,7 +39,7 @@ grouped_name: "Criminal Division" name: Court of Appeal (Criminal Division) link: https://www.gov.uk/courts-tribunals/court-of-appeal-criminal-division - ncn: \[(\d{4})\] (EWCA) (Crim) (\d+) + ncn_pattern: \[(\d{4})\] (EWCA) (Crim) (\d+) param: "ewca/crim" start_year: 2003 selectable: true @@ -52,7 +52,7 @@ grouped_name: "Administrative Court" name: High Court (Administrative Court) link: https://www.gov.uk/courts-tribunals/administrative-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((Admin)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((Admin)\) param: "ewhc/admin" start_year: 2003 selectable: true @@ -61,7 +61,7 @@ grouped_name: "Administrative Court" name: High Court (Administrative Court) link: https://www.gov.uk/courts-tribunals/administrative-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((Admin)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((Admin)\) param: "ewhc/admin" start_year: 2022 selectable: false @@ -70,7 +70,7 @@ grouped_name: "Admiralty Court" name: High Court (Admiralty Division) link: https://www.gov.uk/courts-tribunals/admiralty-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((Admlty)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((Admlty)\) param: "ewhc/admlty" start_year: 2003 selectable: true @@ -79,7 +79,7 @@ grouped_name: "Admiralty Court" name: High Court (Admiralty Division) link: https://www.gov.uk/courts-tribunals/admiralty-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((Admlty)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((Admlty)\) param: "ewhc/admlty" start_year: 2022 selectable: false @@ -118,7 +118,7 @@ grouped_name: Chancery Division name: High Court (Chancery Division) link: https://www.gov.uk/courts-tribunals/chancery-division-of-the-high-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((Ch)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((Ch)\) param: "ewhc/ch" start_year: 2003 selectable: true @@ -127,21 +127,21 @@ grouped_name: Circuit Commercial Court name: High Court (Circuit Commercial Court) link: https://www.gov.uk/courts-tribunals/commercial-circuit-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((Comm)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((Comm)\) selectable: false listable: false - code: EWHC-KBD-Commercial-Circuit grouped_name: Circuit Commercial Court name: High Court (Circuit Commercial Court) link: https://www.gov.uk/courts-tribunals/commercial-circuit-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((Comm)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((Comm)\) selectable: false listable: false - code: EWHC-QBD-Commercial grouped_name: Commercial Court name: High Court (Commercial Court) link: https://www.gov.uk/courts-tribunals/commercial-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((Comm)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((Comm)\) param: "ewhc/comm" start_year: 2003 selectable: true @@ -150,7 +150,7 @@ grouped_name: Commercial Court name: High Court (Commercial Court) link: https://www.gov.uk/courts-tribunals/commercial-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((Comm)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((Comm)\) param: "ewhc/comm" start_year: 2022 selectable: false @@ -159,7 +159,7 @@ grouped_name: Family Division name: High Court (Family Division) link: https://www.gov.uk/courts-tribunals/family-division-of-the-high-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((Fam)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((Fam)\) param: "ewhc/fam" start_year: 2003 selectable: true @@ -168,14 +168,14 @@ grouped_name: Financial List name: High Court (Financial List) link: https://www.gov.uk/courts-tribunals/the-financial-list - ncn: \[(\d{4})\] (EWHC) (\d+) \((Comm)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((Comm)\) selectable: false listable: false - code: EWHC-KBD-Commercial-Financial grouped_name: Financial List name: High Court (Financial List) link: https://www.gov.uk/courts-tribunals/the-financial-list - ncn: \[(\d{4})\] (EWHC) (\d+) \((Comm)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((Comm)\) selectable: false listable: false - code: EWHC-Chancery-Financial @@ -194,7 +194,7 @@ grouped_name: Intellectual Property Enterprise Court name: High Court (Intellectual Property Enterprise Court) link: https://www.gov.uk/courts-tribunals/intellectual-property-enterprise-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((IPEC)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((IPEC)\) param: "ewhc/ipec" start_year: 2003 selectable: true @@ -209,7 +209,7 @@ grouped_name: King's / Queen's Bench Division name: High Court (King's / Queen's Bench Division) link: https://www.gov.uk/courts-tribunals/kings-bench-division-of-the-high-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((KB)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((KB)\) param: "ewhc/kb" extra_params: ["ewhc/qb"] start_year: 2003 @@ -219,7 +219,7 @@ grouped_name: King's Bench Division name: High Court (King's Bench Division) link: https://www.gov.uk/courts-tribunals/kings-bench-division-of-the-high-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((KB)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((KB)\) param: "ewhc/kb" start_year: 2022 selectable: false @@ -228,7 +228,7 @@ grouped_name: Mercantile Court name: High Court (Mercantile Court) link: https://www.gov.uk/courts-tribunals/intellectual-property-enterprise-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((IPEC)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((IPEC)\) param: "ewhc/mercantile" start_year: 2008 end_year: 2014 @@ -238,7 +238,7 @@ grouped_name: Patents Court name: High Court (Patents Court) link: https://www.gov.uk/courts-tribunals/patents-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((Pat)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((Pat)\) param: "ewhc/pat" start_year: 2003 selectable: true @@ -247,7 +247,7 @@ grouped_name: Queen's Bench Division name: High Court (Queen's Bench Division) link: https://www.gov.uk/courts-tribunals/queens-bench-division-of-the-high-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((QB)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((QB)\) param: "ewhc/qb" start_year: 2003 end_year: 2022 @@ -275,7 +275,7 @@ grouped_name: Senior Courts Costs Office name: High Court (Senior Court Costs Office) link: https://www.gov.uk/courts-tribunals/senior-courts-costs-office - ncn: \[(\d{4})\] (EWHC) (\d+) \((SCCO)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((SCCO)\) param: "ewhc/scco" extra_params: ["ewhc/costs"] start_year: 2003 @@ -285,7 +285,7 @@ grouped_name: Technology and Construction Court name: High Court (Technology and Construction Court) link: https://www.gov.uk/courts-tribunals/technology-and-construction-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((TCC)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((TCC)\) param: "ewhc/tcc" start_year: 2003 selectable: true @@ -294,7 +294,7 @@ grouped_name: Technology and Construction Court name: High Court (Technology and Construction Court) link: https://www.gov.uk/courts-tribunals/technology-and-construction-court - ncn: \[(\d{4})\] (EWHC) (\d+) \((TCC)\) + ncn_pattern: \[(\d{4})\] (EWHC) (\d+) \((TCC)\) param: "ewhc/tcc" start_year: 2022 selectable: false @@ -306,7 +306,7 @@ - code: "EWCR" name: "Crown Court" link: https://www.judiciary.uk/courts-and-tribunals/crown-court/ - ncn: \[(\d{4})\] (EWCR) (\d+) + ncn_pattern: \[(\d{4})\] (EWCR) (\d+) param: "ewcr" start_year: 2024 selectable: true @@ -314,7 +314,7 @@ - code: "EWCC" name: "County Court" link: https://www.judiciary.uk/courts-and-tribunals/county-court/ - ncn: \[(\d{4})\] (EWCC) (\d+) + ncn_pattern: \[(\d{4})\] (EWCC) (\d+) param: "ewcc" start_year: 2024 selectable: true @@ -322,7 +322,7 @@ - code: EWFC name: Family Court link: https://www.judiciary.uk/you-and-the-judiciary/going-to-court/family-law-courts/ - ncn: \[(\d{4})\] (EWFC) (\d+) + ncn_pattern: \[(\d{4})\] (EWFC) (\d+) param: "ewfc" start_year: 2009 selectable: true @@ -331,7 +331,7 @@ name: Family Court (B - district and circuit judges) # This is an artifact of how the Family Court creates tiers of importance. link: https://www.judiciary.uk/you-and-the-judiciary/going-to-court/family-law-courts/ - ncn: \[(\d{4})\] (EWFC) (B) (\d+) + ncn_pattern: \[(\d{4})\] (EWFC) (B) (\d+) param: "ewfc/b" start_year: 2009 selectable: false @@ -339,7 +339,7 @@ - code: EWCOP name: Court of Protection link: https://www.gov.uk/courts-tribunals/court-of-protection - ncn: \[(\d{4})\] (EWCOP) (\d+) + ncn_pattern: \[(\d{4})\] (EWCOP) (\d+) param: "ewcop" start_year: 2009 selectable: true @@ -347,7 +347,7 @@ - code: EWCOP-T1 name: Court of Protection (Tier 1 - district judges) link: https://www.gov.uk/courts-tribunals/court-of-protection - ncn: \[(\d{4})\] (EWCOP) (T1) (\d+) + ncn_pattern: \[(\d{4})\] (EWCOP) (T1) (\d+) param: "ewcop/t1" start_year: 2009 selectable: false @@ -355,7 +355,7 @@ - code: EWCOP-T2 name: Court of Protection (Tier 2 - circuit judges) link: https://www.gov.uk/courts-tribunals/court-of-protection - ncn: \[(\d{4})\] (EWCOP) (T2) (\d+) + ncn_pattern: \[(\d{4})\] (EWCOP) (T2) (\d+) param: "ewcop/t2" start_year: 2009 selectable: false @@ -363,7 +363,7 @@ - code: EWCOP-T3 name: Court of Protection (Tier 3 - high court judges) link: https://www.gov.uk/courts-tribunals/court-of-protection - ncn: \[(\d{4})\] (EWCOP) (T3) (\d+) + ncn_pattern: \[(\d{4})\] (EWCOP) (T3) (\d+) param: "ewcop/t3" start_year: 2009 selectable: false @@ -376,7 +376,7 @@ - code: UKIPT name: Investigatory Powers Tribunal link: https://investigatorypowerstribunal.org.uk/about-the-tribunal/ - ncn: \[(\d{4})\] (UKIPTrib) (\d+) + ncn_pattern: \[(\d{4})\] (UKIPTrib) (\d+) selectable: true listable: true param: "ukiptrib" @@ -389,7 +389,7 @@ - code: EAT name: Employment Appeal Tribunal link: https://www.gov.uk/courts-tribunals/employment-appeal-tribunal - ncn: \[(\d{4})\] (EAT) (\d+) + ncn_pattern: \[(\d{4})\] (EAT) (\d+) selectable: true listable: true param: "eat" @@ -402,7 +402,7 @@ grouped_name: Administrative Appeals Chamber name: Upper Tribunal (Administrative Appeals Chamber) link: https://www.gov.uk/courts-tribunals/upper-tribunal-administrative-appeals-chamber - ncn: \[(\d{4})\] (UKUT) (\d+) \((AAC)\) + ncn_pattern: \[(\d{4})\] (UKUT) (\d+) \((AAC)\) selectable: true listable: true param: "ukut/aac" @@ -411,7 +411,7 @@ grouped_name: Immigration and Asylum Chamber name: Upper Tribunal (Immigration and Asylum Chamber) link: https://www.gov.uk/courts-tribunals/upper-tribunal-immigration-and-asylum-chamber - ncn: \[(\d{4})\] (UKUT) (\d+) \((IAC)\) + ncn_pattern: \[(\d{4})\] (UKUT) (\d+) \((IAC)\) selectable: true listable: true param: "ukut/iac" @@ -423,14 +423,14 @@ param: "ukait" name: Asylum & Immigration Tribunal link: https://www.gov.uk/courts-tribunals/upper-tribunal-immigration-and-asylum-chamber - ncn: \[(\d{4})\] (UKAIT) (\d+) + ncn_pattern: \[(\d{4})\] (UKAIT) (\d+) start_year: 2003 end_year: 2010 - code: UKUT-LC grouped_name: Lands Chamber name: Upper Tribunal (Lands Chamber) link: https://www.gov.uk/courts-tribunals/upper-tribunal-lands-chamber - ncn: \[(\d{4})\] (UKUT) (\d+) \((LC)\) + ncn_pattern: \[(\d{4})\] (UKUT) (\d+) \((LC)\) selectable: true listable: true param: "ukut/lc" @@ -439,7 +439,7 @@ grouped_name: Tax and Chancery Chamber name: Upper Tribunal (Tax and Chancery Chamber) link: https://www.gov.uk/courts-tribunals/upper-tribunal-tax-and-chancery-chamber - ncn: \[(\d{4})\] (UKUT) (\d+) \((TCC)\) + ncn_pattern: \[(\d{4})\] (UKUT) (\d+) \((TCC)\) selectable: true listable: true param: "ukut/tcc" @@ -460,7 +460,7 @@ name: First-tier Tribunal (General Regulatory Chamber) param: "ukftt/grc" link: https://www.gov.uk/courts-tribunals/first-tier-tribunal-general-regulatory-chamber - ncn: \[(\d{4})\] (UKFTT) (\d+) \((GRC)\) + ncn_pattern: \[(\d{4})\] (UKFTT) (\d+) \((GRC)\) start_year: 2022 selectable: true listable: true @@ -514,7 +514,7 @@ grouped_name: Tax Chamber name: First-tier Tribunal (Tax Chamber) link: https://www.gov.uk/courts-tribunals/first-tier-tribunal-tax - ncn: \[(\d{4})\] (UKFTT) (\d+) \((TC)\) + ncn_pattern: \[(\d{4})\] (UKFTT) (\d+) \((TC)\) param: "ukftt/tc" start_year: 2022 selectable: true diff --git a/src/ds_caselaw_utils/data/schema/courts.schema.json b/src/ds_caselaw_utils/data/schema/courts.schema.json index c518a87..16f3df0 100644 --- a/src/ds_caselaw_utils/data/schema/courts.schema.json +++ b/src/ds_caselaw_utils/data/schema/courts.schema.json @@ -44,7 +44,9 @@ "pattern": "^[a-z]{2,}(/[a-z]+)?$" } }, - "ncn": { + "ncn_pattern": { + "title": "Neutral Citation Pattern", + "description": "A regular expression pattern which matches valid NCNs from this court.", "type": "string" }, "link": { diff --git a/src/ds_caselaw_utils/test_courts.py b/src/ds_caselaw_utils/test_courts.py index f7f8354..9bf91e5 100644 --- a/src/ds_caselaw_utils/test_courts.py +++ b/src/ds_caselaw_utils/test_courts.py @@ -420,13 +420,13 @@ def test_link(self): self.assertEqual(cwj.link, "court_link") court.mock_link.assert_called() - def test_ncn(self): - # It returns the court ncn - court = mock_with_properties({"ncn": "court_ncn"}) + def test_ncn_pattern(self): + # It returns the court's NCN pattern + court = mock_with_properties({"ncn_pattern": "court_ncn"}) jurisdiction = mock_with_properties() cwj = CourtWithJurisdiction(court, jurisdiction) - self.assertEqual(cwj.ncn, "court_ncn") - court.mock_ncn.assert_called() + self.assertEqual(cwj.ncn_pattern, "court_ncn") + court.mock_ncn_pattern.assert_called() def test_canonical_param(self): # It returns the court canonical_param diff --git a/src/ds_caselaw_utils/types/__init__.py b/src/ds_caselaw_utils/types/__init__.py index abf43be..dbd0287 100644 --- a/src/ds_caselaw_utils/types/__init__.py +++ b/src/ds_caselaw_utils/types/__init__.py @@ -1,3 +1,4 @@ +from re import Pattern from typing import NewType # Types which are used for identifying courts and their jurisdictions @@ -5,6 +6,9 @@ CourtParam = NewType("CourtParam", str) JurisdictionCode = NewType("JurisdictionCode", str) +# Types which are used for identifying a particular detail of a court +NeutralCitationPattern = NewType("NeutralCitationPattern", Pattern[str]) + # Types which are used to identify a judgment NeutralCitationString = NewType("NeutralCitationString", str) NCNBasedUriString = NewType("NCNBasedUriString", str) diff --git a/src/ds_caselaw_utils/types/courts_schema_autogen.py b/src/ds_caselaw_utils/types/courts_schema_autogen.py index ee1b204..050eda4 100644 --- a/src/ds_caselaw_utils/types/courts_schema_autogen.py +++ b/src/ds_caselaw_utils/types/courts_schema_autogen.py @@ -23,7 +23,13 @@ class RawCourt(TypedDict, total=False): """ pattern: ^[a-z]{2,}(?:/[a-z0-9]+)?$ """ extra_params: List["_RawCourtExtraParamsItem"] - ncn: str + ncn_pattern: str + """ + Neutral Citation Pattern. + + A regular expression pattern which matches valid NCNs from this court. + """ + link: Required[str] """ format: uri