Exemple #1
0
class DataCiteProvider(BaseProvider):
    """DOI provider using DataCite API."""

    pid_type = 'doi'
    """Default persistent identifier type."""

    pid_provider = 'datacite'
    """Persistent identifier provider name."""

    default_status = PIDStatus.NEW
    """Default status for newly created PIDs by this provider."""

    @classmethod
    def create(cls, pid_value, **kwargs):
        """Create a new record identifier.

        For more information about parameters,
        see :meth:`invenio_pidstore.providers.base.BaseProvider.create`.

        :param pid_value: Persistent identifier value.
        :params ``**kwargs``: See
            :meth:`invenio_pidstore.providers.base.BaseProvider.create` extra
            parameters.
        :returns: A
            :class:`invenio_pidstore.providers.datacite.DataCiteProvider`
            instance.
        """
        return super(DataCiteProvider, cls).create(
            pid_value=pid_value, **kwargs)

    def __init__(self, pid, client=None):
        """Initialize provider.

        To use the default client, just configure the following variables:

        * `PIDSTORE_DATACITE_USERNAME` as username.

        * `PIDSTORE_DATACITE_PASSWORD` as password.

        * `PIDSTORE_DATACITE_DOI_PREFIX` as DOI prefix.

        * `PIDSTORE_DATACITE_TESTMODE` to `True` if it configured in test mode.

        * `PIDSTORE_DATACITE_URL` as DataCite URL.

        :param pid: A :class:`invenio_pidstore.models.PersistentIdentifier`
            instance.
        :param client: A client to access to DataCite.
            (Default: :class:`datacite.DataCiteMDSClient` instance)
        """
        super(DataCiteProvider, self).__init__(pid)
        if client is not None:
            self.api = client
        else:
            self.api = DataCiteMDSClient(
                username=current_app.config.get('PIDSTORE_DATACITE_USERNAME'),
                password=current_app.config.get('PIDSTORE_DATACITE_PASSWORD'),
                prefix=current_app.config.get('PIDSTORE_DATACITE_DOI_PREFIX'),
                test_mode=current_app.config.get(
                    'PIDSTORE_DATACITE_TESTMODE', False),
                url=current_app.config.get('PIDSTORE_DATACITE_URL'))

    def reserve(self, doc):
        """Reserve a DOI (amounts to upload metadata, but not to mint).

        :param doc: Set metadata for DOI.
        :returns: `True` if is reserved successfully.
        """
        # Only registered PIDs can be updated.
        try:
            self.pid.reserve()
            self.api.metadata_post(doc)
        except (DataCiteError, HttpError):
            logger.exception("Failed to reserve in DataCite",
                             extra=dict(pid=self.pid))
            raise
        logger.info("Successfully reserved in DataCite",
                    extra=dict(pid=self.pid))
        return True

    def register(self, url, doc):
        """Register a DOI via the DataCite API.

        :param url: Specify the URL for the API.
        :param doc: Set metadata for DOI.
        :returns: `True` if is registered successfully.
        """
        try:
            self.pid.register()
            # Set metadata for DOI
            self.api.metadata_post(doc)
            # Mint DOI
            self.api.doi_post(self.pid.pid_value, url)
        except (DataCiteError, HttpError):
            logger.exception("Failed to register in DataCite",
                             extra=dict(pid=self.pid))
            raise
        logger.info("Successfully registered in DataCite",
                    extra=dict(pid=self.pid))
        return True

    def update(self, url, doc):
        """Update metadata associated with a DOI.

        This can be called before/after a DOI is registered.

        :param doc: Set metadata for DOI.
        :returns: `True` if is updated successfully.
        """
        if self.pid.is_deleted():
            logger.info("Reactivate in DataCite",
                        extra=dict(pid=self.pid))

        try:
            # Set metadata
            self.api.metadata_post(doc)
            self.api.doi_post(self.pid.pid_value, url)
        except (DataCiteError, HttpError):
            logger.exception("Failed to update in DataCite",
                             extra=dict(pid=self.pid))
            raise

        if self.pid.is_deleted():
            self.pid.sync_status(PIDStatus.REGISTERED)
        logger.info("Successfully updated in DataCite",
                    extra=dict(pid=self.pid))
        return True

    def delete(self):
        """Delete a registered DOI.

        If the PID is new then it's deleted only locally.
        Otherwise, also it's deleted also remotely.

        :returns: `True` if is deleted successfully.
        """
        try:
            if self.pid.is_new():
                self.pid.delete()
            else:
                self.pid.delete()
                self.api.metadata_delete(self.pid.pid_value)
        except (DataCiteError, HttpError):
            logger.exception("Failed to delete in DataCite",
                             extra=dict(pid=self.pid))
            raise
        logger.info("Successfully deleted in DataCite",
                    extra=dict(pid=self.pid))
        return True

    def sync_status(self):
        """Synchronize DOI status DataCite MDS.

        :returns: `True` if is sync successfully.
        """
        status = None

        try:
            try:
                self.api.doi_get(self.pid.pid_value)
                status = PIDStatus.REGISTERED
            except DataCiteGoneError:
                status = PIDStatus.DELETED
            except DataCiteNoContentError:
                status = PIDStatus.REGISTERED
            except DataCiteNotFoundError:
                pass

            if status is None:
                try:
                    self.api.metadata_get(self.pid.pid_value)
                    status = PIDStatus.RESERVED
                except DataCiteGoneError:
                    status = PIDStatus.DELETED
                except DataCiteNoContentError:
                    status = PIDStatus.REGISTERED
                except DataCiteNotFoundError:
                    pass
        except (DataCiteError, HttpError):
            logger.exception("Failed to sync status from DataCite",
                             extra=dict(pid=self.pid))
            raise

        if status is None:
            status = PIDStatus.NEW

        self.pid.sync_status(status)

        logger.info("Successfully synced status from DataCite",
                    extra=dict(pid=self.pid))
        return True
