ds-caselaw-marklogic-api-client 29.0.0__tar.gz → 29.0.0a2__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 (83) hide show
  1. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/PKG-INFO +2 -3
  2. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/pyproject.toml +2 -3
  3. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/Client.py +0 -20
  4. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/documents/__init__.py +8 -23
  5. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/identifiers/__init__.py +6 -54
  6. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/identifiers/neutral_citation.py +0 -2
  7. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/identifiers/unpacker.py +0 -2
  8. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/judgments.py +13 -10
  9. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/neutral_citation_mixin.py +4 -8
  10. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/press_summaries.py +12 -9
  11. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery_type_dicts.py +0 -6
  12. ds_caselaw_marklogic_api_client-29.0.0/src/caselawclient/identifier_resolution.py +0 -43
  13. ds_caselaw_marklogic_api_client-29.0.0/src/caselawclient/models/identifiers/fclid.py +0 -50
  14. ds_caselaw_marklogic_api_client-29.0.0/src/caselawclient/xquery/get_next_document_sequence_number.xqy +0 -14
  15. ds_caselaw_marklogic_api_client-29.0.0/src/caselawclient/xquery/resolve_from_identifier.xqy +0 -17
  16. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/LICENSE.md +0 -0
  17. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/README.md +0 -0
  18. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/__init__.py +0 -0
  19. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/client_helpers/__init__.py +0 -0
  20. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/client_helpers/search_helpers.py +0 -0
  21. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/content_hash.py +0 -0
  22. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/errors.py +0 -0
  23. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/factories.py +0 -0
  24. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/__init__.py +0 -0
  25. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/documents/body.py +0 -0
  26. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/documents/exceptions.py +0 -0
  27. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/documents/statuses.py +0 -0
  28. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/documents/transforms/html.xsl +0 -0
  29. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/documents/xml.py +0 -0
  30. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/utilities/__init__.py +0 -0
  31. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/utilities/aws.py +0 -0
  32. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/utilities/dates.py +0 -0
  33. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/models/utilities/move.py +0 -0
  34. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/py.typed +0 -0
  35. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/responses/__init__.py +0 -0
  36. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/responses/search_response.py +0 -0
  37. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/responses/search_result.py +0 -0
  38. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/responses/xsl/search_match.xsl +0 -0
  39. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/search_parameters.py +0 -0
  40. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xml_helpers.py +0 -0
  41. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/break_judgment_checkout.xqy +0 -0
  42. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/checkin_judgment.xqy +0 -0
  43. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/checkout_judgment.xqy +0 -0
  44. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/copy_document.xqy +0 -0
  45. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/delete_judgment.xqy +0 -0
  46. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/document_collections.xqy +0 -0
  47. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/document_exists.xqy +0 -0
  48. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_combined_stats_table.xqy +0 -0
  49. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_components_for_document.xqy +0 -0
  50. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_highest_enrichment_version.xqy +0 -0
  51. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_highest_parser_version.xqy +0 -0
  52. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_judgment.xqy +0 -0
  53. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_judgment_checkout_status.xqy +0 -0
  54. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_judgment_version.xqy +0 -0
  55. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_last_modified.xqy +0 -0
  56. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_pending_enrichment_for_version.xqy +0 -0
  57. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_pending_parse_for_version.xqy +0 -0
  58. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_properties_for_search_results.xqy +0 -0
  59. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_property.xqy +0 -0
  60. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_property_as_node.xqy +0 -0
  61. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_recently_enriched.xqy +0 -0
  62. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_recently_parsed.xqy +0 -0
  63. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_version_annotation.xqy +0 -0
  64. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/get_version_created.xqy +0 -0
  65. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/insert_document.xqy +0 -0
  66. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/list_judgment_versions.xqy +0 -0
  67. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/set_boolean_property.xqy +0 -0
  68. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/set_metadata_citation.xqy +0 -0
  69. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/set_metadata_court.xqy +0 -0
  70. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/set_metadata_jurisdiction.xqy +0 -0
  71. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/set_metadata_name.xqy +0 -0
  72. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/set_metadata_this_uri.xqy +0 -0
  73. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/set_metadata_work_expression_date.xqy +0 -0
  74. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/set_property.xqy +0 -0
  75. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/set_property_as_node.xqy +0 -0
  76. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/update_document.xqy +0 -0
  77. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/update_locked_judgment.xqy +0 -0
  78. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/user_has_privilege.xqy +0 -0
  79. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/user_has_role.xqy +0 -0
  80. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/validate_all_documents.xqy +0 -0
  81. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/validate_document.xqy +0 -0
  82. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/xslt.xqy +0 -0
  83. {ds_caselaw_marklogic_api_client-29.0.0 → ds_caselaw_marklogic_api_client-29.0.0a2}/src/caselawclient/xquery/xslt_transform.xqy +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ds-caselaw-marklogic-api-client
