Skip to content

Commit

Permalink
Merge pull request #1 from datopian/feat/user-provide-doi
Browse files Browse the repository at this point in the history
User can provide custom doi identifier
  • Loading branch information
sagargg authored Dec 23, 2024
2 parents 5931ac7 + 44bf7ed commit 92e26b8
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 23 deletions.
16 changes: 5 additions & 11 deletions ckanext/doi/lib/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,29 +75,23 @@ def get_prefix(cls):
def generate_doi(self, identifier=None):
"""
Generate a new DOI which isn't currently in use.
The database is checked for previous
usage, as is Datacite itself. Use whatever value is retuned from this function quickly to
avoid double use as this function uses no locking.
:return: the full, unique DOI
"""
# the list of valid characters is larger than just lowercase and the digits but we don't
# need that many options and URLs with just alphanumeric characters in them are nicer. We
# just use lowercase characters to avoid any issues with case being ignored
valid_characters = string.ascii_lowercase + string.digits

attempts = 5

while attempts > 0:

year = dt.now().year
# form the doi using the prefix
if identifier:
doi = identifier
else:
# generate a random 8 character identifier
random_identifier = ''.join(random.choice(valid_characters) for _ in range(8))
year = dt.now().year
doi = f'{self.prefix}/{year}.{random_identifier}'

if DOIQuery.read_doi(doi) is None:
try:
self.client.metadata_get(doi)
Expand All @@ -109,7 +103,7 @@ def generate_doi(self, identifier=None):
f'error: {e}'
)
attempts -= 1
raise Exception('Failed to generate a DOI')
raise toolkit.ValidationError('Could not generate DOI')

def mint_doi(self, doi, package_id):
"""
Expand Down
29 changes: 20 additions & 9 deletions ckanext/doi/model/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ckan.model import Session

from ckanext.doi.model.doi import DOI, doi_table
from ckan.plugins import toolkit as tk


class DOIQuery:
Expand Down Expand Up @@ -43,7 +44,12 @@ def read_doi(cls, identifier):

@classmethod
def read_package(
cls, package_id, version=None, is_version=None, create_if_none=False
cls,
package_id,
version=None,
is_version=None,
requested_identifier=None,
create_if_none=False,
):
"""
Retrieve a record associated with a given package.
Expand All @@ -57,7 +63,7 @@ def read_package(

doi_exist = Session.query(DOI).filter(DOI.package_id == package_id).first()
identifier = None

if is_version and version:
# Find the DOI for the version
version_doi = (
Expand All @@ -68,14 +74,19 @@ def read_package(
# Remove existing version suffix and append the new version
identifier = re.sub(r'\.v\d+$', '', version_doi.identifier)
identifier = f"{identifier}.v{version}"

# If no DOI exists and creation is allowed
if doi_exist is None and create_if_none:
client = DataciteClient()
new_doi = client.generate_doi(identifier=identifier)
new_doi = cls.create(new_doi, package_id)
return new_doi

# If no DOI exists and creation is allowed
if doi_exist is None and create_if_none:
try:
if requested_identifier:
new_doi = cls.create(requested_identifier, package_id)
else:
client = DataciteClient()
new_doi = client.generate_doi(identifier=identifier)
new_doi = cls.create(new_doi, package_id)
return new_doi
except Exception as e:
raise tk.ValidationError(f"Error creating DOI: {e}")
return doi_exist

@classmethod
Expand Down
21 changes: 18 additions & 3 deletions ckanext/doi/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
# This file is part of ckanext-doi
# Created by the Natural History Museum in London, UK

import re
from datetime import datetime
from logging import getLogger

Expand All @@ -25,6 +25,15 @@
log = getLogger(__name__)


def doi_validator(key, data, errors, context):
value = data.get(key)
PATTERN = re.compile("^10\.[0-9]{4,9}/[-._;()/:a-zA-Z0-9]+$")

if value:
if not PATTERN.match(value):
errors[key].append(toolkit._("Invalid DOI format"))
return

class DOIPlugin(SingletonPlugin, toolkit.DefaultDatasetForm):
"""
CKAN DOI Extension.
Expand All @@ -35,6 +44,7 @@ class DOIPlugin(SingletonPlugin, toolkit.DefaultDatasetForm):
implements(interfaces.ITemplateHelpers, inherit=True)
implements(interfaces.IBlueprint)
implements(interfaces.IClick)
implements(interfaces.IValidators)

## IClick
def get_commands(self):
Expand All @@ -55,11 +65,11 @@ def after_dataset_create(self, context, pkg_dict):
NB: This is called after creation of a dataset, before resources have been
added, so state = draft.
"""

DOIQuery.read_package(
pkg_dict['id'],
version=pkg_dict.get('version'),
is_version=pkg_dict.get('is_version_of'),
requested_identifier= pkg_dict.get('doi', None),
create_if_none=True,
)

Expand All @@ -77,7 +87,6 @@ def after_dataset_update(self, context, pkg_dict):
)
if is_active:
package_id = pkg_dict['id']

# remove user-defined update schemas first (if needed)
context.pop('schema', None)
client = DataciteClient()
Expand Down Expand Up @@ -123,6 +132,12 @@ def get_blueprint(self):
Return the blueprint to be registered.
"""
return registred_views()

# IValidators
def get_validators(self):
return {
'doi_validator': doi_validator,
}

# ITemplateHelpers
def get_helpers(self):
Expand Down

0 comments on commit 92e26b8

Please sign in to comment.