class DataCiteProvider(BaseProvider):
    """DOI provider using DataCite API."""

    pid_type = 'doi'
    """Default persistent identifier type."""

    pid_provider = 'datacite'
    """Persistent identifier provider name."""

    default_status = PIDStatus.NEW
    """Default status for newly created PIDs by this provider."""

    @classmethod
    def create(cls, pid_value, **kwargs):
        """Create a new record identifier.

        For more information about parameters,
        see :meth:`invenio_pidstore.providers.BaseProvider.create`.

        :param pid_value: Persistent identifier value.
        :params **kwargs: See
            :meth:`invenio_pidstore.providers.base.BaseProvider.create` extra
            parameters.
        :returns: A :class:`invenio_pidstore.providers.DataCiteProvider`
            instance.
        """
        return super(DataCiteProvider, cls).create(
            pid_value=pid_value, **kwargs)

    def __init__(self, pid, client=None):
        """Initialize provider.

        To use the default client, just configure the following variables:

        * `PIDSTORE_DATACITE_USERNAME` as username.

        * `PIDSTORE_DATACITE_PASSWORD` as password.

        * `PIDSTORE_DATACITE_DOI_PREFIX` as DOI prefix.

        * `PIDSTORE_DATACITE_TESTMODE` to `True` if it configured in test mode.

        * `PIDSTORE_DATACITE_URL` as DataCite URL.

        :param pid: A :class:`invenio_pidstore.models.PersistentIdentifier`
            instance.
        :param client: A client to access to DataCite.
            (Default: :class:`datacite.DataCiteMDSClient` instance)
        """
        super(DataCiteProvider, self).__init__(pid)
        if client is not None:
            self.api = client
        else:
            self.api = DataCiteMDSClient(
                username=current_app.config.get('PIDSTORE_DATACITE_USERNAME'),
                password=current_app.config.get('PIDSTORE_DATACITE_PASSWORD'),
                prefix=current_app.config.get('PIDSTORE_DATACITE_DOI_PREFIX'),
                test_mode=current_app.config.get(
                    'PIDSTORE_DATACITE_TESTMODE', False),
                url=current_app.config.get('PIDSTORE_DATACITE_URL'))

    def reserve(self, doc):
        """Reserve a DOI (amounts to upload metadata, but not to mint).

        :param doc: Set metadata for DOI.
        :returns: `True` if is reserved successfully.
        """
        # Only registered PIDs can be updated.
        try:
            self.pid.reserve()
            self.api.metadata_post(doc)
        except (DataCiteError, HttpError):
            logger.exception("Failed to reserve in DataCite",
                             extra=dict(pid=self.pid))
            raise
        logger.info("Successfully reserved in DataCite",
                    extra=dict(pid=self.pid))
        return True

    def register(self, url, doc):
        """Register a DOI via the DataCite API.

        :param url: Specify the URL for the API.
        :param doc: Set metadata for DOI.
        :returns: `True` if is registered successfully.
        """
        try:
            self.pid.register()
            # Set metadata for DOI
            self.api.metadata_post(doc)
            # Mint DOI
            self.api.doi_post(self.pid.pid_value, url)
        except (DataCiteError, HttpError):
            logger.exception("Failed to register in DataCite",
                             extra=dict(pid=self.pid))
            raise
        logger.info("Successfully registered in DataCite",
                    extra=dict(pid=self.pid))
        return True

    def update(self, url, doc):
        """Update metadata associated with a DOI.

        This can be called before/after a DOI is registered.

        :param doc: Set metadata for DOI.
        :returns: `True` if is updated successfully.
        """
        if self.pid.is_deleted():
            logger.info("Reactivate in DataCite",
                        extra=dict(pid=self.pid))

        try:
            # Set metadata
            self.api.metadata_post(doc)
            self.api.doi_post(self.pid.pid_value, url)
        except (DataCiteError, HttpError):
            logger.exception("Failed to update in DataCite",
                             extra=dict(pid=self.pid))
            raise

        if self.pid.is_deleted():
            self.pid.sync_status(PIDStatus.REGISTERED)
        logger.info("Successfully updated in DataCite",
                    extra=dict(pid=self.pid))
        return True

    def delete(self):
        """Delete a registered DOI.

        If the PID is new then it's deleted only locally.
        Otherwise, also it's deleted also remotely.

        :returns: `True` if is deleted successfully.
        """
        try:
            if self.pid.is_new():
                self.pid.delete()
            else:
                self.pid.delete()
                self.api.metadata_delete(self.pid.pid_value)
        except (DataCiteError, HttpError):
            logger.exception("Failed to delete in DataCite",
                             extra=dict(pid=self.pid))
            raise
        logger.info("Successfully deleted in DataCite",
                    extra=dict(pid=self.pid))
        return True

    def sync_status(self):
        """Synchronize DOI status DataCite MDS.

        :returns: `True` if is sync successfully.
        """
        status = None

        try:
            try:
                self.api.doi_get(self.pid.pid_value)
                status = PIDStatus.REGISTERED
            except DataCiteGoneError:
                status = PIDStatus.DELETED
            except DataCiteNoContentError:
                status = PIDStatus.REGISTERED
            except DataCiteNotFoundError:
                pass

            if status is None:
                try:
                    self.api.metadata_get(self.pid.pid_value)
                    status = PIDStatus.RESERVED
                except DataCiteGoneError:
                    status = PIDStatus.DELETED
                except DataCiteNoContentError:
                    status = PIDStatus.REGISTERED
                except DataCiteNotFoundError:
                    pass
        except (DataCiteError, HttpError):
            logger.exception("Failed to sync status from DataCite",
                             extra=dict(pid=self.pid))
            raise

        if status is None:
            status = PIDStatus.NEW

        self.pid.sync_status(status)

        logger.info("Successfully synced status from DataCite",
                    extra=dict(pid=self.pid))
        return True