3
- Version: 29.0.0
3
+ Version: 29.0.0a2
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 (>=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
17
  Requires-Dist: ds-caselaw-utils (>=2.0.0,<3.0.0)
@@ -25,7 +25,6 @@ Requires-Dist: pytz (>=2024.1,<2025.0)
25
25
  Requires-Dist: requests (>=2.28.2,<3.0.0)
26
26
  Requires-Dist: requests-toolbelt (>=0.10.1,<1.1.0)
27
27
  Requires-Dist: saxonche (>=12.5.0,<13.0.0)
28
- Requires-Dist: sqids (>=0.5.0,<0.6.0)
29
28
  Requires-Dist: typing-extensions (>=4.7.1,<5.0.0)
30
29
  Description-Content-Type: text/markdown
31
30
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "ds-caselaw-marklogic-api-client"
3
- version = "29.0.0"
3
+ version = "29.0.0-alpha.2"
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"
@@ -12,7 +12,7 @@ packages = [
12
12
 
13
13
  [tool.poetry.dependencies]
14
14
  python = "^3.9"
15
- certifi = ">=2024.12.14,<2024.13.0"
15
+ certifi = ">=2024.8.30,<2024.9.0"
16
16
  charset-normalizer = "^3.0.0"
17
17
  django-environ = "^0.11.0"
18
18
  idna = "^3.4"
@@ -28,7 +28,6 @@ mypy-boto3-sns = "^1.26.69"
28
28
  pytz = "^2024.1"
29
29
  python-dateutil = "^2.9.0-post.0"
30
30
  saxonche = "^12.5.0"
31
- sqids = "^0.5.0"
32
31
 
33
32
  [tool.poetry.group.dev.dependencies]
34
33
  coverage = "^7.2.3"
@@ -20,7 +20,6 @@ from requests_toolbelt.multipart import decoder
20
20
 
21
21
  from caselawclient import xquery_type_dicts as query_dicts
22
22
  from caselawclient.client_helpers import VersionAnnotation
23
- from caselawclient.identifier_resolution import IdentifierResolutions
24
23
  from caselawclient.models.documents import (
25
24
  DOCUMENT_COLLECTION_URI_JUDGMENT,
26
25
  DOCUMENT_COLLECTION_URI_PRESS_SUMMARY,
@@ -1202,22 +1201,3 @@ class MarklogicApiClient:
1202
1201
  )
1203
1202
 
1204
1203
  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)
1220
-
1221
- def get_next_document_sequence_number(self) -> int:
1222
- """Increment the MarkLogic sequence number by one and return the value."""
1223
- return int(self._eval_and_decode({}, "get_next_document_sequence_number.xqy"))
@@ -15,8 +15,6 @@ from caselawclient.errors import (
15
15
  NotSupportedOnVersion,
16
16
  OnlySupportedOnVersion,
17
17
  )
18
- from caselawclient.models.identifiers import Identifier
19
- from caselawclient.models.identifiers.fclid import FindCaseLawIdentifier, FindCaseLawIdentifierSchema
20
18
  from caselawclient.models.identifiers.unpacker import unpack_all_identifiers_from_etree
21
19
  from caselawclient.models.utilities import VersionsDict, extract_version, render_versions
22
20
  from caselawclient.models.utilities.aws import (
@@ -172,11 +170,13 @@ class Document:
172
170
  self.identifiers = unpack_all_identifiers_from_etree(identifiers_element_as_etree)
173
171
 
174
172
  @property
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
173
+ def best_human_identifier(self) -> Optional[str]:
174
+ """
175
+ Some identifier that is understood by legal professionals to refer to this legal event
176
+ that is not the name of the document.
177
+ Typically, this will be the neutral citation number, should it exist.
178
+ Should typically be overridden in subclasses.
179
+ """
180
180
  return None
181
181
 
182
182
  @property
@@ -432,12 +432,6 @@ class Document:
432
432
  if not self.is_publishable:
433
433
  raise CannotPublishUnpublishableDocument
434
434
 
435
- ## If it doesn't already have one, get a new FCLID for this document and assign
436
- if len(self.identifiers.of_type(FindCaseLawIdentifier)) < 1:
437
- document_fclid = FindCaseLawIdentifierSchema.mint(self.api_client)
438
- self.identifiers.add(document_fclid)
439
- self.save_identifiers()
440
-
441
435
  publish_documents(uri_for_s3(self.uri))
442
436
  self.api_client.set_published(self.uri, True)
443
437
  announce_document_event(
@@ -506,17 +500,13 @@ class Document:
506
500
  "documentType": parser_type_noun,
507
501
  "metadata": {
508
502
  "name": self.body.name or None,
509
- "cite": None,
503
+ "cite": self.best_human_identifier or None,
510
504
  "court": self.body.court or None,
511
505
  "date": checked_date,
512
506
  "uri": self.uri,
513
507
  },
514
508
  }
515
509
 
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
-
520
510
  request_parse(
521
511
  uri=self.uri,
522
512
  reference=self.consignment_reference,
@@ -540,11 +530,6 @@ class Document:
540
530
  """
541
531
  return self.docx_exists()
542
532
 
543
- def save_identifiers(self) -> None:
544
- """Save the current state of this Document's identifiers to MarkLogic."""
545
- self.identifiers.validate()
546
- self.api_client.set_property_as_node(self.uri, "identifiers", self.identifiers.as_etree)
547
-
548
533
  def __getattr__(self, name: str) -> Any:
549
534
  warnings.warn(f"{name} no longer exists on Document, using Document.body instead", DeprecationWarning)
550
535
  try:
@@ -32,20 +32,13 @@ 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
-
41
35
  def __init_subclass__(cls: type["IdentifierSchema"], **kwargs: Any) -> None:
42
36
  """Ensure that subclasses have the required attributes set."""
43
37
  for required in (
44
38
  "name",
45
39
  "namespace",
46
- "human_readable",
47
40
  ):
48
- if not hasattr(cls, required):
41
+ if not getattr(cls, required, False):
49
42
  raise NotImplementedError(f"Can't instantiate IdentifierSchema without {required} attribute.")
50
43
  super().__init_subclass__(**kwargs)
51
44
 
@@ -108,15 +101,6 @@ class Identifier(ABC):
108
101
  def url_slug(self) -> str:
109
102
  return self.schema.compile_identifier_url_slug(self.value)
110
103
 
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
-
116
- def same_as(self, other: "Identifier") -> bool:
117
- "Is this the same as another identifier (in value and schema)?"
118
- return self.value == other.value and self.schema == other.schema
119
-
120
104
 
121
105
  class Identifiers(dict[str, Identifier]):
122
106
  def validate(self) -> None:
@@ -125,13 +109,8 @@ class Identifiers(dict[str, Identifier]):
125
109
  msg = "Key of {identifier} in Identifiers is {uuid} not {identifier.uuid}"
126
110
  raise UUIDMismatchError(msg)
127
111
 
128
- def contains(self, other_identifier: Identifier) -> bool:
129
- "Do the identifier's value and namespace already exist in this group?"
130
- return any(other_identifier.same_as(identifier) for identifier in self.values())
131
-
132
112
  def add(self, identifier: Identifier) -> None:
133
- if not self.contains(identifier):
134
- self[identifier.uuid] = identifier
113
+ self[identifier.uuid] = identifier
135
114
 
136
115
  def __delitem__(self, key: Union[Identifier, str]) -> None:
137
116
  if isinstance(key, Identifier):
@@ -139,19 +118,6 @@ class Identifiers(dict[str, Identifier]):
139
118
  else:
140
119
  super().__delitem__(key)
141
120
 
142
- def of_type(self, identifier_type: type[Identifier]) -> list[Identifier]:
143
- """Return a list of all identifiers of a given type."""
144
- uuids = self.keys()
145
- return [self[uuid] for uuid in list(uuids) if isinstance(self[uuid], identifier_type)]
146
-
147
- def delete_type(self, deleted_identifier_type: type[Identifier]) -> None:
148
- "For when we want an identifier to be the only valid identifier of that type, delete the others first"
149
- uuids = self.keys()
150
- for uuid in list(uuids):
151
- # we could use compare to .schema instead, which would have diffferent behaviour for subclasses
152
- if isinstance(self[uuid], deleted_identifier_type):
153
- del self[uuid]
154
-
155
121
  @property
156
122
  def as_etree(self) -> etree._Element:
157
123
  """Return an etree representation of all the Document's identifiers."""
@@ -162,21 +128,7 @@ class Identifiers(dict[str, Identifier]):
162
128
 
163
129
  return identifiers_root
164
130
 
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]
131
+ def save(self, document) -> None: # type: ignore[no-untyped-def, unused-ignore]
132
+ """Save the current state of this Document's identifiers to MarkLogic."""
133
+ self.validate()
134
+ document.api_client.set_property_as_node(document.uri, "identifiers", self.as_etree)
@@ -30,8 +30,6 @@ 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
35
33
 
36
34
  @classmethod
37
35
  def validate_identifier(cls, value: str) -> bool:
@@ -3,11 +3,9 @@ from typing import Optional
3
3
  from lxml import etree
4
4
 
5
5
  from . import IDENTIFIER_UNPACKABLE_ATTRIBUTES, Identifier, Identifiers, InvalidIdentifierXMLRepresentationException
6
- from .fclid import FindCaseLawIdentifier
7
6
  from .neutral_citation import NeutralCitationNumber
8
7
 
9
8
  IDENTIFIER_NAMESPACE_MAP: dict[str, type[Identifier]] = {
10
- "fclid": FindCaseLawIdentifier,
11
9
  "ukncn": NeutralCitationNumber,
12
10
  }
13
11
 
@@ -25,17 +25,20 @@ 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) -> 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
- },
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
+ )
35
37
  )
