ds-caselaw-marklogic-api-client 28.0.0__py3-none-any.whl → 28.1.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.
caselawclient/Client.py CHANGED
@@ -13,12 +13,14 @@ from xml.etree.ElementTree import Element, ParseError, fromstring
13
13
  import environ
14
14
  import requests
15
15
  from ds_caselaw_utils.types import NeutralCitationString
16
+ from lxml import etree
16
17
  from requests.auth import HTTPBasicAuth
17
18
  from requests.structures import CaseInsensitiveDict
18
19
  from requests_toolbelt.multipart import decoder
19
20
 
20
21
  from caselawclient import xquery_type_dicts as query_dicts
21
22
  from caselawclient.client_helpers import VersionAnnotation
23
+ from caselawclient.identifier_resolution import IdentifierResolutions
22
24
  from caselawclient.models.documents import (
23
25
  DOCUMENT_COLLECTION_URI_JUDGMENT,
24
26
  DOCUMENT_COLLECTION_URI_PRESS_SUMMARY,
@@ -864,6 +866,17 @@ class MarklogicApiClient:
864
866
  }
865
867
  return self._eval_and_decode(vars, "get_property.xqy")
866
868
 
869
+ def get_property_as_node(self, judgment_uri: DocumentURIString, name: str) -> Optional[etree._Element]:
870
+ uri = self._format_uri_for_marklogic(judgment_uri)
871
+ vars: query_dicts.GetPropertyAsNodeDict = {
872
+ "uri": uri,
873
+ "name": name,
874
+ }
875
+ value = self._eval_and_decode(vars, "get_property_as_node.xqy")
876
+ if not value:
877
+ return None
878
+ return etree.fromstring(value)
879
+
867
880
  def get_version_annotation(self, judgment_uri: DocumentURIString) -> str:
868
881
  uri = self._format_uri_for_marklogic(judgment_uri)
