caselawclient.models.identifiers.neutral_citation

 1import re
 2
 3from ds_caselaw_utils import neutral_url
 4from ds_caselaw_utils.types import NeutralCitationString
 5
 6from caselawclient.types import DocumentIdentifierSlug
 7
 8from . import Identifier, IdentifierSchema
 9from .exceptions import IdentifierValidationException
10
11VALID_NCN_PATTERN = re.compile(r"(^\[([0-9]{4})\] ([a-zA-Z]+)(?: ([a-zA-Z]+))? ([0-9]+)(?: \(([a-zA-Z0-9]+)\))?$)")
12"""
13This is a catch-all pattern for anything which looks like a Neutral Citation, even if the court itself isn't valid. Checking that an NCN is plausibly correct is handled elsewhere.
14
15This pattern also defines five capture groups to standardise how we interface with the elements:
16
17- `0`: The year of the decision
18- `1`: The court
19- `2`: (Optionally) the jurisdiction or division, depending on the court
20- `3`: The sequence number of the decision
21- `4`: (Optionally) the jurisdiction or division, depending on the court
22
23TODO: When these capture groups are being used in anger (eg to build URL slugs) you should go through and name the groups.
24"""
25
26
27class NCNValidationException(IdentifierValidationException):
28    pass
29
30
31class NCNDoesNotMatchExpectedPatternException(NCNValidationException):
32    pass
33
34
35class NCNCannotConvertToValidURLSlugException(NCNValidationException):
36    pass
37
38
39class NeutralCitationNumberSchema(IdentifierSchema):
40    """
41    Identifier schema describing a Neutral Citation Number.
42
43    https://www.iclr.co.uk/knowledge/case-law/neutral-citations/
44    """
45
46    name = "Neutral Citation Number"
47    namespace = "ukncn"
48    human_readable = True
49    base_score_multiplier = 1.5
50
51    document_types = ["Judgment"]
52
53    @classmethod
54    def validate_identifier_value(cls, value: str) -> bool:
55        # Quick check to see if the NCN matches the expected pattern
56        if not bool(VALID_NCN_PATTERN.match(value)):
57            raise NCNDoesNotMatchExpectedPatternException(f"NCN '{value}' is not in the expected format")
58
59        # Can we convert this to a URL? neutral_url returns False if not.
60        # This functionally tests to see if the court exists, since only valid patterns (where we know how to match the court code) will convert
61        if not neutral_url(NeutralCitationString(value)):
62            raise NCNCannotConvertToValidURLSlugException(f"NCN '{value}' cannot be converted to an NCN-based URL slug")
63
64        return True
65
66    @classmethod
67    def compile_identifier_url_slug(cls, value: str) -> DocumentIdentifierSlug:
68        ncn_based_uri_string = neutral_url(
69            NeutralCitationString(value)
70        )  # TODO: At some point this should move out of utils and into this class.
71        if not ncn_based_uri_string:
72            raise NCNCannotConvertToValidURLSlugException(f"NCN '{value}' cannot be converted to an NCN-based URL slug")
73        return DocumentIdentifierSlug(ncn_based_uri_string)
74
75
76class NeutralCitationNumber(Identifier):
77    schema = NeutralCitationNumberSchema
VALID_NCN_PATTERN = re.compile('(^\\[([0-9]{4})\\] ([a-zA-Z]+)(?: ([a-zA-Z]+))? ([0-9]+)(?: \\(([a-zA-Z0-9]+)\\))?$)')

This is a catch-all pattern for anything which looks like a Neutral Citation, even if the court itself isn't valid. Checking that an NCN is plausibly correct is handled elsewhere.

This pattern also defines five capture groups to standardise how we interface with the elements:

  • 0: The year of the decision
  • 1: The court
  • 2: (Optionally) the jurisdiction or division, depending on the court
  • 3: The sequence number of the decision
  • 4: (Optionally) the jurisdiction or division, depending on the court

TODO: When these capture groups are being used in anger (eg to build URL slugs) you should go through and name the groups.

28class NCNValidationException(IdentifierValidationException):
29    pass

Common base class for all non-exit exceptions.

class NCNDoesNotMatchExpectedPatternException(NCNValidationException):
32class NCNDoesNotMatchExpectedPatternException(NCNValidationException):
33    pass

