Beispiel #1
0
async def write_datacite_metadata(guid, temp_dir):
    data = get_with_retry(
        f"{settings.OSF_API_URL}v2/registrations/{guid}/identifiers/"
    ).json()["data"]
    doi = [
        identifier["attributes"]["value"]
        for identifier in data
        if identifier["attributes"]["category"] == "doi"
    ]
    if not doi:
        raise DataCiteNotFoundError(
            f"Datacite DOI not found for registration {guid} on OSF server."
        )
    else:
        doi = doi[0]

    client = DataCiteMDSClient(
        url=settings.DATACITE_URL,
        username=settings.DATACITE_USERNAME,
        password=settings.DATACITE_PASSWORD,
        prefix=settings.DATACITE_PREFIX,
    )

    try:
        xml_metadata = client.metadata_get(doi)
    except DataCiteNotFoundError:
        raise DataCiteNotFoundError(
            f"Datacite DOI not found for registration {guid} on Datacite server."
        )

    with open(os.path.join(temp_dir, "datacite.xml"), "w") as fp:
        fp.write(xml_metadata)

    return xml_metadata
Beispiel #2
0
async def write_datacite_metadata(guid, temp_dir, metadata):
    try:
        doi = next((
            identifier["attributes"]["value"]
            for identifier in metadata["data"]["embeds"]["identifiers"]["data"]
            if identifier["attributes"]["category"] == "doi"))
    except StopIteration:
        raise DataCiteNotFoundError(
            f"Datacite DOI not found for registration {guid} on OSF server.")
    client = DataCiteMDSClient(
        url=settings.DATACITE_URL,
        username=settings.DATACITE_USERNAME,
        password=settings.DATACITE_PASSWORD,
        prefix=settings.DATACITE_PREFIX,
    )
    try:
        xml_metadata = client.metadata_get(doi)
    except DataCiteNotFoundError:
        raise DataCiteNotFoundError(
            f"Datacite DOI {doi} not found for registration {guid} on Datacite server."
        )

    with open(os.path.join(temp_dir, "datacite.xml"), "w") as fp:
        fp.write(xml_metadata)

    return xml_metadata
Beispiel #3
0
def get_datacite_metadata(doi):
    client = DataCiteMDSClient(
        url=settings.DATACITE_URL,
        username=settings.DATACITE_USERNAME,
        password=settings.DATACITE_PASSWORD,
        prefix=settings.DATACITE_PREFIX,
    )
    return client.metadata_get(doi)
Beispiel #4
0
def get_datacite_metadata(doi, datacite_username, datacite_password,
                          datacite_prefix):
    assert isinstance(datacite_password,
                      str), 'Datacite password not passed to pigeon'
    assert isinstance(datacite_username,
                      str), 'Datacite username not passed to pigeon'
    assert isinstance(datacite_prefix,
                      str), 'Datacite prefix not passed to pigeon'
    client = DataCiteMDSClient(
        url=settings.DATACITE_URL,
        username=datacite_username,
        password=datacite_password,
        prefix=datacite_prefix,
    )
    return client.metadata_get(doi)
Beispiel #5
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.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
Beispiel #6
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
Beispiel #7
0
    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")

# Make DOI inactive
d.metadata_delete("10.5072/test-doi")
Beispiel #8
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'])
Beispiel #9
0
    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")

# Make DOI inactive
d.metadata_delete("10.1234/test-doi")
Beispiel #10
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'])
Beispiel #11
0
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
Beispiel #12
0
    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")

# Make DOI inactive
d.metadata_delete("10.5072/test-doi")
Beispiel #13
0
class DoiHelper(object):

    def __init__(self, conf, errors):
        self.conf = conf
        self.errors = errors
        self.test_mode = False #True
        self._create_client()

    def safe_call(self, f, *args):
        try:
            f(*args)
        except Exception as e:
            self.errors.append("Datacite1 connection failed")

    @on_no_errors
    def _create_client(self):
        datacite_kwargs = {
            'username': self.conf.dc_symbol,
            'password': self.conf.dc_password,
            'prefix': self.conf.dc_prefix,
            'test_mode': self.test_mode}
        #self.client = DataCiteMDSClient(**datacite_kwargs)
        try:
            self.client = DataCiteMDSClient(**datacite_kwargs)
        except Exception as e:
            self.errors.append("Datacite connection failed")

    @on_no_errors
    def find_free_doi(self):
        r = "".join([random.choice(string.ascii_uppercase + string.digits)
                     for _ in range(5)])
        doi = "/".join([self.conf.dc_prefix, self.conf.dc_identifier, r])
        try:
            self.client.metadata_get(doi)
            return self.find_free_doi()
        except DataCiteServerError as e:
            if e.error_code != 404:
                self.errors.append(
                    'Not the expected result from MDS while validation: %s' % e)

        return doi

    @on_no_errors
    def _post_metadata(self, metadata):
        logging.debug("METADATA:" + metadata)
        res = self.client.metadata_post(metadata)
        logging.debug("RES: %s" % res)
        if not res.startswith("OK"):
            self.errors.append(res)

    @on_no_errors
    def _post_doi(self, doi, url):
        for testurl in [
            "https://develop.osl.tib.eu",
            "https://test.osl.tib.eu",
            "https://develop.handbuch.tib.eu",
            "https://test.handbuch.tib.eu",
            "https://handbuch.tib.eu",
            "https://handbuch.local",

        ]:
            if testurl in url:
                url = url.replace(testurl, "http://handbuch.io")

        if "handbuch.io" in url and not self.test_mode:
            logging.debug("Post DOI: Doi: %s\t%s" % (doi, url))
            self.client.doi_post(doi, url)

    def _get_creators_list(self, book):
        creators = []
        for title in ['AUTOREN', 'HERAUSGEBER', 'KONTRIBUTOREN', ]:
            namesstr = book.info.get(title, "")
            dicts = [
                {'creatorName': name.strip()}
                for name in namesstr.split(',')
                if name.strip()
            ]
            creators += [d for d in dicts if d]

        return creators

    def _get_bookdata(self, book):
        publisher = book.info.get('HERAUSGEBER', None) or \
            book.info['AUTOREN']
        data = {
            'identifier': {
                'identifier': book.info['doi'],
                'identifierType': 'DOI',
            },
            'creators': self._get_creators_list(book),
            'titles': [
                {'title': book.book_page.friendly_title}
            ],
            'publisher': publisher,
            'publicationYear': str(datetime.now().year),
            'version': str(self.conf.version)
        }
        return data

    def _create_doi(self, doi, url, data):
        metadata = None
        try:
            schema31.validate(data)
            metadata = schema31.tostring(data)
        except ValidationError as e:
            self.errors.append(str(e))
        #import pdb; pdb.set_trace()
        self._post_metadata(metadata)
        self._post_doi(doi, url)

    @on_no_errors
    def create_chapterdoi(self, doi, url, title, book, username):
        #import pdb; pdb.set_trace()
        data = self._get_bookdata(book)
        data['titles'][0]['title'] = title
        data['identifier']['identifier'] = doi
        data['relatedIdentifiers'] = [{
               'relatedIdentifier': book.info['doi'],
               'relatedIdentifierType': 'DOI',
               'relationType':'IsPartOf'}, ]
        if username:
            data['publisher'] = username

        self._create_doi(doi, url, data)

    @on_no_errors
    def create_bookdoi(self, url, book):
        data = self._get_bookdata(book)
        self._create_doi(book.info['doi'], url, data)