Exemple #3
0
class DataCite(PidProvider):

    """DOI provider using DataCite API."""

    pid_type = 'doi'

    def __init__(self):
        """Initialize provider."""
        self.api = DataCiteMDSClient(
            username=cfg.get('CFG_DATACITE_USERNAME'),
            password=cfg.get('CFG_DATACITE_PASSWORD'),
            prefix=cfg.get('CFG_DATACITE_DOI_PREFIX'),
            test_mode=cfg.get('CFG_DATACITE_TESTMODE', False),
            url=cfg.get('CFG_DATACITE_URL')
        )

    def _get_url(self, kwargs):
        try:
            return kwargs['url']
        except KeyError:
            raise Exception("url keyword argument must be specified.")

    def _get_doc(self, kwargs):
        try:
            return kwargs['doc']
        except KeyError:
            raise Exception("doc keyword argument must be specified.")

    def reserve(self, pid, *args, **kwargs):
        """Reserve a DOI (amounts to upload metadata, but not to mint)."""
        # Only registered PIDs can be updated.
        doc = self._get_doc(kwargs)

        try:
            self.api.metadata_post(doc)
        except DataCiteError as e:
            pid.log("RESERVE", "Failed with %s" % e.__class__.__name__)
            return False
        except HttpError as e:
            pid.log("RESERVE", "Failed with HttpError - %s" % unicode(e))
            return False
        else:
            pid.log("RESERVE", "Successfully reserved in DataCite")
        return True

    def register(self, pid, *args, **kwargs):
        """Register a DOI via the DataCite API."""
        url = self._get_url(kwargs)
        doc = self._get_doc(kwargs)

        try:
            # Set metadata for DOI
            self.api.metadata_post(doc)
            # Mint DOI
            self.api.doi_post(pid.pid_value, url)
        except DataCiteError as e:
            pid.log("REGISTER", "Failed with %s" % e.__class__.__name__)
            return False
        except HttpError as e:
            pid.log("REGISTER", "Failed with HttpError - %s" % unicode(e))
            return False
        else:
            pid.log("REGISTER", "Successfully registered in DataCite")
        return True

    def update(self, pid, *args, **kwargs):
        """Update metadata associated with a DOI.

        This can be called before/after a DOI is registered.
        """
        url = self._get_url(kwargs)
        doc = self._get_doc(kwargs)

        if pid.is_deleted():
            pid.log("UPDATE", "Reactivate in DataCite")

        try:
            # Set metadata
            self.api.metadata_post(doc)
            self.api.doi_post(pid.pid_value, url)
        except DataCiteError as e:
            pid.log("UPDATE", "Failed with %s" % e.__class__.__name__)
            return False
        except HttpError as e:
            pid.log("UPDATE", "Failed with HttpError - %s" % unicode(e))
            return False
        else:
            if pid.is_deleted():
                pid.log(
                    "UPDATE",
                    "Successfully updated and possibly registered in DataCite"
                )
            else:
                pid.log("UPDATE", "Successfully updated in DataCite")
        return True

    def delete(self, pid, *args, **kwargs):
        """Delete a registered DOI."""
        try:
            self.api.metadata_delete(pid.pid_value)
        except DataCiteError as e:
            pid.log("DELETE", "Failed with %s" % e.__class__.__name__)
            return False
        except HttpError as e:
            pid.log("DELETE", "Failed with HttpError - %s" % unicode(e))
            return False
        else:
            pid.log("DELETE", "Successfully deleted in DataCite")
        return True

    def sync_status(self, pid, *args, **kwargs):
        """Synchronize DOI status DataCite MDS."""
        status = None

        try:
            self.api.doi_get(pid.pid_value)
            status = cfg['PIDSTORE_STATUS_REGISTERED']
        except DataCiteGoneError:
            status = cfg['PIDSTORE_STATUS_DELETED']
        except DataCiteNoContentError:
            status = cfg['PIDSTORE_STATUS_REGISTERED']
        except DataCiteNotFoundError:
            pass
        except DataCiteError as e:
            pid.log("SYNC", "Failed with %s" % e.__class__.__name__)
            return False
        except HttpError as e:
            pid.log("SYNC", "Failed with HttpError - %s" % unicode(e))
            return False

        if status is None:
            try:
                self.api.metadata_get(pid.pid_value)
                status = cfg['PIDSTORE_STATUS_RESERVED']
            except DataCiteGoneError:
                status = cfg['PIDSTORE_STATUS_DELETED']
            except DataCiteNoContentError:
                status = cfg['PIDSTORE_STATUS_REGISTERED']
            except DataCiteNotFoundError:
                pass
            except DataCiteError as e:
                pid.log("SYNC", "Failed with %s" % e.__class__.__name__)
                return False
            except HttpError as e:
                pid.log("SYNC", "Failed with HttpError - %s" % unicode(e))
                return False

        if status is None:
            status = cfg['PIDSTORE_STATUS_NEW']

        if pid.status != status:
            pid.log(
                "SYNC", "Fixed status from %s to %s." % (pid.status, status)
            )
            pid.status = status

        return True

    @classmethod
    def is_provider_for_pid(cls, pid_str):
        """Check if DataCite is the provider for this DOI.

        Note: If you e.g. changed DataCite account and received a new prefix,
        then this provider can only update and register DOIs for the new
        prefix.
        """
        return pid_str.startswith("%s/" % cfg['CFG_DATACITE_DOI_PREFIX'])
