diff --git a/README.md b/README.md index a500371..8f71566 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ The following features of OpenAlex are currently supported by PyAlex: - [x] Select fields - [x] Sample - [x] Pagination -- [ ] [Autocomplete endpoint](https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/autocomplete-entities) +- [x] Autocomplete endpoint - [x] N-grams - [x] Authentication @@ -45,10 +45,10 @@ pip install pyalex ## Getting started -PyAlex offers support for all [Entity Objects](https://docs.openalex.org/api-entities/entities-overview): [Works](https://docs.openalex.org/api-entities/works), [Authors](https://docs.openalex.org/api-entities/authors), [Sources](https://docs.openalex.org/api-entities/sourcese), [Institutions](https://docs.openalex.org/api-entities/institutions), [Concepts](https://docs.openalex.org/api-entities/concepts), [Publishers](https://docs.openalex.org/api-entities/publishers), and [Funders](https://docs.openalex.org/api-entities/funders). +PyAlex offers support for all [Entity Objects](https://docs.openalex.org/api-entities/entities-overview): [Works](https://docs.openalex.org/api-entities/works), [Authors](https://docs.openalex.org/api-entities/authors), [Sources](https://docs.openalex.org/api-entities/sourcese), [Institutions](https://docs.openalex.org/api-entities/institutions), [Concepts](https://docs.openalex.org/api-entities/concepts), [Publishers](https://docs.openalex.org/api-entities/publishers), [Funders](https://docs.openalex.org/api-entities/funders), and [Autocomplete](https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/autocomplete-entities). ```python -from pyalex import Works, Authors, Sources, Institutions, Concepts, Publishers, Funders +from pyalex import Works, Authors, Sources, Institutions, Concepts, Publishers, Funders, autocomplete ``` ### The polite pool @@ -63,6 +63,18 @@ import pyalex pyalex.config.email = "mail@example.com" ``` +### Max retries + +By default, PyAlex will raise an error at the first failure when querying the OpenAlex API. You can set `max_retries` to a number higher than 0 to allow PyAlex to retry when an error occurs. `retry_backoff_factor` is related to the delay between two retry, and `retry_http_codes` are the HTTP error codes that should trigger a retry. + +```python +from pyalex import config + +config.max_retries = 0 +config.retry_backoff_factor = 0.1 +config.retry_http_codes = [429, 500, 503] +``` + ### Get single entity Get a single Work, Author, Source, Institution, Concept, Publisher or Funder from OpenAlex by the @@ -305,6 +317,32 @@ for page in pager: ``` +### Autocomplete + +OpenAlex reference: [Autocomplete entities](https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/autocomplete-entities). + +Autocomplete a string: +```python +from pyalex import autocomplete + +autocomplete("stockholm resilience centre") +``` + +Autocomplete a string to get a specific type of entities: +```python +from pyalex import Institutions + +Institutions().autocomplete("stockholm resilience centre") +``` + +You can also use the filters to autocomplete: +```python +from pyalex import Works + +r = Works().filter(publication_year=2023).autocomplete("planetary boundaries") +``` + + ### Get N-grams OpenAlex reference: [Get N-grams](https://docs.openalex.org/api-entities/works/get-n-grams). diff --git a/pyalex/__init__.py b/pyalex/__init__.py index 64cc1f0..7485eaf 100644 --- a/pyalex/__init__.py +++ b/pyalex/__init__.py @@ -23,6 +23,7 @@ from pyalex.api import Venues from pyalex.api import Work from pyalex.api import Works +from pyalex.api import autocomplete from pyalex.api import config from pyalex.api import invert_abstract @@ -45,6 +46,7 @@ "Concept", "People", "Journals", + "autocomplete", "config", "invert_abstract", ] diff --git a/pyalex/api.py b/pyalex/api.py index ba3ea32..eed97c2 100644 --- a/pyalex/api.py +++ b/pyalex/api.py @@ -198,7 +198,11 @@ def _get_multi_items(self, record_list): return self.filter(openalex_id="|".join(record_list)).get() def _full_collection_name(self): - return config.openalex_url + "/" + self.__class__.__name__.lower() + if self.params is not None and 'q' in self.params.keys(): + base_url = config.openalex_url + "/autocomplete/" + return base_url + self.__class__.__name__.lower() + else: + return config.openalex_url + "/" + self.__class__.__name__.lower() def __getattr__(self, key): if key == "groupby": @@ -286,7 +290,6 @@ def get(self, return_meta=False, page=None, per_page=None, cursor=None): self._add_params("per-page", per_page) self._add_params("page", page) self._add_params("cursor", cursor) - return self._get_from_url(self.url, return_meta=return_meta) def paginate(self, method="cursor", page=1, per_page=None, cursor="*", n_max=10000): @@ -343,6 +346,11 @@ def select(self, s): self._add_params("select", s) return self + def autocomplete(self, s, **kwargs): + """ autocomplete the string s, for a specific type of entity """ + self._add_params("q", s) + return self.get(**kwargs) + # The API @@ -421,6 +429,20 @@ class Funders(BaseOpenAlex): resource_class = Funder +class Autocomplete(OpenAlexEntity): + pass + + +class autocompletes(BaseOpenAlex): + """ Class to autocomplete without being based on the type of entity """ + resource_class = Autocomplete + + def __getitem__(self, key): + return self._get_from_url( + config.openalex_url + "/autocomplete" + "?q=" + key, return_meta=False + ) + + def Venue(*args, **kwargs): # deprecated # warn about deprecation warnings.warn( @@ -443,6 +465,10 @@ def Venues(*args, **kwargs): # deprecated return Sources(*args, **kwargs) +def autocomplete(s): + """ autocomplete with any type of entity """ + return autocompletes()[s] + # aliases People = Authors Journals = Sources diff --git a/tests/test_pyalex.py b/tests/test_pyalex.py index 165a4c6..d0cde47 100644 --- a/tests/test_pyalex.py +++ b/tests/test_pyalex.py @@ -14,6 +14,7 @@ from pyalex import Sources from pyalex import Work from pyalex import Works +from pyalex import autocomplete from pyalex.api import QueryError @@ -301,3 +302,15 @@ def test_auth(): pyalex.config.api_key = None assert len(w_no_auth) == len(w_auth) + + +def test_autocomplete_works(): + w = Works().filter(publication_year=2023).autocomplete("planetary boundaries") + + assert len(w) > 5 + + +def test_autocomplete(): + a = autocomplete("stockholm resilience") + + assert len(a) > 5