ds-caselaw-marklogic-api-client 26.0.0__tar.gz → 27.0.0__tar.gz

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.

Files changed (71) hide show
  1. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/PKG-INFO +2 -2
  2. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/pyproject.toml +4 -4
  3. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/Client.py +3 -10
  4. ds_caselaw_marklogic_api_client-27.0.0/src/caselawclient/factories.py +165 -0
  5. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/models/documents/__init__.py +9 -23
  6. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/models/documents/body.py +6 -9
  7. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/models/judgments.py +12 -8
  8. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/models/neutral_citation_mixin.py +7 -11
  9. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/models/press_summaries.py +11 -7
  10. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/models/utilities/dates.py +1 -5
  11. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/models/utilities/move.py +12 -63
  12. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/responses/search_response.py +2 -2
  13. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/responses/search_result.py +6 -4
  14. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/LICENSE.md +0 -0
  15. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/README.md +0 -0
  16. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/__init__.py +0 -0
  17. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/client_helpers/__init__.py +0 -0
  18. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/client_helpers/search_helpers.py +0 -0
  19. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/content_hash.py +0 -0
  20. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/errors.py +0 -0
  21. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/models/__init__.py +0 -0
  22. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/models/documents/exceptions.py +0 -0
  23. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/models/documents/statuses.py +0 -0
  24. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/models/documents/xml.py +0 -0
  25. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/models/utilities/__init__.py +0 -0
  26. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/models/utilities/aws.py +0 -0
  27. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/py.typed +0 -0
  28. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/responses/__init__.py +0 -0
  29. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/responses/xsl/search_match.xsl +0 -0
  30. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/search_parameters.py +0 -0
  31. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xml_helpers.py +0 -0
  32. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/break_judgment_checkout.xqy +0 -0
  33. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/checkin_judgment.xqy +0 -0
  34. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/checkout_judgment.xqy +0 -0
  35. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/copy_document.xqy +0 -0
  36. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/delete_judgment.xqy +0 -0
  37. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/document_collections.xqy +0 -0
  38. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/document_exists.xqy +0 -0
  39. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_combined_stats_table.xqy +0 -0
  40. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_components_for_document.xqy +0 -0
  41. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_highest_enrichment_version.xqy +0 -0
  42. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_highest_parser_version.xqy +0 -0
  43. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_judgment.xqy +0 -0
  44. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_judgment_checkout_status.xqy +0 -0
  45. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_judgment_version.xqy +0 -0
  46. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_last_modified.xqy +0 -0
  47. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_pending_enrichment_for_version.xqy +0 -0
  48. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_pending_parse_for_version.xqy +0 -0
  49. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_properties_for_search_results.xqy +0 -0
  50. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_property.xqy +0 -0
  51. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_version_annotation.xqy +0 -0
  52. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/get_version_created.xqy +0 -0
  53. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/insert_document.xqy +0 -0
  54. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/list_judgment_versions.xqy +0 -0
  55. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/set_boolean_property.xqy +0 -0
  56. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/set_metadata_citation.xqy +0 -0
  57. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/set_metadata_court.xqy +0 -0
  58. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/set_metadata_jurisdiction.xqy +0 -0
  59. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/set_metadata_name.xqy +0 -0
  60. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/set_metadata_this_uri.xqy +0 -0
  61. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/set_metadata_work_expression_date.xqy +0 -0
  62. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/set_property.xqy +0 -0
  63. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/update_document.xqy +0 -0
  64. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/update_locked_judgment.xqy +0 -0
  65. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/user_has_privilege.xqy +0 -0
  66. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/user_has_role.xqy +0 -0
  67. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/validate_all_documents.xqy +0 -0
  68. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/validate_document.xqy +0 -0
  69. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/xslt.xqy +0 -0
  70. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery/xslt_transform.xqy +0 -0
  71. {ds_caselaw_marklogic_api_client-26.0.0 → ds_caselaw_marklogic_api_client-27.0.0}/src/caselawclient/xquery_type_dicts.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ds-caselaw-marklogic-api-client