Exemple #4
0
# Initialize the MDS client.
d = DataCiteMDSClient(
    username='******',
    password='******',
    prefix='10.5072',
)

# Set metadata for DOI
d.metadata_post(doc)

# Mint new DOI
d.doi_post('10.5072/test-doi', 'http://example.org/test-doi')

# Get DOI location
location = d.doi_get("10.5072/test-doi")

# Set alternate URL for content type (available through content negotiation)
d.media_post(
    "10.5072/test-doi",
    {"application/json": "http://example.org/test-doi/json/",
     "application/xml": "http://example.org/test-doi/xml/"}
)

# Get alternate URLs
mapping = d.media_get("10.5072/test-doi")
assert mapping["application/json"] == "http://example.org/test-doi/json/"

# Get metadata for DOI
doc = d.metadata_get("10.5072/test-doi")
Exemple #5
0
# Initialize the MDS client.
d = DataCiteMDSClient(
    username='******',
    password='******',
    prefix='10.1234',
    test_mode=True,
)

# Set metadata for DOI
d.metadata_post(doc)

# Mint new DOI
d.doi_post('10.1234/test-doi', 'http://example.org/test-doi')

# Get DOI location
location = d.doi_get("10.1234/test-doi")

# Set alternate URL for content type (available through content negotiation)
d.media_post(
    "10.1234/test-doi", {
        "application/json": "http://example.org/test-doi/json/",
        "application/xml": "http://example.org/test-doi/xml/"
    })

