pyalex 0.15.1__py3-none-any.whl → 0.16__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyalex/_version.py CHANGED
@@ -1,8 +1,13 @@
1
- # file generated by setuptools_scm
1
+ # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
3
6
  TYPE_CHECKING = False
4
7
  if TYPE_CHECKING:
5
- from typing import Tuple, Union
8
+ from typing import Tuple
9
+ from typing import Union
10
+
6
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
7
12
  else:
8
13
  VERSION_TUPLE = object
@@ -12,5 +17,5 @@ __version__: str
12
17
  __version_tuple__: VERSION_TUPLE
13
18
  version_tuple: VERSION_TUPLE
14
19
 
15
- __version__ = version = '0.15.1'
16
- __version_tuple__ = version_tuple = (0, 15, 1)
20
+ __version__ = version = '0.16'
21
+ __version_tuple__ = version_tuple = (0, 16)
pyalex/api.py CHANGED
@@ -31,6 +31,32 @@ config = AlexConfig(
31
31
  )
32
32
 
33
33
 
34
+ class or_(dict):
35
+ pass
36
+
37
+
38
+ class _LogicalExpression:
39
+ token = None
40
+
41
+ def __init__(self, value):
42
+ self.value = value
43
+
44
+ def __str__(self) -> str:
45
+ return f"{self.token}{self.value}"
46
+
47
+
48
+ class not_(_LogicalExpression):
49
+ token = "!"
50
+
51
+
52
+ class gt_(_LogicalExpression):
53
+ token = ">"
54
+
55
+
56
+ class lt_(_LogicalExpression):
57
+ token = "<"
58
+
59
+
34
60
  def _quote_oa_value(v):
