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
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'])
# 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")
# 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")
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
# 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")
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)