36
- if value_in_xml:
37
- return NeutralCitationString(value_in_xml)
38
- return None
38
+
39
+ @property
40
+ def best_human_identifier(self) -> str:
41
+ return self.neutral_citation
39
42
 
40
43
  @cached_property
41
44
  def linked_document(self) -> Optional["PressSummary"]:
@@ -1,10 +1,9 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from functools import cached_property
3
- from typing import Any, Optional
3
+ from typing import Any
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
8
7
 
9
8
 
10
9
  class NeutralCitationMixin(ABC):
@@ -39,15 +38,12 @@ class NeutralCitationMixin(ABC):
39
38
 
40
39
  @cached_property
41
40
  @abstractmethod
42
- @deprecated("Legacy usage of NCNs is deprecated; you should be moving to the Identifiers framework")
43
- def neutral_citation(self) -> Optional[NeutralCitationString]: ...
41
+ def neutral_citation(self) -> NeutralCitationString: ...
44
42
 
45
43
  @cached_property
46
- @deprecated("Legacy usage of NCNs is deprecated; you should be moving to the Identifiers framework")
47
44
  def has_ncn(self) -> bool:
48
- return self.neutral_citation is not None and self.neutral_citation != ""
45
+ return bool(self.neutral_citation)
49
46
 
50
47
  @cached_property
