diff options
| author | Andrius Merkys <merkys@debian.org> | 2024-01-29 06:55:49 -0500 |
|---|---|---|
| committer | git-ubuntu importer <ubuntu-devel-discuss@lists.ubuntu.com> | 2024-01-30 16:38:29 +0000 |
| commit | 9b0df72c1d53599f8d407fec6b7faa2b90d92b3b (patch) | |
| tree | 5281084ea02a16856213a66b8bc73a596c602144 | |
| parent | 7c294266ae8a7b74c288221cee9f2e17a5f7fb5d (diff) | |
| parent | 0e7e32cee6dea60c11aed26e38fc3b49d2750ac4 (diff) | |
1.5.2-1 (patches applied)applied/1.5.2-1applied/ubuntu/oracular-develapplied/ubuntu/oracularapplied/ubuntu/noble-proposedapplied/ubuntu/noble-develapplied/ubuntu/noble
Imported using git-ubuntu import.
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | .travis.yml | 12 | ||||
| -rw-r--r-- | README.rst | 38 | ||||
| -rw-r--r-- | crossref/__init__.py | 2 | ||||
| -rw-r--r-- | crossref/restful.py | 1012 | ||||
| -rw-r--r-- | crossref/utils.py | 4 | ||||
| -rw-r--r-- | crossref/validators.py | 100 | ||||
| -rw-r--r-- | debian/changelog | 11 | ||||
| -rw-r--r-- | debian/control | 2 | ||||
| -rw-r--r-- | debian/copyright | 4 | ||||
| -rw-r--r-- | debian/patches/disable-network-tests.patch | 3 | ||||
| -rw-r--r-- | debian/patches/relax-requirements.patch | 14 | ||||
| -rw-r--r-- | debian/patches/series | 1 | ||||
| -rw-r--r-- | debian/upstream/metadata | 1 | ||||
| -rw-r--r-- | debian/watch | 6 | ||||
| -rw-r--r-- | setup.py | 3 | ||||
| -rw-r--r-- | tests/test_restful.py | 14 |
17 files changed, 677 insertions, 552 deletions
@@ -83,7 +83,7 @@ celerybeat-schedule .env # virtualenv -.venv +.venv* venv/ ENV/ diff --git a/.travis.yml b/.travis.yml index 07ac867..8a3d6f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,12 @@ +dist: bionic +arch: arm64 language: python -python: - - "3.5" - - "2.7" +matrix: + include: + - python: 3.8 + - python: 3.9 + - python: 3.11 before_install: - - pip install --upgrade setuptools pip + - pip install --upgrade setuptools pip install: python setup.py develop script: python setup.py test
\ No newline at end of file @@ -11,7 +11,9 @@ Library with functions to iterate through the Crossref API. How to Install -------------- -pip install crossrefapi +.. code-block:: shell + + pip install crossrefapi ---------- How to Use @@ -52,6 +54,9 @@ Sample Query ````` + +See valid parameters in :code:`Works.FIELDS_QUERY` + .. code-block:: python In [1]: from crossref.restful import Works @@ -118,9 +123,31 @@ Doi 'type': 'journal-article', 'volume': '32'} +Filter +`````` + +See valid parameters in :code:`Works.FILTER_VALIDATOR`. Replace :code:`.` with :code:`__` and :code:`-` with :code:`_` when using parameters. + +.. code-block:: python + + In [1] from cross.restful import Works + + In [2]: works = Works() + + In [3]: for i in works.filter(license__url='https://creativecommons.org/licenses/by', from_pub_date='2016').sample(5).select('title'): + ...: print(i) + ...: + {'title': ['Vers une économie circulaire... de proximité ? Une spatialité à géométrie variable']} + {'title': ['The stakeholders of the Olympic System']} + {'title': ["Un cas de compensation écologique dans le secteur minier : la réserve forestière Dékpa (Côte d'Ivoire) au secours des forêts et des populations locales"]} + {'title': ['A simple extension of FFT-based methods to strain gradient loadings - Application to the homogenization of beams and plates with linear and non-linear behaviors']} + {'title': ['Gestion des déchets ménagers dans la ville de Kinshasa : Enquête sur la perception des habitants et propositions']} + Select `````` +See valid parameters in :code:`Works.FIELDS_SELECT` + .. code-block:: python In [1]: from crossref.restful import Works @@ -317,11 +344,10 @@ Exemplifying the use of API Library to retrieve data from Journals endpoint. In [10]: journals.works('0102-311X').query('zika').query(author='Diniz').count() Out[10]: 1 - Base Methods ------------ -The base methods could be used compounded with with query, filter, sort, order and facet methods. +The base methods could be used along with the query, filter, sort, order and facet methods. Version ``````` @@ -339,8 +365,8 @@ This method returns the Crossref API version. Count ````` -This method returns the total of itens a query result should retrive. This method will not -iterate and retrieve through the API documents. This method will fetch 0 documents +This method returns the total number of items a query result should retrieve. This method will not +iterate through and retrieve the API documents. This method will fetch 0 documents and retrieve the value of **total-result** attribute. .. code-block:: python @@ -461,10 +487,8 @@ to setup an Etiquette object to be used in the http requests. {'DOI': '10.1016/s0021-9290(17)30138-0'} {'DOI': '10.1109/ancs.2011.11'} - Voilá!!! The requests made for the Crossref API, were made setting the user-agent as: 'My Project Name/0.2alpha (https://myalphaproject.com; mailto:anonymous@myalphaproject.com) BasedOn: CrossrefAPI/1.1.0' - Depositing Metadata to Crossref ------------------------------- diff --git a/crossref/__init__.py b/crossref/__init__.py index b2e8177..fdc685f 100644 --- a/crossref/__init__.py +++ b/crossref/__init__.py @@ -1 +1 @@ -VERSION = '1.4.0' +VERSION = '1.5.2' diff --git a/crossref/restful.py b/crossref/restful.py index 048ad0c..f8016b7 100644 --- a/crossref/restful.py +++ b/crossref/restful.py @@ -1,11 +1,8 @@ # coding: utf-8 import requests -import json from time import sleep -from datetime import datetime, timedelta - from crossref import validators, VERSION LIMIT = 100 @@ -29,40 +26,55 @@ class UrlSyntaxError(CrossrefAPIError, ValueError): class HTTPRequest(object): - THROTTLING_TUNNING_TIME = 600 - def __init__(self, throttle=True): self.throttle = throttle - self.rate_limits = { - 'X-Rate-Limit-Limit': 50, - 'X-Rate-Limit-Interval': 1 - } + self.rate_limits = {"x-rate-limit-limit": 50, "x-rate-limit-interval": 1} def _update_rate_limits(self, headers): - self.rate_limits['X-Rate-Limit-Limit'] = int(headers.get('X-Rate-Limit-Limit', 50)) + try: + self.rate_limits["x-rate-limit-limit"] = int(headers.get("x-rate-limit-limit", 50)) + except ValueError: + # Assume the default value in case the parameter can not be parsed + # Waiting for definitions in https://github.com/fabiobatalha/crossrefapi/issues/38 + pass + + try: + interval_value = int(headers.get("x-rate-limit-interval", "1s")[:-1]) + except ValueError: + # Assume the default value in case the parameter can not be parsed + # Waiting for definitions in https://github.com/fabiobatalha/crossrefapi/issues/38 + pass - interval_value = int(headers.get('X-Rate-Limit-Interval', '1s')[:-1]) - interval_scope = headers.get('X-Rate-Limit-Interval', '1s')[-1] + interval_scope = headers.get("x-rate-limit-interval", "1s")[-1] - if interval_scope == 'm': + if interval_scope == "m": interval_value = interval_value * 60 - if interval_scope == 'h': + if interval_scope == "h": interval_value = interval_value * 60 * 60 - self.rate_limits['X-Rate-Limit-Interval'] = interval_value + self.rate_limits["x-rate-limit-interval"] = interval_value @property def throttling_time(self): - return self.rate_limits['X-Rate-Limit-Interval'] / self.rate_limits['X-Rate-Limit-Limit'] - - def do_http_request(self, method, endpoint, data=None, files=None, timeout=100, only_headers=False, custom_header=None): + return self.rate_limits["x-rate-limit-interval"] / self.rate_limits["x-rate-limit-limit"] + + def do_http_request( + self, + method, + endpoint, + data=None, + files=None, + timeout=100, + only_headers=False, + custom_header=None, + ): if only_headers is True: return requests.head(endpoint) - if method == 'post': + if method == "post": action = requests.post else: action = requests.get @@ -70,8 +82,8 @@ class HTTPRequest(object): if custom_header: headers = custom_header else: - headers = {'user-agent': str(Etiquette())} - if method == 'post': + headers = {"user-agent": str(Etiquette())} + if method == "post": result = action(endpoint, data=data, files=files, timeout=timeout, headers=headers) else: result = action(endpoint, params=data, timeout=timeout, headers=headers) @@ -85,14 +97,19 @@ class HTTPRequest(object): def build_url_endpoint(endpoint, context=None): - endpoint = '/'.join([i for i in [context, endpoint] if i]) + endpoint = "/".join([i for i in [context, endpoint] if i]) - return 'https://%s/%s' % (API, endpoint) + return "https://%s/%s" % (API, endpoint) class Etiquette: - - def __init__(self, application_name='undefined', application_version='undefined', application_url='undefined', contact_email='anonymous'): + def __init__( + self, + application_name="undefined", + application_version="undefined", + application_url="undefined", + contact_email="anonymous", + ): self.application_name = application_name self.application_version = application_version self.application_url = application_url @@ -100,12 +117,12 @@ class Etiquette: def __str__(self): - return '%s/%s (%s; mailto:%s) BasedOn: CrossrefAPI/%s' % ( + return "%s/%s (%s; mailto:%s) BasedOn: CrossrefAPI/%s" % ( self.application_name, self.application_version, self.application_url, self.contact_email, - VERSION + VERSION, ) @@ -113,16 +130,26 @@ class Endpoint: CURSOR_AS_ITER_METHOD = False - def __init__(self, request_url=None, request_params=None, context=None, etiquette=None, throttle=True, crossref_plus_token=None): + def __init__( + self, + request_url=None, + request_params=None, + context=None, + etiquette=None, + throttle=True, + crossref_plus_token=None, + timeout=30, + ): self.do_http_request = HTTPRequest(throttle=throttle).do_http_request self.etiquette = etiquette or Etiquette() - self.custom_header = {'user-agent': str(self.etiquette)} + self.custom_header = {"user-agent": str(self.etiquette)} self.crossref_plus_token = crossref_plus_token if crossref_plus_token: self.custom_header["Crossref-Plus-API-Token"] = self.crossref_plus_token self.request_url = request_url or build_url_endpoint(self.ENDPOINT, context) self.request_params = request_params or dict() - self.context = context or '' + self.context = context or "" + self.timeout = timeout @property def _rate_limits(self): @@ -130,27 +157,28 @@ class Endpoint: request_url = str(self.request_url) result = self.do_http_request( - 'get', + "get", request_url, only_headers=True, custom_header=self.custom_header, - throttle=False + timeout=self.timeout, + throttle=False, ) rate_limits = { - 'X-Rate-Limit-Limit': result.headers.get('X-Rate-Limit-Limit', 'undefined'), - 'X-Rate-Limit-Interval': result.headers.get('X-Rate-Limit-Interval', 'undefined') + "x-rate-limit-limit": result.headers.get("x-rate-limit-limit", "undefined"), + "x-rate-limit-interval": result.headers.get("x-rate-limit-interval", "undefined"), } return rate_limits def _escaped_pagging(self): - escape_pagging = ['offset', 'rows'] + escape_pagging = ["offset", "rows"] request_params = dict(self.request_params) for item in escape_pagging: try: - del(request_params[item]) + del request_params[item] except KeyError: pass @@ -159,32 +187,33 @@ class Endpoint: @property def version(self): """ - This attribute retrieve the API version. + This attribute retrieve the API version. - >>> Works().version - '1.0.0' + >>> Works().version + '1.0.0' """ request_params = dict(self.request_params) request_url = str(self.request_url) result = self.do_http_request( - 'get', + "get", request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ).json() - return result['message-version'] + return result["message-version"] @property def x_rate_limit_limit(self): - return self._rate_limits.get('X-Rate-Limit-Limit', 'undefined') + return self._rate_limits.get("x-rate-limit-limit", "undefined") @property def x_rate_limit_interval(self): - return self._rate_limits.get('X-Rate-Limit-Interval', 'undefined') + return self._rate_limits.get("x-rate-limit-interval", "undefined") def count(self): """ @@ -206,16 +235,17 @@ class Endpoint: """ request_params = dict(self.request_params) request_url = str(self.request_url) - request_params['rows'] = 0 + request_params["rows"] = 0 result = self.do_http_request( - 'get', + "get", request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ).json() - return int(result['message']['total-results']) + return int(result["message"]["total-results"]) @property def url(self): @@ -240,301 +270,312 @@ class Endpoint: request_params = self._escaped_pagging() sorted_request_params = sorted([(k, v) for k, v in request_params.items()]) - req = requests.Request( - 'get', self.request_url, params=sorted_request_params).prepare() + req = requests.Request("get", self.request_url, params=sorted_request_params).prepare() return req.url - def all(self): + def all(self, request_params: dict = None): context = str(self.context) request_url = build_url_endpoint(self.ENDPOINT, context) - request_params = {} - return iter(self.__class__(request_url, request_params, context, etiquette=self.etiquette, crossref_plus_token=self.crossref_plus_token)) + if request_params is None: + request_params = {} + + return iter( + self.__class__( + request_url=request_url, + request_params=request_params, + context=context, + etiquette=self.etiquette, + crossref_plus_token=self.crossref_plus_token, + timeout=self.timeout, + ) + ) def __iter__(self): request_url = str(self.request_url) - if 'sample' in self.request_params: + if "sample" in self.request_params: request_params = self._escaped_pagging() result = self.do_http_request( - 'get', + "get", self.request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: - raise StopIteration() + return result = result.json() - for item in result['message']['items']: + for item in result["message"]["items"]: yield item return if self.CURSOR_AS_ITER_METHOD is True: request_params = dict(self.request_params) - request_params['cursor'] = '*' - request_params['rows'] = LIMIT + request_params["cursor"] = "*" + request_params["rows"] = LIMIT while True: result = self.do_http_request( - 'get', + "get", request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: - raise StopIteration() + return result = result.json() - if len(result['message']['items']) == 0: + if len(result["message"]["items"]) == 0: return - for item in result['message']['items']: + for item in result["message"]["items"]: yield item - request_params['cursor'] = result['message']['next-cursor'] + request_params["cursor"] = result["message"]["next-cursor"] else: request_params = dict(self.request_params) - request_params['offset'] = 0 - request_params['rows'] = LIMIT + request_params["offset"] = 0 + request_params["rows"] = LIMIT while True: result = self.do_http_request( - 'get', + "get", request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: - raise StopIteration() + return result = result.json() - if len(result['message']['items']) == 0: + if len(result["message"]["items"]) == 0: return - for item in result['message']['items']: + for item in result["message"]["items"]: yield item - request_params['offset'] += LIMIT + 1 + request_params["offset"] += LIMIT - if request_params['offset'] >= MAXOFFSET: - raise MaxOffsetError( - 'Offset exceded the max offset of %d', - MAXOFFSET - ) + if request_params["offset"] >= MAXOFFSET: + raise MaxOffsetError("Offset exceded the max offset of %d", MAXOFFSET) class Works(Endpoint): CURSOR_AS_ITER_METHOD = True - ENDPOINT = 'works' + ENDPOINT = "works" - ORDER_VALUES = ('asc', 'desc', '1', '-1') + ORDER_VALUES = ("asc", "desc", "1", "-1") SORT_VALUES = ( - 'created', - 'deposited', - 'indexed', - 'is-referenced-by-count', - 'issued', - 'published', - 'published-online', - 'published-print', - 'references-count', - 'relevance', - 'score', - 'submitted', - 'updated' + "created", + "deposited", + "indexed", + "is-referenced-by-count", + "issued", + "published", + "published-online", + "published-print", + "references-count", + "relevance", + "score", + "submitted", + "updated", ) FIELDS_QUERY = ( - 'affiliation', - 'author', - 'bibliographic', - 'chair', - 'container_title', - 'contributor', - 'editor', - 'event_acronym', - 'event_location', - 'event_name', - 'event_sponsor', - 'event_theme', - 'funder_name', - 'publisher_location', - 'publisher_name', - 'translator' + "affiliation", + "author", + "bibliographic", + "chair", + "container_title", + "contributor", + "editor", + "event_acronym", + "event_location", + "event_name", + "event_sponsor", + "event_theme", + "funder_name", + "publisher_location", + "publisher_name", + "translator", ) FIELDS_SELECT = ( - 'abstract', - 'URL', - 'member', - 'posted', - 'score', - 'created', - 'degree', - 'update-policy', - 'short-title', - 'license', - 'ISSN', - 'container-title', - 'issued', - 'update-to', - 'issue', - 'prefix', - 'approved', - 'indexed', - 'article-number', - 'clinical-trial-number', - 'accepted', - 'author', - 'group-title', - 'DOI', - 'is-referenced-by-count', - 'updated-by', - 'event', - 'chair', - 'standards-body', - 'original-title', - 'funder', - 'translator', - 'archive', - 'published-print', - 'alternative-id', - 'subject', - 'subtitle', - 'published-online', - 'publisher-location', - 'content-domain', - 'reference', - 'title', - 'link', - 'type', - 'publisher', - 'volume', - 'references-count', - 'ISBN', - 'issn-type', - 'assertion', - 'deposited', - 'page', - 'content-created', - 'short-container-title', - 'relation', - 'editor' + "DOI", + "ISBN", + "ISSN", + "URL", + "abstract", + "accepted", + "alternative-id", + "approved", + "archive", + "article-number", + "assertion", + "author", + "chair", + "clinical-trial-number", + "container-title", + "content-created", + "content-domain", + "created", + "degree", + "deposited", + "editor", + "event", + "funder", + "group-title", + "indexed", + "is-referenced-by-count", + "issn-type", + "issue", + "issued", + "license", + "link", + "member", + "original-title", + "page", + "posted", + "prefix", + "published", + "published-online", + "published-print", + "publisher", + "publisher-location", + "reference", + "references-count", + "relation", + "score", + "short-container-title", + "short-title", + "standards-body", + "subject", + "subtitle", + "title", + "translator", + "type", + "update-policy", + "update-to", + "updated-by", + "volume" ) FILTER_VALIDATOR = { - 'alternative_id': None, - 'archive': validators.archive, - 'article_number': None, - 'assertion': None, - 'assertion-group': None, - 'award.funder': None, - 'award.number': None, - 'category-name': None, - 'clinical-trial-number': None, - 'container-title': None, - 'content-domain': None, - 'directory': validators.directory, - 'doi': None, - 'from-accepted-date': validators.is_date, - 'from-created-date': validators.is_date, - 'from-deposit-date': validators.is_date, - 'from-event-end-date': validators.is_date, - 'from-event-start-date': validators.is_date, - 'from-index-date': validators.is_date, - 'from-issued-date': validators.is_date, - 'from-online-pub-date': validators.is_date, - 'from-posted-date': validators.is_date, - 'from-print-pub-date': validators.is_date, - 'from-pub-date': validators.is_date, - 'from-update-date': validators.is_date, - 'full-text.application': None, - 'full-text.type': None, - 'full-text.version': None, - 'funder': None, - 'funder-doi-asserted-by': None, - 'group-title': None, - 'has-abstract': validators.is_bool, - 'has-affiliation': validators.is_bool, - 'has-archive': validators.is_bool, - 'has-assertion': validators.is_bool, - 'has-authenticated-orcid': validators.is_bool, - 'has-award': validators.is_bool, - 'has-clinical-trial-number': validators.is_bool, - 'has-content-domain': validators.is_bool, - 'has-domain-restriction': validators.is_bool, - 'has-event': validators.is_bool, - 'has-full-text': validators.is_bool, - 'has-funder': validators.is_bool, - 'has-funder-doi': validators.is_bool, - 'has-license': validators.is_bool, - 'has-orcid': validators.is_bool, - 'has-references': validators.is_bool, - 'has-relation': validators.is_bool, - 'has-update': validators.is_bool, - 'has-update-policy': validators.is_bool, - 'is-update': validators.is_bool, - 'isbn': None, - 'issn': None, - 'license.delay': validators.is_integer, - 'license.url': None, - 'license.version': None, - 'location': None, - 'member': validators.is_integer, - 'orcid': None, - 'prefix': None, - 'relation.object': None, - 'relation.object-type': None, - 'relation.type': None, - 'type': validators.document_type, - 'type-name': None, - 'until-accepted-date': validators.is_date, - 'until-created-date': validators.is_date, - 'until-deposit-date': validators.is_date, - 'until-event-end-date': validators.is_date, - 'until-event-start-date': validators.is_date, - 'until-index-date': validators.is_date, - 'until-issued-date': validators.is_date, - 'until-online-pub-date': validators.is_date, - 'until-posted-date': validators.is_date, - 'until-print-pub-date': validators.is_date, - 'until-pub-date': validators.is_date, - 'until-update-date': validators.is_date, - 'update-type': None, - 'updates': None - } + "alternative_id": None, + "archive": validators.archive, + "article_number": None, + "assertion": None, + "assertion-group": None, + "award.funder": None, + "award.number": None, + "category-name": None, + "clinical-trial-number": None, + "container-title": None, + "content-domain": None, + "directory": validators.directory, + "doi": None, + "from-accepted-date": validators.is_date, + "from-created-date": validators.is_date, + "from-deposit-date": validators.is_date, + "from-event-end-date": validators.is_date, + "from-event-start-date": validators.is_date, + "from-index-date": validators.is_date, + "from-issued-date": validators.is_date, + "from-online-pub-date": validators.is_date, + "from-posted-date": validators.is_date, + "from-print-pub-date": validators.is_date, + "from-pub-date": validators.is_date, + "from-update-date": validators.is_date, + "full-text.application": None, + "full-text.type": None, + "full-text.version": None, + "funder": None, + "funder-doi-asserted-by": None, + "group-title": None, + "has-abstract": validators.is_bool, + "has-affiliation": validators.is_bool, + "has-archive": validators.is_bool, + "has-assertion": validators.is_bool, + "has-authenticated-orcid": validators.is_bool, + "has-award": validators.is_bool, + "has-clinical-trial-number": validators.is_bool, + "has-content-domain": validators.is_bool, + "has-domain-restriction": validators.is_bool, + "has-event": validators.is_bool, + "has-full-text": validators.is_bool, + "has-funder": validators.is_bool, + "has-funder-doi": validators.is_bool, + "has-license": validators.is_bool, + "has-orcid": validators.is_bool, + "has-references": validators.is_bool, + "has-relation": validators.is_bool, + "has-update": validators.is_bool, + "has-update-policy": validators.is_bool, + "is-update": validators.is_bool, + "isbn": None, + "issn": None, + "license.delay": validators.is_integer, + "license.url": None, + "license.version": None, + "location": None, + "member": validators.is_integer, + "orcid": None, + "prefix": None, + "relation.object": None, + "relation.object-type": None, + "relation.type": None, + "type": validators.document_type, + "type-name": None, + "until-accepted-date": validators.is_date, + "until-created-date": validators.is_date, + "until-deposit-date": validators.is_date, + "until-event-end-date": validators.is_date, + "until-event-start-date": validators.is_date, + "until-index-date": validators.is_date, + "until-issued-date": validators.is_date, + "until-online-pub-date": validators.is_date, + "until-posted-date": validators.is_date, + "until-print-pub-date": validators.is_date, + "until-pub-date": validators.is_date, + "until-update-date": validators.is_date, + "update-type": None, + "updates": None, + } FACET_VALUES = { - 'archive': None, - 'affiliation': None, - 'assertion': None, - 'assertion-group': None, - 'category-name': None, - 'container-title': 1000, - 'license': None, - 'funder-doi': None, - 'funder-name': None, - 'issn': 1000, - 'orcid': 1000, - 'published': None, - 'publisher-name': None, - 'relation-type': None, - 'source': None, - 'type-name': None, - 'update-type': None + "archive": None, + "affiliation": None, + "assertion": None, + "assertion-group": None, + "category-name": None, + "container-title": 1000, + "license": None, + "funder-doi": None, + "funder-name": None, + "issn": 1000, + "orcid": 1000, + "published": None, + "publisher-name": None, + "relation-type": None, + "source": None, + "type-name": None, + "update-type": None, } - def order(self, order='asc'): + def order(self, order="asc"): """ This method retrieve an iterable object that implements the method __iter__. The arguments given will compose the parameters in the @@ -580,15 +621,19 @@ class Works(Endpoint): if order not in self.ORDER_VALUES: raise UrlSyntaxError( - 'Sort order specified as %s but must be one of: %s' % ( - str(order), - ', '.join(self.ORDER_VALUES) - ) + "Sort order specified as %s but must be one of: %s" + % (str(order), ", ".join(self.ORDER_VALUES)) ) - request_params['order'] = order + request_params["order"] = order - return self.__class__(request_url, request_params, context, self.etiquette) + return self.__class__( + request_url=request_url, + request_params=request_params, + context=context, + etiquette=self.etiquette, + timeout=self.timeout, + ) def select(self, *args): """ @@ -666,25 +711,29 @@ class Works(Endpoint): select_args += [i.strip() for i in item] if isinstance(item, str): - select_args += [i.strip() for i in item.split(',')] + select_args += [i.strip() for i in item.split(",")] invalid_select_args = set(select_args) - set(self.FIELDS_SELECT) if len(invalid_select_args) != 0: raise UrlSyntaxError( - 'Select field\'s specified as (%s) but must be one of: %s' % ( - ', '.join(invalid_select_args), - ', '.join(self.FIELDS_SELECT) - ) + "Select field's specified as (%s) but must be one of: %s" + % (", ".join(invalid_select_args), ", ".join(self.FIELDS_SELECT)) ) - request_params['select'] = ','.join( - sorted([i for i in set(request_params.get('select', '').split(',') + select_args) if i]) + request_params["select"] = ",".join( + sorted([i for i in set(request_params.get("select", "").split(",") + select_args) if i]) ) - return self.__class__(request_url, request_params, context, self.etiquette) + return self.__class__( + request_url=request_url, + request_params=request_params, + context=context, + etiquette=self.etiquette, + timeout=self.timeout, + ) - def sort(self, sort='score'): + def sort(self, sort="score"): """ This method retrieve an iterable object that implements the method __iter__. The arguments given will compose the parameters in the @@ -730,15 +779,19 @@ class Works(Endpoint): if sort not in self.SORT_VALUES: raise UrlSyntaxError( - 'Sort field specified as %s but must be one of: %s' % ( - str(sort), - ', '.join(self.SORT_VALUES) - ) + "Sort field specified as %s but must be one of: %s" + % (str(sort), ", ".join(self.SORT_VALUES)) ) - request_params['sort'] = sort + request_params["sort"] = sort - return self.__class__(request_url, request_params, context, self.etiquette) + return self.__class__( + request_url=request_url, + request_params=request_params, + context=context, + etiquette=self.etiquette, + timeout=self.timeout, + ) def filter(self, **kwargs): """ @@ -749,7 +802,8 @@ class Works(Endpoint): This method can be used compounded and recursively with query, filter, order, sort and facet methods. - kwargs: valid FILTER_VALIDATOR arguments. + kwargs: valid FILTER_VALIDATOR arguments. Replace `.` with `__` and + `-` with `_` when using parameters. return: iterable object of Works metadata @@ -769,48 +823,59 @@ class Works(Endpoint): request_params = dict(self.request_params) for fltr, value in kwargs.items(): - decoded_fltr = fltr.replace('__', '.').replace('_', '-') + decoded_fltr = fltr.replace("__", ".").replace("_", "-") if decoded_fltr not in self.FILTER_VALIDATOR.keys(): raise UrlSyntaxError( - 'Filter %s specified but there is no such filter for this route. Valid filters for this route are: %s' % ( - str(decoded_fltr), - ', '.join(self.FILTER_VALIDATOR.keys()) - ) + "Filter %s specified but there is no such filter for this route. Valid filters for this route are: %s" + % (str(decoded_fltr), ", ".join(self.FILTER_VALIDATOR.keys())) ) if self.FILTER_VALIDATOR[decoded_fltr] is not None: self.FILTER_VALIDATOR[decoded_fltr](str(value)) - if 'filter' not in request_params: - request_params['filter'] = decoded_fltr + ':' + str(value) + if "filter" not in request_params: + request_params["filter"] = decoded_fltr + ":" + str(value) else: - request_params['filter'] += ',' + decoded_fltr + ':' + str(value) - - return self.__class__(request_url, request_params, context, self.etiquette) + request_params["filter"] += "," + decoded_fltr + ":" + str(value) + + return self.__class__( + request_url=request_url, + request_params=request_params, + context=context, + etiquette=self.etiquette, + timeout=self.timeout, + ) def facet(self, facet_name, facet_count=100): context = str(self.context) request_url = build_url_endpoint(self.ENDPOINT, context) request_params = dict(self.request_params) - request_params['rows'] = 0 + request_params["rows"] = 0 if facet_name not in self.FACET_VALUES.keys(): - raise UrlSyntaxError('Facet %s specified but there is no such facet for this route. Valid facets for this route are: *, affiliation, funder-name, funder-doi, publisher-name, orcid, container-title, assertion, archive, update-type, issn, published, source, type-name, license, category-name, relation-type, assertion-group' % - str(facet_name), - ', '.join(self.FACET_VALUES.keys()) - ) + raise UrlSyntaxError( + "Facet %s specified but there is no such facet for this route. Valid facets for this route are: *, affiliation, funder-name, funder-doi, publisher-name, orcid, container-title, assertion, archive, update-type, issn, published, source, type-name, license, category-name, relation-type, assertion-group" + % str(facet_name), + ", ".join(self.FACET_VALUES.keys()), + ) - facet_count = self.FACET_VALUES[facet_name] if self.FACET_VALUES[facet_name] is not None and self.FACET_VALUES[facet_name] <= facet_count else facet_count + facet_count = ( + self.FACET_VALUES[facet_name] + if self.FACET_VALUES[facet_name] is not None + and self.FACET_VALUES[facet_name] <= facet_count + else facet_count + ) - request_params['facet'] = '%s:%s' % (facet_name, facet_count) + request_params["facet"] = "%s:%s" % (facet_name, facet_count) result = self.do_http_request( - 'get', + "get", request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ).json() - return result['message']['facets'] + return result["message"]["facets"] def query(self, *args, **kwargs): """ @@ -850,18 +915,21 @@ class Works(Endpoint): request_params = dict(self.request_params) if args: - request_params['query'] = ' '.join([str(i) for i in args]) + request_params["query"] = " ".join([str(i) for i in args]) for field, value in kwargs.items(): if field not in self.FIELDS_QUERY: raise UrlSyntaxError( - 'Field query %s specified but there is no such field query for this route. Valid field queries for this route are: %s' % ( - str(field), ', '.join(self.FIELDS_QUERY) - ) + "Field query %s specified but there is no such field query for this route. Valid field queries for this route are: %s" + % (str(field), ", ".join(self.FIELDS_QUERY)) ) - request_params['query.%s' % field.replace('_', '-')] = value + request_params["query.%s" % field.replace("_", "-")] = value - return self.__class__(request_url, request_params, context, self.etiquette) + return self.__class__(request_url=request_url, + request_params=request_params, + context=context, + etiquette=self.etiquette, + timeout=self.timeout) def sample(self, sample_size=20): """ @@ -889,16 +957,24 @@ class Works(Endpoint): try: if sample_size > 100: raise UrlSyntaxError( - 'Integer specified as %s but must be a positive integer less than or equal to 100.' % str(sample_size) + "Integer specified as %s but must be a positive integer less than or equal to 100." + % str(sample_size) ) except TypeError: raise UrlSyntaxError( - 'Integer specified as %s but must be a positive integer less than or equal to 100.' % str(sample_size) + "Integer specified as %s but must be a positive integer less than or equal to 100." + % str(sample_size) ) - request_params['sample'] = sample_size + request_params["sample"] = sample_size - return self.__class__(request_url, request_params, context, self.etiquette) + return self.__class__( + request_url=request_url, + request_params=request_params, + context=context, + etiquette=self.etiquette, + timeout=self.timeout, + ) def doi(self, doi, only_message=True): """ @@ -941,22 +1017,21 @@ class Works(Endpoint): {'name': 'Universidade de São Paulo, Brasil'}], 'family': 'Moraes-Filho', 'given': 'Joaquim Prado P.'}], 'score': 1.0, 'issue': '2'} """ - request_url = build_url_endpoint( - '/'.join([self.ENDPOINT, doi]) - ) + request_url = build_url_endpoint("/".join([self.ENDPOINT, doi])) request_params = {} result = self.do_http_request( - 'get', + "get", request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: return result = result.json() - return result['message'] if only_message is True else result + return result["message"] if only_message is True else result def agency(self, doi, only_message=True): """ @@ -973,16 +1048,15 @@ class Works(Endpoint): >>> works.agency('10.1590/S0004-28032013005000001') {'DOI': '10.1590/s0004-28032013005000001', 'agency': {'label': 'CrossRef', 'id': 'crossref'}} """ - request_url = build_url_endpoint( - '/'.join([self.ENDPOINT, doi, 'agency']) - ) + request_url = build_url_endpoint("/".join([self.ENDPOINT, doi, "agency"])) request_params = {} result = self.do_http_request( - 'get', + "get", request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: @@ -990,7 +1064,7 @@ class Works(Endpoint): result = result.json() - return result['message'] if only_message is True else result + return result["message"] if only_message is True else result def doi_exists(self, doi): """ @@ -1013,17 +1087,16 @@ class Works(Endpoint): >>> works.doi_exists('10.1590/S0004-28032013005000001_invalid_doi') False """ - request_url = build_url_endpoint( - '/'.join([self.ENDPOINT, doi]) - ) + request_url = build_url_endpoint("/".join([self.ENDPOINT, doi])) request_params = {} result = self.do_http_request( - 'get', + "get", request_url, data=request_params, only_headers=True, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: @@ -1036,10 +1109,10 @@ class Funders(Endpoint): CURSOR_AS_ITER_METHOD = False - ENDPOINT = 'funders' + ENDPOINT = "funders" FILTER_VALIDATOR = { - 'location': None, + "location": None, } def query(self, *args): @@ -1067,9 +1140,14 @@ class Funders(Endpoint): request_params = dict(self.request_params) if args: - request_params['query'] = ' '.join([str(i) for i in args]) + request_params["query"] = " ".join([str(i) for i in args]) - return self.__class__(request_url, request_params, self.etiquette) + return self.__class__( + request_url=request_url, + request_params=request_params, + etiquette=self.etiquette, + timeout=self.timeout, + ) def filter(self, **kwargs): """ @@ -1101,24 +1179,28 @@ class Funders(Endpoint): request_params = dict(self.request_params) for fltr, value in kwargs.items(): - decoded_fltr = fltr.replace('__', '.').replace('_', '-') + decoded_fltr = fltr.replace("__", ".").replace("_", "-") if decoded_fltr not in self.FILTER_VALIDATOR.keys(): raise UrlSyntaxError( - 'Filter %s specified but there is no such filter for this route. Valid filters for this route are: %s' % ( - str(decoded_fltr), - ', '.join(self.FILTER_VALIDATOR.keys()) - ) + "Filter %s specified but there is no such filter for this route. Valid filters for this route are: %s" + % (str(decoded_fltr), ", ".join(self.FILTER_VALIDATOR.keys())) ) if self.FILTER_VALIDATOR[decoded_fltr] is not None: self.FILTER_VALIDATOR[decoded_fltr](str(value)) - if 'filter' not in request_params: - request_params['filter'] = decoded_fltr + ':' + str(value) + if "filter" not in request_params: + request_params["filter"] = decoded_fltr + ":" + str(value) else: - request_params['filter'] += ',' + decoded_fltr + ':' + str(value) - - return self.__class__(request_url, request_params, context, self.etiquette) + request_params["filter"] += "," + decoded_fltr + ":" + str(value) + + return self.__class__( + request_url=request_url, + request_params=request_params, + context=context, + etiquette=self.etiquette, + timeout=self.timeout, + ) def funder(self, funder_id, only_message=True): """funder @@ -1138,16 +1220,15 @@ class Funders(Endpoint): 'descendant-work-count': 3, 'descendants': [], 'name': 'ABBEY AWARDS', 'id': '501100000314', 'tokens': ['abbey', 'awards', 'abbey']} """ - request_url = build_url_endpoint( - '/'.join([self.ENDPOINT, str(funder_id)]) - ) + request_url = build_url_endpoint("/".join([self.ENDPOINT, str(funder_id)])) request_params = {} result = self.do_http_request( - 'get', + "get", request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: @@ -1155,7 +1236,7 @@ class Funders(Endpoint): result = result.json() - return result['message'] if only_message is True else result + return result["message"] if only_message is True else result def funder_exists(self, funder_id): """ @@ -1178,17 +1259,16 @@ class Funders(Endpoint): >>> funders.funder_exists('999999999999') False """ - request_url = build_url_endpoint( - '/'.join([self.ENDPOINT, str(funder_id)]) - ) + request_url = build_url_endpoint("/".join([self.ENDPOINT, str(funder_id)])) request_params = {} result = self.do_http_request( - 'get', + "get", request_url, data=request_params, only_headers=True, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: @@ -1204,7 +1284,7 @@ class Funders(Endpoint): return: Works() """ - context = '%s/%s' % (self.ENDPOINT, str(funder_id)) + context = "%s/%s" % (self.ENDPOINT, str(funder_id)) return Works(context=context) @@ -1212,13 +1292,13 @@ class Members(Endpoint): CURSOR_AS_ITER_METHOD = False - ENDPOINT = 'members' + ENDPOINT = "members" FILTER_VALIDATOR = { - 'prefix': None, - 'has-public-references': validators.is_bool, - 'backfile-doi-count': validators.is_integer, - 'current-doi-count': validators.is_integer + "prefix": None, + "has-public-references": validators.is_bool, + "backfile-doi-count": validators.is_integer, + "current-doi-count": validators.is_integer, } def query(self, *args): @@ -1264,9 +1344,15 @@ class Members(Endpoint): request_params = dict(self.request_params) if args: - request_params['query'] = ' '.join([str(i) for i in args]) - - return self.__class__(request_url, request_params, context, self.etiquette) + request_params["query"] = " ".join([str(i) for i in args]) + + return self.__class__( + request_url=request_url, + request_params=request_params, + context=context, + etiquette=self.etiquette, + timeout=self.timeout, + ) def filter(self, **kwargs): """ @@ -1297,24 +1383,28 @@ class Members(Endpoint): request_params = dict(self.request_params) for fltr, value in kwargs.items(): - decoded_fltr = fltr.replace('__', '.').replace('_', '-') + decoded_fltr = fltr.replace("__", ".").replace("_", "-") if decoded_fltr not in self.FILTER_VALIDATOR.keys(): raise UrlSyntaxError( - 'Filter %s specified but there is no such filter for this route. Valid filters for this route are: %s' % ( - str(decoded_fltr), - ', '.join(self.FILTER_VALIDATOR.keys()) - ) + "Filter %s specified but there is no such filter for this route. Valid filters for this route are: %s" + % (str(decoded_fltr), ", ".join(self.FILTER_VALIDATOR.keys())) ) if self.FILTER_VALIDATOR[decoded_fltr] is not None: self.FILTER_VALIDATOR[decoded_fltr](str(value)) - if 'filter' not in request_params: - request_params['filter'] = decoded_fltr + ':' + str(value) + if "filter" not in request_params: + request_params["filter"] = decoded_fltr + ":" + str(value) else: - request_params['filter'] += ',' + decoded_fltr + ':' + str(value) - - return self.__class__(request_url, request_params, context, self.etiquette) + request_params["filter"] += "," + decoded_fltr + ":" + str(value) + + return self.__class__( + request_url=request_url, + request_params=request_params, + context=context, + etiquette=self.etiquette, + timeout=self.timeout, + ) def member(self, member_id, only_message=True): """ @@ -1357,16 +1447,15 @@ class Members(Endpoint): '10.1026'], 'last-status-check-time': 1496034132646, 'id': 101, 'tokens': ['hogrefe', 'publishing', 'group'], 'primary-name': 'Hogrefe Publishing Group'} """ - request_url = build_url_endpoint( - '/'.join([self.ENDPOINT, str(member_id)]) - ) + request_url = build_url_endpoint("/".join([self.ENDPOINT, str(member_id)])) request_params = {} result = self.do_http_request( - 'get', + "get", request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: @@ -1374,7 +1463,7 @@ class Members(Endpoint): result = result.json() - return result['message'] if only_message is True else result + return result["message"] if only_message is True else result def member_exists(self, member_id): """ @@ -1397,17 +1486,16 @@ class Members(Endpoint): >>> members.member_exists(88888) False """ - request_url = build_url_endpoint( - '/'.join([self.ENDPOINT, str(member_id)]) - ) + request_url = build_url_endpoint("/".join([self.ENDPOINT, str(member_id)])) request_params = {} result = self.do_http_request( - 'get', + "get", request_url, data=request_params, only_headers=True, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: @@ -1423,7 +1511,7 @@ class Members(Endpoint): return: Works() """ - context = '%s/%s' % (self.ENDPOINT, str(member_id)) + context = "%s/%s" % (self.ENDPOINT, str(member_id)) return Works(context=context) @@ -1431,7 +1519,7 @@ class Types(Endpoint): CURSOR_AS_ITER_METHOD = False - ENDPOINT = 'types' + ENDPOINT = "types" def type(self, type_id, only_message=True): """ @@ -1444,16 +1532,15 @@ class Types(Endpoint): >>> types.type('journal-article') {'label': 'Journal Article', 'id': 'journal-article'} """ - request_url = build_url_endpoint( - '/'.join([self.ENDPOINT, str(type_id)]) - ) + request_url = build_url_endpoint("/".join([self.ENDPOINT, str(type_id)])) request_params = {} result = self.do_http_request( - 'get', + "get", request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: @@ -1461,7 +1548,7 @@ class Types(Endpoint): result = result.json() - return result['message'] if only_message is True else result + return result["message"] if only_message is True else result def all(self): """ @@ -1486,18 +1573,19 @@ class Types(Endpoint): request_params = dict(self.request_params) result = self.do_http_request( - 'get', + "get", request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: - raise StopIteration() + return result = result.json() - for item in result['message']['items']: + for item in result["message"]["items"]: yield item def type_exists(self, type_id): @@ -1521,17 +1609,16 @@ class Types(Endpoint): >>> types.type_exists('unavailable type') False """ - request_url = build_url_endpoint( - '/'.join([self.ENDPOINT, str(type_id)]) - ) + request_url = build_url_endpoint("/".join([self.ENDPOINT, str(type_id)])) request_params = {} result = self.do_http_request( - 'get', + "get", request_url, data=request_params, only_headers=True, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: @@ -1547,7 +1634,7 @@ class Types(Endpoint): return: Works() """ - context = '%s/%s' % (self.ENDPOINT, str(type_id)) + context = "%s/%s" % (self.ENDPOINT, str(type_id)) return Works(context=context) @@ -1555,7 +1642,7 @@ class Prefixes(Endpoint): CURSOR_AS_ITER_METHOD = False - ENDPOINT = 'prefixes' + ENDPOINT = "prefixes" def prefix(self, prefix_id, only_message=True): """ @@ -1572,16 +1659,15 @@ class Prefixes(Endpoint): {'name': 'FapUNIFESP (SciELO)', 'member': 'http://id.crossref.org/member/530', 'prefix': 'http://id.crossref.org/prefix/10.1590'} """ - request_url = build_url_endpoint( - '/'.join([self.ENDPOINT, str(prefix_id)]) - ) + request_url = build_url_endpoint("/".join([self.ENDPOINT, str(prefix_id)])) request_params = {} result = self.do_http_request( - 'get', + "get", request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: @@ -1589,7 +1675,7 @@ class Prefixes(Endpoint): result = result.json() - return result['message'] if only_message is True else result + return result["message"] if only_message is True else result def works(self, prefix_id): """ @@ -1599,7 +1685,7 @@ class Prefixes(Endpoint): return: Works() """ - context = '%s/%s' % (self.ENDPOINT, str(prefix_id)) + context = "%s/%s" % (self.ENDPOINT, str(prefix_id)) return Works(context=context) @@ -1607,7 +1693,7 @@ class Journals(Endpoint): CURSOR_AS_ITER_METHOD = False - ENDPOINT = 'journals' + ENDPOINT = "journals" def query(self, *args): """ @@ -1635,9 +1721,15 @@ class Journals(Endpoint): request_params = dict(self.request_params) if args: - request_params['query'] = ' '.join([str(i) for i in args]) - - return self.__class__(request_url, request_params, context, self.etiquette) + request_params["query"] = " ".join([str(i) for i in args]) + + return self.__class__( + request_url=request_url, + request_params=request_params, + context=context, + etiquette=self.etiquette, + timeout=self.timeout, + ) def journal(self, issn, only_message=True): """ @@ -1656,16 +1748,15 @@ class Journals(Endpoint): 'flags': None, 'breakdowns': None, 'ISSN': ['2320-4664', '2277-338X'], 'title': 'International Journal of Medical Science and Public Health'} """ - request_url = build_url_endpoint( - '/'.join([self.ENDPOINT, str(issn)]) - ) + request_url = build_url_endpoint("/".join([self.ENDPOINT, str(issn)])) request_params = {} result = self.do_http_request( - 'get', + "get", request_url, data=request_params, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: @@ -1673,7 +1764,7 @@ class Journals(Endpoint): result = result.json() - return result['message'] if only_message is True else result + return result["message"] if only_message is True else result def journal_exists(self, issn): """ @@ -1697,17 +1788,16 @@ class Journals(Endpoint): >>> journals.journal_exists('9999-AAAA') False """ - request_url = build_url_endpoint( - '/'.join([self.ENDPOINT, str(issn)]) - ) + request_url = build_url_endpoint("/".join([self.ENDPOINT, str(issn)])) request_params = {} result = self.do_http_request( - 'get', + "get", request_url, data=request_params, only_headers=True, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) if result.status_code == 404: @@ -1724,24 +1814,22 @@ class Journals(Endpoint): return: Works() """ - context = '%s/%s' % (self.ENDPOINT, str(issn)) + context = "%s/%s" % (self.ENDPOINT, str(issn)) return Works(context=context) class Depositor(object): - - def __init__(self, prefix, api_user, api_key, etiquette=None, - use_test_server=False): + def __init__(self, prefix, api_user, api_key, etiquette=None, use_test_server=False): self.do_http_request = HTTPRequest(throttle=False).do_http_request self.etiquette = etiquette or Etiquette() - self.custom_header = {'user-agent': str(self.etiquette)} + self.custom_header = {"user-agent": str(self.etiquette)} self.prefix = prefix self.api_user = api_user self.api_key = api_key self.use_test_server = use_test_server def get_endpoint(self, verb): - subdomain = 'test' if self.use_test_server else 'doi' + subdomain = "test" if self.use_test_server else "doi" return "https://{}.crossref.org/servlet/{}".format(subdomain, verb) def register_doi(self, submission_id, request_xml): @@ -1756,30 +1844,28 @@ class Depositor(object): compliance with the Crossref Submission Schema. """ - endpoint = self.get_endpoint('deposit') + endpoint = self.get_endpoint("deposit") - files = { - 'mdFile': ('%s.xml' % submission_id, request_xml) - } + files = {"mdFile": ("%s.xml" % submission_id, request_xml)} params = { - 'operation': 'doMDUpload', - 'login_id': self.api_user, - 'login_passwd': self.api_key + "operation": "doMDUpload", + "login_id": self.api_user, + "login_passwd": self.api_key, } result = self.do_http_request( - 'post', + "post", endpoint, data=params, files=files, - timeout=10, - custom_header=self.custom_header + custom_header=self.custom_header, + timeout=self.timeout, ) return result - def request_doi_status_by_filename(self, file_name, data_type='result'): + def request_doi_status_by_filename(self, file_name, data_type="result"): """ This method retrieve the DOI requests status. @@ -1790,26 +1876,22 @@ class Depositor(object): result - retrieve a JSON with the status of the submission """ - endpoint = self.get_endpoint('submissionDownload') + endpoint = self.get_endpoint("submissionDownload") params = { - 'usr': self.api_user, - 'pwd': self.api_key, - 'file_name': file_name, - 'type': data_type + "usr": self.api_user, + "pwd": self.api_key, + "file_name": file_name, + "type": data_type, } result = self.do_http_request( - 'get', - endpoint, - data=params, - timeout=10, - custom_header=self.custom_header + "get", endpoint, data=params, custom_header=self.custom_header, timeout=self.timeout ) return result - def request_doi_status_by_batch_id(self, doi_batch_id, data_type='result'): + def request_doi_status_by_batch_id(self, doi_batch_id, data_type="result"): """ This method retrieve the DOI requests status. @@ -1820,21 +1902,17 @@ class Depositor(object): result - retrieve a XML with the status of the submission """ - endpoint = self.get_endpoint('submissionDownload') + endpoint = self.get_endpoint("submissionDownload") params = { - 'usr': self.api_user, - 'pwd': self.api_key, - 'doi_batch_id': doi_batch_id, - 'type': data_type + "usr": self.api_user, + "pwd": self.api_key, + "doi_batch_id": doi_batch_id, + "type": data_type, } result = self.do_http_request( - 'get', - endpoint, - data=params, - timeout=10, - custom_header=self.custom_header + "get", endpoint, data=params, custom_header=self.custom_header, timeout=self.timeout ) return result diff --git a/crossref/utils.py b/crossref/utils.py index 401c632..c17267e 100644 --- a/crossref/utils.py +++ b/crossref/utils.py @@ -1,10 +1,10 @@ # coding: utf-8 -truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1')) +truthy = frozenset(("t", "true", "y", "yes", "on", "1")) def asbool(s): - """ Return the boolean value ``True`` if the case-lowered value of string + """Return the boolean value ``True`` if the case-lowered value of string input ``s`` is a :term:`truthy string`. If ``s`` is already one of the boolean values ``True`` or ``False``, return it.""" if s is None: diff --git a/crossref/validators.py b/crossref/validators.py index b8b8b40..398eabf 100644 --- a/crossref/validators.py +++ b/crossref/validators.py @@ -5,109 +5,97 @@ from datetime import datetime def directory(value): - expected = ( - 'DOAJ' - ) + expected = "DOAJ" if str(value) in expected: return True - raise ValueError('Directory specified as %s but must be one of: %s' % ( - str(value), - ', '.join(expected) - ) + raise ValueError( + "Directory specified as %s but must be one of: %s" % (str(value), ", ".join(expected)) ) def archive(value): - expected = ( - 'Portico', - 'CLOCKSS', - 'DWT' - ) + expected = ("Portico", "CLOCKSS", "DWT") if str(value) in expected: return True - raise ValueError('Archive specified as %s but must be one of: %s' % ( - str(value), - ', '.join(expected) - ) + raise ValueError( + "Archive specified as %s but must be one of: %s" % (str(value), ", ".join(expected)) ) def document_type(value): expected = ( - 'book-section', - 'monograph', - 'report', - 'book-track', - 'journal-article', - 'book-part', - 'other', - 'book', - 'journal-volume', - 'book-set', - 'reference-entry', - 'proceedings-article', - 'journal', - 'component', - 'book-chapter', - 'report-series', - 'proceedings', - 'standard', - 'reference-book', - 'posted-content', - 'journal-issue', - 'dissertation', - 'dataset', - 'book-series', - 'edited-book', - 'standard-series' + "book-section", + "monograph", + "report", + "book-track", + "journal-article", + "book-part", + "other", + "book", + "journal-volume", + "book-set", + "reference-entry", + "proceedings-article", + "journal", + "component", + "book-chapter", + "report-series", + "proceedings", + "standard", + "reference-book", + "posted-content", + "journal-issue", + "dissertation", + "dataset", + "book-series", + "edited-book", + "standard-series", ) if str(value) in expected: return True - raise ValueError('Type specified as %s but must be one of: %s' % ( - str(value), - ', '.join(expected) - ) + raise ValueError( + "Type specified as %s but must be one of: %s" % (str(value), ", ".join(expected)) ) def is_bool(value): - expected = ['t', 'true', '1', 'f', 'false', '0'] + expected = ["t", "true", "1", "f", "false", "0"] if str(value) in expected: return True - raise ValueError('Boolean specified %s True but must be one of: %s' % ( - str(value), - ', '.join(expected) - ) + raise ValueError( + "Boolean specified %s True but must be one of: %s" % (str(value), ", ".join(expected)) ) def is_date(value): try: - datetime.strptime(value, '%Y') + datetime.strptime(value, "%Y") return True except ValueError: try: - datetime.strptime(value, '%Y-%m') + datetime.strptime(value, "%Y-%m") return True except ValueError: try: - datetime.strptime(value, '%Y-%m-%d') + datetime.strptime(value, "%Y-%m-%d") return True except: pass - raise ValueError('Date specified as %s but must be of the form: yyyy or yyyy-mm or yyyy-mm-dd ' % str(value)) + raise ValueError( + "Date specified as %s but must be of the form: yyyy or yyyy-mm or yyyy-mm-dd " % str(value) + ) def is_integer(value): @@ -119,4 +107,4 @@ def is_integer(value): except ValueError: pass - raise ValueError('Integer specified as %s but must be a positive integer.' % str(value)) + raise ValueError("Integer specified as %s but must be a positive integer." % str(value)) diff --git a/debian/changelog b/debian/changelog index 2e7a751..d6ff2e4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,14 @@ +python-crossrefapi (1.5.2-1) unstable; urgency=medium + + * New upstream version 1.5.2 + * Watch GitHub tags. + * Set upstream metadata fields: Repository. + * Update standards version to 4.6.2, no changes needed. + * Bump copyright years. + * Fix upstream name. + + -- Andrius Merkys <merkys@debian.org> Mon, 29 Jan 2024 06:55:49 -0500 + python-crossrefapi (1.5.0-3) unstable; urgency=medium [ Debian Janitor ] diff --git a/debian/control b/debian/control index 3bb145d..f72c817 100644 --- a/debian/control +++ b/debian/control @@ -10,7 +10,7 @@ Build-Depends: python3-all:any, python3-requests <!nocheck>, python3-setuptools, -Standards-Version: 4.6.1 +Standards-Version: 4.6.2 Homepage: https://github.com/fabiobatalha/crossrefapi Vcs-Browser: https://salsa.debian.org/python-team/packages/python-crossrefapi Vcs-Git: https://salsa.debian.org/python-team/packages/python-crossrefapi.git diff --git a/debian/copyright b/debian/copyright index 21d245f..2fd5370 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,5 +1,5 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: python-crossrefapi +Upstream-Name: crossrefapi Upstream-Contact: https://github.com/fabiobatalha/crossrefapi/issues Source: https://github.com/fabiobatalha/crossrefapi @@ -8,7 +8,7 @@ Copyright: 2017, Fabio Batalha License: BSD-2-Clause Files: debian/* -Copyright: 2021, Andrius Merkys <merkys@debian.org> +Copyright: 2021-2024, Andrius Merkys <merkys@debian.org> License: BSD-2-Clause License: BSD-2-Clause diff --git a/debian/patches/disable-network-tests.patch b/debian/patches/disable-network-tests.patch index 0dd0fd6..67af955 100644 --- a/debian/patches/disable-network-tests.patch +++ b/debian/patches/disable-network-tests.patch @@ -1,3 +1,6 @@ +Description: Disable network tests. +Author: Andrius Merkys <merkys@debian.org> +Forwarded: not-needed --- a/tests/test_restful.py +++ b/tests/test_restful.py @@ -23,6 +23,7 @@ diff --git a/debian/patches/relax-requirements.patch b/debian/patches/relax-requirements.patch new file mode 100644 index 0000000..e506691 --- /dev/null +++ b/debian/patches/relax-requirements.patch @@ -0,0 +1,14 @@ +Description: Relax overly strict version requirement. +Author: Andrius Merkys <merkys@debian.org> +Forwarded: not-needed +--- a/setup.py ++++ b/setup.py +@@ -5,7 +5,7 @@ + + install_requires = [ + 'requests>=2.31.0', +- 'urllib3==1.26.16', ++ 'urllib3>=1.26.16', + ] + + tests_require = [] diff --git a/debian/patches/series b/debian/patches/series index ac5717b..0e389e8 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1 +1,2 @@ disable-network-tests.patch +relax-requirements.patch diff --git a/debian/upstream/metadata b/debian/upstream/metadata index a82eabf..3d7d096 100644 --- a/debian/upstream/metadata +++ b/debian/upstream/metadata @@ -1,2 +1,3 @@ --- +Repository: https://github.com/fabiobatalha/crossrefapi.git Repository-Browse: https://github.com/fabiobatalha/crossrefapi diff --git a/debian/watch b/debian/watch index 17f6df2..d62b058 100644 --- a/debian/watch +++ b/debian/watch @@ -1,4 +1,4 @@ version=4 -opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%python-crossrefapi-$1.tar.gz%" \ - https://github.com/fabiobatalha/crossrefapi/releases \ - (?:.*?/)?v?(\d[\d.]*)\.tar\.gz +opts="filenamemangle=s%(?:.*?)?v?@ANY_VERSION@(@ARCHIVE_EXT@)%@PACKAGE@-$1$2%" \ + https://github.com/fabiobatalha/crossrefapi/tags \ + (?:.*?/)?v?@ANY_VERSION@@ARCHIVE_EXT@ @@ -4,7 +4,8 @@ from setuptools import setup, find_packages from crossref import VERSION install_requires = [ - 'requests>=2.11.1' + 'requests>=2.31.0', + 'urllib3>=1.26.16', ] tests_require = [] diff --git a/tests/test_restful.py b/tests/test_restful.py index cb12a8c..9457966 100644 --- a/tests/test_restful.py +++ b/tests/test_restful.py @@ -88,36 +88,36 @@ class HTTPRequestTest(unittest.TestCase): def test_default_rate_limits(self): - expected = {'X-Rate-Limit-Interval': 1, 'X-Rate-Limit-Limit': 50} + expected = {'x-rate-limit-interval': 1, 'x-rate-limit-limit': 50} self.assertEqual(self.httprequest.rate_limits, expected) def test_update_rate_limits_seconds(self): - headers = {'X-Rate-Limit-Interval': '2s', 'X-Rate-Limit-Limit': 50} + headers = {'x-rate-limit-interval': '2s', 'x-rate-limit-limit': 50} self.httprequest._update_rate_limits(headers) - expected = {'X-Rate-Limit-Interval': 2, 'X-Rate-Limit-Limit': 50} + expected = {'x-rate-limit-interval': 2, 'x-rate-limit-limit': 50} self.assertEqual(self.httprequest.rate_limits, expected) def test_update_rate_limits_minutes(self): - headers = {'X-Rate-Limit-Interval': '2m', 'X-Rate-Limit-Limit': 50} + headers = {'x-rate-limit-interval': '2m', 'x-rate-limit-limit': 50} self.httprequest._update_rate_limits(headers) - expected = {'X-Rate-Limit-Interval': 120, 'X-Rate-Limit-Limit': 50} + expected = {'x-rate-limit-interval': 120, 'x-rate-limit-limit': 50} self.assertEqual(self.httprequest.rate_limits, expected) def test_update_rate_limits_hours(self): - headers = {'X-Rate-Limit-Interval': '2h', 'X-Rate-Limit-Limit': 50} + headers = {'x-rate-limit-interval': '2h', 'x-rate-limit-limit': 50} self.httprequest._update_rate_limits(headers) - expected = {'X-Rate-Limit-Interval': 7200, 'X-Rate-Limit-Limit': 50} + expected = {'x-rate-limit-interval': 7200, 'x-rate-limit-limit': 50} self.assertEqual(self.httprequest.rate_limits, expected) |