3
- Version: 26.0.0
3
+ Version: 27.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
@@ -14,7 +14,7 @@ Requires-Dist: boto3 (>=1.26.112,<2.0.0)
14
14
  Requires-Dist: certifi (>=2024.8.30,<2024.9.0)
15
15
  Requires-Dist: charset-normalizer (>=3.0.0,<4.0.0)
16
16
  Requires-Dist: django-environ (>=0.11.0,<0.12.0)
17
- Requires-Dist: ds-caselaw-utils (>=1.4.1,<2.0.0)
17
+ Requires-Dist: ds-caselaw-utils (>=2.0.0,<3.0.0)
18
18
  Requires-Dist: idna (>=3.4,<4.0)
19
19
  Requires-Dist: lxml (>=5.0.0,<6.0.0)
20
20
  Requires-Dist: memoization (>=0.4.0,<0.5.0)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "ds-caselaw-marklogic-api-client"
3
- version = "26.0.0"
3
+ version = "27.0.0"
4
4
  description = "An API client for interacting with the underlying data in Find Caselaw."
5
5
  authors = ["The National Archives"]
6
6
  homepage = "https://github.com/nationalarchives/ds-caselaw-custom-api-client"
@@ -20,7 +20,7 @@ requests = "^2.28.2"
20
20
  requests-toolbelt = ">=0.10.1,<1.1.0"
21
21
  memoization = "^0.4.0"
22
22
  lxml = "^5.0.0"
23
- ds-caselaw-utils = "^1.4.1"
23
+ ds-caselaw-utils = "^2.0.0"
24
24
  boto3 = "^1.26.112"
25
25
  typing-extensions = "^4.7.1"
26
26
  mypy-boto3-s3 = "^1.26.104"
@@ -64,9 +64,9 @@ line-length = 120
64
64
 
65
65
  [tool.ruff.lint]
66
66
  ignore = ["E501", "G004", "PLR2004", "RUF005", "RUF012", "UP040"] # longlines, fstrings in logs, magic values, consider not concat, mutable classbits, type instead of TypeAlias
67
- extend-select = ["W", "I", "SLF"]
67
+ extend-select = ["W", "I", "SLF", "SIM"]
68
68
  # extend-select = [ "B", "Q", "C90", "I", "UP", "YTT", "ASYNC", "S", "BLE", "A", "COM", "C4", "DTZ", "T10", "DJ", "EM", "EXE", "FA",
69
- # "ISC", "ICN", "G", "INP", "PIE", "T20", "PYI", "PT", "Q", "RSE", "RET", "SLOT", "SIM", "TID", "TCH", "INT", "PTH",
69
+ # "ISC", "ICN", "G", "INP", "PIE", "T20", "PYI", "PT", "Q", "RSE", "RET", "SLOT", "TID", "TCH", "INT", "PTH",
70
70
  # "FIX", "PGH", "PL", "TRY", "FLY", "PERF", "RUF"]
71
71
  unfixable = ["ERA"]
72
72
 
@@ -12,6 +12,7 @@ from xml.etree.ElementTree import Element, ParseError, fromstring
12
12
 
13
13
  import environ
14
14
  import requests
15
+ from ds_caselaw_utils.types import NeutralCitationString
15
16
  from requests.auth import HTTPBasicAuth
16
17
  from requests.structures import CaseInsensitiveDict
17
18
  from requests_toolbelt.multipart import decoder
@@ -800,10 +801,7 @@ class MarklogicApiClient:
800
801
  else None
801
802
  )
802
803
 
803
- if os.getenv("XSLT_IMAGE_LOCATION"):
804
- image_location = os.getenv("XSLT_IMAGE_LOCATION")
805
- else:
806
- image_location = ""
804
+ image_location = os.getenv("XSLT_IMAGE_LOCATION", "")
807
805
 
808
806
  show_unpublished = self.verify_show_unpublished(show_unpublished)
809
807
 