869
882
  vars: query_dicts.GetVersionAnnotationDict = {
@@ -896,6 +909,22 @@ class MarklogicApiClient:
896
909
 
897
910
  return self._send_to_eval(vars, "set_property.xqy")
898
911
 
912
+ def set_property_as_node(
913
+ self,
914
+ judgment_uri: DocumentURIString,
915
+ name: str,
916
+ value: etree._Element,
917
+ ) -> requests.Response:
918
+ """Given a root node, set the value of the MarkLogic property for a document to the _contents_ of that root node. The root node itself is discarded."""
919
+ uri = self._format_uri_for_marklogic(judgment_uri)
920
+ vars: query_dicts.SetPropertyAsNodeDict = {
921
+ "uri": uri,
922
+ "value": etree.tostring(value).decode(),
923
+ "name": name,
924
+ }
925
+
926
+ return self._send_to_eval(vars, "set_property_as_node.xqy")
927
+
899
928
  def set_boolean_property(
900
929
  self,
901
930
  judgment_uri: DocumentURIString,
@@ -1173,3 +1202,18 @@ class MarklogicApiClient:
1173
1202
  )
1174
1203
 
1175
1204
  return results
1205
+
1206
+ def resolve_from_identifier(self, identifier_uri: str, published_only: bool = True) -> IdentifierResolutions:
1207
+ """Given a PUI/EUI url, look up the precomputed slug and return the
1208
+ MarkLogic document URIs which match that slug. Multiple returns should be anticipated"""
1209
+ vars: query_dicts.ResolveFromIdentifierDict = {
1210
+ "identifier_uri": DocumentURIString(identifier_uri),
1211
+ "published_only": int(published_only),
1212
+ }
1213
+ raw_results: list[str] = get_multipart_strings_from_marklogic_response(
1214
+ self._send_to_eval(
1215
+ vars,
1216
+ "resolve_from_identifier.xqy",
1217
+ ),
1218
+ )
1219
+ return IdentifierResolutions.from_marklogic_output(raw_results)
@@ -62,6 +62,7 @@ class DocumentFactory:
62
62
  if not api_client:
63
63
  api_client = Mock(spec=MarklogicApiClient)
64
64
  api_client.get_judgment_xml_bytestring.return_value = DEFAULT_DOCUMENT_BODY_XML.encode(encoding="utf-8")
65
+ api_client.get_property_as_node.return_value = None
65
66
 
66
67
  document = cls.target_class(uri, api_client=api_client)
67
68
  document.content_as_html = Mock(return_value=html) # type: ignore[method-assign]
@@ -0,0 +1,43 @@
1
+ import json
2
+ from typing import NamedTuple
3
+
4
+ from caselawclient.models.documents import DocumentURIString
5
+ from caselawclient.xquery_type_dicts import MarkLogicDocumentURIString
6
+
7
+
8
+ class IdentifierResolutions(list["IdentifierResolution"]):
9
+ """
10
+ A list of candidate MarkLogic documents which correspond to a Public UI uri
11
+
12
+ MarkLogic returns a list of dictionaries; IdentifierResolution handles a single dictionary
13
+ which corresponds to a single identifier to MarkLogic document mapping.
14
+
15
+ see `xquery/resolve_from_identifier.xqy` and `resolve_from_identifier` in `Client.py`
16
+ """
17
+
18
+ @staticmethod
19
+ def from_marklogic_output(table: list[str]) -> "IdentifierResolutions":
20
+ return IdentifierResolutions(list(IdentifierResolution.from_marklogic_output(row) for row in table))
21
+
22
+ def published(self) -> "IdentifierResolutions":
23
+ "Filter the list so that only published documents are returned"
24
+ return IdentifierResolutions(list(x for x in self if x.document_published))
25
+
26
+
27
+ class IdentifierResolution(NamedTuple):
28
+ """A single response from MarkLogic about a single identifier / document mapping"""
29
+
30
+ identifier_uuid: str
31
+ document_uri: MarkLogicDocumentURIString
32
+ identifier_slug: DocumentURIString
33
+ document_published: bool
34
+
35
+ @staticmethod
36
+ def from_marklogic_output(raw_row: str) -> "IdentifierResolution":
37
+ row = json.loads(raw_row)
38
+ return IdentifierResolution(
39
+ identifier_uuid=row["documents.compiled_url_slugs.identifier_uuid"],
40
+ document_uri=MarkLogicDocumentURIString(row["documents.compiled_url_slugs.document_uri"]),
41
+ identifier_slug=DocumentURIString(row["documents.compiled_url_slugs.identifier_slug"]),
42
+ document_published=row["documents.compiled_url_slugs.document_published"] == "true",
43
+ )
@@ -15,6 +15,7 @@ from caselawclient.errors import (
15
15
  NotSupportedOnVersion,
16
16
  OnlySupportedOnVersion,
17
17
  )
18
+ from caselawclient.models.identifiers.unpacker import unpack_all_identifiers_from_etree
18
19
  from caselawclient.models.utilities import VersionsDict, extract_version, render_versions
19
20
  from caselawclient.models.utilities.aws import (
20
21
  ParserInstructionsDict,
@@ -146,6 +147,8 @@ class Document:
146
147
  )
147
148
  """ `Document.body` represents the body of the document itself, without any information such as version tracking or properties. """
148
149
 
150
+ self._initialise_identifiers()
151
+
149
152
  def __repr__(self) -> str:
150
153
  name = self.body.name or "un-named"
151
154
  return f"<{self.document_noun} {self.uri}: {name}>"
@@ -160,6 +163,12 @@ class Document:
160
163
  """There is a docx in S3 private bucket for this Document"""
161
164
  return check_docx_exists(self.uri)
162
165
 
166
+ def _initialise_identifiers(self) -> None:
167
+ """Load this document's identifiers from MarkLogic."""
168
+
169
+ identifiers_element_as_etree = self.api_client.get_property_as_node(self.uri, "identifiers")
170
+ self.identifiers = unpack_all_identifiers_from_etree(identifiers_element_as_etree)
171
+
163
172
  @property
164
173
  def best_human_identifier(self) -> Optional[str]:
165
174
  """
@@ -521,6 +530,11 @@ class Document:
521
530
  """
522
531
  return self.docx_exists()
523
532
 
533
+ def save_identifiers(self) -> None:
534
+ """Save the current state of this Document's identifiers to MarkLogic."""
535
+ self.identifiers.validate()
536
+ self.api_client.set_property_as_node(self.uri, "identifiers", self.identifiers.as_etree)
537
+
524
538
  def __getattr__(self, name: str) -> Any:
525
539
  warnings.warn(f"{name} no longer exists on Document, using Document.body instead", DeprecationWarning)
526
540
  try:
@@ -17,7 +17,7 @@ class XML:
17
17
  :raises NonXMLDocumentError: This document is not valid XML
18
18
  """
19
19
  try:
20
- self.xml_as_tree: etree.Element = etree.fromstring(xml_bytestring)
20
+ self.xml_as_tree: etree._Element = etree.fromstring(xml_bytestring)
21
21
  except etree.XMLSyntaxError:
22
22
  raise NonXMLDocumentError
23
23
 
@@ -0,0 +1,146 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, Optional, Union
3
+ from uuid import uuid4
4
+
5
+ from lxml import etree
6
+
7
+ IDENTIFIER_PACKABLE_ATTRIBUTES: list[str] = [
8
+ "uuid",
9
+ "value",
10
+ "url_slug",
11
+ ]
12
+
13
+ IDENTIFIER_UNPACKABLE_ATTRIBUTES: list[str] = [
14
+ "uuid",
15
+ "value",
16
+ ]
17
+
18
+
19
+ class InvalidIdentifierXMLRepresentationException(Exception):
20
+ pass
21
+
22
+
23
+ class UUIDMismatchError(Exception):
24
+ pass
25
+
26
+
27
+ class IdentifierSchema(ABC):
28
+ """
29
+ A base class which describes what an identifier schema should look like.
30
+ """
31
+
32
+ name: str
33
+ namespace: str
34
+
35
+ def __init_subclass__(cls: type["IdentifierSchema"], **kwargs: Any) -> None:
36
+ """Ensure that subclasses have the required attributes set."""
37
+ for required in (
38
+ "name",
39
+ "namespace",
40
+ ):
41
+ if not getattr(cls, required, False):
42
+ raise NotImplementedError(f"Can't instantiate IdentifierSchema without {required} attribute.")
43
+ super().__init_subclass__(**kwargs)
44
+
45
+ def __repr__(self) -> str:
46
+ return self.name
47
+
48
+ @classmethod
49
+ @abstractmethod
50
+ def validate_identifier(cls, value: str) -> bool:
51
+ """Check that any given identifier value is valid for this schema."""
52
+ pass
53
+
54
+ @classmethod
55
+ @abstractmethod
56
+ def compile_identifier_url_slug(cls, value: str) -> str:
57
+ """Convert an identifier into a precompiled URL slug."""
58
+ pass
59
+
60
+
61
+ class Identifier(ABC):
62
+ """A base class for subclasses representing a concrete identifier."""
63
+
64
+ schema: type[IdentifierSchema]
65
+
66
+ uuid: str
67
+ value: str
68
+
69
+ def __init_subclass__(cls: type["Identifier"], **kwargs: Any) -> None:
70
+ """Ensure that subclasses have the required attributes set."""
71
+ for required in ("schema",):
72
+ if not getattr(cls, required, False):
73
+ raise NotImplementedError(f"Can't instantiate Identifier without {required} attribute.")
74
+ super().__init_subclass__(**kwargs)
75
+
76
+ def __repr__(self) -> str:
77
+ return f"<{self.schema.name} {self.value}: {self.uuid}>"
78
+
79
+ def __init__(self, value: str, uuid: Optional[str] = None) -> None:
80
+ self.value = value
81
+ if uuid:
82
+ self.uuid = uuid
83
+ else:
84
+ self.uuid = "id-" + str(uuid4())
85
+
86
+ @property
87
+ def as_xml_tree(self) -> etree._Element:
88
+ """Convert this Identifier into a packed XML representation for storage."""
89
+ identifier_root = etree.Element("identifier")
90
+
91
+ namespace_attribute = etree.SubElement(identifier_root, "namespace")
92
+ namespace_attribute.text = self.schema.namespace
93
+
94
+ for attribute in IDENTIFIER_PACKABLE_ATTRIBUTES:
95
+ packed_attribute = etree.SubElement(identifier_root, attribute)
96
+ packed_attribute.text = getattr(self, attribute)
97
+
98
+ return identifier_root
99
+
100
+ @property
101
+ def url_slug(self) -> str:
102
+ return self.schema.compile_identifier_url_slug(self.value)
103
+
104
+ def same_as(self, other: "Identifier") -> bool:
105
+ "Is this the same as another identifier (in value and schema)?"
106
+ return self.value == other.value and self.schema == other.schema
107
+
108
+
109
+ class Identifiers(dict[str, Identifier]):
110
+ def validate(self) -> None:
111
+ for uuid, identifier in self.items():
112
+ if uuid != identifier.uuid:
113
+ msg = "Key of {identifier} in Identifiers is {uuid} not {identifier.uuid}"
114
+ raise UUIDMismatchError(msg)
115
+
116
+ def contains(self, other_identifier: Identifier) -> bool:
117
+ "Do the identifier's value and namespace already exist in this group?"
118
+ return any(other_identifier.same_as(identifier) for identifier in self.values())
119
+
120
+ def add(self, identifier: Identifier) -> None:
121
+ if not self.contains(identifier):
122
+ self[identifier.uuid] = identifier
123
+
124
+ def __delitem__(self, key: Union[Identifier, str]) -> None:
125
+ if isinstance(key, Identifier):
126
+ super().__delitem__(key.uuid)
127
+ else:
128
+ super().__delitem__(key)
129
+
130
+ def delete_type(self, deleted_identifier_type: type[Identifier]) -> None:
131
+ "For when we want an identifier to be the only valid identifier of that type, delete the others first"
132
+ uuids = self.keys()
133
+ for uuid in list(uuids):
134
+ # we could use compare to .schema instead, which would have diffferent behaviour for subclasses
135
+ if isinstance(self[uuid], deleted_identifier_type):
136
+ del self[uuid]
137
+
138
+ @property
139
+ def as_etree(self) -> etree._Element:
140
+ """Return an etree representation of all the Document's identifiers."""
141
+ identifiers_root = etree.Element("identifiers")
142
+
143
+ for identifier in self.values():
144
+ identifiers_root.append(identifier.as_xml_tree)
145
+
146
+ return identifiers_root
@@ -0,0 +1,49 @@
1
+ import re
2
+
3
+ from ds_caselaw_utils import neutral_url
4
+ from ds_caselaw_utils.types import NeutralCitationString
5
+
6
+ from . import Identifier, IdentifierSchema
7
+
8
+ VALID_NCN_PATTERN = re.compile(r"(^\[([0-9]{4})\] ([a-zA-Z]+)(?: ([a-zA-Z]+))? ([0-9]+)(?: \(([a-zA-Z]+)\))?$)")
9
+ """
10
+ 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.
11
+
12
+ This pattern also defines five capture groups to standardise how we interface with the elements:
13
+
14
+ - `0`: The year of the decision
15
+ - `1`: The court
16
+ - `2`: (Optionally) the jurisdiction or division, depending on the court
17
+ - `3`: The sequence number of the decision
18
+ - `4`: (Optionally) the jurisdiction or division, depending on the court
19
+
20
+ TODO: When these capture groups are being used in anger (eg to build URL slugs) you should go through and name the groups.
21
+ """
22
+
23
+
24
+ class NeutralCitationNumberSchema(IdentifierSchema):
25
+ """
26
+ Identifier schema describing a Neutral Citation Number.
27
+
28
+ https://www.iclr.co.uk/knowledge/case-law/neutral-citations/
29
+ """
30
+
31
+ name = "Neutral Citation Number"
32
+ namespace = "ukncn"
33
+
34
+ @classmethod
35
+ def validate_identifier(cls, value: str) -> bool:
36
+ return bool(VALID_NCN_PATTERN.match(value))
37
+
38
+ @classmethod
39
+ def compile_identifier_url_slug(cls, value: str) -> str:
40
+ ncn_based_uri_string = neutral_url(
41
+ NeutralCitationString(value)
42
+ ) # TODO: At some point this should move out of utils and into this class.
43
+ if not ncn_based_uri_string:
44
+ raise Exception(f"Unable to convert NCN {value} into NCN-based URL slug")
45
+ return ncn_based_uri_string
46
+
47
+
48
+ class NeutralCitationNumber(Identifier):
49
+ schema = NeutralCitationNumberSchema
@@ -0,0 +1,44 @@
1
+ from typing import Optional
2
+
3
+ from lxml import etree
4
+
5
+ from . import IDENTIFIER_UNPACKABLE_ATTRIBUTES, Identifier, Identifiers, InvalidIdentifierXMLRepresentationException
6
+ from .neutral_citation import NeutralCitationNumber
7
+
8
+ IDENTIFIER_NAMESPACE_MAP: dict[str, type[Identifier]] = {
9
+ "ukncn": NeutralCitationNumber,
10
+ }
11
+
12
+
13
+ def unpack_all_identifiers_from_etree(identifiers_etree: Optional[etree._Element]) -> Identifiers:
14
+ """This expects the entire <identifiers> tag, and unpacks all Identifiers inside it"""
15
+ identifiers = Identifiers()
16
+ if identifiers_etree is None:
17
+ return identifiers
18
+ for identifier_etree in identifiers_etree.findall("identifier"):
19
+ identifier = unpack_an_identifier_from_etree(identifier_etree)
20
+ identifiers.add(identifier)
21
+ return identifiers
22
+
23
+
24
+ def unpack_an_identifier_from_etree(identifier_xml: etree._Element) -> Identifier:
25
+ """Given an etree representation of a single identifier, unpack it into an appropriate instance of an Identifier."""
26
+
27
+ namespace_element = identifier_xml.find("namespace")
28
+
29
+ if namespace_element is None or not namespace_element.text:
30
+ raise InvalidIdentifierXMLRepresentationException(
31
+ "Identifer XML representation is not valid: namespace not present or empty"
32
+ )
33
+
34
+ kwargs: dict[str, str] = {}
35
+
36
+ for attribute in IDENTIFIER_UNPACKABLE_ATTRIBUTES:
37
+ element = identifier_xml.find(attribute)
38
+ if element is None or not element.text:
39
+ raise InvalidIdentifierXMLRepresentationException(
40
+ f"Identifer XML representation is not valid: {element} not present or empty"
41
+ )
42
+ kwargs[attribute] = element.text
43
+
44
+ return IDENTIFIER_NAMESPACE_MAP[namespace_element.text](**kwargs)
@@ -137,12 +137,14 @@ def publish_documents(uri: str) -> None:
137
137
  response = client.list_objects(Bucket=private_bucket, Prefix=uri)
138
138
 
139
139
  for result in response.get("Contents", []):
140
+ print(f"Contemplating copying {result!r}")
140
141
  key = str(result["Key"])
141
142
 
142
143
  if not key.endswith("parser.log") and not key.endswith(".tar.gz"):
143
144
  source: CopySourceTypeDef = {"Bucket": private_bucket, "Key": key}
144
145
  extra_args: dict[str, str] = {}
145
146
  try:
147
+ print(f"Copying {key!r} from {private_bucket!r} to {public_bucket!r}")
146
148
  client.copy(source, public_bucket, key, extra_args)
147
149
  except botocore.client.ClientError as e:
148
150
  logging.warning(
@@ -9,8 +9,7 @@ def get_xpath_match_string(
9
9
  namespaces: Optional[Dict[str, str]] = None,
10
10
  fallback: str = "",
11
11
  ) -> str:
12
- kwargs = {"namespaces": namespaces} if namespaces else {}
13
- return str((node.xpath(path, **kwargs) or [fallback])[0])
12
+ return str((node.xpath(path, namespaces=namespaces) or [fallback])[0])
14
13
 
15
14
 
16
15
  def get_xpath_match_strings(
@@ -18,5 +17,4 @@ def get_xpath_match_strings(
18
17
  path: str,
19
18
  namespaces: Optional[Dict[str, str]] = None,
20
19
  ) -> list[str]:
21
- kwargs = {"namespaces": namespaces} if namespaces else {}
22
- return [str(x) for x in node.xpath(path, **kwargs)]
20
+ return [str(x) for x in node.xpath(path, namespaces=namespaces)]
@@ -0,0 +1,9 @@
1
+ xquery version "1.0-ml";
2
+
3
+ declare variable $uri as xs:string external;
4
+
5
+ declare variable $name as xs:string external;
6
+
7
+ let $prop := fn:QName("", $name)
8
+
9
+ return xdmp:document-get-properties($uri, $prop)
@@ -0,0 +1,17 @@
1
+ xquery version "1.0-ml";
2
+
3
+ declare namespace xdmp="http://marklogic.com/xdmp";
4
+ declare variable $identifier_uri as xs:string external;
5
+ declare variable $published_only as xs:int? external := 1;
6
+
7
+ let $published_query := if ($published_only) then " AND document_published = 'true'" else ""
8
+ let $query := "SELECT * from compiled_url_slugs WHERE (identifier_slug = @uri)" || $published_query
9
+
10
+ return xdmp:sql(
11
+ $query,
12
+ "map",
13
+ map:new((
14
+ map:entry("uri", $identifier_uri)
15
+ ))
16
+ )
17
+
@@ -0,0 +1,11 @@
1
+ xquery version "1.0-ml";
2
+
3
+ import module namespace dls = "http://marklogic.com/xdmp/dls" at "/MarkLogic/dls.xqy";
4
+
5
+ declare variable $uri as xs:string external;
6
+ declare variable $value as xs:string external;
7
+ declare variable $name as xs:string external;
8
+
9
+ let $props := ( element {$name} {xdmp:unquote($value)/*/*} )
10
+
11
+ return dls:document-set-property($uri, $props)
@@ -113,6 +113,12 @@ class GetPropertyDict(MarkLogicAPIDict):
113
113
  uri: MarkLogicDocumentURIString
114
114
 
115
115
 
116
+ # get_property_as_node.xqy
117
+ class GetPropertyAsNodeDict(MarkLogicAPIDict):
118
+ name: str
119
+ uri: MarkLogicDocumentURIString
120
+
121
+
116
122
  # get_version_annotation.xqy
117
123
  class GetVersionAnnotationDict(MarkLogicAPIDict):
118
124
  uri: MarkLogicDocumentURIString
@@ -135,6 +141,12 @@ class ListJudgmentVersionsDict(MarkLogicAPIDict):
135
141
  uri: MarkLogicDocumentURIString
136
142
 
137
143
 
144
+ # resolve_from_identifier.xqy
145
+ class ResolveFromIdentifierDict(MarkLogicAPIDict):
146
+ identifier_uri: DocumentURIString
147
+ published_only: Optional[int]
148
+
149
+
138
150
  # set_boolean_property.xqy
139
151
  class SetBooleanPropertyDict(MarkLogicAPIDict):
140
152
  name: str
@@ -187,6 +199,13 @@ class SetPropertyDict(MarkLogicAPIDict):
187
199
  value: str
188
200
 
189
201
 
202
+ # set_property_as_node.xqy
203
+ class SetPropertyAsNodeDict(MarkLogicAPIDict):
204
+ name: str
205
+ uri: MarkLogicDocumentURIString
206
+ value: str
207
+
208
+
190
209
  # update_document.xqy
191
210
  class UpdateDocumentDict(MarkLogicAPIDict):
192
211
  annotation: str
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ds-caselaw-marklogic-api-client
3
- Version: 28.0.0
3
+ Version: 28.1.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
@@ -1,22 +1,26 @@
1
- caselawclient/Client.py,sha256=M1fsZhEJDb7_ikUOYt2qNKPOjBMZB-QnX0FQFvX92nM,41741
1
+ caselawclient/Client.py,sha256=OFqiKNYuz8C7UF6AP36Bie64UP3p8mKLZ-NQIc_LEWc,43636
2
2
  caselawclient/__init__.py,sha256=DY-caubLDQWWingSdsBWgovDNXh8KcnkI6kwz08eIFk,612
3
3
  caselawclient/client_helpers/__init__.py,sha256=fyDNKCdrTb2N0Ks23YDhmvlXKfLTHnYQCXhnZb-QQbg,3832
4
4
  caselawclient/client_helpers/search_helpers.py,sha256=R99HyRLeYHgsw2L3DOidEqlKLLvs6Tga5rKTuWQViig,1525
5
5
  caselawclient/content_hash.py,sha256=0cPC4OoABq0SC2wYFX9-24DodNigeOqksDxgxQH_hUA,2221
6
6
  caselawclient/errors.py,sha256=tV0vs3wYSd331BzmfuRiZV6GAdsd91rtN65ymRaSx3s,3164
7
- caselawclient/factories.py,sha256=PGLn2rjiQsj_JGixkgLMoe-krcvRI084kEeyDnCalo0,4408
7
+ caselawclient/factories.py,sha256=6-xZMVmvtXA8AnyWJgJTums1EWfM6lPIhrWQu0NopJo,4472
8
+ caselawclient/identifier_resolution.py,sha256=IOqrZcIHoHhNOCAkNveOBcWddBNpkOB8cz1r0zFa8mQ,1829
8
9
  caselawclient/models/__init__.py,sha256=kd23EUpvaC7aLHdgk8farqKAQEx3lf7RvNT2jEatvlg,68
9
- caselawclient/models/documents/__init__.py,sha256=GjCx1_X5Q08ANae_e44jTrYPj7gPuhQsKNSeuFxXQBo,18074
10
+ caselawclient/models/documents/__init__.py,sha256=OF-0tTSA5VO6qi630zONrL2lC768yTkwHWmNyRxZ_D8,18762
10
11
  caselawclient/models/documents/body.py,sha256=mtdjmG1WU2qSpyRLS8-PWcSoXpDa2Qz6xlcTbxZgxvA,5603
11
12
  caselawclient/models/documents/exceptions.py,sha256=rw1xId16vBKvBImgFmFUpeFgKqU7VTNtVLIEVBPGKyk,374
12
13
  caselawclient/models/documents/statuses.py,sha256=Cp4dTQmJOtsU41EJcxy5dV1841pGD2PNWH0VrkDEv4Q,579
13
14
  caselawclient/models/documents/transforms/html.xsl,sha256=oSSO-IBX4qLiSWexQYmWJfGNevF09aCBx4D1NYqXxpo,38322
14
- caselawclient/models/documents/xml.py,sha256=afEsgcnTThqW_gKYq-VGtFr4ovOoT2J7h2gXX7F8BbE,1267
15
+ caselawclient/models/documents/xml.py,sha256=HlmPb63lLMnySSOLP4iexcAyQiLByKBZtTd25f8sY8M,1268
16
+ caselawclient/models/identifiers/__init__.py,sha256=Ft0cFE7LlCecsS5In_hi9So0x6La1kRJ8Who-VgC4YM,4795
17
+ caselawclient/models/identifiers/neutral_citation.py,sha256=yddlfumdnkrNpoTIOf8dB1foA7hE41-zmlfa17-Ulug,1790
18
+ caselawclient/models/identifiers/unpacker.py,sha256=V79m4rd0FZx5TRueL1m3MbrSUWO8c0f5NoNxSP3dlFw,1757
15
19
  caselawclient/models/judgments.py,sha256=NVOg4ZTU7Jtr33UuswL2TXCaN6_W0fKFPK4EdQ-jUhE,1915
16
20
  caselawclient/models/neutral_citation_mixin.py,sha256=5ktKCPIDidVRwxVTzx5e242O1BxOdP--1dnatZyTbYI,1773
17
21
  caselawclient/models/press_summaries.py,sha256=06flQ8wSLnNxoQtXO0ckmotFKszYZcub0oPcDzYbVQw,1879
18
22
  caselawclient/models/utilities/__init__.py,sha256=u3yIhbTjFQ1JJyAm5wsMEBswWl4t6Z7UMORF5FqC2xQ,1257
19
- caselawclient/models/utilities/aws.py,sha256=YQeuFdF5NvhUxo3Ejj3PURDlygA73oq2T42ltuQZ6Oo,8073
23
+ caselawclient/models/utilities/aws.py,sha256=umfPzjtykg_-OP1ACirpXfOLN4QenqA0A8tJk3DzYRk,8211
20
24
  caselawclient/models/utilities/dates.py,sha256=WwORxVjUHM1ZFcBF6Qtwo3Cj0sATsnSECkUZ6ls1N1Q,492
21
25
  caselawclient/models/utilities/move.py,sha256=Rsx1eGHVjbGz0WMVDjy8b_5t4Ig8aP55sLudL07MVUs,3621
22
26
  caselawclient/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -25,7 +29,7 @@ caselawclient/responses/search_response.py,sha256=Z76Zj4VvM-EV_vdiehv2-Jfkr9HZD3
25
29
  caselawclient/responses/search_result.py,sha256=2yR3FP4CQsVymE7RrOMbh1owjYaRTrqkjMObbIkSlhE,8216
26
30
  caselawclient/responses/xsl/search_match.xsl,sha256=4Sv--MrwBd7J48E9aI7jlFSXGlNi4dBqgzJ3bdMJ_ZU,1018
27
31
  caselawclient/search_parameters.py,sha256=nR-UC1aWZbdXzXBrVDaHECU4Ro8Zi4JZATtgrpAVsKY,3342
28
- caselawclient/xml_helpers.py,sha256=qmqdGhwrQ-zhvvB-8akwzWnC2uHponKkEnnRExqkx_A,591
32
+ caselawclient/xml_helpers.py,sha256=FEtE8gxaEZmcgua-Xu8awPmiOm9K58OSabEYVGpiVEY,493
29
33
  caselawclient/xquery/break_judgment_checkout.xqy,sha256=rISzoBKxQKrP5ZRdCSoRqOXW8T_NDBSZRFjOXo_H3ns,220
30
34
  caselawclient/xquery/checkin_judgment.xqy,sha256=QeGqO3kL-q0UrjopCVU0lCbkwbyoc5SuNLYFAIbbyMg,197
31
35
  caselawclient/xquery/checkout_judgment.xqy,sha256=aRwVo4KXoEKXfXRZ6IrVfvh0pXK-7pFxVIgEyzE5DRY,385
@@ -45,12 +49,14 @@ caselawclient/xquery/get_pending_enrichment_for_version.xqy,sha256=8J5Pi-jMXJd_B
45
49
  caselawclient/xquery/get_pending_parse_for_version.xqy,sha256=9cjVZtHeBBjm-a7RMsn1PVJt_Ug78YFlmp5CN8VJ1jQ,1197
46
50
  caselawclient/xquery/get_properties_for_search_results.xqy,sha256=Tlv3EKwVV_q-JyQyhjWVHIleicPDpucxP4ScuQjpgSw,625
47
51
  caselawclient/xquery/get_property.xqy,sha256=RHlOTrK0aH-S7s_ykYzGmUeKOJxXlI4vE5sKRt556NY,209
52
+ caselawclient/xquery/get_property_as_node.xqy,sha256=7EXNgjVD1QugJ1621pvg8PdjBRIuh7GugwARv04TuBk,202
48
53
  caselawclient/xquery/get_recently_enriched.xqy,sha256=qSA9EYlKaAyYwewBRj494aC_JnzlLzbsWyTgWIJqFbw,614
49
54
  caselawclient/xquery/get_recently_parsed.xqy,sha256=k8NP72O11FyDZ3MeKAJWlcrzPsynhPTddYIeS0pRaII,594
50
55
  caselawclient/xquery/get_version_annotation.xqy,sha256=pFDMGA9SxI59iUPaoAeUsq23kUp4Op7cUg-PFNFqxcI,262
51
56
  caselawclient/xquery/get_version_created.xqy,sha256=bRweaXFtwMBNzL16SlOdiOxHkbqNUwpwDHLxpZYVCh0,250
52
57
  caselawclient/xquery/insert_document.xqy,sha256=iP2xTaLGa-u6X9KfS1yJ6yPCKQUWQFYdEW1S4YcMY7w,531
53
58
  caselawclient/xquery/list_judgment_versions.xqy,sha256=WShga8igeD21hSLfVSvCOiDMPDhNH6KGf1OW6G0SAkY,190
59
+ caselawclient/xquery/resolve_from_identifier.xqy,sha256=Fa-RSw9ZwD__BmT5LLJ0J0HcDstDbedkEccv45M3L4g,484
54
60
  caselawclient/xquery/set_boolean_property.xqy,sha256=8Vg3yDWqeDynUJQHw2OF4daDIKTnp8ARol1_OCqY0Dk,355
55
61
  caselawclient/xquery/set_metadata_citation.xqy,sha256=ImwijXowvOCiH_br_LepnKsEpys9tg4Cf3uz6MoC5-c,659
56
62
  caselawclient/xquery/set_metadata_court.xqy,sha256=xQGR3e4pdJuDPMlzdAdzrBDSeQbEFiLVIm2z_KQI_Ds,996
@@ -59,6 +65,7 @@ caselawclient/xquery/set_metadata_name.xqy,sha256=7UeCo13ePNPxXcz9v7o1Pw7MoL6b5v
59
65
  caselawclient/xquery/set_metadata_this_uri.xqy,sha256=lBHk2EJxcRcxu1JqNdBlSzUZmFvVaSs0vu2cVrt84c8,1762
60
66
  caselawclient/xquery/set_metadata_work_expression_date.xqy,sha256=eN4VvRA5FqP3aKrlvnqwa7e9mcuuNXqLH5_yHWvrjrU,1017
61
67
  caselawclient/xquery/set_property.xqy,sha256=wUDCMLrUUgTMqn_-_T48DXsdDLtFncrVjidVuCwtpS4,343
68
+ caselawclient/xquery/set_property_as_node.xqy,sha256=85ywMo5VPGXBnHepIajDOPYEy_ZmeU_9glgDcJ_CwEI,362
62
69
  caselawclient/xquery/update_document.xqy,sha256=-kbv3Ig2nuBviCyn0R_H6GPE1kpDP4UOhWsYk5oBgtU,420
63
70
  caselawclient/xquery/update_locked_judgment.xqy,sha256=wFDEtKh7MxvPmEHrzOHTJIRlY01ATneESl-n_lIQ7cU,556
64
71
  caselawclient/xquery/user_has_privilege.xqy,sha256=TfqPjhdpXt-4Fo7E2kEYRhfqQm6uES-IuLbAlCxylbg,371
@@ -67,8 +74,8 @@ caselawclient/xquery/validate_all_documents.xqy,sha256=z_0YEXmRcZ-FaJM0ouKiTjdI4
67
74
  caselawclient/xquery/validate_document.xqy,sha256=PgaDcnqCRJPIVqfmWsNlXmCLNKd21qkJrvY1RtNP7eA,140
68
75
  caselawclient/xquery/xslt.xqy,sha256=w57wNijH3dkwHkpKeAxqjlghVflQwo8cq6jS_sm-erM,199
69
76
  caselawclient/xquery/xslt_transform.xqy,sha256=smyFFxqmtkuOzBd2l7uw6K2oAsYctudrP8omdv_XNAM,2463
70
- caselawclient/xquery_type_dicts.py,sha256=AUEmycChASKl8sbqVQWe6WA0s-lIfEqh9mA-GdnUCQ8,5692
71
- ds_caselaw_marklogic_api_client-28.0.0.dist-info/LICENSE.md,sha256=fGMzyyLuQW-IAXUeDSCrRdsYW536aEWThdbpCjo6ZKg,1108
72
- ds_caselaw_marklogic_api_client-28.0.0.dist-info/METADATA,sha256=a2fOk_l8-GbQTwo9GYl2PdqaCSj_jrKNv48gqXl0eOE,4232
73
- ds_caselaw_marklogic_api_client-28.0.0.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
74
- ds_caselaw_marklogic_api_client-28.0.0.dist-info/RECORD,,
77
+ caselawclient/xquery_type_dicts.py,sha256=kybL-YzwK34Fr6MeWfqVOJHYrs0ZNeDWXDsp8o2Yb1U,6114
78
+ ds_caselaw_marklogic_api_client-28.1.0.dist-info/LICENSE.md,sha256=fGMzyyLuQW-IAXUeDSCrRdsYW536aEWThdbpCjo6ZKg,1108
79
+ ds_caselaw_marklogic_api_client-28.1.0.dist-info/METADATA,sha256=e9Z30AABT4vao0GmVnuD8LPfK2dRXHXkBltukUpNzVM,4232
80
+ ds_caselaw_marklogic_api_client-28.1.0.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
81
+ ds_caselaw_marklogic_api_client-28.1.0.dist-info/RECORD,,