# Get alternate URLs
mapping = d.media_get("10.1234/test-doi")
assert mapping["application/json"] == "http://example.org/test-doi/json/"

# Get metadata for DOI
doc = d.metadata_get("10.1234/test-doi")
Exemple #6
0
class DataCite(PidProvider):
    """DOI provider using DataCite API."""

    pid_type = 'doi'

    def __init__(self):
        """Initialize provider."""
        self.api = DataCiteMDSClient(username=cfg.get('CFG_DATACITE_USERNAME'),
                                     password=cfg.get('CFG_DATACITE_PASSWORD'),
                                     prefix=cfg.get('CFG_DATACITE_DOI_PREFIX'),
                                     test_mode=cfg.get('CFG_DATACITE_TESTMODE',
                                                       False),
                                     url=cfg.get('CFG_DATACITE_URL'))

    def _get_url(self, kwargs):
        try:
            return kwargs['url']
        except KeyError:
            raise Exception("url keyword argument must be specified.")

    def _get_doc(self, kwargs):
        try:
            return kwargs['doc']
        except KeyError:
            raise Exception("doc keyword argument must be specified.")

    def reserve(self, pid, *args, **kwargs):
        """Reserve a DOI (amounts to upload metadata, but not to mint)."""
        # Only registered PIDs can be updated.
        doc = self._get_doc(kwargs)

        try:
            self.api.metadata_post(doc)
        except DataCiteError as e:
            pid.log("RESERVE", "Failed with %s" % e.__class__.__name__)
            return False
        except HttpError as e:
            pid.log("RESERVE", "Failed with HttpError - %s" % unicode(e))
            return False
        else:
            pid.log("RESERVE", "Successfully reserved in DataCite")
        return True

    def register(self, pid, *args, **kwargs):
        """Register a DOI via the DataCite API."""
        url = self._get_url(kwargs)
        doc = self._get_doc(kwargs)

        try:
            # Set metadata for DOI
            self.api.metadata_post(doc)
            # Mint DOI
            self.api.doi_post(pid.pid_value, url)
        except DataCiteError as e:
            pid.log("REGISTER", "Failed with %s" % e.__class__.__name__)
            return False
        except HttpError as e:
            pid.log("REGISTER", "Failed with HttpError - %s" % unicode(e))
            return False
        else:
            pid.log("REGISTER", "Successfully registered in DataCite")
        return True

    def update(self, pid, *args, **kwargs):
        """Update metadata associated with a DOI.

        This can be called before/after a DOI is registered.
        """
        url = self._get_url(kwargs)
        doc = self._get_doc(kwargs)

        if pid.is_deleted():
            pid.log("UPDATE", "Reactivate in DataCite")

        try:
            # Set metadata
            self.api.metadata_post(doc)
            self.api.doi_post(pid.pid_value, url)
        except DataCiteError as e:
            pid.log("UPDATE", "Failed with %s" % e.__class__.__name__)
            return False
        except HttpError as e:
            pid.log("UPDATE", "Failed with HttpError - %s" % unicode(e))
            return False
        else:
            if pid.is_deleted():
                pid.log(
                    "UPDATE",
                    "Successfully updated and possibly registered in DataCite")
            else:
                pid.log("UPDATE", "Successfully updated in DataCite")
        return True

    def delete(self, pid, *args, **kwargs):
        """Delete a registered DOI."""
        try:
            self.api.metadata_delete(pid.pid_value)
        except DataCiteError as e:
            pid.log("DELETE", "Failed with %s" % e.__class__.__name__)
            return False
        except HttpError as e:
            pid.log("DELETE", "Failed with HttpError - %s" % unicode(e))
            return False
        else:
            pid.log("DELETE", "Successfully deleted in DataCite")
        return True

    def sync_status(self, pid, *args, **kwargs):
        """Synchronize DOI status DataCite MDS."""
        status = None

        try:
            self.api.doi_get(pid.pid_value)
            status = cfg['PIDSTORE_STATUS_REGISTERED']
        except DataCiteGoneError:
            status = cfg['PIDSTORE_STATUS_DELETED']
        except DataCiteNoContentError:
            status = cfg['PIDSTORE_STATUS_REGISTERED']
        except DataCiteNotFoundError:
            pass
        except DataCiteError as e:
            pid.log("SYNC", "Failed with %s" % e.__class__.__name__)
            return False
        except HttpError as e:
            pid.log("SYNC", "Failed with HttpError - %s" % unicode(e))
            return False

        if status is None:
            try:
                self.api.metadata_get(pid.pid_value)
                status = cfg['PIDSTORE_STATUS_RESERVED']
            except DataCiteGoneError:
                status = cfg['PIDSTORE_STATUS_DELETED']
            except DataCiteNoContentError:
                status = cfg['PIDSTORE_STATUS_REGISTERED']
            except DataCiteNotFoundError:
                pass
            except DataCiteError as e:
                pid.log("SYNC", "Failed with %s" % e.__class__.__name__)
                return False
            except HttpError as e:
                pid.log("SYNC", "Failed with HttpError - %s" % unicode(e))
                return False

        if status is None:
            status = cfg['PIDSTORE_STATUS_NEW']

        if pid.status != status:
            pid.log("SYNC",
                    "Fixed status from %s to %s." % (pid.status, status))
            pid.status = status

        return True

    @classmethod
    def is_provider_for_pid(cls, pid_str):
        """Check if DataCite is the provider for this DOI.

        Note: If you e.g. changed DataCite account and received a new prefix,
        then this provider can only update and register DOIs for the new
        prefix.
        """
        return pid_str.startswith("%s/" % cfg['CFG_DATACITE_DOI_PREFIX'])
