def __init__(self, client, pid_type="doi", default_status=PIDStatus.NEW, generate_id_func=None, generate_doi_func=None, **kwargs): """Constructor.""" self.client = client self.api_client = None self.is_api_client_setup = False if client and client.has_credentials(): self.api_client = DataCiteRESTClient(client.username, client.password, client.prefix, client.test_mode) self.is_api_client_setup = True super().__init__(self.api_client, pid_type, default_status) self.generate_id = generate_id_func or \ DOIDataCitePIDProvider._generate_id default_generate_doi = self._generate_doi format_func = current_app.config['RDM_RECORDS_DOI_DATACITE_FORMAT'] if format_func and callable(format_func): default_generate_doi = format_func self.generate_doi = generate_doi_func or default_generate_doi
def get_doi(metadata, doi=None, production=False): """ Use datacite to get DOI for metadata """ # development dcprefix_dev = '10.22013' dcurl_dev = 'http://doi.test.datacite.org' # production dcprefix_prod = '10.25800' dcurl_prod = 'http://doi.datacite.org' if production: url = dcurl_prod prefix = dcprefix_prod else: url = dcurl_dev prefix = dcprefix_dev # either generate doi or fix doi without prefix if doi is not None: if prefix not in doi: print( f'DOI provided ({doi}) does not have required prefix ({prefix}). Adding prefix.' ) doi = prefix + '/' + doi else: d = DataCiteRESTClient(username='******', password=dcp, prefix=prefix, test_mode=(not production)) doi = d.public_doi(metadata, url) return doi
def get_rest(username="******", password="******", prefix='10.1234', with_fake_url=True): """Create a REST API client.""" client = DataCiteRESTClient( username=username, password=password, prefix=prefix, test_mode=True, ) if with_fake_url: # change URL for tests client.api_url = RESTURL return client
def __init__(self, name, client, pid_type="doi", default_status=PIDStatus.NEW, **kwargs): """Constructor.""" self._client_credentials = client self.client = DataCiteRESTClient(client.username, client.password, client.prefix, client.test_mode) super(DataCiteProvider, self).__init__(name, client, pid_type, default_status)
def __init__(self, client, pid_type="doi", default_status=PIDStatus.NEW, generate_suffix_func=None, generate_id_func=None, **kwargs): """Constructor.""" self.client = client self.api_client = DataCiteRESTClient(client.username, client.password, client.prefix, client.test_mode) super().__init__(self.api_client, pid_type, default_status) self.generate_suffix = generate_suffix_func or \ DOIDataCitePIDProvider._generate_suffix self.generate_id = generate_id_func or self._generate_id
def get_rest(username="******", password="******", test_mode=True, timeout=None): """Create a API client.""" return DataCiteRESTClient( username=username, password=password, test_mode=test_mode, timeout=timeout, )
def api(self): """DataCite REST API client instance.""" if self._api is None: self.check_credentials() self._api = DataCiteRESTClient( self.cfg('username'), self.cfg('password'), self.cfg('prefix'), self.cfg('test_mode', True), ) return self._api
def get_rest(username="******", password="******", prefix='10.5072', test_mode=False, timeout=None): """Create a REST API client.""" return DataCiteRESTClient( username=username, password=password, prefix=prefix, url=RESTURL, test_mode=test_mode, timeout=timeout, )
if args.prefix != None: prefix = args.prefix else: prefix = "10.7907" # Get our DataCite password infile = open("pw", "r") password = infile.readline().strip() if args.test == True: # Existing test record record_number = 5756 d = DataCiteRESTClient( username="******", password=password, prefix="10.33569", test_mode=True, ) repo_url = "http://authorstest.library.caltech.edu" else: d = DataCiteRESTClient( username="******", password=password, prefix="10.7907", ) repo_url = "https://authors.library.caltech.edu" # Command line option sets DOI if args.doi: if "doi" in eprint: print(
r = requests.get(next_link) data = r.json() for doi in data["data"]: item_url = doi["attributes"]["url"] if "http://resolver.caltech.edu" in item_url: links[doi["id"]] = item_url if "next" in data["links"]: next_link = data["links"]["next"] else: next_link = None return links if __name__ == "__main__": prefix = "10.26206" # DataCite Setup d = DataCiteRESTClient( username="******", password="", prefix=prefix, ) client_ids = ["caltech.library"] links = get_datacite_dois(client_ids) for l in progressbar(links): if l.split("/")[0] == prefix: print(l) new_link = links[l].replace("http://", "https://") d.update_url(l, new_link)
{'name': 'Smith, John'}, ], 'titles': [ {'title': 'Minimal Test Case', } ], 'publisher': 'Invenio Software', 'publicationYear': '2015', 'types': { 'resourceType': 'Dataset', 'resourceTypeGeneral': 'Dataset' }, 'schemaVersion': 'http://datacite.org/schema/kernel-4', } # Validate dictionary assert schema42.validate(data) # Generate DataCite XML from dictionary. doc = schema42.tostring(data) # Initialize the REST client. d = DataCiteRESTClient( username="******", password="******", prefix="10.1234", test_mode=True ) # Reserve a draft DOI doi = d.draft_doi()
class DOIDataCitePIDProvider(BasePIDProvider): """DataCite Provider class. Note that DataCite is only contacted when a DOI is reserved or registered, or any action posterior to it. Its creation happens only at PIDStore level. """ name = "datacite" def __init__(self, client, pid_type="doi", default_status=PIDStatus.NEW, generate_suffix_func=None, generate_id_func=None, **kwargs): """Constructor.""" self.client = client self.api_client = DataCiteRESTClient(client.username, client.password, client.prefix, client.test_mode) super().__init__(self.api_client, pid_type, default_status) self.generate_suffix = generate_suffix_func or \ DOIDataCitePIDProvider._generate_suffix self.generate_id = generate_id_func or self._generate_id @staticmethod def _generate_suffix(record, client, **kwargs): """Generate a unique DOI suffix. The content after the slash. """ recid = record.pid.pid_value return f"{client.name}.{recid}" def _generate_id(self, record, **kwargs): """Generate a unique DOI.""" prefix = self.client.prefix suffix = self.generate_suffix(record, self.client, **kwargs) return f"{prefix}/{suffix}" def create(self, record, **kwargs): """Create a new unique DOI PID based on the record ID.""" doi = self.generate_id(record, **kwargs) return super().create(pid_value=doi, object_type="rec", object_uuid=record.id, **kwargs) def reserve(self, pid, record, **kwargs): """Reserve a DOI only in the local system. It does not reserve the DOI in DataCite. :param pid: the PID to reserve. :param record: the record. :returns: `True` if is reserved successfully. """ return super().reserve(pid, record) def register(self, pid, record, url, **kwargs): """Register a DOI via the DataCite API. :param pid: the PID to register. :param record: the record metadata for the DOI. :returns: `True` if is registered successfully. """ super().register(pid, record) # PIDS-FIXME: move to async task, exception handling included try: doc = DataCite43JSONSerializer().dump_one(record) self.api_client.public_doi(metadata=doc, url=url, doi=pid.pid_value) except DataCiteError: pass return True def update(self, pid, record, url, **kwargs): """Update metadata associated with a DOI. This can be called before/after a DOI is registered. :param pid: the PID to register. :param record: the record metadata for the DOI. :returns: `True` if is updated successfully. """ # PIDS-FIXME: Do we want to log when reactivate the DOI # if pid.is_deleted(): try: # PIDS-FIXME: move to async task, exception handling included # Set metadata doc = DataCite43JSONSerializer().dump_one(record) self.api_client.update_doi(metadata=doc, doi=pid.pid_value, url=url) except DataCiteError: pass if pid.is_deleted(): # PIDS-FIXME: Is this correct? pid.sync_status(PIDStatus.REGISTERED) return True def delete(self, pid, record, **kwargs): """Delete/unregister a registered DOI. If the PID has not been reserved then it's deleted only locally. Otherwise, also it's deleted also remotely. :returns: `True` if is deleted successfully. """ # PIDS-FIXME: move to async task, exception handling included try: if pid.is_reserved(): # Delete only works for draft DOIs self.api_client.delete_doi(pid.pid_value) elif pid.is_registered(): self.api_client.hide_doi(pid.pid_value) except DataCiteError: pass return super().delete(pid, record) def validate(self, identifier=None, provider=None, client=None, **kwargs): """Validate the attributes of the identifier.""" super().validate(identifier, provider, client, **kwargs) if identifier: self.api_client.check_doi(identifier) return True
class DOIDataCitePIDProvider(BasePIDProvider): """DataCite Provider class. Note that DataCite is only contacted when a DOI is reserved or registered, or any action posterior to it. Its creation happens only at PIDStore level. """ name = "datacite" def __init__(self, client, pid_type="doi", default_status=PIDStatus.NEW, generate_id_func=None, generate_doi_func=None, **kwargs): """Constructor.""" self.client = client self.api_client = None self.is_api_client_setup = False if client and client.has_credentials(): self.api_client = DataCiteRESTClient(client.username, client.password, client.prefix, client.test_mode) self.is_api_client_setup = True super().__init__(self.api_client, pid_type, default_status) self.generate_id = generate_id_func or \ DOIDataCitePIDProvider._generate_id default_generate_doi = self._generate_doi format_func = current_app.config['RDM_RECORDS_DOI_DATACITE_FORMAT'] if format_func and callable(format_func): default_generate_doi = format_func self.generate_doi = generate_doi_func or default_generate_doi @staticmethod def _log_errors(errors): """Log errors from DataCiteError class.""" # DataCiteError is a tuple with the errors on the first errors = json.loads(errors.args[0])["errors"] for error in errors: field = error["source"] reason = error["title"] current_app.logger.warning(f"Error in {field}: {reason}") @staticmethod def _generate_id(record, **kwargs): """Generate a unique DOI suffix. The content after the slash. """ recid = record.pid.pid_value return f"{recid}" def _generate_doi(self, record, **kwargs): """Generate a unique DOI.""" prefix = self.client.prefix id = self.generate_id(record, **kwargs) format_string = current_app.config['RDM_RECORDS_DOI_DATACITE_FORMAT'] if format_string: return format_string.format(prefix=prefix, id=id) else: return f"{prefix}/datacite.{id}" def create(self, record, **kwargs): """Create a new unique DOI PID based on the record ID.""" doi = self.generate_doi(record, **kwargs) return super().create(record, doi, **kwargs) def reserve(self, pid, record, **kwargs): """Constant True. It does not reserve locally, nor externally. This is to avoid storing many PIDs as cause of reserve/discard, which would then be soft deleted. Therefore we want to pass from status.NEW to status.RESERVED. :param pid: the PID to reserve. :param record: the record. :returns: `True` """ return True def register(self, pid, record, **kwargs): """Register a DOI via the DataCite API. :param pid: the PID to register. :param record: the record metadata for the DOI. :returns: `True` if is registered successfully. """ local_success = super().register(pid, record) if not local_success: return False if self.is_api_client_setup: # PIDS-FIXME: move to async task, exception handling included try: doc = DataCite43JSONSerializer().dump_one(record) url = kwargs["url"] self.api_client.public_doi(metadata=doc, url=url, doi=pid.pid_value) except DataCiteError as e: current_app.logger.warning("DataCite provider error when " f"updating DOI for {pid.pid_value}") self._log_errors(e) return False else: current_app.logger.warning("DataCite client not configured. Cannot" f" register DOI for {pid.pid_value}") return True def update(self, pid, record, url, **kwargs): """Update metadata associated with a DOI. This can be called before/after a DOI is registered. :param pid: the PID to register. :param record: the record metadata for the DOI. :returns: `True` if is updated successfully. """ # PIDS-FIXME: Do we want to log when reactivate the DOI # if pid.is_deleted(): if self.is_api_client_setup: try: # PIDS-FIXME: move to async task, exception handling included # Set metadata doc = DataCite43JSONSerializer().dump_one(record) self.api_client.update_doi(metadata=doc, doi=pid.pid_value, url=url) except DataCiteError as e: current_app.logger.warning("DataCite provider error when " f"updating DOI for {pid.pid_value}") self._log_errors(e) return False else: current_app.logger.warning("DataCite client not configured. Cannot" f" update DOI for {pid.pid_value}") if pid.is_deleted(): return pid.sync_status(PIDStatus.REGISTERED) return True def delete(self, pid, record, **kwargs): """Delete/unregister a registered DOI. If the PID has not been reserved then it's deleted only locally. Otherwise, also it's deleted also remotely. :returns: `True` if is deleted successfully. """ # PIDS-FIXME: move to async task, exception handling included try: if pid.is_reserved(): # Delete only works for draft DOIs if self.is_api_client_setup: self.api_client.delete_doi(pid.pid_value) else: current_app.logger.warning("DataCite client not " "configured. Cannot delete DOI " f"for {pid.pid_value}") elif pid.is_registered(): if self.is_api_client_setup: self.api_client.hide_doi(pid.pid_value) else: current_app.logger.warning("DataCite client not " "configured. Cannot delete DOI " f"for {pid.pid_value}") except DataCiteError as e: current_app.logger.warning("DataCite provider error when deleting " f"DOI for {pid.pid_value}") self._log_errors(e) return False return super().delete(pid, record) def validate(self, record, identifier=None, provider=None, client=None, **kwargs): """Validate the attributes of the identifier. :returns: A tuple (success, errors). The first specifies if the validation was passed successfully. The second one is an array of error messages. """ success, errors = super().validate(record, identifier, provider, client, **kwargs) if identifier and self.is_api_client_setup: # format check try: self.api_client.check_doi(identifier) except ValueError as e: errors.append(str(e)) return (True, []) if not errors else (False, errors)
class DataCiteProvider(BaseProvider): """DataCite Provider class. Note that DataCite is only contacted when a DOI is reserved or registered, or any action posterior to it. Its creation happens only at PIDStore level. """ def __init__(self, name, client, pid_type="doi", default_status=PIDStatus.NEW, **kwargs): """Constructor.""" self._client_credentials = client self.client = DataCiteRESTClient(client.username, client.password, client.prefix, client.test_mode) super(DataCiteProvider, self).__init__(name, client, pid_type, default_status) def reserve(self, record, pid): """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: pid.reserve() self.client.draft_doi(metadata=record, doi=pid.pid_value) except (DataCiteError, HttpError): raise return True def register(self, record, pid, **kwargs): """Register a DOI via the DataCite API. :param record: Record with the metadata for the DOI. :returns: `True` if is registered successfully. """ try: pid.register() # Set metadata for DOI self.client.public_doi(metadata=record, doi=pid.pid_value) except (DataCiteError, HttpError): # PIDS-FIXME: PIDSTore logs, but invenio has almost no logs # A custom exception maybe better? raise return True def update(self, record, pid, **kwargs): """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. """ # PIDS-FIXME: Do we want to log when reactivate the DOI # if pid.is_deleted(): try: # Set metadata self.client.update_doi(metadata=record, doi=pid.pid_value) except (DataCiteError, HttpError): raise if pid.is_deleted(): # PIDS-FIXME: Is this correct? pid.sync_status(PIDStatus.REGISTERED) return True def unregister(self, pid, **kwargs): """Delete/unregister a registered DOI. If the PID has not been reserved then it's deleted only locally. Otherwise, also it's deleted also remotely. :returns: `True` if is deleted successfully. """ try: if pid.is_reserved(): # Delete only works for draft DOIs self.client.delete_doi(pid.pid_value) elif pid.is_registered(): # First try external in case of errors self.client.hide_doi(pid.pid_value) # In any case gets deleted locally (New, Reserved or Registered) pid.delete() except (DataCiteError, HttpError): raise return True def validate(self, pid_attrs, **kwargs): """Validate the attributes of the identifier.""" # PIDS-FIXME: Anything needed here pass
}, 'schemaVersion': 'http://datacite.org/schema/kernel-4', } # Validate dictionary assert schema42.validate(data) # Generate DataCite XML from dictionary. doc = schema42.tostring(data) # Initialize the REST client. username = os.environ["DATACITE_USER"] password = os.environ["DATACITE_PW"] prefix = os.environ["DATACITE_PREFIX"] d = DataCiteRESTClient(username=username, password=password, prefix=prefix, test_mode=True) # Reserve a draft DOI doi = d.draft_doi() # Mint new DOI #d.doi_post('10.5072/test-doi', 'http://example.org/test-doi') # Get DOI location #location = d.doi_get(doi) # Set alternate URL for content type (available through api) #d.media_post( # "10.5072/test-doi", # {"application/json": "http://example.org/test-doi/json/",