@@ -1038,12 +1036,7 @@ class MarklogicApiClient:
1038
1036
  search_parameters.collections = [DOCUMENT_COLLECTION_URI_JUDGMENT]
1039
1037
  return self.search_and_decode_response(search_parameters)
1040
1038
 
1041
- def overwrite_document(self, old_uri: str, new_citation: str) -> str:
1042
- """Move the judgment at old_uri on top of the new citation, which must already exist
1043
- Compare to update_document_uri"""
1044
- return move.overwrite_document(old_uri, new_citation, api_client=self)
1045
-
1046
- def update_document_uri(self, old_uri: str, new_citation: str) -> str:
1039
+ def update_document_uri(self, old_uri: DocumentURIString, new_citation: NeutralCitationString) -> DocumentURIString:
1047
1040
  """
1048
1041
  Move the document at old_uri to the correct location based on the neutral citation
1049
1042
  The new neutral citation *must* not already exist (that is handled elsewhere)
@@ -0,0 +1,165 @@
1
+ import datetime
2
+ from typing import Any, Optional, cast
3
+ from unittest.mock import Mock, patch
4
+
5
+ from typing_extensions import TypeAlias
6
+
7
+ from caselawclient.Client import MarklogicApiClient
8
+ from caselawclient.models.documents import Document
9
+ from caselawclient.models.documents.body import DocumentBody
10
+ from caselawclient.models.judgments import Judgment
11
+ from caselawclient.models.press_summaries import PressSummary
12
+ from caselawclient.responses.search_result import SearchResult, SearchResultMetadata
13
+
14
+ DEFAULT_DOCUMENT_BODY_XML = "<akomantoso>This is some XML of a judgment.</akomantoso>"
15
+
16
+
17
+ class DocumentBodyFactory:
18
+ # "name_of_attribute": "default value"
19
+ PARAMS_MAP: dict[str, Any] = {
20
+ "name": "Judgment v Judgement",
21
+ "court": "Court of Testing",
22
+ "document_date_as_string": "2023-02-03",
23
+ }
24
+
25
+ @classmethod
26
+ def build(cls, xml_string: str = DEFAULT_DOCUMENT_BODY_XML, **kwargs: Any) -> DocumentBody:
27
+ document_body = DocumentBody(
28
+ xml_bytestring=xml_string.encode(encoding="utf-8"),
29
+ )
30
+
31
+ for param_name, default_value in cls.PARAMS_MAP.items():
32
+ value = kwargs.get(param_name, default_value)
33
+ setattr(document_body, param_name, value)
34
+
35
+ return document_body
36
+
37
+
38
+ class DocumentFactory:
39
+ # "name_of_attribute": "default value"
40
+ PARAMS_MAP: dict[str, Any] = {
41
+ "is_published": False,
42
+ "is_sensitive": False,
43
+ "is_anonymised": False,
44
+ "is_failure": False,
45
+ "source_name": "Example Uploader",
46
+ "source_email": "uploader@example.com",
47
+ "consignment_reference": "TDR-12345",
48
+ "assigned_to": "",
49
+ "versions": [],
50
+ }
51
+
52
+ target_class: TypeAlias = Document
53
+
54
+ @classmethod
55
+ def build(
56
+ cls,
57
+ uri: str = "test/2023/123",
58
+ html: str = "<p>This is a judgment.</p>",
59
+ api_client: Optional[MarklogicApiClient] = None,
60
+ **kwargs: Any,
61
+ ) -> target_class:
62
+ if not api_client:
63
+ api_client = Mock(spec=MarklogicApiClient)
64
+ api_client.get_judgment_xml_bytestring.return_value = DEFAULT_DOCUMENT_BODY_XML.encode(encoding="utf-8")
65
+
66
+ with patch.object(cls.target_class, "content_as_html") as mock_content_as_html:
67
+ mock_content_as_html.return_value = html
68
+ document = cls.target_class(uri, api_client=api_client)
69
+
70
+ document.body = kwargs.pop("body") if "body" in kwargs else DocumentBodyFactory.build()
71
+
72
+ for param_name, default_value in cls.PARAMS_MAP.items():
73
+ value = kwargs.get(param_name, default_value)
74
+ setattr(document, param_name, value)
75
+
76
+ return document
77
+
78
+
79
+ class JudgmentFactory(DocumentFactory):
80
+ target_class = Judgment
81
+
82
+ def __init__(self) -> None:
83
+ self.PARAMS_MAP = self.PARAMS_MAP | {
84
+ "neutral_citation": "[2023] Test 123",
85
+ "best_human_identifier": "[2023] Test 123",
86
+ }
87
+
88
+ super().__init__()
89
+
90
+ @classmethod
91
+ def build(
92
+ cls,
93
+ uri: str = "test/2023/123",
94
+ html: str = "<p>This is a judgment.</p>",
95
+ api_client: Optional[MarklogicApiClient] = None,
96
+ **kwargs: Any,
97
+ ) -> Judgment:
98
+ return cast(Judgment, super().build(uri, html, api_client, **kwargs))
99
+
100
+
101
+ class PressSummaryFactory(DocumentFactory):
102
+ target_class = PressSummary
103
+
104
+ def __init__(self) -> None:
105
+ self.PARAMS_MAP = self.PARAMS_MAP | {
106
+ "neutral_citation": "[2023] Test 123",
107
+ "best_human_identifier": "[2023] Test 123",
108
+ }
109
+
110
+ super().__init__()
111
+
112
+ @classmethod
113
+ def build(
114
+ cls,
115
+ uri: str = "test/2023/123/press-summary/1",
116
+ html: str = "<p>This is a judgment.</p>",
117
+ api_client: Optional[MarklogicApiClient] = None,
118
+ **kwargs: Any,
119
+ ) -> PressSummary:
120
+ return cast(PressSummary, super().build(uri, html, api_client, **kwargs))
121
+
122
+
123
+ class SimpleFactory:
124
+ # "name_of_attribute": "default value"
125
+ PARAMS_MAP: dict[str, Any]
126
+
127
+ target_class: TypeAlias = object
128
+
129
+ @classmethod
130
+ def build(cls, **kwargs: Any) -> target_class:
131
+ mock_object = Mock(spec=cls.target_class, autospec=True)
132
+
133
+ for param, default in cls.PARAMS_MAP.items():
134
+ if param in kwargs:
135
+ setattr(mock_object.return_value, param, kwargs[param])
136
+ else:
137
+ setattr(mock_object.return_value, param, default)
138
+
139
+ return mock_object()
140
+
141
+
142
+ class SearchResultMetadataFactory(SimpleFactory):
143
+ target_class = SearchResultMetadata
144
+ # "name_of_attribute": "default value"
145
+ PARAMS_MAP = {
146
+ "author": "Fake Name",
147
+ "author_email": "fake.email@gov.invalid",
148
+ "consignment_reference": "TDR-2023-ABC",
149
+ "submission_datetime": datetime.datetime(2023, 2, 3, 9, 12, 34),
150
+ }
151
+
152
+
153
+ class SearchResultFactory(SimpleFactory):
154
+ target_class = SearchResult
155
+
156
+ # "name_of_attribute": ("name of incoming param", "default value")
157
+ PARAMS_MAP = {
158
+ "uri": "test/2023/123",
159
+ "name": "Judgment v Judgement",
160
+ "neutral_citation": "[2023] Test 123",
161
+ "court": "Court of Testing",
162
+ "date": datetime.date(2023, 2, 3),
163
+ "metadata": SearchResultMetadataFactory.build(),
164
+ "is_failure": False,
165
+ }
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Any, NewType, Optional
5
5
 
6
6
  from ds_caselaw_utils import courts
7
7
  from ds_caselaw_utils.courts import CourtNotFoundException
8
+ from ds_caselaw_utils.types import NeutralCitationString
8
9
  from lxml import html as html_parser
9
10
  from requests_toolbelt.multipart import decoder
10
11
 
@@ -281,22 +282,15 @@ class Document:
281
282
 
282
283
  :return: `True` if this document is in a 'failure' state, otherwise `False`
283
284
  """