class DataCiteProvider(BaseProvider):
    """DOI provider using DataCite API."""

    pid_type = 'doi'
    pid_provider = 'datacite'
    default_status = PIDStatus.NEW

    @classmethod
    def create(cls, pid_value, **kwargs):
        """Create a new record identifier."""
        return super(DataCiteProvider, cls).create(
            pid_value=pid_value, **kwargs)

    def __init__(self, pid, client=None):
        """Initialize provider."""
        super(DataCiteProvider, self).__init__(pid)
        if client is not None:
            self.api = client
        else:
            self.api = DataCiteMDSClient(
                username=current_app.config.get('PIDSTORE_DATACITE_USERNAME'),
                password=current_app.config.get('PIDSTORE_DATACITE_PASSWORD'),
                prefix=current_app.config.get('PIDSTORE_DATACITE_DOI_PREFIX'),
                test_mode=current_app.config.get(
                    'PIDSTORE_DATACITE_TESTMODE', False),
                url=current_app.config.get('PIDSTORE_DATACITE_URL'))

    def reserve(self, doc):
        """Reserve a DOI (amounts to upload metadata, but not to mint)."""
        # Only registered PIDs can be updated.
        try:
            self.pid.reserve()
            self.api.metadata_post(doc)
        except (DataCiteError, HttpError):
            logger.exception("Failed to reserve in DataCite",
                             extra=dict(pid=self.pid))
            raise
        logger.info("Successfully reserved in DataCite",
                    extra=dict(pid=self.pid))
        return True

    def register(self, url, doc):
        """Register a DOI via the DataCite API."""
        try:
            self.pid.register()
            # Set metadata for DOI
            self.api.metadata_post(doc)
            # Mint DOI
            self.api.doi_post(self.pid.pid_value, url)
        except (DataCiteError, HttpError):
            logger.exception("Failed to register in DataCite",
                             extra=dict(pid=self.pid))
            raise
        logger.info("Successfully registered in DataCite",
                    extra=dict(pid=self.pid))
        return True

    def update(self, url, doc):
        """Update metadata associated with a DOI.

        This can be called before/after a DOI is registered.
        """
        if self.pid.is_deleted():
            logger.info("Reactivate in DataCite",
                        extra=dict(pid=self.pid))

        try:
            # Set metadata
            self.api.metadata_post(doc)
            self.api.doi_post(self.pid.pid_value, url)
        except (DataCiteError, HttpError):
            logger.exception("Failed to update in DataCite",
                             extra=dict(pid=self.pid))
            raise

        if self.pid.is_deleted():
            self.pid.sync_status(PIDStatus.REGISTERED)
        logger.info("Successfully updated in DataCite",
                    extra=dict(pid=self.pid))
        return True

    def delete(self):
        """Delete a registered DOI."""
        try:
            if self.pid.is_new():
                self.pid.delete()
            else:
                self.pid.delete()
                self.api.metadata_delete(self.pid.pid_value)
        except (DataCiteError, HttpError):
            logger.exception("Failed to delete in DataCite",
                             extra=dict(pid=self.pid))
            raise
        logger.info("Successfully deleted in DataCite",
                    extra=dict(pid=self.pid))
        return True

    def sync_status(self):
        """Synchronize DOI status DataCite MDS."""
        status = None

        try:
            try:
                self.api.doi_get(self.pid.pid_value)
                status = PIDStatus.REGISTERED
            except DataCiteGoneError:
                status = PIDStatus.DELETED
            except DataCiteNoContentError:
                status = PIDStatus.REGISTERED
            except DataCiteNotFoundError:
                pass

            if status is None:
                try:
                    self.api.metadata_get(self.pid.pid_value)
                    status = PIDStatus.RESERVED
                except DataCiteGoneError:
                    status = PIDStatus.DELETED
                except DataCiteNoContentError:
                    status = PIDStatus.REGISTERED
                except DataCiteNotFoundError:
                    pass
        except (DataCiteError, HttpError):
            logger.exception("Failed to sync status from DataCite",
                             extra=dict(pid=self.pid))
            raise

        if status is None:
            status = PIDStatus.NEW

        self.pid.sync_status(status)

        logger.info("Successfully synced status from DataCite",
                    extra=dict(pid=self.pid))
        return True
