diff --git a/src/caselawclient/errors.py b/src/caselawclient/errors.py index a033228f..da0b590a 100644 --- a/src/caselawclient/errors.py +++ b/src/caselawclient/errors.py @@ -83,3 +83,14 @@ class DocumentNotFoundError(MarklogicAPIError): # This error does not come from Marklogic, but is an error raised by this API... status_code = 404 default_message = "The document was not found" + + +class NotSupportedOnVersion(MarklogicAPIError): + # This error does not come from Marklogic, but is an error raised by this API... + status_code = 400 + default_message = "An operation was attempted on a version of a document which cannot occur on a version." + + +class OnlySupportedOnVersion(MarklogicAPIError): + status_code = 400 + default_message = "The operation requested cannot be performed on a document that is not a version." diff --git a/src/caselawclient/models/documents.py b/src/caselawclient/models/documents.py index 30e5d128..45b5e5ee 100644 --- a/src/caselawclient/models/documents.py +++ b/src/caselawclient/models/documents.py @@ -7,7 +7,13 @@ from lxml import etree from requests_toolbelt.multipart import decoder -from ..errors import DocumentNotFoundError +from caselawclient.models.utilities import extract_version + +from ..errors import ( + DocumentNotFoundError, + NotSupportedOnVersion, + OnlySupportedOnVersion, +) from ..xml_helpers import get_xpath_match_string, get_xpath_match_strings from .utilities import VersionsDict, get_judgment_root, render_versions from .utilities.aws import ( @@ -237,6 +243,44 @@ def versions(self) -> list[VersionsDict]: except AttributeError: return [] + @cached_property + def versions_as_documents(self) -> list[Any]: + """ + Returns a list of `Document` subclasses corresponding to the versions of the document. The first entry is: + * the most recent + * the highest numbered + + Note that this is only valid on the managed document -- a `DLS-DOCUMENTVERSION` error will occur if the document + this is called on is itself a version. + """ + if self.is_version: + raise NotSupportedOnVersion( + "Cannot get versions of a version for {self.uri}" + ) + docs = [] + for version in self.versions: + doc_uri = DocumentURIString(version["uri"]) + docs.append(self.api_client.get_document_by_uri(doc_uri)) + return docs + + @cached_property + def version_number(self) -> int: + """ + Note that the highest number is the most recent version. + Raises an exception if it is not a version (e.g. /2022/eat/1 is not a version) + """ + version = extract_version(self.uri) + if version == 0: + raise OnlySupportedOnVersion( + f"Version number requested for {self.uri} which is not a version" + ) + return version + + @cached_property + def is_version(self) -> bool: + "Is this document a potentially historic version of a document, or is it the main document itself?" + return extract_version(self.uri) != 0 + @cached_property def content_as_xml(self) -> str: return self.api_client.get_judgment_xml(self.uri, show_unpublished=True) diff --git a/tests/models/test_documents.py b/tests/models/test_documents.py index bd05f875..f4fc7098 100644 --- a/tests/models/test_documents.py +++ b/tests/models/test_documents.py @@ -5,7 +5,11 @@ from lxml import etree from caselawclient.Client import MarklogicApiClient -from caselawclient.errors import DocumentNotFoundError +from caselawclient.errors import ( + DocumentNotFoundError, + NotSupportedOnVersion, + OnlySupportedOnVersion, +) from caselawclient.models.documents import ( DOCUMENT_STATUS_HOLD, DOCUMENT_STATUS_IN_PROGRESS, @@ -169,6 +173,32 @@ def test_document_best_identifier(self, mock_api_client): example_document = Document("uri", mock_api_client) assert example_document.best_human_identifier is None + def test_document_version_of_a_version_fails(self, mock_api_client): + version_document = Document("test/1234_xml_versions/9-1234", mock_api_client) + with pytest.raises(NotSupportedOnVersion): + version_document.versions_as_documents + + def test_document_versions_happy_case(self, mock_api_client): + version_document = Document("test/1234", mock_api_client) + version_document.versions = [ + {"uri": "test/1234_xml_versions/2-1234.xml"}, + {"uri": "test/1234_xml_versions/1-1234.xml"}, + ] + version_document.versions_as_documents[ + 0 + ].uri = "test/1234_xml_versions/2-1234.xml" + + def test_document_version_number_when_not_version(self, mock_api_client): + base_document = Document("test/1234", mock_api_client) + with pytest.raises(OnlySupportedOnVersion): + base_document.version_number + assert not base_document.is_version + + def test_document_version_number_when_is_version(self, mock_api_client): + version_document = Document("test/1234_xml_versions/9-1234", mock_api_client) + assert version_document.version_number == 9 + assert version_document.is_version + class TestDocumentValidation: def test_judgment_is_failure(self, mock_api_client):