51
- @deprecated("Legacy usage of NCNs is deprecated; you should be moving to the Identifiers framework")
52
48
  def has_valid_ncn(self) -> bool:
53
- return self.neutral_citation is not None and neutral_url(self.neutral_citation) is not None
49
+ return self.has_ncn and neutral_url(self.neutral_citation) is not None
@@ -27,16 +27,19 @@ 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) -> 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
- },
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
+ )
36
38
  )
37
- if value_in_xml:
38
- return NeutralCitationString(value_in_xml)
39
- return None
39
+
40
+ @property
41
+ def best_human_identifier(self) -> str:
42
+ return self.neutral_citation
40
43
 
41
44
  @cached_property
42
45
  def linked_document(self) -> Optional[Judgment]:
@@ -141,12 +141,6 @@ class ListJudgmentVersionsDict(MarkLogicAPIDict):
141
141
  uri: MarkLogicDocumentURIString
142
142
 
143
143
 
144
- # resolve_from_identifier.xqy
145
- class ResolveFromIdentifierDict(MarkLogicAPIDict):
146
- identifier_uri: DocumentURIString
147
- published_only: Optional[int]
148
-
149
-
150
144
  # set_boolean_property.xqy
151
145
  class SetBooleanPropertyDict(MarkLogicAPIDict):