284
- if self.body.failed_to_parse:
285
- return True
286
- return False
285
+ return self.body.failed_to_parse
287
286
 
288
287
  @cached_property
289
288
  def is_parked(self) -> bool:
290
- if "parked" in self.uri:
291
- return True
292
- return False
289
+ return "parked" in self.uri
293
290
 
294
291
  @cached_property
295
292
  def has_name(self) -> bool:
296
- if not self.body.name:
297
- return False
298
-
299
- return True
293
+ return bool(self.body.name)
300
294
 
301
295
  @cached_property
302
296
  def has_valid_court(self) -> bool:
@@ -373,9 +367,7 @@ class Document:
373
367
  """
374
368
  Is it sensible to enrich this document?
375
369
  """
376
- if (self.enriched_recently is False) and self.validates_against_schema:
377
- return True
378
- return False
370
+ return (self.enriched_recently is False) and self.validates_against_schema
379
371
 
380
372
  @cached_property
381
373
  def enriched_recently(self) -> bool:
@@ -388,9 +380,8 @@ class Document:
388
380
  return False
389
381
 
390
382
  now = datetime.datetime.now(tz=datetime.timezone.utc)
391
- if now - last_enrichment < MINIMUM_ENRICHMENT_TIME:
392
- return True
393
- return False
383
+
384
+ return now - last_enrichment < MINIMUM_ENRICHMENT_TIME
394
385
 
