diff --git a/cds/modules/deposit/api.py b/cds/modules/deposit/api.py index c2b5b7347..ea7168ce9 100644 --- a/cds/modules/deposit/api.py +++ b/cds/modules/deposit/api.py @@ -25,6 +25,7 @@ """Deposit API.""" +import idutils import datetime import os import re @@ -52,7 +53,7 @@ PIDInvalidAction, ResolverError, ) -from invenio_pidstore.models import PersistentIdentifier +from invenio_pidstore.models import PersistentIdentifier, PIDStatus from invenio_pidstore.resolver import Resolver from invenio_records_files.models import RecordsBuckets from invenio_records_files.utils import sorted_files_from_bucket @@ -73,10 +74,11 @@ CDSRecord, CDSVideosFilesIterator, ) -from ..records.minters import doi_minter, is_local_doi, report_number_minter +from ..records.minters import cds_doi_generator, is_local_doi, report_number_minter from ..records.resolver import record_resolver from ..records.utils import is_record, lowercase_value from ..records.validators import PartialDraft4Validator +from ..records.permissions import is_public from .errors import DiscardConflict from .resolver import get_video_pid @@ -899,8 +901,6 @@ def _publish_edited(self): # dump again renamed subtitles self["_files"] = self.files.dumps() - # Call the 'doi_minter' function if needed and if is_public - return super(Video, self)._publish_edited() @mark_as_action @@ -1087,6 +1087,28 @@ def _create_tags(self): return + def mint_doi(self): + """Mint DOI.""" + assert self.has_record() + assert not self.has_minted_doi(), "DOI already exists for this video." + assert is_public(self, "read"), "Record is not public and cannot mint a DOI." + + doi = cds_doi_generator(self["recid"]) + # Make sure it's a proper DOI + assert idutils.is_doi(doi) + + self["doi"] = doi + PersistentIdentifier.create( + "doi", + doi, + pid_provider="datacite", + object_type="rec", + object_uuid=self.id, + status=PIDStatus.RESERVED, + ) + return self + + project_resolver = Resolver( pid_type="depid", object_type="rec", diff --git a/cds/modules/maintenance/cli.py b/cds/modules/maintenance/cli.py index 68f33c811..440857d18 100644 --- a/cds/modules/maintenance/cli.py +++ b/cds/modules/maintenance/cli.py @@ -39,11 +39,10 @@ from cds.modules.records.api import CDSVideosFilesIterator from cds.modules.deposit.api import Video from cds.modules.records.resolver import record_resolver -from cds.modules.records.minters import doi_minter from cds.modules.deposit.tasks import datacite_register from invenio_db import db -from cds.modules.records.permissions import is_public from invenio_indexer.api import RecordIndexer +from datacite.errors import DataCiteError @@ -52,41 +51,35 @@ def abort_if_false(ctx, param, value): ctx.abort() @click.command() -@click.option("--recid", "recid", help="ID of the video record", default=None) +@click.option("--recid", "recid", help="ID of the video record", default=None, required=True) @with_appcontext def create_doi(recid): - if not recid: - raise ClickException('Missing option "--recid') - + """Mints the DOI for a video record.""" + # Get the video object with recid + _, record = record_resolver.resolve(recid) + depid = record.depid + video_deposit = Video.get_record(depid.object_uuid) + try: - # Get the video object with recid - _, record = record_resolver.resolve(recid) - depid = record.depid - video_deposit = Video.get_record(depid.object_uuid) - except Exception as exc: - raise ClickException("Failed to fetch the video record") - - if is_public(video_deposit, "read") and video_deposit.is_published(): - try: - # sync deposit files <--> record files - edit_record = video_deposit.edit() - doi_minter(record_uuid=edit_record.id, data=edit_record) - edit_record.publish().commit() - - # save changes - db.session.commit() - - # Register the doi to datacite - datacite_register(recid, str(record.id)) - except Exception as exc: - db.session.rollback() - # index the record again + # Mint the doi and publish + edit_record = video_deposit.edit().mint_doi().publish().commit() + # Save changes + db.session.commit() + + # Index the record again _, record_video = edit_record.fetch_published() RecordIndexer().index(record_video) - click.echo(f"DOI created, registered, and indexed successfully for record '{recid}'") - else: - click.echo(f"Record '{recid}' is either not public or not published. Skipping DOI creation.") + click.echo(f"DOI minted, and indexed successfully for record '{recid}'") + # Register the doi to datacite + datacite_register(recid, str(record_video.id)) + + except DataCiteError as dexc: + raise ClickException(f"Failed to register DOI on datacite for the video record '{recid}': {str(dexc)}") + except Exception as exc: + db.session.rollback() + raise ClickException(f"Failed to mint DOI for the video record '{recid}': {str(exc)}") + @click.group() diff --git a/cds/modules/records/minters.py b/cds/modules/records/minters.py index fdb500e4f..9425b7cc7 100644 --- a/cds/modules/records/minters.py +++ b/cds/modules/records/minters.py @@ -37,11 +37,6 @@ def cds_record_minter(record_uuid, data): """Mint record identifiers.""" provider = _rec_minter(record_uuid, data) - # from cds.modules.deposit.api import Project - # from .permissions import is_public - # project_schema = current_jsonschemas.path_to_url(Project._schema) - # Call the 'doi_minter' function if needed (not project and published) - return provider.pid diff --git a/tests/unit/test_video_rest.py b/tests/unit/test_video_rest.py index d28f979a3..0fa87973b 100644 --- a/tests/unit/test_video_rest.py +++ b/tests/unit/test_video_rest.py @@ -31,7 +31,6 @@ import mock from cds.modules.maintenance.cli import create_doi -from cds.modules.records.minters import doi_minter from click.testing import CliRunner import pytest @@ -99,7 +98,7 @@ def test_video_publish_registering_the_datacite( assert datacite_mock().metadata_post.call_count == 0 datacite_mock().doi_post.assert_not_called() - # [[ UPDATE DATACITE ]] + # [[ ENSURE NO UPDATE IN DATACITE ]] datacite_register_after_publish( sender=api_app, action="publish", deposit=video_1 ) @@ -393,6 +392,9 @@ def test_video_publish_edit_publish_again( # [[ PUBLISH VIDEO ]] _deposit_publish(client, json_headers, video_1["_deposit"]["id"]) + # [[ MINT DOI TO VIDEO ]] + video_1 = deposit_video_resolver(video_1_depid) + video_1.edit().mint_doi().publish().commit() datacite_register.s(pid_value="123", record_uuid=str(video_1.id)).apply() # [[ EDIT VIDEO ]] @@ -401,8 +403,8 @@ def test_video_publish_edit_publish_again( # [[ MODIFY DOI -> SAVE ]] video_1 = deposit_video_resolver(video_1_depid) video_1_dict = copy.deepcopy(video_1) - old_doi = video_1_dict.get("doi") - # video_1_dict["doi"] = "10.1123/doi" + old_doi = video_1_dict["doi"] + video_1_dict["doi"] = "10.1123/doi" del video_1_dict["_files"] res = client.put( url_for( @@ -416,7 +418,7 @@ def test_video_publish_edit_publish_again( assert res.status_code == 200 data = json.loads(res.data.decode("utf-8")) # Ensure that doi once minted cannot be changed to another value - assert data["metadata"].get("doi") == old_doi + assert data["metadata"]["doi"] == old_doi video_1 = deposit_video_resolver(video_1_depid) # video_1['doi'] = old_doi