@@ -31,6 +31,32 @@ def __setattr__(self, key, value):
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 @@ def __getattr__(self, key):
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 @@ def paginate(self, method="cursor", page=1, per_page=None, cursor="*", n_max=100
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 @@ def filter(self, **kwargs):
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
0 commit comments