ds-caselaw-marklogic-api-client 30.0.0__py3-none-any.whl → 31.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.

@@ -1,10 +1,12 @@
1
1
  import datetime
2
+ import json
2
3
  from typing import Any, Optional
3
4
  from unittest.mock import Mock
4
5
 
5
6
  from typing_extensions import TypeAlias
6
7
 
7
8
  from caselawclient.Client import MarklogicApiClient
9
+ from caselawclient.identifier_resolution import IdentifierResolution, IdentifierResolutions
8
10
  from caselawclient.models.documents import Document
9
11
  from caselawclient.models.documents.body import DocumentBody
10
12
  from caselawclient.models.judgments import Judgment
@@ -12,7 +14,16 @@ from caselawclient.models.press_summaries import PressSummary
12
14
  from caselawclient.responses.search_result import SearchResult, SearchResultMetadata
13
15
  from caselawclient.types import DocumentURIString
14
16
 
15
- DEFAULT_DOCUMENT_BODY_XML = "<akomantoso>This is some XML of a judgment.</akomantoso>"
17
+ DEFAULT_DOCUMENT_BODY_XML = """<akomaNtoso xmlns="http://docs.oasis-open.org/legaldocml/ns/akn/3.0" xmlns:uk="https://caselaw.nationalarchives.gov.uk/akn">
18
+ <judgment name="decision">
19
+ <meta/><header/>
20
+ <judgmentBody>
21
+ <decision>
22
+ <p>This is a document.</p>
23
+ </decision>
24
+ </judgmentBody>
25
+ </judgment>
26
+ </akomaNtoso>"""
16
27
 
17
28
 
18
29
  class DocumentBodyFactory:
@@ -56,7 +67,6 @@ class DocumentFactory:
56
67
  def build(
57
68
  cls,
58
69
  uri: DocumentURIString = DocumentURIString("test/2023/123"),
59
- html: str = "<p>This is a judgment.</p>",
60
70
  api_client: Optional[MarklogicApiClient] = None,
61
71
  **kwargs: Any,
62
72
  ) -> target_class:
@@ -66,7 +76,6 @@ class DocumentFactory:
66
76
  api_client.get_property_as_node.return_value = None
67
77
 
68
78
  document = cls.target_class(uri, api_client=api_client)
69
- document.content_as_html = Mock(return_value=html) # type: ignore[method-assign]
70
79
  document.body = kwargs.pop("body") if "body" in kwargs else DocumentBodyFactory.build()
71
80
 
72
81
  for param_name, default_value in cls.PARAMS_MAP.items():
@@ -133,3 +142,33 @@ class SearchResultFactory(SimpleFactory):
133
142
  "metadata": SearchResultMetadataFactory.build(),
134
143
  "is_failure": False,
135
144
  }
145
+
146
+
147
+ class IdentifierResolutionFactory:
148
+ @classmethod
149
+ def build(
150
+ self,
151
+ resolution_uuid: Optional[str] = None,
152
+ document_uri: Optional[str] = None,
153
+ identifier_slug: Optional[str] = None,
154
+ published: Optional[bool] = True,
155
+ namespace: Optional[str] = None,
156
+ value: Optional[str] = None,
157
+ ) -> IdentifierResolution:
158
+ raw_resolution = {
159
+ "documents.compiled_url_slugs.identifier_uuid": resolution_uuid or "24b9a384-8bcf-4f20-996a-5c318f8dc657",
160
+ "documents.compiled_url_slugs.document_uri": document_uri or "/ewca/civ/2003/547.xml",
161
+ "documents.compiled_url_slugs.identifier_slug": identifier_slug or "ewca/civ/2003/54721",
162
+ "documents.compiled_url_slugs.document_published": "true" if published else "false",
163
+ "documents.compiled_url_slugs.identifier_namespace": namespace or "ukncn",
164
+ "documents.compiled_url_slugs.identifier_value": value or "[2003] EWCA 54721 (Civ)",
165
+ }
166
+ return IdentifierResolution.from_marklogic_output(json.dumps(raw_resolution))
167
+
168
+
169
+ class IdentifierResolutionsFactory:
170
+ @classmethod
171
+ def build(self, resolutions: Optional[list[IdentifierResolution]] = None) -> IdentifierResolutions:
172
+ if resolutions is None:
173
+ resolutions = [IdentifierResolutionFactory.build()]
174
+ return IdentifierResolutions(resolutions)
@@ -6,15 +6,14 @@ from typing import TYPE_CHECKING, Any, Optional
6
6
  from ds_caselaw_utils import courts