395
386
  @cached_property
396
387
  def validates_against_schema(self) -> bool:
@@ -451,10 +442,7 @@ class Document:
451
442
  else:
452
443
  raise DocumentNotSafeForDeletion
453
444
 
454
- def overwrite(self, new_citation: str) -> None:
455
- self.api_client.overwrite_document(self.uri, new_citation)
456
-
457
- def move(self, new_citation: str) -> None:
445
+ def move(self, new_citation: NeutralCitationString) -> None:
458
446
  self.api_client.update_document_uri(self.uri, new_citation)
459
447
 
460
448
  def force_reparse(self) -> None:
@@ -506,6 +494,4 @@ class Document:
506
494
  """
507
495
  Is it sensible to reparse this document?
508
496
  """
509
- if self.docx_exists():
510
- return True
511
- return False
497
+ return self.docx_exists()
@@ -1,16 +1,15 @@
1
1
  import datetime
2
2
  import warnings
3
3
  from functools import cached_property
4
- from typing import NewType, Optional
4
+ from typing import Optional
5
5
 
6
6
  import pytz
7
+ from ds_caselaw_utils.types import CourtCode
7
8
 
8
9
  from caselawclient.models.utilities.dates import parse_string_date_as_utc
9
10
 
10
11
  from .xml import XML
11
12
 
12
- CourtIdentifierString = NewType("CourtIdentifierString", str)
13
-
14
13
 
15
14
  class UnparsableDate(Warning):
16
15
  pass
@@ -56,10 +55,10 @@ class DocumentBody:
56
55
  )
57
56
 
58
57
  @property
59
- def court_and_jurisdiction_identifier_string(self) -> CourtIdentifierString:
58
+ def court_and_jurisdiction_identifier_string(self) -> CourtCode:
60
59
  if self.jurisdiction != "":
61
- return CourtIdentifierString("/".join((self.court, self.jurisdiction)))
62
- return CourtIdentifierString(self.court)
60
+ return CourtCode("/".join((self.court, self.jurisdiction)))
61
+ return CourtCode(self.court)
63
62
 
64
63
  @cached_property
65
64
  def document_date_as_string(self) -> str:
@@ -137,6 +136,4 @@ class DocumentBody:
137
136
 
138
137
  :return: `True` if there was a complete parser failure, otherwise `False`
