ds-caselaw-marklogic-api-client 28.2.0__py3-none-any.whl → 29.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ds-caselaw-marklogic-api-client might be problematic. Click here for more details.

@@ -15,6 +15,7 @@ from caselawclient.errors import (
15
15
  NotSupportedOnVersion,
16
16
  OnlySupportedOnVersion,
17
17
  )
18
+ from caselawclient.models.identifiers import Identifier
18
19
  from caselawclient.models.identifiers.fclid import FindCaseLawIdentifier, FindCaseLawIdentifierSchema
19
20
  from caselawclient.models.identifiers.unpacker import unpack_all_identifiers_from_etree
20
21
  from caselawclient.models.utilities import VersionsDict, extract_version, render_versions
@@ -171,13 +172,11 @@ class Document:
171
172
  self.identifiers = unpack_all_identifiers_from_etree(identifiers_element_as_etree)
172
173
 
173
174
  @property
174
- def best_human_identifier(self) -> Optional[str]:
175
- """
176
- Some identifier that is understood by legal professionals to refer to this legal event
177
- that is not the name of the document.
178
- Typically, this will be the neutral citation number, should it exist.
179
- Should typically be overridden in subclasses.
180
- """
175
+ def best_human_identifier(self) -> Optional[Identifier]:
176
+ """Return the preferred identifier for the document, providing that it is considered human readable."""
177
+ preferred_identifier = self.identifiers.preferred()
178
+ if preferred_identifier and preferred_identifier.schema.human_readable:
179
+ return preferred_identifier
181
180
  return None
182
181
 
183
182
  @property
@@ -507,13 +506,17 @@ class Document:
507
506
  "documentType": parser_type_noun,
508
507
  "metadata": {
509
508
  "name": self.body.name or None,
510
- "cite": self.best_human_identifier or None,
509
+ "cite": None,
511
510
  "court": self.body.court or None,
512
511
  "date": checked_date,
513
512
  "uri": self.uri,
514
513
  },
515
514
  }
516
515
 