7
7
  from ds_caselaw_utils.courts import CourtNotFoundException
8
8
  from ds_caselaw_utils.types import NeutralCitationString
9
- from lxml import html as html_parser
10
9
  from requests_toolbelt.multipart import decoder
11
10
 
12
11
  from caselawclient.errors import (
13
12
  DocumentNotFoundError,
14
- GatewayTimeoutError,
15
13
  NotSupportedOnVersion,
16
14
  OnlySupportedOnVersion,
17
15
  )
16
+ from caselawclient.identifier_resolution import IdentifierResolutions
18
17
  from caselawclient.models.identifiers import Identifier
19
18
  from caselawclient.models.identifiers.fclid import FindCaseLawIdentifier, FindCaseLawIdentifierSchema
20
19
  from caselawclient.models.identifiers.unpacker import unpack_all_identifiers_from_etree
@@ -252,39 +251,6 @@ class Document:
252
251
  "Is this document a potentially historic version of a document, or is it the main document itself?"
253
252
  return extract_version(self.uri) != 0
254
253
 
255
- def content_as_html(
256
- self,
257
- version_uri: Optional[DocumentURIString] = None,
258
- query: Optional[str] = None,
259
- ) -> str:
260
- try:
261
- results = self.api_client.eval_xslt(
262
- self.uri,
263
- version_uri,
264
- show_unpublished=True,
265
- query=query,
266
- )
267
- multipart_data = decoder.MultipartDecoder.from_response(results)
268
- return str(multipart_data.parts[0].text)
269
- except GatewayTimeoutError as e:
270
- if query is not None:
271
- warnings.warn(
272
- (
273
- "Gateway timeout when getting content with query"
274
- "highlighting for document %s, version %s, and query"
275
- '"%s", falling back to unhighlighted content...'
276
- )
277
- % (self.uri, version_uri, query),
278
- GatewayTimeoutGettingHTMLWithQuery,
279
- )
280
- return self.content_as_html(version_uri)
281
- raise e
282
-
283
- def number_of_mentions(self, query: str) -> int:
284
- html = self.content_as_html(query=query)
285
- tree = html_parser.fromstring(html.encode("utf-8"))
286
- return len(tree.findall(".//mark"))
287
-
288
254
  @cached_property
289
255
  def is_failure(self) -> bool:
290
256
  """
@@ -529,3 +495,21 @@ class Document:
529
495
  return getattr(self.body, name)
530
496
  except Exception:
531
497
  raise AttributeError(f"Neither 'Document' nor 'DocumentBody' objects have an attribute '{name}'")
498
+
499
+ def linked_document_resolutions(self, namespaces: list[str], only_published: bool = True) -> IdentifierResolutions:
500
+ """Get documents which share the same neutral citation as this document."""
501
+ if not hasattr(self, "neutral_citation") or not self.neutral_citation:
502
+ return IdentifierResolutions([])
503
+
504
+ resolutions = self.api_client.resolve_from_identifier_value(self.neutral_citation)
505
+ if only_published:
506
+ resolutions = resolutions.published()
507
+
508
+ # only documents which aren't this one and have a right namespace
509
+ return IdentifierResolutions(
510
+ [
511
+ resolution
512
+ for resolution in resolutions
513
+ if resolution.document_uri != self.uri.as_marklogic() and resolution.identifier_namespace in namespaces
514
+ ]
515
+ )
@@ -3,7 +3,6 @@ import os
3
3
  import warnings
4
4
  from functools import cache, cached_property
5
5
  from typing import Optional
6
- from xml.etree.ElementTree import Element
7
6
 
8
7
  import pytz
9
8
  from ds_caselaw_utils.types import CourtCode
@@ -128,13 +127,14 @@ class DocumentBody:
128
127
  def has_content(self) -> bool:
129
128
  """If we do not have a word document, the XML will not contain
