diff --git a/invenio.cfg b/invenio.cfg index 50929839..d0fee6c3 100644 --- a/invenio.cfg +++ b/invenio.cfg @@ -20,73 +20,51 @@ from datetime import timedelta from celery.schedules import crontab from flask_resources import create_error_handler from invenio_administration.permissions import administration_permission -from invenio_app_rdm.config import ( - APP_RDM_DEPOSIT_FORM_DEFAULTS as DEPOSIT_FORM_DEFAULTS, -) -from invenio_app_rdm.config import ( - APP_RDM_ROUTES, - CELERY_BEAT_SCHEDULE, -) -from invenio_app_rdm.config import ( - COMMUNITIES_ERROR_HANDLERS as BASE_COMMUNITIES_ERROR_HANDLERS, -) +from invenio_app_rdm.config import \ + APP_RDM_DEPOSIT_FORM_DEFAULTS as DEPOSIT_FORM_DEFAULTS +from invenio_app_rdm.config import APP_RDM_ROUTES, CELERY_BEAT_SCHEDULE +from invenio_app_rdm.config import \ + COMMUNITIES_ERROR_HANDLERS as BASE_COMMUNITIES_ERROR_HANDLERS from invenio_communities.communities.services import facets as community_facets from invenio_github.oauth.remote_app import github_app as github_remote_app from invenio_i18n import lazy_gettext as _ from invenio_oauthclient.contrib.keycloak import KeycloakSettingsHelper -from invenio_oauthclient.contrib.orcid import ( - REMOTE_MEMBER_APP, - REMOTE_SANDBOX_MEMBER_APP, -) +from invenio_oauthclient.contrib.orcid import (REMOTE_MEMBER_APP, + REMOTE_SANDBOX_MEMBER_APP) from invenio_oauthclient.views.client import auto_redirect_login -from invenio_rdm_records.config import ( - RDM_FACETS, -) -from invenio_rdm_records.config import ( - RDM_SORT_OPTIONS as BASE_SORT_OPTIONS, -) +from invenio_rdm_records.config import (RDM_FACETS, + RDM_RECORDS_PERSONORG_SCHEMES) +from invenio_rdm_records.config import RDM_SORT_OPTIONS as BASE_SORT_OPTIONS from invenio_rdm_records.contrib.journal import JOURNAL_SORT_OPTIONS from invenio_rdm_records.contrib.meeting import MEETING_SORT_OPTIONS -from invenio_rdm_records.resources.config import ( - error_handlers as base_records_error_handlers, -) +from invenio_rdm_records.resources.config import \ + error_handlers as base_records_error_handlers from invenio_rdm_records.resources.errors import HTTPJSONException from invenio_rdm_records.services.components import DefaultRecordsComponents from invenio_rdm_records.services.components.signal import SignalComponent from invenio_records_resources.services.records.queryparser import ( - FieldValueMapper, - QueryParser, - SearchFieldTransformer, -) -from invenio_vocabularies.services.custom_fields import ( - SUBJECT_FIELDS, - SUBJECT_FIELDS_UI, -) + FieldValueMapper, QueryParser, SearchFieldTransformer) +from invenio_vocabularies.services.custom_fields import (SUBJECT_FIELDS, + SUBJECT_FIELDS_UI) from zenodo_rdm import facets as zenodo_community_facets from zenodo_rdm import providers as zenodo_providers from zenodo_rdm.api import ZenodoRDMDraft, ZenodoRDMRecord -from zenodo_rdm.custom_fields import ( - CUSTOM_FIELDS, - CUSTOM_FIELDS_FACETS, - CUSTOM_FIELDS_UI, - NAMESPACES, -) +from zenodo_rdm.custom_fields import (CUSTOM_FIELDS, CUSTOM_FIELDS_FACETS, + CUSTOM_FIELDS_UI, NAMESPACES) +from zenodo_rdm.custom_schemes import is_edmo from zenodo_rdm.files import storage_factory from zenodo_rdm.github.schemas import CitationMetadataSchema from zenodo_rdm.legacy.resources import record_serializers from zenodo_rdm.metrics.config import METRICS_CACHE_UPDATE_INTERVAL from zenodo_rdm.moderation.errors import UserBlockedException -from zenodo_rdm.moderation.handlers import CommunityScoreHandler, RecordScoreHandler +from zenodo_rdm.moderation.handlers import (CommunityScoreHandler, + RecordScoreHandler) from zenodo_rdm.openaire.records.components import OpenAIREComponent -from zenodo_rdm.permissions import ( - ZenodoCommunityPermissionPolicy, - ZenodoRDMRecordPermissionPolicy, -) +from zenodo_rdm.permissions import (ZenodoCommunityPermissionPolicy, + ZenodoRDMRecordPermissionPolicy) from zenodo_rdm.queryparser import word_communities, word_doi -from zenodo_rdm.subcommunities import ( - ZenodoSubCommunityRequest, - ZenodoSubcommunityRequestSchema, -) +from zenodo_rdm.subcommunities import (ZenodoSubCommunityRequest, + ZenodoSubcommunityRequestSchema) from zenodo_rdm.tokens import RATSubjectSchema from zenodo_rdm.views import frontpage_view_function @@ -739,16 +717,10 @@ USERPROFILES_READ_ONLY = ( THEME_SHOW_FRONTPAGE_INTRO_SECTION = False APP_RDM_RECORD_LANDING_PAGE_TEMPLATE = "zenodo_rdm/records/detail.html" -from zenodo_rdm.utils import ( - annostor_link_render, - blr_link_render, - briefideas_link_render, - f1000_link_render, - github_link_render, - openaire_link_render, - reana_link_render, - swh_link_render, -) +from zenodo_rdm.utils import (annostor_link_render, blr_link_render, + briefideas_link_render, f1000_link_render, + github_link_render, openaire_link_render, + reana_link_render, swh_link_render) APP_RDM_RECORD_LANDING_PAGE_EXTERNAL_LINKS = [ {"id": "f1000", "render": f1000_link_render}, @@ -925,6 +897,11 @@ OAISERVER_XSL_URL = "/static/oai2.xsl" RDM_DATACITE_FUNDER_IDENTIFIERS_PRIORITY = ("doi", "ror", "grid", "isni", "gnd") """Priority of funder identifiers types for Datacite serialization, prioritize DOI.""" +RDM_RECORDS_PERSONORG_SCHEMES = { +**RDM_RECORDS_PERSONORG_SCHEMES, + "edmo": {"label": _("EDMO"), "validator": is_edmo, "datacite": "URL"}, +} + RECORDS_RESOURCES_ALLOW_EMPTY_FILES = False """Disable empty files for Zenodo.""" diff --git a/site/setup.cfg b/site/setup.cfg index 7bd0290c..58bfa192 100644 --- a/site/setup.cfg +++ b/site/setup.cfg @@ -88,6 +88,8 @@ invenio_previewer.previewers = image_previewer = zenodo_rdm.previewer.image_previewer invenio_search.index_templates = zenodo_rdm = zenodo_rdm.index_templates +idutils.custom_schemes = + edmo = zenodo_rdm.custom_schemes:get_scheme_config_func [bdist_wheel] universal = 1 diff --git a/site/zenodo_rdm/custom_schemes.py b/site/zenodo_rdm/custom_schemes.py new file mode 100644 index 00000000..ce643f35 --- /dev/null +++ b/site/zenodo_rdm/custom_schemes.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2024 CERN. +# +# ZenodoRDM is free software; you can redistribute it and/or modify it +# under the terms of the MIT License; see LICENSE file for more details. + +"""Custom scheme config.""" + +import re + +# Regular expression to validate EDMO identifiers in the format "edmo:" +edmo_regexp = re.compile(r"^edmo:(\d+)$") + +# Regular expression to match the EDMO URL format +edmo_url_regexp = re.compile( + r"(?:https?://)?(?:edmo\.seadatanet\.org/report/)?(\d+)$", re.IGNORECASE +) + + +def is_edmo(val): + """ + Validate and normalize an EDMO identifier or URL. + + Returns the normalized 'edmo:' format if valid, otherwise None. + """ + # Match against the URL or the edmo: format + match = edmo_url_regexp.match(val) or edmo_regexp.match(val) + if match: + # Extract the numeric ID from the matched group + return f"edmo:{match.group(1)}" + return None + + +def normalize_edmo(val): + """Normalize an EDMO identifier or URL to the 'edmo:' format.""" + return is_edmo(val) + + +def generate_edmo_url(_, normalized_pid): + """ + Generate a URL for a given normalized EDMO identifier. + + Assumes the input is in the form 'edmo:'. + """ + edmo_id = normalized_pid.split(":")[1] + return f"https://edmo.seadatanet.org/report/{edmo_id}" + + +def get_scheme_config_func(): + """Return the configuration for the EDMO custom scheme.""" + return { + "validator": is_edmo, + "normalizer": normalize_edmo, + "filter": [], + "url_generator": generate_edmo_url, + }