139
138
  """
140
- if "error" in self._xml.root_element:
141
- return True
142
- return False
139
+ return "error" in self._xml.root_element
@@ -2,6 +2,8 @@ import importlib
2
2
  from functools import cached_property
3
3
  from typing import TYPE_CHECKING, Any, Optional
4
4
 
5
+ from ds_caselaw_utils.types import NeutralCitationString
6
+
5
7
  from caselawclient.errors import DocumentNotFoundError
6
8
  from caselawclient.models.neutral_citation_mixin import NeutralCitationMixin
7
9
 
@@ -20,16 +22,18 @@ class Judgment(NeutralCitationMixin, Document):
20
22
  document_noun_plural = "judgments"
21
23
 
22
24
  def __init__(self, *args: Any, **kwargs: Any) -> None:
23
- super(Judgment, self).__init__(self.document_noun, *args, **kwargs)
25
+ super().__init__(self.document_noun, *args, **kwargs)
24
26
 
25
27
  @cached_property
26
- def neutral_citation(self) -> str:
27
- return self.body.get_xpath_match_string(
28
- "/akn:akomaNtoso/akn:*/akn:meta/akn:proprietary/uk:cite/text()",
29
- {
30
- "uk": "https://caselaw.nationalarchives.gov.uk/akn",
31
- "akn": "http://docs.oasis-open.org/legaldocml/ns/akn/3.0",
32
- },
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
+ )
33
37
  )
34
38
 
35
39
  @property
@@ -1,10 +1,12 @@
1
+ from abc import ABC, abstractmethod
1
2
  from functools import cached_property
2
3
  from typing import Any
3
4
 
4
5
  from ds_caselaw_utils import neutral_url
6
+ from ds_caselaw_utils.types import NeutralCitationString
5
7
 
6
8
 
7
- class NeutralCitationMixin:
9
+ class NeutralCitationMixin(ABC):
8
10
  """
9
11
  A mixin class that provides functionality related to neutral citation.
10
12
 
@@ -35,19 +37,13 @@ class NeutralCitationMixin:
35
37
  super(NeutralCitationMixin, self).__init__(*args, **kwargs)
36
38
 
37
39
  @cached_property
38
- def neutral_citation(self) -> str:
39
- return ""
40
+ @abstractmethod
41
+ def neutral_citation(self) -> NeutralCitationString: ...
40
42
 
41
43
  @cached_property
42
44
  def has_ncn(self) -> bool:
43
- if not self.neutral_citation:
44
- return False
45
-
46
- return True
45
+ return bool(self.neutral_citation)
47
46
 
48
47
  @cached_property
49
48
  def has_valid_ncn(self) -> bool:
50
- if not self.has_ncn or not neutral_url(self.neutral_citation):
51
- return False
52
-
53
- return True
49
+ return self.has_ncn and neutral_url(self.neutral_citation) is not None
@@ -4,6 +4,8 @@ import importlib
4
4
  from functools import cached_property
5
5
  from typing import TYPE_CHECKING, Any, Optional
6
6
 
7
+ from ds_caselaw_utils.types import NeutralCitationString
8
+
7
9
  from caselawclient.errors import DocumentNotFoundError
8
10
  from caselawclient.models.neutral_citation_mixin import NeutralCitationMixin
9
11
 
@@ -22,15 +24,17 @@ class PressSummary(NeutralCitationMixin, Document):
22
24
  document_noun_plural = "press summaries"
23
25
 
24
26
  def __init__(self, *args: Any, **kwargs: Any) -> None:
25
- super(PressSummary, self).__init__(self.document_noun, *args, **kwargs)
27
+ super().__init__(self.document_noun, *args, **kwargs)
26
28
 
27
29
  @cached_property
28
- def neutral_citation(self) -> str:
29
- return self.body.get_xpath_match_string(
30
- "/akn:akomaNtoso/akn:doc/akn:preface/akn:p/akn:neutralCitation/text()",
31
- {
32
- "akn": "http://docs.oasis-open.org/legaldocml/ns/akn/3.0",
33
- },
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
+ )
34
38
  )
35
39
 
36
40
  @property