130
129
  the contents of the judgment, but will contain a preamble."""
131
-
132
- def stripped_tag_text(tag: Element) -> str:
133
- return "".join(tag.itertext()).strip()
134
-
135
- header = self._xml.xml_as_tree.xpath("//akn:header", namespaces=DEFAULT_NAMESPACES)[0]
136
- content = self._xml.xml_as_tree.xpath("//akn:judgmentBody", namespaces=DEFAULT_NAMESPACES)[0]
137
- return not (stripped_tag_text(header) == "" and stripped_tag_text(content) == "")
130
+ trailing_tags = self._xml.xml_as_tree.xpath("//*[preceding::akn:meta]", namespaces=DEFAULT_NAMESPACES)
131
+ for tag in trailing_tags:
132
+ if tag.tail and tag.tail.strip():
133
+ return True
134
+ if tag.text and tag.text.strip():
135
+ return True
136
+
137
+ return False
138
138
 
139
139
  @cache
140
140
  def content_as_html(self, image_base_url: Optional[str] = None) -> Optional[str]:
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Any, Optional
5
5
  from ds_caselaw_utils.types import NeutralCitationString
6
6
 
7
7
  from caselawclient.errors import DocumentNotFoundError
8
+ from caselawclient.identifier_resolution import IdentifierResolutions
8
9
  from caselawclient.models.neutral_citation_mixin import NeutralCitationMixin
9
10
 
10
11
  if TYPE_CHECKING:
@@ -51,3 +52,7 @@ class Judgment(NeutralCitationMixin, Document):
51
52
  return PressSummary(uri, self.api_client)
52
53
  except DocumentNotFoundError:
53
54
  return None
55
+
56
+ @cached_property
57
+ def linked_press_summaries(self, only_published: bool = True) -> "IdentifierResolutions":
58
+ return self.linked_document_resolutions(["uksummaryofncn"], only_published)
@@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Any, Optional
7
7
  from ds_caselaw_utils.types import NeutralCitationString
8
8
 
9
9
  from caselawclient.errors import DocumentNotFoundError
10
+ from caselawclient.identifier_resolution import IdentifierResolutions
10
11
  from caselawclient.models.neutral_citation_mixin import NeutralCitationMixin
11
12
  from caselawclient.types import DocumentURIString
12
13
 
@@ -51,3 +52,6 @@ class PressSummary(NeutralCitationMixin, Document):
51
52
  return Judgment(uri, self.api_client)
52
53
  except DocumentNotFoundError:
53
54
  return None
55
+
56
+ def linked_judgments(self, only_published: bool = True) -> "IdentifierResolutions":
57
+ return self.linked_document_resolutions(["ukncn"], only_published)
caselawclient/types.py CHANGED
@@ -2,6 +2,10 @@ class InvalidDocumentURIException(Exception):
2
2
  """The document URI is not valid."""
3
3
 
4
4
 
5
+ class MarkLogicDocumentURIString(str):
6
+ pass
7
+
8
+
5
9
  class DocumentURIString(str):
6
10
  """
7
11
  This class checks that the string is actually a valid Document URI on creation. It does _not_ manipulate the string.
@@ -22,3 +26,6 @@ class DocumentURIString(str):
22
26
 
23
27
  # If everything is good, return as usual
24
28
  return str.__new__(cls, content)
29
+
30
+ def as_marklogic(self) -> MarkLogicDocumentURIString:
31
+ return MarkLogicDocumentURIString(f"/{self}.xml")
@@ -8,8 +8,8 @@ checks. They are used to enforce appropriately typed variables being passed in t
8
8
 
9
9
  from typing import Any, NewType, Optional, TypedDict
10
10
  from caselawclient.types import DocumentURIString
11
+ from caselawclient.types import MarkLogicDocumentURIString as MarkLogicDocumentURIString
11
12
 
12
- MarkLogicDocumentURIString = NewType("MarkLogicDocumentURIString", str)
13
13
  MarkLogicDocumentVersionURIString = NewType("MarkLogicDocumentVersionURIString", MarkLogicDocumentURIString)
14
14
 
15
15
  MarkLogicPrivilegeURIString = NewType("MarkLogicPrivilegeURIString", str)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ds-caselaw-marklogic-api-client
3
- Version: 30.0.0
3
+ Version: 31.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
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3.9
11
11
  Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Requires-Dist: boto3 (>=1.26.112,<2.0.0)
