Esempio n. 1
0
    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
Esempio n. 2
0
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
Esempio n. 3
0
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
Esempio n. 4
0
    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)
Esempio n. 5
0
    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
Esempio n. 6
0
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,
    )
Esempio n. 7
0
 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
Esempio n. 8
0
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)
Esempio n. 11
0
        {'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()
Esempio n. 12
0
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
Esempio n. 13
0
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)
Esempio n. 14
0
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
Esempio n. 15
0
    },
    '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/",