@@ -9,11 +9,7 @@ def parse_string_date_as_utc(iso_string: str, timezone: tzinfo.BaseTzInfo) -> da
9
9
  ensure that it is converted to a UTC-aware datetime"""
10
10
 
11
11
  mixed_date = isoparse(iso_string)
12
- if not mixed_date.tzinfo:
13
- # it is an unaware time
14
- aware_date = timezone.localize(mixed_date)
15
- else:
16
- aware_date = mixed_date
12
+ aware_date = mixed_date if mixed_date.tzinfo else timezone.localize(mixed_date)
17
13
 
18
14
  # make UTC
19
15
  utc_date = aware_date.astimezone(UTC)
@@ -1,16 +1,17 @@
1
- from typing import Any, Optional
1
+ from typing import TYPE_CHECKING, Any, Optional
2
2
 
3
3
  import ds_caselaw_utils as caselawutils
4
+ from ds_caselaw_utils.types import NeutralCitationString
4
5
 
5
6
  from caselawclient.errors import MarklogicAPIError
7
+ from caselawclient.models.documents import DocumentURIString
6
8
  from caselawclient.models.utilities.aws import copy_assets
7
9
 
8
-
9
- class NeutralCitationToUriError(Exception):
10
- pass
10
+ if TYPE_CHECKING:
11
+ from caselawclient.Client import MarklogicApiClient
11
12
 
12
13
 
13
- class OverwriteJudgmentError(Exception):
14
+ class NeutralCitationToUriError(Exception):
14
15
  pass
15
16
 
16
17
 
@@ -18,62 +19,9 @@ class MoveJudgmentError(Exception):
18
19
  pass
19
20
 
20
21
 
21
- def overwrite_document(
22
- source_uri: str,
23
- target_citation: str,
24
- api_client: Any,
25
- ) -> str:
26
- """Move the document at source_uri on top of the new citation, which must already exist
27
- Compare to update_document_uri
28
-
29
- :param source_uri: The URI with the contents of the document to be written. (possibly a failure url)
30
- :param target_citation: The NCN (implying a URL) whose contents will be overwritten
31
- :param api_client: An instance of MarklogicApiClient used to make the search request
32
- :return: The URL associated with the `target_citation`
33
- """
34
-
35
- new_uri: Optional[str] = caselawutils.neutral_url(target_citation.strip())
36
-
37
- if new_uri == source_uri:
38
- raise RuntimeError(
39
- f"Attempted to overwrite document {source_uri} with itself, which is not permitted.",
40
- )
41
- if new_uri is None:
42
- raise NeutralCitationToUriError(
43
- f"Unable to form new URI for {source_uri} from neutral citation: {target_citation}",
44
- )
45
- if not api_client.document_exists(new_uri):
46
- raise OverwriteJudgmentError(
47
- f"The URI {new_uri} generated from {target_citation} does not already exist, so cannot be overwritten",
48
- )
49
- old_doc = api_client.get_document_by_uri_or_404(source_uri)
50
- try:
51
- old_doc_xml = old_doc.content_as_xml
52
- api_client.update_document_xml(
53
- new_uri,
54
- old_doc_xml,
55
- annotation=f"overwritten from {source_uri}",
56
- )
57
- set_metadata(source_uri, new_uri, api_client)
58
- # TODO: consider deleting existing public assets at that location
59
- copy_assets(source_uri, new_uri)
60
- api_client.set_judgment_this_uri(new_uri)
61
- except MarklogicAPIError as e:
62
- raise OverwriteJudgmentError(
63
- f"Failure when attempting to copy judgment from {source_uri} to {new_uri}: {e}",
64
- )
65
-
66
- try:
67
- api_client.delete_judgment(source_uri)
68
- except MarklogicAPIError as e:
69
- raise OverwriteJudgmentError(
70
- f"Failure when attempting to delete judgment from {source_uri}: {e}",
71
- )
72
-
73
- return new_uri
74
-
75
-
76
- def update_document_uri(source_uri: str, target_citation: str, api_client: Any) -> str:
22
+ def update_document_uri(
23
+ source_uri: DocumentURIString, target_citation: NeutralCitationString, api_client: "MarklogicApiClient"
24
+ ) -> DocumentURIString:
77
25
  """