516
+ ## TODO: Remove this hack around the fact that NCNs are assumed to be present for all documents' metadata, but actually different document classes may have different metadata
517
+ if hasattr(self, "neutral_citation"):
518
+ parser_instructions["metadata"]["cite"] = self.neutral_citation
519
+
517
520
  request_parse(
518
521
  uri=self.uri,
519
522
  reference=self.consignment_reference,
@@ -32,13 +32,20 @@ class IdentifierSchema(ABC):
32
32
  name: str
33
33
  namespace: str
34
34
 
35
+ human_readable: bool
36
+ """ Should this identifier type be considered for display as a 'human readable' identifier? """
37
+
38
+ base_score_multiplier: float = 1.0
39
+ """ A multiplier used to adjust the relative ranking of this identifier when calculating preferred identifiers. """
40
+
35
41
  def __init_subclass__(cls: type["IdentifierSchema"], **kwargs: Any) -> None:
36
42
  """Ensure that subclasses have the required attributes set."""
37
43
  for required in (
38
44
  "name",
39
45
  "namespace",
46
+ "human_readable",
40
47
  ):
41
- if not getattr(cls, required, False):
48
+ if not hasattr(cls, required):
42
49
  raise NotImplementedError(f"Can't instantiate IdentifierSchema without {required} attribute.")
43
50
  super().__init_subclass__(**kwargs)
44
51
 
@@ -101,6 +108,11 @@ class Identifier(ABC):
101
108
  def url_slug(self) -> str:
102
109
  return self.schema.compile_identifier_url_slug(self.value)
103
110
 
111
+ @property
112
+ def score(self) -> float:
113
+ """Return the score of this identifier, used to calculate the preferred identifier for a document."""
114
+ return 1 * self.schema.base_score_multiplier
115
+
104
116
  def same_as(self, other: "Identifier") -> bool:
105
117
  "Is this the same as another identifier (in value and schema)?"
106
118
  return self.value == other.value and self.schema == other.schema
@@ -149,3 +161,22 @@ class Identifiers(dict[str, Identifier]):
149
161
  identifiers_root.append(identifier.as_xml_tree)
150
162
 
151
163
  return identifiers_root
164
+
165
+ def by_score(self, type: Optional[type[Identifier]] = None) -> list[Identifier]:
166
+ """
167
+ :param type: Optionally, an identifier type to constrain this list to.
168
+
169
+ :return: Return a list of identifiers, sorted by their score in descending order.
170
+ """
171
+ identifiers = self.of_type(type) if type else list(self.values())
172
+ return sorted(identifiers, key=lambda v: v.score, reverse=True)
173
+
174
+ def preferred(self, type: Optional[type[Identifier]] = None) -> Optional[Identifier]:
175
+ """
176
+ :param type: Optionally, an identifier type to constrain the results to.
177
+
178
+ :return: Return the highest scoring identifier of the given type (or of any type, if none is specified). Returns `None` if no identifier is available.
179
+ """
180
+ if len(self) == 0:
181
+ return None
182
+ return self.by_score(type)[0]
@@ -27,6 +27,8 @@ class FindCaseLawIdentifierSchema(IdentifierSchema):
27
27
 
28
28
  name = "Find Case Law Identifier"
29
29
  namespace = "fclid"
30
+ human_readable = False
31
+ base_score_multiplier = 0.8
30
32
 
31
33
  @classmethod
32
34
  def validate_identifier(cls, value: str) -> bool:
@@ -30,6 +30,8 @@ class NeutralCitationNumberSchema(IdentifierSchema):
30
30
 
31
31
  name = "Neutral Citation Number"
32
32
  namespace = "ukncn"
33
+ human_readable = True
34
+ base_score_multiplier = 1.5
33
35
 
34
36
  @classmethod
35
37
  def validate_identifier(cls, value: str) -> bool:
@@ -25,20 +25,17 @@ class Judgment(NeutralCitationMixin, Document):
25
25
  super().__init__(self.document_noun, uri, *args, **kwargs)
26
26
 
27
27
  @cached_property
28
- def neutral_citation(self) -> NeutralCitationString:
29
- return NeutralCitationString(
30
- self.body.get_xpath_match_string(
31
- "/akn:akomaNtoso/akn:*/akn:meta/akn:proprietary/uk:cite/text()",
32
- {
33
- "uk": "https://caselaw.nationalarchives.gov.uk/akn",
34
- "akn": "http://docs.oasis-open.org/legaldocml/ns/akn/3.0",
35
- },
36
- )
28
+ def neutral_citation(self) -> Optional[NeutralCitationString]:
29
+ value_in_xml = self.body.get_xpath_match_string(
30
+ "/akn:akomaNtoso/akn:*/akn:meta/akn:proprietary/uk:cite/text()",
31
+ {
32
+ "uk": "https://caselaw.nationalarchives.gov.uk/akn",
33
+ "akn": "http://docs.oasis-open.org/legaldocml/ns/akn/3.0",
34
+ },
37
35
  )
38
-
39
- @property
40
- def best_human_identifier(self) -> str:
41
- return self.neutral_citation
36
+ if value_in_xml:
37
+ return NeutralCitationString(value_in_xml)
38
+ return None
42
39
 
43
40
  @cached_property
44
41
  def linked_document(self) -> Optional["PressSummary"]:
@@ -1,9 +1,10 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from functools import cached_property
3
- from typing import Any
3
+ from typing import Any, Optional
4
4
 
5
5
  from ds_caselaw_utils import neutral_url
6
6
  from ds_caselaw_utils.types import NeutralCitationString
7
+ from typing_extensions import deprecated
7
8
 
8
9
 
9
10
  class NeutralCitationMixin(ABC):
@@ -38,12 +39,15 @@ class NeutralCitationMixin(ABC):
38
39
 
39
40
  @cached_property
40
41
  @abstractmethod
41
- def neutral_citation(self) -> NeutralCitationString: ...
42
+ @deprecated("Legacy usage of NCNs is deprecated; you should be moving to the Identifiers framework")
43
+ def neutral_citation(self) -> Optional[NeutralCitationString]: ...
42
44
 
43
45
  @cached_property
46
+ @deprecated("Legacy usage of NCNs is deprecated; you should be moving to the Identifiers framework")
44
47
  def has_ncn(self) -> bool:
45
- return bool(self.neutral_citation)
48
+ return self.neutral_citation is not None and self.neutral_citation != ""
46
49
 
47
50
  @cached_property
51
+ @deprecated("Legacy usage of NCNs is deprecated; you should be moving to the Identifiers framework")
48
52
  def has_valid_ncn(self) -> bool:
49
- return self.has_ncn and neutral_url(self.neutral_citation) is not None
53
+ return self.neutral_citation is not None and neutral_url(self.neutral_citation) is not None
@@ -27,19 +27,16 @@ class PressSummary(NeutralCitationMixin, Document):
27
27
  super().__init__(self.document_noun, uri, *args, **kwargs)
28
28
 
29
29
  @cached_property
30
- def neutral_citation(self) -> NeutralCitationString:
31
- return NeutralCitationString(
32
- self.body.get_xpath_match_string(
33
- "/akn:akomaNtoso/akn:doc/akn:preface/akn:p/akn:neutralCitation/text()",
34
- {
35
- "akn": "http://docs.oasis-open.org/legaldocml/ns/akn/3.0",
36
- },
37
- )
30
+ def neutral_citation(self) -> Optional[NeutralCitationString]:
31
+ value_in_xml = self.body.get_xpath_match_string(
32
+ "/akn:akomaNtoso/akn:doc/akn:preface/akn:p/akn:neutralCitation/text()",
33
+ {
34
+ "akn": "http://docs.oasis-open.org/legaldocml/ns/akn/3.0",
35
+ },
38
36
  )
39
-
40
- @property
41
- def best_human_identifier(self) -> str:
42
- return self.neutral_citation
37
+ if value_in_xml:
38
+ return NeutralCitationString(value_in_xml)
39
+ return None
43
40
 
44
41
  @cached_property
45
42
  def linked_document(self) -> Optional[Judgment]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ds-caselaw-marklogic-api-client
3
- Version: 28.2.0
3
+ Version: 29.0.0
4
4
  Summary: An API client for interacting with the underlying data in Find Caselaw.
5
5
  Home-page: https://github.com/nationalarchives/ds-caselaw-custom-api-client
6
6
  Keywords: national archives,caselaw
@@ -7,19 +7,19 @@ caselawclient/errors.py,sha256=tV0vs3wYSd331BzmfuRiZV6GAdsd91rtN65ymRaSx3s,3164
7
7
  caselawclient/factories.py,sha256=6-xZMVmvtXA8AnyWJgJTums1EWfM6lPIhrWQu0NopJo,4472
8
8
  caselawclient/identifier_resolution.py,sha256=IOqrZcIHoHhNOCAkNveOBcWddBNpkOB8cz1r0zFa8mQ,1829
9
9
  caselawclient/models/__init__.py,sha256=kd23EUpvaC7aLHdgk8farqKAQEx3lf7RvNT2jEatvlg,68
10
- caselawclient/models/documents/__init__.py,sha256=DS2jqj4ShRHxAWIqQeeyCCcXctLor4xUvT3sc903D2E,19186
10
+ caselawclient/models/documents/__init__.py,sha256=Lzb18MyCPLlG0Y4EdnjZvnGFSSpsNs_BUgJA6BQqL28,19520
11
11
  caselawclient/models/documents/body.py,sha256=mtdjmG1WU2qSpyRLS8-PWcSoXpDa2Qz6xlcTbxZgxvA,5603
12
12
  caselawclient/models/documents/exceptions.py,sha256=rw1xId16vBKvBImgFmFUpeFgKqU7VTNtVLIEVBPGKyk,374
13
13
  caselawclient/models/documents/statuses.py,sha256=Cp4dTQmJOtsU41EJcxy5dV1841pGD2PNWH0VrkDEv4Q,579
14
14
  caselawclient/models/documents/transforms/html.xsl,sha256=oSSO-IBX4qLiSWexQYmWJfGNevF09aCBx4D1NYqXxpo,38322
15
15
  caselawclient/models/documents/xml.py,sha256=HlmPb63lLMnySSOLP4iexcAyQiLByKBZtTd25f8sY8M,1268
16
- caselawclient/models/identifiers/__init__.py,sha256=CxKu5iZqPb6BOjrQ3upkzyjU3YSsJhBZd7VATYxDI-A,5061
17
- caselawclient/models/identifiers/fclid.py,sha256=pAxZKKlKRSHwJqrEHOSlbuCt3gBOgS3sNv98bBjuNBc,1295
18
- caselawclient/models/identifiers/neutral_citation.py,sha256=yddlfumdnkrNpoTIOf8dB1foA7hE41-zmlfa17-Ulug,1790
16
+ caselawclient/models/identifiers/__init__.py,sha256=4D_ssfNK7gj7Z4XMHqCzn-EwsPM4a6IofYkU1JjnRF4,6449
17
+ caselawclient/models/identifiers/fclid.py,sha256=pTO586ra0sr4DbjHSxuI8UlxfNXLm9nDNVoDUIxiUrE,1354
18
+ caselawclient/models/identifiers/neutral_citation.py,sha256=3Jw1_-NmGfGmrWGFSzLdTHBYHIHq4tPkF8U7Jba-jGo,1848
19
19
  caselawclient/models/identifiers/unpacker.py,sha256=01gWWlOd_2PxT1GJzOIXbp0G4iGGIxHaTaJMBOQ1TDs,1834
20
- caselawclient/models/judgments.py,sha256=NVOg4ZTU7Jtr33UuswL2TXCaN6_W0fKFPK4EdQ-jUhE,1915
21
- caselawclient/models/neutral_citation_mixin.py,sha256=5ktKCPIDidVRwxVTzx5e242O1BxOdP--1dnatZyTbYI,1773
22
- caselawclient/models/press_summaries.py,sha256=06flQ8wSLnNxoQtXO0ckmotFKszYZcub0oPcDzYbVQw,1879
20
+ caselawclient/models/judgments.py,sha256=xFjfOspa9ZL29gvvGVNq11JC7h-LKebFMrQYvVIIoEI,1868
21
+ caselawclient/models/neutral_citation_mixin.py,sha256=LDaxNndLcTKsjJCyEKO1kGTJ6YD6h-6SzQVE0-gwPSI,2208
22
+ caselawclient/models/press_summaries.py,sha256=bEqJxu-7eBLhwulOsDXYDl2ptIp3RkkMpOcdVvWB5ds,1836
23
23
  caselawclient/models/utilities/__init__.py,sha256=u3yIhbTjFQ1JJyAm5wsMEBswWl4t6Z7UMORF5FqC2xQ,1257
24
24
  caselawclient/models/utilities/aws.py,sha256=umfPzjtykg_-OP1ACirpXfOLN4QenqA0A8tJk3DzYRk,8211
25
25
  caselawclient/models/utilities/dates.py,sha256=WwORxVjUHM1ZFcBF6Qtwo3Cj0sATsnSECkUZ6ls1N1Q,492
@@ -77,7 +77,7 @@ caselawclient/xquery/validate_document.xqy,sha256=PgaDcnqCRJPIVqfmWsNlXmCLNKd21q
77
77
  caselawclient/xquery/xslt.xqy,sha256=w57wNijH3dkwHkpKeAxqjlghVflQwo8cq6jS_sm-erM,199
78
78
  caselawclient/xquery/xslt_transform.xqy,sha256=smyFFxqmtkuOzBd2l7uw6K2oAsYctudrP8omdv_XNAM,2463
79
79
  caselawclient/xquery_type_dicts.py,sha256=kybL-YzwK34Fr6MeWfqVOJHYrs0ZNeDWXDsp8o2Yb1U,6114
80
- ds_caselaw_marklogic_api_client-28.2.0.dist-info/LICENSE.md,sha256=fGMzyyLuQW-IAXUeDSCrRdsYW536aEWThdbpCjo6ZKg,1108
81
- ds_caselaw_marklogic_api_client-28.2.0.dist-info/METADATA,sha256=i7v85V8SC6lm8oYboo4aGxSmVVaO4QZwj3WVSWDU5Cs,4272
82
- ds_caselaw_marklogic_api_client-28.2.0.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
83
- ds_caselaw_marklogic_api_client-28.2.0.dist-info/RECORD,,
80
+ ds_caselaw_marklogic_api_client-29.0.0.dist-info/LICENSE.md,sha256=fGMzyyLuQW-IAXUeDSCrRdsYW536aEWThdbpCjo6ZKg,1108
81
+ ds_caselaw_marklogic_api_client-29.0.0.dist-info/METADATA,sha256=x0GZ0GgA91sa_O9Eq2aal3FONl6Vs2lknsPdrPUmlzE,4272
82
+ ds_caselaw_marklogic_api_client-29.0.0.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
83
+ ds_caselaw_marklogic_api_client-29.0.0.dist-info/RECORD,,