summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrius Merkys <merkys@debian.org>2024-01-29 06:55:49 -0500
committergit-ubuntu importer <ubuntu-devel-discuss@lists.ubuntu.com>2024-01-30 16:38:29 +0000
commit9b0df72c1d53599f8d407fec6b7faa2b90d92b3b (patch)
tree5281084ea02a16856213a66b8bc73a596c602144
parent7c294266ae8a7b74c288221cee9f2e17a5f7fb5d (diff)
parent0e7e32cee6dea60c11aed26e38fc3b49d2750ac4 (diff)
Imported using git-ubuntu import.
-rw-r--r--.gitignore2
-rw-r--r--.travis.yml12
-rw-r--r--README.rst38
-rw-r--r--crossref/__init__.py2
-rw-r--r--crossref/restful.py1012
-rw-r--r--crossref/utils.py4
-rw-r--r--crossref/validators.py100
-rw-r--r--debian/changelog11
-rw-r--r--debian/control2
-rw-r--r--debian/copyright4
-rw-r--r--debian/patches/disable-network-tests.patch3
-rw-r--r--debian/patches/relax-requirements.patch14
-rw-r--r--debian/patches/series1
-rw-r--r--debian/upstream/metadata1
-rw-r--r--debian/watch6
-rw-r--r--setup.py3
-rw-r--r--tests/test_restful.py14
17 files changed, 677 insertions, 552 deletions
diff --git a/.gitignore b/.gitignore
index 7bbc71c..8c1df2a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/README.rst b/README.rst
index 4b735a5..2e2c02f 100644
--- a/README.rst
+++ b/README.rst
@@ -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@
diff --git a/setup.py b/setup.py
index 2e623c3..5b96d9a 100644
--- a/setup.py
+++ b/setup.py
@@ -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)