78
26
  Move the document at source_uri to the correct location based on the neutral citation
79
27
  The new neutral citation *must* not already exist (that is handled elsewhere)
@@ -83,7 +31,8 @@ def update_document_uri(source_uri: str, target_citation: str, api_client: Any)
83
31
  :param api_client: An instance of MarklogicApiClient used to make the search request
84
32
  :return: The URL associated with the `target_citation`
85
33
  """
86
- new_uri: Optional[str] = caselawutils.neutral_url(target_citation.strip())
34
+ new_ncn_based_uri = caselawutils.neutral_url(target_citation)
35
+ new_uri: Optional[DocumentURIString] = DocumentURIString(new_ncn_based_uri) if new_ncn_based_uri else None
87
36
  if new_uri is None:
88
37
  raise NeutralCitationToUriError(
89
38
  f"Unable to form new URI for {source_uri} from neutral citation: {target_citation}",
@@ -115,7 +64,7 @@ def update_document_uri(source_uri: str, target_citation: str, api_client: Any)
115
64
  return new_uri
116
65
 
117
66
 
118
- def set_metadata(old_uri: str, new_uri: str, api_client: Any) -> None:
67
+ def set_metadata(old_uri: DocumentURIString, new_uri: DocumentURIString, api_client: Any) -> None:
119
68
  source_organisation = api_client.get_property(old_uri, "source-organisation")
120
69
  source_name = api_client.get_property(old_uri, "source-name")
121
70
  source_email = api_client.get_property(old_uri, "source-email")
@@ -24,13 +24,13 @@ class SearchResponse:
24
24
  self.client = client
25
25
 
26
26
  @property
27
- def total(self) -> str:
27
+ def total(self) -> int:
28
28
  """
29
29
  The total number of search results.
30
30
 
31
31
  :return: The total number of search results
32
32
  """
33
- return str(
33
+ return int(
34
34
  self.node.xpath("//search:response/@total", namespaces=self.NAMESPACES)[0],
35
35
  )
36
36
 
@@ -8,6 +8,7 @@ from typing import Dict, Optional
8
8
  from dateutil import parser as dateparser
9
9
  from dateutil.parser import ParserError
10
10
  from ds_caselaw_utils.courts import Court, CourtNotFoundException, courts
11
+ from ds_caselaw_utils.types import CourtCode, JurisdictionCode
11
12
  from lxml import etree
12
13
 
13
14
  from caselawclient.Client import MarklogicApiClient
@@ -193,15 +194,16 @@ class SearchResult:
193
194
  """
194
195
  :return: The court of the search result
195
196
  """
196
- court = None
197
+ court: Optional[Court] = None
197
198
  court_code = self._get_xpath_match_string("search:extracted/uk:court/text()")
198
199
  jurisdiction_code = self._get_xpath_match_string(
199
200
  "search:extracted/uk:jurisdiction/text()",
200
201
  )
201
202
  if jurisdiction_code:
202
- court_code_with_jurisdiction = "%s/%s" % (court_code, jurisdiction_code)
203
203
  try:
204
- court = courts.get_by_code(court_code_with_jurisdiction)
204
+ court = courts.get_court_with_jurisdiction_by_code(
205
+ CourtCode(court_code), JurisdictionCode(jurisdiction_code)
206
+ )
205
207
  except CourtNotFoundException:
206
208
  logging.warning(
207
209
  "Court not found with court code %s and jurisdiction code %s for judgment with NCN %s, falling back to court."
@@ -209,7 +211,7 @@ class SearchResult:
209
211
  )
210
212
  if court is None:
211
213
  try:
212
- court = courts.get_by_code(court_code)
214
+ court = courts.get_by_code(CourtCode(court_code))
213
215
  except CourtNotFoundException:
214
216
  logging.warning(
215
217
  "Court not found with court code %s for judgment with NCN %s, returning None."