14
- Requires-Dist: certifi (>=2024.12.14,<2024.13.0)
14
+ Requires-Dist: certifi (>=2025.1.31,<2025.2.0)
15
15
  Requires-Dist: charset-normalizer (>=3.0.0,<4.0.0)
16
16
  Requires-Dist: django-environ (>=0.12.0)
17
17
  Requires-Dist: ds-caselaw-utils (>=2.0.0,<3.0.0)
@@ -4,11 +4,11 @@ caselawclient/client_helpers/__init__.py,sha256=fyDNKCdrTb2N0Ks23YDhmvlXKfLTHnYQ
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=JC16fEGq_MRJX-_KFzfINCV2Cqx8o6OWOt3C16rQd84,3142
7
- caselawclient/factories.py,sha256=yJmecrJNmzvI0_gJZFrpiONI6qt2jTFId7cXCA68-iY,4503
7
+ caselawclient/factories.py,sha256=_ey9KTu393H9q1hWAjr5LIp4oYzDre55QpVMkmxNSb0,6223
8
8
  caselawclient/identifier_resolution.py,sha256=pqapUH8oiZF3ie-s_CI0hvZwH__JVcjJ4VxkpBxswmA,2354
9
9
  caselawclient/models/__init__.py,sha256=kd23EUpvaC7aLHdgk8farqKAQEx3lf7RvNT2jEatvlg,68
10
- caselawclient/models/documents/__init__.py,sha256=SKYgOOpO4i-lhTWSB16eI6052GEh5ZlqvK3Dggd3sOg,18644
11
- caselawclient/models/documents/body.py,sha256=2rhNzCsXU13n4nw8m_GU2f_FyYGE8wUYOecBqFmZFHo,5999
10
+ caselawclient/models/documents/__init__.py,sha256=LNo9FVKkF9rGzmJ4swEKrKXvimpvenE3OU_Z3Rt6Jic,18230
11
+ caselawclient/models/documents/body.py,sha256=mhPOV1cOF3RJr69UzNPlo1KrzePaj7KDPYi1exP06L0,5880
12
12
  caselawclient/models/documents/exceptions.py,sha256=Mz1P8uNqf5w6uLnRwJt6xK7efsVqtd5VA-WXUUH7QLk,285
13
13
  caselawclient/models/documents/statuses.py,sha256=Cp4dTQmJOtsU41EJcxy5dV1841pGD2PNWH0VrkDEv4Q,579
14
14
  caselawclient/models/documents/transforms/html.xsl,sha256=oSSO-IBX4qLiSWexQYmWJfGNevF09aCBx4D1NYqXxpo,38322
@@ -18,9 +18,9 @@ caselawclient/models/identifiers/fclid.py,sha256=pTO586ra0sr4DbjHSxuI8UlxfNXLm9n
18
18
  caselawclient/models/identifiers/neutral_citation.py,sha256=3Jw1_-NmGfGmrWGFSzLdTHBYHIHq4tPkF8U7Jba-jGo,1848
19
19
  caselawclient/models/identifiers/press_summary_ncn.py,sha256=r55-qgi9LDnGxY8vTKijzotGknA6mNLpu55QQTV8Lxo,652
20
20
  caselawclient/models/identifiers/unpacker.py,sha256=xvp480QESbN36NEc6qeo-orqOBq6WchnLI7thY7A1qs,2156
21
- caselawclient/models/judgments.py,sha256=hYPmzWcxS_Pi87vWEezLI5geQqm1tRh_HtGqHpm49Zg,1900
21
+ caselawclient/models/judgments.py,sha256=H_-t4mCa3LdYu1cLOhQB5n045RdJghWpqS5hgPPDE4U,2170
22
22
  caselawclient/models/neutral_citation_mixin.py,sha256=jAac3PPuWyPdj9N-n-U_JfwkbgbSIXaqFVQahfu95do,2086
23
- caselawclient/models/press_summaries.py,sha256=f0Qyv5_7K8tWv-HVGv2QHlS4WMh5Dh4ZhbYJOJYD9lw,1867
23
+ caselawclient/models/press_summaries.py,sha256=a50KdX_v41G_eI5h6_HuA2hvrhOA2EDvoTYBi5va1e8,2101
24
24
  caselawclient/models/utilities/__init__.py,sha256=u3yIhbTjFQ1JJyAm5wsMEBswWl4t6Z7UMORF5FqC2xQ,1257