35
61
  """Prepare a value for the OpenAlex API.
36
62
 
@@ -41,30 +67,40 @@ def _quote_oa_value(v):
41
67
  if isinstance(v, bool):
42
68
  return str(v).lower()
43
69
 
70
+ if isinstance(v, _LogicalExpression) and isinstance(v.value, str):
71
+ v.value = quote_plus(v.value)
72
+ return v
73
+
44
74
  if isinstance(v, str):
45
75
  return quote_plus(v)
46
76
 
47
77
  return v
48
78
 
49
79
 
50
- def _flatten_kv(d, prefix=""):
80
+ def _flatten_kv(d, prefix=None, logical="+"):
81
+ if prefix is None and not isinstance(d, dict):
82
+ raise ValueError("prefix should be set if d is not a dict")
83
+
51
84
  if isinstance(d, dict):
85
+ logical_subd = "|" if isinstance(d, or_) else logical
86
+
52
87
  t = []
53
88
  for k, v in d.items():
54
- if isinstance(v, list):
55
- t.extend([f"{prefix}.{k}:{_quote_oa_value(i)}" for i in v])
56
- else:
57
- new_prefix = f"{prefix}.{k}" if prefix else f"{k}"
58
- x = _flatten_kv(v, prefix=new_prefix)
59
- t.append(x)
89
+ x = _flatten_kv(
90
+ v, prefix=f"{prefix}.{k}" if prefix else f"{k}", logical=logical_subd
91
+ )
92
+ t.append(x)
60
93
 
61
94
  return ",".join(t)
95
+ elif isinstance(d, list):
96
+ list_str = logical.join([f"{_quote_oa_value(i)}" for i in d])
97
+ return f"{prefix}:{list_str}"
62
98
  else:
63
99
  return f"{prefix}:{_quote_oa_value(d)}"
64
100
 
65
101
 
66
102
  def _params_merge(params, add_params):
67
- for k, _v in add_params.items():
103
+ for k in add_params.keys():
68
104
  if (
69
105
  k in params
70
106
  and isinstance(params[k], dict)
@@ -113,6 +149,18 @@ def invert_abstract(inv_index):
113
149
  return " ".join(map(lambda x: x[0], sorted(l_inv, key=lambda x: x[1])))
114
150
 
115
151
 
152
+ def _wrap_values_nested_dict(d, func):
153
+ for k, v in d.items():
154
+ if isinstance(v, dict):
155
+ d[k] = _wrap_values_nested_dict(v, func)
156
+ elif isinstance(v, list):
157
+ d[k] = [func(i) for i in v]
158
+ else:
159
+ d[k] = func(v)
160
+
161
+ return d
162
+
163
+
116
164
  class QueryError(ValueError):
117
165
  pass
118
166
 
@@ -207,9 +255,6 @@ class BaseOpenAlex:
207
255
  def __init__(self, params=None):
208
256
  self.params = params
209
257
 
210
- def _get_multi_items(self, record_list):
211
- return self.filter(openalex_id="|".join(record_list)).get()
212
-
213
258
  def _full_collection_name(self):
214
259
  if self.params is not None and "q" in self.params.keys():
215
260
  return (
@@ -234,10 +279,14 @@ class BaseOpenAlex:
234
279
 
235
280
  def __getitem__(self, record_id):
236
281
  if isinstance(record_id, list):
237
- return self._get_multi_items(record_id)
282
+ if len(record_id) > 100:
283
+ raise ValueError("OpenAlex does not support more than 100 ids")
284
+
285
+ return self.filter_or(openalex_id=record_id).get(per_page=len(record_id))
238
286
 
239
287
  return self._get_from_url(
240
- f"{self._full_collection_name()}/{record_id}", return_meta=False
288
+ f"{self._full_collection_name()}/{_quote_oa_value(record_id)}",
289
+ return_meta=False,
241
290
  )
242
291
 
243
292
  @property
@@ -322,7 +371,10 @@ class BaseOpenAlex:
322
371
  def random(self):
323
372
  return self.__getitem__("random")
324
373
 
325
- def _add_params(self, argument, new_params):
374
+ def _add_params(self, argument, new_params, raise_if_exists=False):
375
+ if raise_if_exists:
376
+ raise NotImplementedError("raise_if_exists is not implemented")
377
+
326
378
  if self.params is None:
327
379
  self.params = {argument: new_params}
328
380
  elif argument in self.params and isinstance(self.params[argument], dict):
@@ -336,6 +388,25 @@ class BaseOpenAlex:
336
388
  self._add_params("filter", kwargs)
337
389
  return self
338
390
 
391
+ def filter_and(self, **kwargs):
392
+ return self.filter(**kwargs)
393
+
394
+ def filter_or(self, **kwargs):
395
+ self._add_params("filter", or_(kwargs), raise_if_exists=False)
396
+ return self
397
+
398
+ def filter_not(self, **kwargs):
399
+ self._add_params("filter", _wrap_values_nested_dict(kwargs, not_))
400
+ return self
401
+
402
+ def filter_gt(self, **kwargs):
403
+ self._add_params("filter", _wrap_values_nested_dict(kwargs, gt_))
404
+ return self
405
+
406
+ def filter_lt(self, **kwargs):
407
+ self._add_params("filter", _wrap_values_nested_dict(kwargs, lt_))
408
+ return self
409
+
339
410
  def search_filter(self, **kwargs):
340
411
  self._add_params("filter", {f"{k}.search": v for k, v in kwargs.items()})
341
412
  return self
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: pyalex
3
- Version: 0.15.1
3
+ Version: 0.16
4
4
  Summary: Python interface to the OpenAlex database
5
5
  Author-email: Jonathan de Bruin <jonathandebruinos@gmail.com>
6
6
  License: MIT
@@ -17,10 +17,10 @@ License-File: LICENSE
17
17
  Requires-Dist: requests
18
18
  Requires-Dist: urllib3
19
19
  Provides-Extra: lint
20
- Requires-Dist: ruff ; extra == 'lint'
20
+ Requires-Dist: ruff; extra == "lint"
21
21
  Provides-Extra: test
22
- Requires-Dist: pytest ; extra == 'test'
23
- Requires-Dist: pytest-xdist ; extra == 'test'
22
+ Requires-Dist: pytest; extra == "test"
23
+ Requires-Dist: pytest-xdist; extra == "test"
24
24
 
25
25
  <p align="center">
26
26
  <img alt="PyAlex - a Python wrapper for OpenAlex" src="https://github.com/J535D165/pyalex/raw/main/pyalex_repocard.svg">
@@ -126,7 +126,7 @@ Works()["W2741809807"]["open_access"]
126
126
  The previous works also for Authors, Sources, Institutions, Concepts and Topics
127
127
 
128
128
  ```python
129
- Authors()["A2887243803"]
129
+ Authors()["A5027479191"]
130
130
  Authors()["https://orcid.org/0000-0002-4297-0502"] # same
131
131
  ```
132
132
 
@@ -139,7 +139,6 @@ Works().random()
139
139
  Authors().random()
140
140
  Sources().random()
141
141
  Institutions().random()
142
- Concepts().random()
143
142
  Topics().random()
144
143
  Publishers().random()
145
144
  Funders().random()
@@ -383,6 +382,10 @@ Works()["W2023271753"].ngrams()
383
382
  All results from PyAlex can be serialized. For example, save the results to a JSON file:
384
383
 
385
384
  ```python
385
+ import json
386
+ from pathlib import Path
387
+ from pyalex import Work
388
+
386
389
  with open(Path("works.json"), "w") as f:
387
390
  json.dump(Works().get(), f)
388
391
 
@@ -394,7 +397,7 @@ with open(Path("works.json")) as f:
394
397
 
395
398
  A list of awesome use cases of the OpenAlex dataset.
396
399
 
397
- ### Cited publications (referenced works)
400
+ ### Cited publications (works referenced by this paper, outgoing citations)
398
401
 
399
402
  ```python
400
403
  from pyalex import Works
@@ -405,6 +408,13 @@ w = Works()["W2741809807"]
405
408
  Works()[w["referenced_works"]]
406
409
  ```
407
410
 
411
+ ### Citing publications (other works that reference this paper, incoming citations)
412
+
413
+ ```python
414
+ from pyalex import Works
415
+ Works().filter(cites="W2741809807").get()
416
+ ```
417
+
408
418
  ### Get works of a single author
409
419
 
410
420
  ```python
@@ -463,6 +473,7 @@ R users can use the excellent [OpenAlexR](https://github.com/ropensci/openalexR)
463
473
 
464
474
  > This library is a community contribution. The authors of this Python library aren't affiliated with OpenAlex.
465
475
 
476
+ This library is maintained by [J535D165](https://github.com/J535D165) and [PeterLombaers](https://github.com/PeterLombaers).
466
477
  Feel free to reach out with questions, remarks, and suggestions. The
467
- [issue tracker](/issues) is a good starting point. You can also email me at
478
+ [issue tracker](/issues) is a good starting point. You can also reach out via
468
479
  [jonathandebruinos@gmail.com](mailto:jonathandebruinos@gmail.com).
@@ -0,0 +1,8 @@
1
+ pyalex/__init__.py,sha256=52XK8om6IVD1Yiq_HYOCR6PUY56sPRHutGM03NOrGMQ,1467
2
+ pyalex/_version.py,sha256=5zc7xTIiU-8qDWOhJyQ0RIy_saYu1BECCONIFoa0eLw,508
3
+ pyalex/api.py,sha256=YqVMwNEkg_LQdIVzOSXHe4bRhfNFg_YmCJqG1MoY23Y,14993
4
+ pyalex-0.16.dist-info/LICENSE,sha256=Mhf5MImRYP06a1EPVJCpkpTstOOEfGajN3T_Fz4izMg,1074
5
+ pyalex-0.16.dist-info/METADATA,sha256=7qIHsL5k8LL01z7Yc8_zpuJnGH3Av23THNz4g6QLZWI,14224
6
+ pyalex-0.16.dist-info/WHEEL,sha256=nn6H5-ilmfVryoAQl3ZQ2l8SH5imPWFpm1A5FgEuFV4,91
7
+ pyalex-0.16.dist-info/top_level.txt,sha256=D0An8hWy9e0xPhTaT6K-yuJKVeVV3bYGxZ6Y-v2WXSU,7
8
+ pyalex-0.16.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (73.0.1)
2
+ Generator: setuptools (75.8.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,8 +0,0 @@
1
- pyalex/__init__.py,sha256=52XK8om6IVD1Yiq_HYOCR6PUY56sPRHutGM03NOrGMQ,1467
2
- pyalex/_version.py,sha256=po5_rvCFTU8xU9IC56wyK0-zfBgz_U4xX6CO2mv9Mzs,413
3
- pyalex/api.py,sha256=JTOx0P037IOhhYVAigQO5uPsd1HMQC-SM3wny0_52uU,13236
4
- pyalex-0.15.1.dist-info/LICENSE,sha256=Mhf5MImRYP06a1EPVJCpkpTstOOEfGajN3T_Fz4izMg,1074
5
- pyalex-0.15.1.dist-info/METADATA,sha256=JSa8cKcjZUZ-lDo28WyuFN1GHEEWUHWLvSLaKh3IP10,13859
6
- pyalex-0.15.1.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
7
- pyalex-0.15.1.dist-info/top_level.txt,sha256=D0An8hWy9e0xPhTaT6K-yuJKVeVV3bYGxZ6Y-v2WXSU,7
8
- pyalex-0.15.1.dist-info/RECORD,,