Skip to content

Commit

Permalink
refactor: prefix doi management commands
Browse files Browse the repository at this point in the history
- prefix all one-off destructive DOI commands with `doi_`
- add reset_staging to mint new DOIs on staging using the datacite
  sandbox, doi_reset_staging -> step 3, doi_mint_parent_codebase_dois
- bump deps for datacite schema 4.5 and django cve
  • Loading branch information
alee committed Dec 5, 2024
1 parent abfa27c commit 9754634
Show file tree
Hide file tree
Showing 22 changed files with 1,054 additions and 851 deletions.
2 changes: 1 addition & 1 deletion django/core/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def _to_search_terms(query_params):
@staticmethod
def _to_filter_display_terms(query_params):
"""
Convert query parameters into a list of displayable filter terms (replaces underscores withs paces, etc)
Convert query parameters into a list of displayable filter terms (replaces underscores withs spaces, etc)
Args:
query_params (QueryDict): The query parameters.
Returns:
Expand Down
16 changes: 0 additions & 16 deletions django/curator/management/commands/curator_register_dois.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import argparse
import logging
from django.core.management.base import BaseCommand
from django.conf import settings
import logging

from library.models import CodebaseRelease
from library.doi import (
DataCiteApi,
VERIFICATION_MESSAGE,
doi_matches_pattern,
is_valid_doi,
get_welcome_message,
)

Expand All @@ -29,7 +30,7 @@ def update_existing_dois(interactive=True, dry_run=True):
total_peer_reviewed_releases_count = peer_reviewed_releases.count()

logger.info(
"Updating DOIs for %s peer reviewed CodebaseReleases with DOIs",
"Updating DOIs for parent Codebases of %s peer reviewed CodebaseReleases with DOIs",
total_peer_reviewed_releases_count,
)

Expand All @@ -56,21 +57,22 @@ def update_existing_dois(interactive=True, dry_run=True):
if not codebase_doi:
# request to DataCite API
logger.debug("Minting DOI for parent codebase: %s", codebase.pk)
codebase_doi, success = datacite_api.mint_new_doi_for_codebase(codebase)
log, ok = datacite_api.mint_public_doi(codebase)

if not success:
if not ok:
logger.error(
"Could not mint DOI for parent codebase %s. Skipping release %s.",
"Unable to mint DOI for parent codebase %s of release %s: %s",
codebase.pk,
release.pk,
log.status_code,
)
if interactive:
input("Press Enter to continue or CTRL+C to quit...")
continue

logger.debug("New codebase DOI: %s. Saving codebase...", codebase_doi)
if not dry_run:
codebase.doi = codebase_doi
codebase.doi = log.doi
codebase.save()
else:
logger.debug(
Expand Down Expand Up @@ -108,12 +110,12 @@ def update_existing_dois(interactive=True, dry_run=True):
release_doi,
)
# set up DataCite API request to mint new DOI
release_doi, success = datacite_api.mint_new_doi_for_release(release)
if not success:
log, ok = datacite_api.mint_public_doi(release)
if not ok:
logger.error(
"Could not mint DOI for release %s. DOI: %s. Skipping.",
"Could not mint DOI for release %s - status code: %s.",
release.pk,
release_doi,
log.status_code,
)
if interactive:
input("Press Enter to continue or CTRL+C to quit...")
Expand All @@ -126,7 +128,7 @@ def update_existing_dois(interactive=True, dry_run=True):
release.doi,
)
if not dry_run:
release.doi = release_doi
release.doi = log.doi
release.save()

if interactive:
Expand All @@ -139,12 +141,13 @@ def update_existing_dois(interactive=True, dry_run=True):
release_doi,
)
# request to DataCite API: mint new DOI!
release_doi, success = datacite_api.mint_new_doi_for_release(release)
if not success:
log, ok = datacite_api.mint_public_doi(release)
release_doi = log.doi
if not ok:
logger.error(
"Could not mint DOI for release %s. DOI: %s. Skipping.",
"Could not mint DOI for release %s - status code: %s.",
release.pk,
release_doi,
log.status_code,
)
if interactive:
input("Press Enter to continue or CTRL+C to quit...")
Expand Down Expand Up @@ -190,13 +193,13 @@ def update_existing_dois(interactive=True, dry_run=True):
if release.doi is None:
logger.error("DOI should not be None for release %s", release.pk)

if not doi_matches_pattern(release.codebase.doi):
if not is_valid_doi(release.codebase.doi):
logger.error(
"%s Codebase DOI doesn't match DataCite pattern!",
release.codebase.doi,
)

if not doi_matches_pattern(release.doi):
if not is_valid_doi(release.doi):
logger.error(
"%s CodebaseRelease DOI doesn't match DataCite pattern!",
release.doi,
Expand All @@ -212,12 +215,14 @@ class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument(
"--interactive",
action="store_true",
action=argparse.BooleanOptionalAction,
help="Wait for user to press enter to continue.",
default=True,
default=False,
)
parser.add_argument(
"--dry-run", action="store_true", help="Output what would have happened."
"--dry-run",
action=argparse.BooleanOptionalAction,
help="Output what would have happened.",
)

def handle(self, *args, **options):
Expand Down
44 changes: 44 additions & 0 deletions django/curator/management/commands/doi_mint_pending_releases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from django.core.management.base import BaseCommand
from library.doi import DataCiteApi, VERIFICATION_MESSAGE, get_welcome_message

import argparse
import logging

logger = logging.getLogger(__name__)


def mint_pending_dois(interactive=True, dry_run=True):
print(get_welcome_message(dry_run))
if interactive:
input(
"Minting new DOIs for all reviewed releases and parent codebases without DOIs. Press Enter to continue or CTRL+C to quit..."
)
api = DataCiteApi(dry_run)
api.mint_pending_dois()
print(VERIFICATION_MESSAGE)


class Command(BaseCommand):
"""
Syncs metadata for all codebases and releases with Datacite metadata service.
"""

def add_arguments(self, parser):
parser.add_argument(
"--interactive",
action=argparse.BooleanOptionalAction,
help="Wait for explicit user confirmation to continue.",
default=True,
)
parser.add_argument(
"--dry-run",
action=argparse.BooleanOptionalAction,
help="Emit what would have happened.",
default=True,
)

def handle(self, *args, **options):
interactive = options["interactive"]
dry_run = options["dry_run"]
logger.info("minting new DOIs for reviewed releases without DOIs")
mint_pending_dois(interactive, dry_run)
113 changes: 113 additions & 0 deletions django/curator/management/commands/doi_reset_production.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import argparse
import csv
import logging
import sys
from django.core.management.base import BaseCommand
from library.doi import VERIFICATION_MESSAGE, get_welcome_message, DataCiteApi
from library.models import Codebase, CodebaseRelease

logger = logging.getLogger(__name__)


def cleanup_existing_dois(interactive=True, dry_run=True):
print(get_welcome_message(dry_run))

api = DataCiteApi(dry_run=dry_run)

# clean up all Codebases with existing DOIs
codebases_with_dois = Codebase.objects.with_doi()
logger.info("Removing all Codebase DOIs")
if interactive and codebases_with_dois.exists():
confirm = input(
"WARNING: this will remove all existing codebase DOIs and is unrecoverable. Type 'DELETE' to continue or Ctrl+C to quit: "
)
if not confirm.lower() == "delete":
logger.info("Aborting.")
sys.exit()

"""
assert that all Codebase DOIs have been reset
"""
if not dry_run:
print(VERIFICATION_MESSAGE)
with open("cleanup_codebases_with_doi.csv", "w") as f:
writer = csv.writer(f)
writer.writerow(["Codebase ID", "Codebase DOI"])
for codebase in codebases_with_dois:
writer.writerow([codebase.pk, codebase.doi])
codebases_with_dois.update(doi=None)
assert not Codebase.objects.with_doi().exists()
logger.info("Success. All existing codebase DOIs deleted.")

# clean up unreviewed release DOIs

unreviewed_releases_with_dois = CodebaseRelease.objects.unreviewed().with_doi()
total_unreviewed_releases_with_dois = unreviewed_releases_with_dois.count()
logger.info(
"Removing %s unreviewed CodebaseRelease DOIs",
total_unreviewed_releases_with_dois,
)
if interactive:
confirm = input(
f"Deleting all DOIs for {total_unreviewed_releases_with_dois} unreviewed CodebaseReleases. Enter 'DELETE' to continue or CTRL+C to quit: "
)
if not confirm.lower() == "delete":
logger.debug("Aborting...")
sys.exit()

if not dry_run:
with open("cleanup_unreviewed_releases_with_doi.csv", "w") as f:
writer = csv.writer(f)
writer.writerow(["CodebaseRelease ID", "CodebaseRelease DOI"])
for release in unreviewed_releases_with_dois:
writer.writerow([release.pk, release.doi])
unreviewed_releases_with_dois.update(doi=None)

# mint DOIs for all public peer reviewed CodebaseReleases without a DOI
reviewed_releases_without_dois = (
CodebaseRelease.objects.reviewed().public().without_doi()
)
invalid_releases = []
for release in reviewed_releases_without_dois:
try:
log, ok = api.mint_public_doi(release)
if not ok:
invalid_releases.append((release, log))
except Exception as e:
logger.error("Error minting DOI for release %s", release)
invalid_releases.append((release, e))

for release, log in invalid_releases:
with open("invalid_releases.csv", "w") as f:
writer = csv.writer(f)
writer.writerow(
["CodebaseRelease ID", "Status Code", "Reason", "Datacite Metadata"]
)
writer.writerow(
[release.pk, log.status_code, log.message, release.datacite.to_dict()]
)


class Command(BaseCommand):
"""
Removes all existing parent Codebase DOIs and mints new DOIs for all Peer Reviewed CodebaseReleases
"""

def add_arguments(self, parser):
parser.add_argument(
"--interactive",
action=argparse.BooleanOptionalAction,
help="Wait for user to press enter to continue.",
default=True,
)
parser.add_argument(
"--dry-run",
action=argparse.BooleanOptionalAction,
help="Output what would have happened.",
default=False,
)

def handle(self, *args, **options):
interactive = options["interactive"]
dry_run = options["dry_run"]
cleanup_existing_dois(interactive, dry_run)
Loading

0 comments on commit 9754634

Please sign in to comment.