Skip to content

Commit

Permalink
More robust DOI commitment flow
Browse files Browse the repository at this point in the history
  • Loading branch information
zachmullen authored and danlamanna committed Aug 28, 2023
1 parent 5072aa1 commit 92cbc8a
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 17 deletions.
71 changes: 55 additions & 16 deletions isic/core/services/collection/doi.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
import random
from urllib import parse

from django.conf import settings
from django.contrib.auth.models import User
Expand Down Expand Up @@ -57,6 +58,17 @@ def collection_build_doi(*, collection: Collection, doi_id: str) -> dict:
}


def collection_build_draft_doi(*, doi_id: str) -> dict:
return {
"data": {
"type": "dois",
"attributes": {
"doi": doi_id,
},
}
}


def collection_generate_random_doi_id():
# pad DOI with leading zeros so all DOIs are prefix/6 digits
return f"{settings.ISIC_DATACITE_DOI_PREFIX}/{random.randint(10_000, 999_999):06}"
Expand Down Expand Up @@ -84,27 +96,54 @@ def _datacite_create_doi(doi: dict) -> None:
timeout=5,
json=doi,
)
# TODO: check for already exists
r.raise_for_status()

try:
r.raise_for_status()
except HTTPError:
logger.exception(f"DOI draft creation failed: {r.json()}")
raise ValidationError("Something went wrong creating the DOI.")


def _datacite_update_doi(doi: dict, doi_id: str):
doi_quoted = parse.quote(doi_id, safe="") # escape the / for path
r = requests.put(
f"{settings.ISIC_DATACITE_API_URL}/dois/{doi_quoted}",
auth=(settings.ISIC_DATACITE_USERNAME, settings.ISIC_DATACITE_PASSWORD),
timeout=5,
json=doi,
)

try:
r.raise_for_status()
except HTTPError:
logger.exception(f"DOI update failed: {r.json()}")
raise ValidationError("Something went wrong publishing the DOI.")


def collection_create_doi(*, user: User, collection: Collection) -> Doi:
collection_check_create_doi_allowed(user=user, collection=collection)
doi_id = collection_generate_random_doi_id()
doi = collection_build_doi(collection=collection, doi_id=doi_id)
draft_doi_dict = collection_build_draft_doi(doi_id=doi_id)
doi_dict = collection_build_doi(collection=collection, doi_id=doi_id)

try:
_datacite_create_doi(doi)
except HTTPError as e:
logger.error(e)
raise ValidationError("Something went wrong creating the DOI.")
else:
with transaction.atomic():
doi = Doi(id=doi_id, creator=user, url=f"https://doi.org/{doi_id}")
doi.full_clean()
doi.save()
collection_lock(collection=collection)
collection_update(collection=collection, doi=doi, ignore_lock=True)
logger.info("User %d created DOI %s for collection %d", user.id, doi.id, collection.id)
with transaction.atomic():
# First, create the local DOI record to validate uniqueness within our known set
doi = Doi(id=doi_id, creator=user, url=f"https://doi.org/{doi_id}")
doi.full_clean()
doi.save()

# Lock the collection, set the DOI on it
collection_lock(collection=collection)
collection_update(collection=collection, doi=doi, ignore_lock=True)

# Reserve the DOI using the draft mechanism.
# If it fails, transaction will rollback, nothing in our database will change.
_datacite_create_doi(draft_doi_dict)

# Convert to a published DOI. If this fails, someone will have to come along later and
# retry to publish it. (May want a django-admin action for this if it ever happens.)
_datacite_update_doi(doi_dict, doi_id)

logger.info("User %d created DOI %s for collection %d", user.id, doi.id, collection.id)

return doi
4 changes: 3 additions & 1 deletion isic/core/tests/test_doi.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@

@pytest.fixture
def mock_datacite_create_doi(mocker):
yield mocker.patch(
mocker.patch(
"isic.core.services.collection.doi._datacite_create_doi", lambda doi: {"doi": "123456"}
)
mocker.patch("isic.core.services.collection.doi._datacite_update_doi")
yield


@pytest.fixture
Expand Down

0 comments on commit 92cbc8a

Please sign in to comment.