Exemple #8
0
# Initialize the MDS client.
d = DataCiteMDSClient(
    username='******',
    password='******',
    prefix='10.5072',
    test_mode=True
)

# Set metadata for DOI
d.metadata_post(doc)

# Mint new DOI
d.doi_post('10.5072/test-doi', 'http://example.org/test-doi')

# Get DOI location
location = d.doi_get("10.5072/test-doi")

# Set alternate URL for content type (availble through content negotiation)
d.media_post(
    "10.5072/test-doi",
    {"application/json": "http://example.org/test-doi/json/",
     "application/xml": "http://example.org/test-doi/xml/"}
)

# Get alternate URLs
mapping = d.media_get("10.5072/test-doi")
assert mapping["application/json"] == "http://example.org/test-doi/json/"

# Get metadata for DOI
doc = d.metadata_get("10.5072/test-doi")
Exemple #9
0
class DataCiteClient(AbstractIdentifierClient):
    def __init__(self, node):
        try:
            assert settings.DATACITE_URL and (getattr(
                node.provider, 'doi_prefix', None) or settings.DATACITE_PREFIX)
        except AssertionError:
            raise ImproperlyConfigured(
                'OSF\'Datacite client\'s settings are not configured')

        self._client = DataCiteMDSClient(
            url=settings.DATACITE_URL,
            username=settings.DATACITE_USERNAME,
            password=settings.DATACITE_PASSWORD,
            prefix=getattr(node.provider, 'doi_prefix', None)
            or settings.DATACITE_PREFIX)

    def build_metadata(self, node):
        """Return the formatted datacite metadata XML as a string.
         """

        data = {
            'identifier': {
                'identifier': self.build_doi(node),
                'identifierType': 'DOI',
            },
            'creators':
            datacite_format_creators([node.creator]),
            'contributors':
            datacite_format_contributors(node.visible_contributors),
            'titles': [{
                'title': node.title
            }],
            'publisher':
            'Open Science Framework',
            'publicationYear':
            str(datetime.datetime.now().year),
            'resourceType': {
                'resourceType':
                'Pre-registration'
                if node.type == 'osf.registration' else 'Project',
                'resourceTypeGeneral':
                'Text'
            },
            'dates': [
                {
                    'date': node.created.isoformat(),
                    'dateType': 'Created'
                },
                {
                    'date': node.modified.isoformat(),
                    'dateType': 'Updated'
                },
            ]
        }

        article_doi = node.article_doi
        if article_doi:
            data['relatedIdentifiers'] = [{
                'relatedIdentifier': article_doi,
                'relatedIdentifierType': 'DOI',
                'relationType': 'IsSupplementTo'
            }]

        if node.description:
            data['descriptions'] = [{
                'descriptionType': 'Abstract',
                'description': node.description
            }]

        if node.node_license:
            data['rightsList'] = [{
                'rights': node.node_license.name,
                'rightsURI': node.node_license.url
            }]

        data['subjects'] = datacite_format_subjects(node)

        # Validate dictionary
        assert schema40.validate(data)

        # Generate DataCite XML from dictionary.
        return schema40.tostring(data)

    def build_doi(self, object):
        return settings.DOI_FORMAT.format(
            prefix=getattr(object.provider, 'doi_prefix', None)
            or settings.DATACITE_PREFIX,
            guid=object._id)

    def get_identifier(self, identifier):
        self._client.doi_get(identifier)

    def create_identifier(self, node, category):
        if category == 'doi':
            if settings.DATACITE_ENABLED:
                metadata = self.build_metadata(node)
                resp = self._client.metadata_post(metadata)
                # Typical response: 'OK (10.70102/FK2osf.io/cq695)' to doi 10.70102/FK2osf.io/cq695
                doi = re.match(r'OK \((?P<doi>[a-zA-Z0-9 .\/]{0,})\)',
                               resp).groupdict()['doi']
                self._client.doi_post(doi, node.absolute_url)
                return {'doi': doi}
            logger.info('TEST ENV: DOI built but not minted')
            return {'doi': self.build_doi(node)}
        else:
            raise NotImplementedError(
                'Creating an identifier with category {} is not supported'.
                format(category))

    def update_identifier(self, node, category):
        if settings.DATACITE_ENABLED and not node.is_public or node.is_deleted:
            if category == 'doi':
                doi = self.build_doi(node)
                self._client.metadata_delete(doi)
                return {'doi': doi}
            else:
                raise NotImplementedError(
                    'Updating metadata not supported for {}'.format(category))
        else:
            return self.create_identifier(node, category)