25
25
  caselawclient/models/utilities/aws.py,sha256=E4nFcNC2xxPUv0Xkfi2XTO3FyIH6jaAy9pgsvR48Eg8,8759
26
26
  caselawclient/models/utilities/dates.py,sha256=WwORxVjUHM1ZFcBF6Qtwo3Cj0sATsnSECkUZ6ls1N1Q,492
@@ -31,7 +31,7 @@ caselawclient/responses/search_response.py,sha256=Z76Zj4VvM-EV_vdiehv2-Jfkr9HZD3
31
31
  caselawclient/responses/search_result.py,sha256=hotJHJ9wQusjb4PjZm1DDZv2HEsXWTtZU5TOTj2T0rw,8205
32
32
  caselawclient/responses/xsl/search_match.xsl,sha256=4Sv--MrwBd7J48E9aI7jlFSXGlNi4dBqgzJ3bdMJ_ZU,1018
33
33
  caselawclient/search_parameters.py,sha256=nR-UC1aWZbdXzXBrVDaHECU4Ro8Zi4JZATtgrpAVsKY,3342
34
- caselawclient/types.py,sha256=vVOK78bFsnHXdOGx1899biR2QiCSVNBKoDbziJPCb68,920
34
+ caselawclient/types.py,sha256=gedGlrO1ZA_wYdCuquwi6seFr5OqTPxi1O0D_lKdEps,1087
35
35
  caselawclient/xml_helpers.py,sha256=FEtE8gxaEZmcgua-Xu8awPmiOm9K58OSabEYVGpiVEY,493
36
36
  caselawclient/xquery/break_judgment_checkout.xqy,sha256=rISzoBKxQKrP5ZRdCSoRqOXW8T_NDBSZRFjOXo_H3ns,220
37
37
  caselawclient/xquery/checkin_judgment.xqy,sha256=QeGqO3kL-q0UrjopCVU0lCbkwbyoc5SuNLYFAIbbyMg,197
@@ -79,8 +79,8 @@ caselawclient/xquery/validate_all_documents.xqy,sha256=z_0YEXmRcZ-FaJM0ouKiTjdI4
79
79
  caselawclient/xquery/validate_document.xqy,sha256=PgaDcnqCRJPIVqfmWsNlXmCLNKd21qkJrvY1RtNP7eA,140
80
80
  caselawclient/xquery/xslt.xqy,sha256=w57wNijH3dkwHkpKeAxqjlghVflQwo8cq6jS_sm-erM,199
81
81
  caselawclient/xquery/xslt_transform.xqy,sha256=smyFFxqmtkuOzBd2l7uw6K2oAsYctudrP8omdv_XNAM,2463
82
- caselawclient/xquery_type_dicts.py,sha256=qZ_bUiceoHw8c02FAFWe75zdpmK8GYUJzi2q-bc1NXA,6266
83
- ds_caselaw_marklogic_api_client-30.0.0.dist-info/LICENSE.md,sha256=fGMzyyLuQW-IAXUeDSCrRdsYW536aEWThdbpCjo6ZKg,1108
84
- ds_caselaw_marklogic_api_client-30.0.0.dist-info/METADATA,sha256=6DnjxKcXIc01ysu22YKZBC5mek5oQtmxiRnTEljxzN0,4264
85
- ds_caselaw_marklogic_api_client-30.0.0.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
86
- ds_caselaw_marklogic_api_client-30.0.0.dist-info/RECORD,,
82
+ caselawclient/xquery_type_dicts.py,sha256=IElFK9aFZboNgKytRjK2EQPIJJcXXw9NVfRTWKx2zJo,6283
83
+ ds_caselaw_marklogic_api_client-31.0.0.dist-info/LICENSE.md,sha256=fGMzyyLuQW-IAXUeDSCrRdsYW536aEWThdbpCjo6ZKg,1108
84
+ ds_caselaw_marklogic_api_client-31.0.0.dist-info/METADATA,sha256=QR0bbqSK9ZROZon7RZII--oK8fBuKoMRk94JTOE57Zc,4262
85
+ ds_caselaw_marklogic_api_client-31.0.0.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
86
+ ds_caselaw_marklogic_api_client-31.0.0.dist-info/RECORD,,