152
146
  name: str
@@ -1,43 +0,0 @@
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
- )
@@ -1,50 +0,0 @@
1
- import re
2
- from typing import TYPE_CHECKING
3
-
4
- from sqids import Sqids
5
-
6
- from . import Identifier, IdentifierSchema
7
-
8
- if TYPE_CHECKING:
9
- from caselawclient.Client import MarklogicApiClient
10
-
11
-
12
- VALID_FCLID_PATTERN = re.compile(r"^[bcdfghjkmnpqrstvwxyz23456789]{4,}$")
13
-
14
- FCLID_MINIMUM_LENGTH = 8
15
- FCLID_ALPHABET = "bcdfghjkmnpqrstvwxyz23456789"
16
-
17
- sqids = Sqids(
18
- min_length=FCLID_MINIMUM_LENGTH,
19
- alphabet=FCLID_ALPHABET,
20
- )
21
-
22
-
23
- class FindCaseLawIdentifierSchema(IdentifierSchema):
24
- """
25
- Identifier schema describing a Find Case Law Identifier.
26
- """
27
-
28
- name = "Find Case Law Identifier"
29
- namespace = "fclid"
30
- human_readable = False
31
- base_score_multiplier = 0.8
32
-
33
- @classmethod
34
- def validate_identifier(cls, value: str) -> bool:
35
- return bool(VALID_FCLID_PATTERN.match(value))
36
-
37
- @classmethod
38
- def compile_identifier_url_slug(cls, value: str) -> str:
39
- return "tna." + value
40
-
41
- @classmethod
42
- def mint(cls, api_client: "MarklogicApiClient") -> "FindCaseLawIdentifier":
43
- """Generate a totally new Find Case Law identifier."""
44
- next_sequence_number = api_client.get_next_document_sequence_number()
45
- new_identifier = sqids.encode([next_sequence_number])
46
- return FindCaseLawIdentifier(value=new_identifier)
47
-
48
-
49
- class FindCaseLawIdentifier(Identifier):
50
- schema = FindCaseLawIdentifierSchema
@@ -1,14 +0,0 @@
1
- xquery version "1.0-ml";
2
- declare option xdmp:transaction-mode "update";
3
-
4
- let $_ := xdmp:set-transaction-mode("update")
5
- let $state_doc := fn:doc("state.xml")
6
- let $counter_node := $state_doc/state/document_counter
7
-
8
- let $current_counter := $counter_node/text()
9
- let $new_counter := fn:sum(($current_counter, 1))
10
-
11
- let $_ := xdmp:node-replace($counter_node, <document_counter>{$new_counter}</document_counter>)
12
- let $_ := xdmp:commit()
13
-
14
- return $new_counter
@@ -1,17 +0,0 @@
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
-