Common base class for all non-exit exceptions.

class NCNCannotConvertToValidURLSlugException(NCNValidationException):
36class NCNCannotConvertToValidURLSlugException(NCNValidationException):
37    pass

Common base class for all non-exit exceptions.

class NeutralCitationNumberSchema(caselawclient.models.identifiers.IdentifierSchema):
40class NeutralCitationNumberSchema(IdentifierSchema):
41    """
42    Identifier schema describing a Neutral Citation Number.
43
44    https://www.iclr.co.uk/knowledge/case-law/neutral-citations/
45    """
46
47    name = "Neutral Citation Number"
48    namespace = "ukncn"
49    human_readable = True
50    base_score_multiplier = 1.5
51
52    document_types = ["Judgment"]
53
54    @classmethod
55    def validate_identifier_value(cls, value: str) -> bool:
56        # Quick check to see if the NCN matches the expected pattern
57        if not bool(VALID_NCN_PATTERN.match(value)):
58            raise NCNDoesNotMatchExpectedPatternException(f"NCN '{value}' is not in the expected format")
59
60        # Can we convert this to a URL? neutral_url returns False if not.
61        # This functionally tests to see if the court exists, since only valid patterns (where we know how to match the court code) will convert
62        if not neutral_url(NeutralCitationString(value)):
63            raise NCNCannotConvertToValidURLSlugException(f"NCN '{value}' cannot be converted to an NCN-based URL slug")
64
65        return True
66
67    @classmethod
68    def compile_identifier_url_slug(cls, value: str) -> DocumentIdentifierSlug:
69        ncn_based_uri_string = neutral_url(
70            NeutralCitationString(value)
71        )  # TODO: At some point this should move out of utils and into this class.
72        if not ncn_based_uri_string:
73            raise NCNCannotConvertToValidURLSlugException(f"NCN '{value}' cannot be converted to an NCN-based URL slug")
74        return DocumentIdentifierSlug(ncn_based_uri_string)

Identifier schema describing a Neutral Citation Number.

https://www.iclr.co.uk/knowledge/case-law/neutral-citations/

name = 'Neutral Citation Number'
namespace = 'ukncn'
human_readable = True

Should this identifier type be considered for display as a 'human readable' identifier?

base_score_multiplier = 1.5

A multiplier used to adjust the relative ranking of this identifier when calculating preferred identifiers.

document_types = ['Judgment']

If present, a list of the names of document classes which can have this identifier.

If None, this identifier is valid for all document types.

@classmethod
def validate_identifier_value(cls, value: str) -> bool:
54    @classmethod
55    def validate_identifier_value(cls, value: str) -> bool:
56        # Quick check to see if the NCN matches the expected pattern
57        if not bool(VALID_NCN_PATTERN.match(value)):
58            raise NCNDoesNotMatchExpectedPatternException(f"NCN '{value}' is not in the expected format")
59
60        # Can we convert this to a URL? neutral_url returns False if not.
61        # This functionally tests to see if the court exists, since only valid patterns (where we know how to match the court code) will convert
62        if not neutral_url(NeutralCitationString(value)):
63            raise NCNCannotConvertToValidURLSlugException(f"NCN '{value}' cannot be converted to an NCN-based URL slug")
64
65        return True

Check that any given identifier value is valid for this schema.

@classmethod
def compile_identifier_url_slug(cls, value: str) -> caselawclient.types.DocumentIdentifierSlug:
67    @classmethod
68    def compile_identifier_url_slug(cls, value: str) -> DocumentIdentifierSlug:
69        ncn_based_uri_string = neutral_url(
70            NeutralCitationString(value)
71        )  # TODO: At some point this should move out of utils and into this class.
72        if not ncn_based_uri_string:
73            raise NCNCannotConvertToValidURLSlugException(f"NCN '{value}' cannot be converted to an NCN-based URL slug")
74        return DocumentIdentifierSlug(ncn_based_uri_string)

Convert an identifier into a precompiled URL slug.

class NeutralCitationNumber(caselawclient.models.identifiers.Identifier):
77class NeutralCitationNumber(Identifier):
78    schema = NeutralCitationNumberSchema

A base class for subclasses representing a concrete identifier.

schema = <class 'NeutralCitationNumberSchema'>