malac-utils 1.1.0__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.
malac/utils/fhirpath.py
ADDED
|
@@ -0,0 +1,1385 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import dateutil
|
|
3
|
+
import inspect
|
|
4
|
+
import io
|
|
5
|
+
import re
|
|
6
|
+
import decimal
|
|
7
|
+
import math
|
|
8
|
+
from urllib.parse import urlparse
|
|
9
|
+
|
|
10
|
+
import dateutil.parser
|
|
11
|
+
|
|
12
|
+
time_units = ["year", "years", "month", "months", "week", "weeks", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds", "millisecond", "milliseconds"]
|
|
13
|
+
|
|
14
|
+
class SimpleTypeInfo(object):
|
|
15
|
+
|
|
16
|
+
def __init__(self, namespace: str, name: str, baseType: str="System.Any") -> None:
|
|
17
|
+
self.namespace = namespace
|
|
18
|
+
self.name = name
|
|
19
|
+
self.baseType = baseType
|
|
20
|
+
|
|
21
|
+
class ClassInfoElement(object):
|
|
22
|
+
|
|
23
|
+
def __init__(self, name: str, type_: str, isOneBased: bool=None) -> None:
|
|
24
|
+
self.name = name
|
|
25
|
+
self.type = type_
|
|
26
|
+
self.isOneBased = isOneBased
|
|
27
|
+
|
|
28
|
+
class ClassInfo(object):
|
|
29
|
+
|
|
30
|
+
def __init__(self, namespace: str, name: str, baseType: str, element: list[ClassInfoElement]) -> None:
|
|
31
|
+
self.namespace = namespace
|
|
32
|
+
self.name = name
|
|
33
|
+
self.baseType = baseType
|
|
34
|
+
self.element = element
|
|
35
|
+
|
|
36
|
+
class QuanitityComparable(object):
|
|
37
|
+
|
|
38
|
+
def __init__(self, quantity):
|
|
39
|
+
self.value = decimal.Decimal(quantity.value.value)
|
|
40
|
+
self.system = quantity.system.value if quantity.system else None
|
|
41
|
+
self.code = quantity.code.value if quantity.code else None
|
|
42
|
+
self.unit = quantity.unit.value if quantity.unit else None
|
|
43
|
+
|
|
44
|
+
def __lt__(self, other):
|
|
45
|
+
if not self.check_unit(other):
|
|
46
|
+
return False
|
|
47
|
+
return self.value < other.value
|
|
48
|
+
|
|
49
|
+
def __le__(self, other):
|
|
50
|
+
if not self.check_unit(other):
|
|
51
|
+
return False
|
|
52
|
+
return self.value <= other.value
|
|
53
|
+
|
|
54
|
+
def __gt__(self, other):
|
|
55
|
+
if not self.check_unit(other):
|
|
56
|
+
return False
|
|
57
|
+
return self.value > other.value
|
|
58
|
+
|
|
59
|
+
def __ge__(self, other):
|
|
60
|
+
if not self.check_unit(other):
|
|
61
|
+
return False
|
|
62
|
+
return self.value >= other.value
|
|
63
|
+
|
|
64
|
+
def __eq__(self, other):
|
|
65
|
+
if not isinstance(other, QuanitityComparable):
|
|
66
|
+
return False
|
|
67
|
+
if self.value != other.value:
|
|
68
|
+
return False
|
|
69
|
+
return self.check_unit(other)
|
|
70
|
+
|
|
71
|
+
def check_unit(self, other):
|
|
72
|
+
if self.system is not None and other.system is not None and self.system != other.system:
|
|
73
|
+
return False
|
|
74
|
+
if self.code is not None and other.code is not None:
|
|
75
|
+
return self.code == other.code
|
|
76
|
+
if self.unit is not None and other.unit is not None:
|
|
77
|
+
return self.unit == other.unit
|
|
78
|
+
if self.unit is not None and other.code is not None:
|
|
79
|
+
return False
|
|
80
|
+
if other.unit is not None and self.code is not None:
|
|
81
|
+
return False
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
class FHIRPathUtils(object):
|
|
85
|
+
|
|
86
|
+
def __init__(self, model):
|
|
87
|
+
self.model = model
|
|
88
|
+
self.SystemDate = type('SystemDate', (model.date,), {})
|
|
89
|
+
self.SystemDateTime = type('SystemDateTime', (model.dateTime,), {})
|
|
90
|
+
self.SystemTime = type('SystemTime', (model.time,), {})
|
|
91
|
+
self.SystemQuantity = type('SystemQuantity', (model.Quantity,), {})
|
|
92
|
+
self.simple_type_names = {
|
|
93
|
+
str: "String",
|
|
94
|
+
bool: "Boolean",
|
|
95
|
+
self.SystemDate: "Date",
|
|
96
|
+
self.SystemDateTime: "DateTime",
|
|
97
|
+
self.SystemTime: "Time",
|
|
98
|
+
int: "Integer",
|
|
99
|
+
decimal.Decimal: "Decimal",
|
|
100
|
+
self.SystemQuantity: "Quantity"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
def get(self, val, *attrs, **options):
|
|
104
|
+
attr = attrs[0]
|
|
105
|
+
try:
|
|
106
|
+
result = getattr(val, attr) or []
|
|
107
|
+
except AttributeError:
|
|
108
|
+
found = False
|
|
109
|
+
result = []
|
|
110
|
+
annotations = inspect.get_annotations(type(val).__init__)
|
|
111
|
+
for var, var_type in annotations.items():
|
|
112
|
+
if var.startswith(attr):
|
|
113
|
+
found = True
|
|
114
|
+
result = getattr(val, var, []) or []
|
|
115
|
+
if result:
|
|
116
|
+
break
|
|
117
|
+
if not found and hasattr(val, "__dict__"):
|
|
118
|
+
for key in val.__dict__.keys():
|
|
119
|
+
if key.endswith("_nsprefix_"):
|
|
120
|
+
prefix = getattr(val, key)
|
|
121
|
+
if prefix and attr.lower() == (prefix + key[:-10]).lower():
|
|
122
|
+
result = getattr(val, key[:-10]) or []
|
|
123
|
+
if result != [] and len(attrs) == 1 and options.get("strip", False):
|
|
124
|
+
result = str(result).strip()
|
|
125
|
+
result = result if isinstance(result, list) else [result]
|
|
126
|
+
if len(attrs) == 1:
|
|
127
|
+
return result
|
|
128
|
+
else:
|
|
129
|
+
return [v2 for v in result for v2 in self.get(v, *attrs[1:], **options)]
|
|
130
|
+
|
|
131
|
+
def string_value(self, val):
|
|
132
|
+
if isinstance(val, str):
|
|
133
|
+
return val
|
|
134
|
+
elif isinstance(val, self.model.string) or isinstance(val, self.model.uri):
|
|
135
|
+
return val.value
|
|
136
|
+
elif isinstance(val, self.model.div):
|
|
137
|
+
tmp = io.StringIO()
|
|
138
|
+
val.export(tmp, 0)
|
|
139
|
+
return tmp.getvalue()
|
|
140
|
+
elif val == []:
|
|
141
|
+
return val
|
|
142
|
+
else:
|
|
143
|
+
raise BaseException("Unexpected type, expected string: %s" % val)
|
|
144
|
+
|
|
145
|
+
def boolean_value(self, val):
|
|
146
|
+
if isinstance(val, list):
|
|
147
|
+
if len(val) > 1:
|
|
148
|
+
raise BaseException("Operation not supported for lists with more than one element")
|
|
149
|
+
elif len(val) == 1:
|
|
150
|
+
val = val[0]
|
|
151
|
+
else:
|
|
152
|
+
return []
|
|
153
|
+
if isinstance(val, bool):
|
|
154
|
+
return val
|
|
155
|
+
elif isinstance(val, self.model.boolean):
|
|
156
|
+
return val.value == "true"
|
|
157
|
+
else:
|
|
158
|
+
return True
|
|
159
|
+
|
|
160
|
+
def at_index(self, val, index):
|
|
161
|
+
[idx] = index
|
|
162
|
+
if len(val) > idx:
|
|
163
|
+
return [val[idx]]
|
|
164
|
+
else:
|
|
165
|
+
return []
|
|
166
|
+
|
|
167
|
+
def flatten(self, val):
|
|
168
|
+
result = []
|
|
169
|
+
for v in val:
|
|
170
|
+
if isinstance(v, list):
|
|
171
|
+
result += v
|
|
172
|
+
elif v is not None:
|
|
173
|
+
result.append(v)
|
|
174
|
+
return result
|
|
175
|
+
|
|
176
|
+
def single(self, val):
|
|
177
|
+
if len(val) > 1:
|
|
178
|
+
raise BaseException("More than one element")
|
|
179
|
+
else:
|
|
180
|
+
return val
|
|
181
|
+
|
|
182
|
+
def single_value(self, val, *expected_types):
|
|
183
|
+
if isinstance(val, list):
|
|
184
|
+
if len(val) > 1:
|
|
185
|
+
raise BaseException("Operation not supported for lists with more than one element")
|
|
186
|
+
elif len(val) == 1:
|
|
187
|
+
val = val[0]
|
|
188
|
+
else:
|
|
189
|
+
return []
|
|
190
|
+
if not expected_types:
|
|
191
|
+
return val
|
|
192
|
+
for expected_type in expected_types:
|
|
193
|
+
if isinstance(val, expected_type):
|
|
194
|
+
return val
|
|
195
|
+
if bool in expected_types:
|
|
196
|
+
return True
|
|
197
|
+
elif str in expected_types:
|
|
198
|
+
return str(val)
|
|
199
|
+
else:
|
|
200
|
+
raise BaseException("Unexpected type: %s (expected %s)" % (val, expected_types))
|
|
201
|
+
|
|
202
|
+
def bool_and(self, *vals):
|
|
203
|
+
if len(vals) > 2:
|
|
204
|
+
result = self.bool_and(*vals[1:])
|
|
205
|
+
return self.bool_and(vals[0], result)
|
|
206
|
+
else:
|
|
207
|
+
val_a, val_b = vals
|
|
208
|
+
val_a = self.boolean_value(val_a)
|
|
209
|
+
val_b = self.boolean_value(val_b)
|
|
210
|
+
if val_a == [] or val_b == []:
|
|
211
|
+
if val_a == False or val_b == False:
|
|
212
|
+
return [False]
|
|
213
|
+
else:
|
|
214
|
+
return []
|
|
215
|
+
else:
|
|
216
|
+
return [val_a and val_b]
|
|
217
|
+
|
|
218
|
+
def bool_or(self, *vals):
|
|
219
|
+
if len(vals) > 2:
|
|
220
|
+
result = self.bool_or(*vals[1:])
|
|
221
|
+
return self.bool_or(vals[0], result)
|
|
222
|
+
else:
|
|
223
|
+
val_a, val_b = vals
|
|
224
|
+
val_a = self.boolean_value(val_a)
|
|
225
|
+
val_b = self.boolean_value(val_b)
|
|
226
|
+
if val_a == [] or val_b == []:
|
|
227
|
+
if val_a == True or val_b == True:
|
|
228
|
+
return [True]
|
|
229
|
+
else:
|
|
230
|
+
return []
|
|
231
|
+
else:
|
|
232
|
+
return [val_a or val_b]
|
|
233
|
+
|
|
234
|
+
def bool_xor(self, val_a, val_b):
|
|
235
|
+
val_a = self.boolean_value(val_a)
|
|
236
|
+
val_b = self.boolean_value(val_b)
|
|
237
|
+
if val_a == [] or val_b == []:
|
|
238
|
+
return []
|
|
239
|
+
else:
|
|
240
|
+
return [val_a != val_b]
|
|
241
|
+
|
|
242
|
+
def bool_implies(self, val_a, val_b):
|
|
243
|
+
val_a = self.boolean_value(val_a)
|
|
244
|
+
val_b = self.boolean_value(val_b)
|
|
245
|
+
if val_a:
|
|
246
|
+
return val_b if val_b == [] else [val_b]
|
|
247
|
+
elif val_a == False or val_b:
|
|
248
|
+
return [True]
|
|
249
|
+
else:
|
|
250
|
+
return []
|
|
251
|
+
|
|
252
|
+
def bool_not(self, val):
|
|
253
|
+
val = self.boolean_value(val)
|
|
254
|
+
if val == []:
|
|
255
|
+
return []
|
|
256
|
+
else:
|
|
257
|
+
return [not val]
|
|
258
|
+
|
|
259
|
+
def union(self, val_a, val_b):
|
|
260
|
+
result_dict = {}
|
|
261
|
+
for val in val_a + val_b:
|
|
262
|
+
value_annotation = inspect.get_annotations(type(val).__init__).get("value", "")
|
|
263
|
+
if value_annotation == type(val).__name__ + "Enum" or value_annotation == type(val).__name__ + "_primitive":
|
|
264
|
+
key = val.value
|
|
265
|
+
else:
|
|
266
|
+
key = val
|
|
267
|
+
result_dict[key] = val
|
|
268
|
+
return list(result_dict.values())
|
|
269
|
+
|
|
270
|
+
def membership(self, val_a, val_b):
|
|
271
|
+
val_a = self.single_value(val_a)
|
|
272
|
+
if val_a == []:
|
|
273
|
+
return [False]
|
|
274
|
+
value_annotation = inspect.get_annotations(type(val_a).__init__).get("value", "")
|
|
275
|
+
if value_annotation == type(val_a).__name__ + "Enum" or value_annotation == type(val_a).__name__ + "_primitive":
|
|
276
|
+
key_a = val_a.value
|
|
277
|
+
else:
|
|
278
|
+
key_a = val_a
|
|
279
|
+
for val in val_b:
|
|
280
|
+
value_annotation = inspect.get_annotations(type(val).__init__).get("value", "")
|
|
281
|
+
if value_annotation == type(val).__name__ + "Enum" or value_annotation == type(val).__name__ + "_primitive":
|
|
282
|
+
key = val.value
|
|
283
|
+
else:
|
|
284
|
+
key = val
|
|
285
|
+
if key == key_a:
|
|
286
|
+
return [True]
|
|
287
|
+
return [False]
|
|
288
|
+
|
|
289
|
+
def containership(self, val_a, val_b):
|
|
290
|
+
return self.membership(val_b, val_a)
|
|
291
|
+
|
|
292
|
+
def exclude(self, val_a, val_b):
|
|
293
|
+
results = []
|
|
294
|
+
list_b = []
|
|
295
|
+
for val in val_b:
|
|
296
|
+
value_annotation = inspect.get_annotations(type(val).__init__).get("value", "")
|
|
297
|
+
if value_annotation == type(val).__name__ + "Enum" or value_annotation == type(val).__name__ + "_primitive":
|
|
298
|
+
key = val.value
|
|
299
|
+
else:
|
|
300
|
+
key = val
|
|
301
|
+
list_b.append(key)
|
|
302
|
+
for val in val_a:
|
|
303
|
+
value_annotation = inspect.get_annotations(type(val).__init__).get("value", "")
|
|
304
|
+
if value_annotation == type(val).__name__ + "Enum" or value_annotation == type(val).__name__ + "_primitive":
|
|
305
|
+
key = val.value
|
|
306
|
+
else:
|
|
307
|
+
key = val
|
|
308
|
+
if key not in list_b:
|
|
309
|
+
results.append(key)
|
|
310
|
+
return results
|
|
311
|
+
|
|
312
|
+
def add(self, *vals):
|
|
313
|
+
if len(vals) > 2:
|
|
314
|
+
result = self.add(*vals[1:])
|
|
315
|
+
return self.add(vals[0], result)
|
|
316
|
+
else:
|
|
317
|
+
val_a, val_b = vals
|
|
318
|
+
val_a = self.single_value(val_a)
|
|
319
|
+
val_b = self.single_value(val_b)
|
|
320
|
+
if val_a == [] or val_b == []:
|
|
321
|
+
return []
|
|
322
|
+
else:
|
|
323
|
+
if isinstance(val_a, (str, self.model.string)) or isinstance(val_b, (str, self.model.string)):
|
|
324
|
+
if isinstance(val_a, self.model.string):
|
|
325
|
+
val_a = val_a.value
|
|
326
|
+
elif isinstance(val_b, self.model.string):
|
|
327
|
+
val_b = val_b.value
|
|
328
|
+
return [val_a + val_b] # TODO check types and add support for quantity
|
|
329
|
+
|
|
330
|
+
def subtract(self, val_a, val_b):
|
|
331
|
+
val_a = self.single_value(val_a)
|
|
332
|
+
val_b = self.single_value(val_b)
|
|
333
|
+
if val_a == [] or val_b == []:
|
|
334
|
+
return []
|
|
335
|
+
else:
|
|
336
|
+
return [val_a - val_b] # TODO check types and add support for quantity
|
|
337
|
+
|
|
338
|
+
def multiply(self, val_a, val_b):
|
|
339
|
+
val_a = self.single_value(val_a)
|
|
340
|
+
val_b = self.single_value(val_b)
|
|
341
|
+
if val_a == [] or val_b == []:
|
|
342
|
+
return []
|
|
343
|
+
if isinstance(val_a, self.model.decimal):
|
|
344
|
+
val_a = val_a.value.value
|
|
345
|
+
if isinstance(val_b, self.model.decimal):
|
|
346
|
+
val_b = val_b.value.value
|
|
347
|
+
if isinstance(val_a, self.model.Quantity) or isinstance(val_b, self.model.Quantity):
|
|
348
|
+
v_a = val_a.value.value if isinstance(val_a, self.model.Quantity) else val_a
|
|
349
|
+
v_b = val_b.value.value if isinstance(val_b, self.model.Quantity) else val_b
|
|
350
|
+
unit = val_a if isinstance(val_a, self.model.Quantity) else val_b # TODO multiply units (m * m -> m2)
|
|
351
|
+
return [self.model.Quantity(value=self.model.decimal(value=v_a * v_b), unit=unit.unit, code=unit.code, system=unit.system)]
|
|
352
|
+
else:
|
|
353
|
+
return [val_a * val_b]
|
|
354
|
+
|
|
355
|
+
def negate(self, val_a):
|
|
356
|
+
return self.multiply(val_a, [-1])
|
|
357
|
+
|
|
358
|
+
def divide(self, val_a, val_b):
|
|
359
|
+
val_a = self.single_value(val_a)
|
|
360
|
+
val_b = self.single_value(val_b)
|
|
361
|
+
if val_a == [] or val_b == []:
|
|
362
|
+
return []
|
|
363
|
+
elif val_b == 0:
|
|
364
|
+
return []
|
|
365
|
+
if isinstance(val_a, self.model.Quantity) or isinstance(val_b, self.model.Quantity):
|
|
366
|
+
v_a = val_a.value.value if isinstance(val_a, self.model.Quantity) else val_a
|
|
367
|
+
v_b = val_b.value.value if isinstance(val_b, self.model.Quantity) else val_b
|
|
368
|
+
if val_b == 0:
|
|
369
|
+
return []
|
|
370
|
+
unit = val_a if isinstance(val_a, self.model.Quantity) else val_b # TODO divide units (g / m -> g/m)
|
|
371
|
+
return [self.model.Quantity(value=self.model.decimal(value=v_a / v_b), unit=unit.unit, code=unit.code, system=unit.system)]
|
|
372
|
+
else:
|
|
373
|
+
return [decimal.Decimal(round(val_a / val_b, 8))]
|
|
374
|
+
|
|
375
|
+
def div(self, val_a, val_b):
|
|
376
|
+
val_a = self.single_value(val_a)
|
|
377
|
+
val_b = self.single_value(val_b)
|
|
378
|
+
if val_a == [] or val_b == []:
|
|
379
|
+
return []
|
|
380
|
+
elif val_b == 0:
|
|
381
|
+
return []
|
|
382
|
+
else:
|
|
383
|
+
return [int(val_a / val_b)]
|
|
384
|
+
|
|
385
|
+
def mod(self, val_a, val_b):
|
|
386
|
+
val_a = self.single_value(val_a)
|
|
387
|
+
val_b = self.single_value(val_b)
|
|
388
|
+
if val_a == [] or val_b == []:
|
|
389
|
+
return []
|
|
390
|
+
elif val_b == 0:
|
|
391
|
+
return []
|
|
392
|
+
else:
|
|
393
|
+
return [val_a % val_b]
|
|
394
|
+
|
|
395
|
+
def decimal_round(self, val_a, val_b):
|
|
396
|
+
val_a = self.single_value(val_a) # check type
|
|
397
|
+
val_b = self.single_value(val_b) # check type
|
|
398
|
+
if val_b == [] or val_b < 0:
|
|
399
|
+
raise BaseException("Invalid precision %s" % val_a)
|
|
400
|
+
elif val_a == []:
|
|
401
|
+
return []
|
|
402
|
+
else:
|
|
403
|
+
return [round(val_a, val_b)]
|
|
404
|
+
|
|
405
|
+
def decimal_truncate(self, val_a):
|
|
406
|
+
val_a = self.single_value(val_a) # check type
|
|
407
|
+
if val_a == []:
|
|
408
|
+
return []
|
|
409
|
+
else:
|
|
410
|
+
return [int(val_a)]
|
|
411
|
+
|
|
412
|
+
def decimal_sqrt(self, val_a):
|
|
413
|
+
val_a = self.single_value(val_a) # check type
|
|
414
|
+
if val_a == []:
|
|
415
|
+
return []
|
|
416
|
+
elif val_a < 0:
|
|
417
|
+
return []
|
|
418
|
+
else:
|
|
419
|
+
return [round(decimal.Decimal(val_a).sqrt(), 8)]
|
|
420
|
+
|
|
421
|
+
def decimal_abs(self, val_a):
|
|
422
|
+
val_a = self.single_value(val_a) # check type
|
|
423
|
+
if val_a == []:
|
|
424
|
+
return []
|
|
425
|
+
elif isinstance(val_a, self.model.Quantity):
|
|
426
|
+
return [self.model.Quantity(value=self.model.decimal(value=abs(val_a.value.value)), unit=val_a.unit, code=val_a.code, system=val_a.system)]
|
|
427
|
+
else:
|
|
428
|
+
return [abs(val_a)]
|
|
429
|
+
|
|
430
|
+
def decimal_ceiling(self, val_a):
|
|
431
|
+
val_a = self.single_value(val_a) # check type
|
|
432
|
+
if val_a == []:
|
|
433
|
+
return []
|
|
434
|
+
else:
|
|
435
|
+
return [math.ceil(val_a)]
|
|
436
|
+
|
|
437
|
+
def decimal_floor(self, val_a):
|
|
438
|
+
val_a = self.single_value(val_a) # check type
|
|
439
|
+
if val_a == []:
|
|
440
|
+
return []
|
|
441
|
+
else:
|
|
442
|
+
return [math.floor(val_a)]
|
|
443
|
+
|
|
444
|
+
def decimal_exp(self, val_a):
|
|
445
|
+
val_a = self.single_value(val_a) # check type
|
|
446
|
+
if val_a == []:
|
|
447
|
+
return []
|
|
448
|
+
else:
|
|
449
|
+
if isinstance(val_a, int):
|
|
450
|
+
val_a = decimal.Decimal(val_a)
|
|
451
|
+
return [round(val_a.exp(), 8)]
|
|
452
|
+
|
|
453
|
+
def decimal_ln(self, val_a):
|
|
454
|
+
val_a = self.single_value(val_a) # check type
|
|
455
|
+
if val_a == []:
|
|
456
|
+
return []
|
|
457
|
+
else:
|
|
458
|
+
if isinstance(val_a, int):
|
|
459
|
+
val_a = decimal.Decimal(val_a)
|
|
460
|
+
return [round(val_a.ln(), 8)]
|
|
461
|
+
|
|
462
|
+
def decimal_log(self, val_a, val_b):
|
|
463
|
+
val_a = self.single_value(val_a) # check type
|
|
464
|
+
val_b = self.single_value(val_b) # check type
|
|
465
|
+
if val_a == [] or val_b == []:
|
|
466
|
+
return []
|
|
467
|
+
else:
|
|
468
|
+
return [decimal.Decimal(round(math.log(val_a, val_b), 8))]
|
|
469
|
+
|
|
470
|
+
def decimal_power(self, val_a, val_b):
|
|
471
|
+
val_a = self.single_value(val_a) # check type
|
|
472
|
+
val_b = self.single_value(val_b) # check type
|
|
473
|
+
if val_a == [] or val_b == []:
|
|
474
|
+
return []
|
|
475
|
+
elif val_a < 0 and val_b > 0 and val_b < 1:
|
|
476
|
+
return []
|
|
477
|
+
else:
|
|
478
|
+
return [decimal.Decimal(round(math.pow(val_a, val_b), 8))]
|
|
479
|
+
|
|
480
|
+
def lower(self, val):
|
|
481
|
+
val = self.single_value(val, str)
|
|
482
|
+
if val == []:
|
|
483
|
+
return val
|
|
484
|
+
else:
|
|
485
|
+
return [val.lower()]
|
|
486
|
+
|
|
487
|
+
def upper(self, val):
|
|
488
|
+
val = self.single_value(val, str)
|
|
489
|
+
if val == []:
|
|
490
|
+
return val
|
|
491
|
+
else:
|
|
492
|
+
return [val.upper()]
|
|
493
|
+
|
|
494
|
+
def substring(self, val, start, length):
|
|
495
|
+
val = self.string_value(val)
|
|
496
|
+
start = self.single_value(start, int)
|
|
497
|
+
length = self.single_value(length, int)
|
|
498
|
+
if val == [] or start == [] or start < 0 or start >= len(val):
|
|
499
|
+
return []
|
|
500
|
+
elif length == []:
|
|
501
|
+
return [val[start:]]
|
|
502
|
+
else:
|
|
503
|
+
return [val[start:(start+length)]]
|
|
504
|
+
|
|
505
|
+
def matches(self, val, regex):
|
|
506
|
+
val = self.string_value(val)
|
|
507
|
+
regex = self.string_value(self.single_value(regex, str, self.model.string))
|
|
508
|
+
if val == [] or regex == []:
|
|
509
|
+
return []
|
|
510
|
+
else:
|
|
511
|
+
return [re.fullmatch(val, regex)]
|
|
512
|
+
|
|
513
|
+
def contains(self, val, other):
|
|
514
|
+
val = self.string_value(val)
|
|
515
|
+
other = self.string_value(self.single_value(other, str, self.model.string))
|
|
516
|
+
if val == [] or other == []:
|
|
517
|
+
return []
|
|
518
|
+
else:
|
|
519
|
+
return [other in val]
|
|
520
|
+
|
|
521
|
+
def startswith(self, val, other):
|
|
522
|
+
val = self.string_value(val)
|
|
523
|
+
other = self.string_value(self.single_value(other, str, self.model.string))
|
|
524
|
+
if val == [] or other == []:
|
|
525
|
+
return []
|
|
526
|
+
else:
|
|
527
|
+
return [val.startswith(other)]
|
|
528
|
+
|
|
529
|
+
def endswith(self, val, other):
|
|
530
|
+
val = self.string_value(val)
|
|
531
|
+
other = self.string_value(self.single_value(other, str, self.model.string))
|
|
532
|
+
if val == [] or other == []:
|
|
533
|
+
return []
|
|
534
|
+
else:
|
|
535
|
+
return [val.endswith(other)]
|
|
536
|
+
|
|
537
|
+
def indexof(self, val, other):
|
|
538
|
+
val = self.string_value(val)
|
|
539
|
+
other = self.string_value(self.single_value(other, str, self.model.string))
|
|
540
|
+
if val == [] or other == []:
|
|
541
|
+
return []
|
|
542
|
+
elif val == "":
|
|
543
|
+
return [0]
|
|
544
|
+
elif other in val:
|
|
545
|
+
return [val.index(other)]
|
|
546
|
+
else:
|
|
547
|
+
return [-1]
|
|
548
|
+
|
|
549
|
+
def concat(self, val, other):
|
|
550
|
+
val = self.string_value(self.single_value(val, str, self.model.string))
|
|
551
|
+
other = self.string_value(self.single_value(other, str, self.model.string))
|
|
552
|
+
if val == []:
|
|
553
|
+
val = ""
|
|
554
|
+
if other == []:
|
|
555
|
+
other = ""
|
|
556
|
+
return [val + other]
|
|
557
|
+
|
|
558
|
+
def skip(self, val, count):
|
|
559
|
+
count = self.single_value(count, int, self.model.integer)
|
|
560
|
+
if isinstance(count, self.model.integer):
|
|
561
|
+
count = count.value
|
|
562
|
+
if count <= 0:
|
|
563
|
+
return val
|
|
564
|
+
elif count >= len(val):
|
|
565
|
+
return []
|
|
566
|
+
else:
|
|
567
|
+
return val[count:]
|
|
568
|
+
|
|
569
|
+
def take(self, val, count):
|
|
570
|
+
count = self.single_value(count, int, self.model.integer)
|
|
571
|
+
if isinstance(count, self.model.integer):
|
|
572
|
+
count = count.value
|
|
573
|
+
if count <= 0:
|
|
574
|
+
return []
|
|
575
|
+
elif count >= len(val):
|
|
576
|
+
return val
|
|
577
|
+
else:
|
|
578
|
+
return val[:count]
|
|
579
|
+
|
|
580
|
+
def first(self, val):
|
|
581
|
+
if val == []:
|
|
582
|
+
return val
|
|
583
|
+
else:
|
|
584
|
+
return val[:1]
|
|
585
|
+
|
|
586
|
+
def last(self, val):
|
|
587
|
+
if val == []:
|
|
588
|
+
return val
|
|
589
|
+
else:
|
|
590
|
+
return val[-1:]
|
|
591
|
+
|
|
592
|
+
def tail(self, val):
|
|
593
|
+
if val == []:
|
|
594
|
+
return val
|
|
595
|
+
else:
|
|
596
|
+
return val[1:]
|
|
597
|
+
|
|
598
|
+
def children(self, val):
|
|
599
|
+
result = []
|
|
600
|
+
for v in val:
|
|
601
|
+
value_annotation = inspect.get_annotations(type(v).__init__)
|
|
602
|
+
for elem in value_annotation:
|
|
603
|
+
result += self.get(v, elem)
|
|
604
|
+
return result
|
|
605
|
+
|
|
606
|
+
def descendants(self, val):
|
|
607
|
+
result = []
|
|
608
|
+
queue = list(val)
|
|
609
|
+
next_queue = queue
|
|
610
|
+
while next_queue != []:
|
|
611
|
+
next_queue = []
|
|
612
|
+
for v in queue:
|
|
613
|
+
value_annotation = inspect.get_annotations(type(v).__init__)
|
|
614
|
+
for elem in value_annotation:
|
|
615
|
+
e = self.get(v, elem)
|
|
616
|
+
result += e
|
|
617
|
+
next_queue += e
|
|
618
|
+
queue = next_queue
|
|
619
|
+
return result
|
|
620
|
+
|
|
621
|
+
def repeat(self, val, proj):
|
|
622
|
+
result = []
|
|
623
|
+
queue = list(val)
|
|
624
|
+
next_queue = queue
|
|
625
|
+
while next_queue != []:
|
|
626
|
+
next_queue = []
|
|
627
|
+
for v in queue:
|
|
628
|
+
e = proj(v)
|
|
629
|
+
result += e
|
|
630
|
+
next_queue += e
|
|
631
|
+
queue = next_queue
|
|
632
|
+
return result
|
|
633
|
+
|
|
634
|
+
def aggregate(self, val, aggr, init):
|
|
635
|
+
result = init
|
|
636
|
+
for idx, v in enumerate(val):
|
|
637
|
+
result = aggr([v], idx, result)
|
|
638
|
+
return result
|
|
639
|
+
|
|
640
|
+
def allTrue(self, val):
|
|
641
|
+
for v in val:
|
|
642
|
+
if isinstance(v, bool):
|
|
643
|
+
if not v:
|
|
644
|
+
return [False]
|
|
645
|
+
elif isinstance(v, self.model.boolean):
|
|
646
|
+
if v.value != "true":
|
|
647
|
+
return [False]
|
|
648
|
+
else:
|
|
649
|
+
raise BaseException("Unexpected type, expected boolean for allTrue: %s" % val)
|
|
650
|
+
return [True]
|
|
651
|
+
|
|
652
|
+
def allFalse(self, val):
|
|
653
|
+
for v in val:
|
|
654
|
+
if isinstance(v, bool):
|
|
655
|
+
if v:
|
|
656
|
+
return [False]
|
|
657
|
+
elif isinstance(v, self.model.boolean):
|
|
658
|
+
if v.value == "true":
|
|
659
|
+
return [False]
|
|
660
|
+
else:
|
|
661
|
+
raise BaseException("Unexpected type, expected boolean for allFalse: %s" % val)
|
|
662
|
+
return [True]
|
|
663
|
+
|
|
664
|
+
def anyTrue(self, val):
|
|
665
|
+
return not self.allFalse(val)
|
|
666
|
+
|
|
667
|
+
def anyFalse(self, val):
|
|
668
|
+
return not self.allTrue(val)
|
|
669
|
+
|
|
670
|
+
def trace(self, val, name, projection=None):
|
|
671
|
+
if projection is not None:
|
|
672
|
+
print(f"TRACE {name}: {projection}")
|
|
673
|
+
else:
|
|
674
|
+
print(f"TRACE {name}: {val}")
|
|
675
|
+
return val
|
|
676
|
+
|
|
677
|
+
def subset_of(self, val, other):
|
|
678
|
+
coll1, coll2 = [], []
|
|
679
|
+
for src, trg in [(val, coll1), (other, coll2)]:
|
|
680
|
+
for val in src:
|
|
681
|
+
value_annotation = inspect.get_annotations(type(val).__init__).get("value", "")
|
|
682
|
+
if value_annotation == type(val).__name__ + "Enum" or value_annotation == type(val).__name__ + "_primitive":
|
|
683
|
+
key = val.value
|
|
684
|
+
else:
|
|
685
|
+
key = val
|
|
686
|
+
trg.append(key)
|
|
687
|
+
return [set(coll1) <= set(coll2)]
|
|
688
|
+
|
|
689
|
+
def superset_of(self, val, other):
|
|
690
|
+
coll1, coll2 = [], []
|
|
691
|
+
for src, trg in [(val, coll1), (other, coll2)]:
|
|
692
|
+
for val in src:
|
|
693
|
+
value_annotation = inspect.get_annotations(type(val).__init__).get("value", "")
|
|
694
|
+
if value_annotation == type(val).__name__ + "Enum" or value_annotation == type(val).__name__ + "_primitive":
|
|
695
|
+
key = val.value
|
|
696
|
+
else:
|
|
697
|
+
key = val
|
|
698
|
+
trg.append(key)
|
|
699
|
+
return [set(coll1) >= set(coll2)]
|
|
700
|
+
|
|
701
|
+
def intersect(self, val, other):
|
|
702
|
+
coll1, coll2 = [], []
|
|
703
|
+
for src, trg in [(val, coll1), (other, coll2)]:
|
|
704
|
+
for val in src:
|
|
705
|
+
value_annotation = inspect.get_annotations(type(val).__init__).get("value", "")
|
|
706
|
+
if value_annotation == type(val).__name__ + "Enum" or value_annotation == type(val).__name__ + "_primitive":
|
|
707
|
+
key = val.value
|
|
708
|
+
else:
|
|
709
|
+
key = val
|
|
710
|
+
trg.append(key)
|
|
711
|
+
return list(set(coll1) & set(coll2)) # TODO return container of basic types?
|
|
712
|
+
|
|
713
|
+
def distinct(self, val):
|
|
714
|
+
return self.union(val, [])
|
|
715
|
+
|
|
716
|
+
def is_distinct(self, val):
|
|
717
|
+
return [len(self.distinct(val)) == len(val)]
|
|
718
|
+
|
|
719
|
+
def _make_comparable(self, val):
|
|
720
|
+
if isinstance(val, self.model.integer) or isinstance(val, int):
|
|
721
|
+
type_ = "number"
|
|
722
|
+
prec = 0
|
|
723
|
+
if isinstance(val, self.model.integer):
|
|
724
|
+
key = int(val.value)
|
|
725
|
+
else:
|
|
726
|
+
key = val
|
|
727
|
+
elif isinstance(val, self.model.decimal) or isinstance(val, decimal.Decimal):
|
|
728
|
+
type_ = "number"
|
|
729
|
+
prec = 0
|
|
730
|
+
if isinstance(val, self.model.decimal):
|
|
731
|
+
key = decimal.Decimal(val.value)
|
|
732
|
+
else:
|
|
733
|
+
key = val
|
|
734
|
+
elif isinstance(val, self.model.string) or isinstance(val, str):
|
|
735
|
+
type_ = "string"
|
|
736
|
+
prec = 0
|
|
737
|
+
if isinstance(val, self.model.string):
|
|
738
|
+
key = val.value
|
|
739
|
+
else:
|
|
740
|
+
key = val
|
|
741
|
+
elif isinstance(val, self.model.dateTime) or isinstance(val, datetime.datetime):
|
|
742
|
+
val = val.value if isinstance(val, self.model.dateTime) else val
|
|
743
|
+
key = dateutil.parser.parse(val) if isinstance(val, str) else val
|
|
744
|
+
if isinstance(val, str):
|
|
745
|
+
prec = -1 * len(val.split(":"))
|
|
746
|
+
if key.tzinfo:
|
|
747
|
+
prec += -1
|
|
748
|
+
elif key.tzinfo:
|
|
749
|
+
prec = -4
|
|
750
|
+
else:
|
|
751
|
+
prec = -3
|
|
752
|
+
type_ = "date"
|
|
753
|
+
elif isinstance(val, self.model.date) or (isinstance(val, datetime.date) and not isinstance(val, datetime.datetime)):
|
|
754
|
+
key = val.value if isinstance(val, self.model.date) else val
|
|
755
|
+
if isinstance(key, str):
|
|
756
|
+
prec = len(key.split("-"))
|
|
757
|
+
if prec == 3:
|
|
758
|
+
key = dateutil.parser.parse(key).date()
|
|
759
|
+
else:
|
|
760
|
+
prec = 3
|
|
761
|
+
type_ = "date"
|
|
762
|
+
elif isinstance(val, self.model.time) or isinstance(val, datetime.time):
|
|
763
|
+
if isinstance(val, self.model.time):
|
|
764
|
+
key = val.value
|
|
765
|
+
prec = len(key.split(":"))
|
|
766
|
+
ms = "." in key
|
|
767
|
+
tz = "+" in key or "-" in key
|
|
768
|
+
form = ":".join("%H:%M:%S".split(":")[:prec]) + (".%f" if ms else "") + ("%Z" if tz else "")
|
|
769
|
+
key = datetime.datetime.strptime(key, form).time()
|
|
770
|
+
else:
|
|
771
|
+
prec = 3
|
|
772
|
+
key = val
|
|
773
|
+
type_ = "time"
|
|
774
|
+
elif isinstance(val, self.model.Quantity):
|
|
775
|
+
key = QuanitityComparable(val)
|
|
776
|
+
prec = 0
|
|
777
|
+
type_ = "number"
|
|
778
|
+
else:
|
|
779
|
+
value_annotation = inspect.get_annotations(type(val).__init__).get("value", "")
|
|
780
|
+
if value_annotation == type(val).__name__ + "Enum" or value_annotation == type(val).__name__ + "_primitive":
|
|
781
|
+
key = val.value
|
|
782
|
+
type_ = "string"
|
|
783
|
+
else:
|
|
784
|
+
key = val
|
|
785
|
+
type_ = "complex"
|
|
786
|
+
prec = 0
|
|
787
|
+
return key, prec, type_
|
|
788
|
+
|
|
789
|
+
def compare(self, val_a, operator, val_b):
|
|
790
|
+
val_a, prec_a, type_a = self._make_comparable(self.single_value(val_a))
|
|
791
|
+
val_b, prec_b, type_b = self._make_comparable(self.single_value(val_b))
|
|
792
|
+
if isinstance(val_a, datetime.date) and not isinstance(val_a, datetime.datetime) and isinstance(val_b, datetime.datetime):
|
|
793
|
+
val_b = val_b.date()
|
|
794
|
+
prec_b = 3
|
|
795
|
+
if isinstance(val_b, datetime.date) and not isinstance(val_b, datetime.datetime) and isinstance(val_a, datetime.datetime):
|
|
796
|
+
val_a = val_a.date()
|
|
797
|
+
prec_a = 3
|
|
798
|
+
if isinstance(val_b, int) and isinstance(val_a, str):
|
|
799
|
+
try:
|
|
800
|
+
val_a = int(val_a)
|
|
801
|
+
type_a = "number"
|
|
802
|
+
except:
|
|
803
|
+
pass
|
|
804
|
+
elif isinstance(val_a, int) and isinstance(val_b, str):
|
|
805
|
+
try:
|
|
806
|
+
val_b = int(val_b)
|
|
807
|
+
type_b = "number"
|
|
808
|
+
except:
|
|
809
|
+
pass
|
|
810
|
+
if val_a == [] or val_b == []:
|
|
811
|
+
return []
|
|
812
|
+
elif type_a != type_b:
|
|
813
|
+
raise BaseException("Invalid comparison with different types")
|
|
814
|
+
elif type_a == "complex" or type_b == "complex":
|
|
815
|
+
raise BaseException("Cannot compare complex types")
|
|
816
|
+
elif prec_a != prec_b:
|
|
817
|
+
return []
|
|
818
|
+
elif operator == "<=":
|
|
819
|
+
return [val_a <= val_b]
|
|
820
|
+
elif operator == "<":
|
|
821
|
+
return [val_a < val_b]
|
|
822
|
+
elif operator == ">":
|
|
823
|
+
return [val_a > val_b]
|
|
824
|
+
elif operator == ">=":
|
|
825
|
+
return [val_a >= val_b]
|
|
826
|
+
else:
|
|
827
|
+
raise BaseException("Invalid compare operator")
|
|
828
|
+
|
|
829
|
+
def _item_equal(self, v1, p1, v2, p2):
|
|
830
|
+
if isinstance(v1, int) and isinstance(v2, int) or isinstance(v1, bool) and isinstance(v2, bool):
|
|
831
|
+
return v1 == v2
|
|
832
|
+
elif (isinstance(v1, decimal.Decimal) or isinstance(v1, int)) and (isinstance(v2, decimal.Decimal) or isinstance(v2, int)):
|
|
833
|
+
return v1 == v2
|
|
834
|
+
elif isinstance(v1, str) and isinstance(v2, str):
|
|
835
|
+
return v1 == v2
|
|
836
|
+
elif isinstance(v1, datetime.date) and isinstance(v2, datetime.date) or isinstance(v1, datetime.time) and isinstance(v2. datetime.time):
|
|
837
|
+
if p1 != p2:
|
|
838
|
+
return []
|
|
839
|
+
return v1 == v2
|
|
840
|
+
elif isinstance(v1, QuanitityComparable) and isinstance(v2, QuanitityComparable):
|
|
841
|
+
# TODO conversion for decimal?
|
|
842
|
+
return v1 == v2
|
|
843
|
+
elif isinstance(v1, self.model.Element) and isinstance(v2, self.model.Element) and type(v1) == type(v2):
|
|
844
|
+
value_annotation = inspect.get_annotations(type(v1).__init__)
|
|
845
|
+
for elem in value_annotation:
|
|
846
|
+
e1 = self.get(v1, elem)
|
|
847
|
+
e2 = self.get(v2, elem)
|
|
848
|
+
if isinstance(e1, list):
|
|
849
|
+
if len(e1) != len(e2):
|
|
850
|
+
return False
|
|
851
|
+
for idx in range(0, len(e1)):
|
|
852
|
+
ev1 = e1[idx]
|
|
853
|
+
ev2 = e2[idx]
|
|
854
|
+
if not self._item_equal(ev1, 0, ev2, 0):
|
|
855
|
+
return False
|
|
856
|
+
elif not self._item_equal(e1, 0, e2, 0):
|
|
857
|
+
return False
|
|
858
|
+
return True
|
|
859
|
+
else:
|
|
860
|
+
return False
|
|
861
|
+
|
|
862
|
+
def equals(self, val_a, operator, val_b):
|
|
863
|
+
coll1, coll2, type1 = [], [], []
|
|
864
|
+
prec1, prec2, type2 = [], [], []
|
|
865
|
+
for src, trg, precs, types in [(val_a, coll1, prec1, type1), (val_b, coll2, prec2, type2)]:
|
|
866
|
+
for val in src:
|
|
867
|
+
key, prec, type_ = self._make_comparable(val)
|
|
868
|
+
trg.append(key)
|
|
869
|
+
precs.append(prec)
|
|
870
|
+
types.append(type_)
|
|
871
|
+
pos_result = operator == "=="
|
|
872
|
+
if coll1 == [] or coll2 == []:
|
|
873
|
+
return []
|
|
874
|
+
elif len(coll1) != len(coll2):
|
|
875
|
+
return [not pos_result]
|
|
876
|
+
elif operator in ["==", "!="]:
|
|
877
|
+
for idx in range(0, len(coll1)):
|
|
878
|
+
v1 = coll1[idx]
|
|
879
|
+
v2 = coll2[idx]
|
|
880
|
+
p1 = prec1[idx]
|
|
881
|
+
p2 = prec2[idx]
|
|
882
|
+
result = self._item_equal(v1, p1, v2, p2)
|
|
883
|
+
if not result:
|
|
884
|
+
return [not pos_result] if result != [] else []
|
|
885
|
+
return [pos_result]
|
|
886
|
+
else:
|
|
887
|
+
raise BaseException("Invalid equals operator")
|
|
888
|
+
|
|
889
|
+
def _item_equivalent(self, v1, v2):
|
|
890
|
+
if isinstance(v1, int) and isinstance(v2, int) or isinstance(v1, bool) and isinstance(v2, bool):
|
|
891
|
+
if v1 == v2:
|
|
892
|
+
return True
|
|
893
|
+
elif (isinstance(v1, decimal.Decimal) or isinstance(v1, int)) and (isinstance(v2, decimal.Decimal) or isinstance(v2, int)):
|
|
894
|
+
prec = 8
|
|
895
|
+
for v in [str(v1), str(v2)]:
|
|
896
|
+
prec = min(prec, len(v[v.find("."):][1:].rstrip("0")))
|
|
897
|
+
if round(v1, prec) == round(v2, prec):
|
|
898
|
+
return True
|
|
899
|
+
elif isinstance(v1, str) and isinstance(v2, str):
|
|
900
|
+
v1_ = v1.lower().replace("\t", " ").replace("\t", " ").replace("\n", " ")
|
|
901
|
+
v2_ = v2.lower().replace("\t", " ").replace("\t", " ").replace("\n", " ")
|
|
902
|
+
if v1_ == v2_:
|
|
903
|
+
return True
|
|
904
|
+
elif isinstance(v1, datetime.date) and isinstance(v2, datetime.date) or isinstance(v1, datetime.time) and isinstance(v2. datetime.time):
|
|
905
|
+
if v1 == v2:
|
|
906
|
+
return True
|
|
907
|
+
elif isinstance(v1, QuanitityComparable) and isinstance(v2, QuanitityComparable):
|
|
908
|
+
if v1 == v2:
|
|
909
|
+
return True
|
|
910
|
+
elif isinstance(v1, self.model.Element) and isinstance(v2, self.model.Element) and type(v1) == type(v2):
|
|
911
|
+
value_annotation = inspect.get_annotations(type(v1).__init__)
|
|
912
|
+
for elem in value_annotation:
|
|
913
|
+
e1 = self.get(v1, elem)
|
|
914
|
+
e2 = self.get(v2, elem)
|
|
915
|
+
if isinstance(e1, list):
|
|
916
|
+
for ev1 in e1:
|
|
917
|
+
found = False
|
|
918
|
+
for ev2 in e2:
|
|
919
|
+
if self._item_equivalent(ev1, ev2):
|
|
920
|
+
found = True
|
|
921
|
+
e2.remove(ev2)
|
|
922
|
+
break
|
|
923
|
+
if not found:
|
|
924
|
+
return False
|
|
925
|
+
elif not self._item_equivalent(e1, e2):
|
|
926
|
+
return False
|
|
927
|
+
return True
|
|
928
|
+
else:
|
|
929
|
+
return False
|
|
930
|
+
|
|
931
|
+
def equivalent(self, val_a, operator, val_b):
|
|
932
|
+
coll1, coll2 = [], []
|
|
933
|
+
for src, trg in [(val_a, coll1), (val_b, coll2)]:
|
|
934
|
+
for val in src:
|
|
935
|
+
trg.append(self._make_comparable(val)[0])
|
|
936
|
+
pos_result = operator == "~"
|
|
937
|
+
if coll1 == [] and coll2 == []:
|
|
938
|
+
return [pos_result]
|
|
939
|
+
elif len(coll1) != len(coll2):
|
|
940
|
+
return [not pos_result]
|
|
941
|
+
elif operator in ["~", "!~"]:
|
|
942
|
+
for v1 in coll1:
|
|
943
|
+
found = False
|
|
944
|
+
for v2 in coll2:
|
|
945
|
+
if self._item_equivalent(v1, v2):
|
|
946
|
+
found = True
|
|
947
|
+
coll2.remove(v2)
|
|
948
|
+
break
|
|
949
|
+
if not found:
|
|
950
|
+
return [not pos_result]
|
|
951
|
+
return [pos_result]
|
|
952
|
+
else:
|
|
953
|
+
raise BaseException("Invalid equals operator")
|
|
954
|
+
|
|
955
|
+
def strlength(self, val):
|
|
956
|
+
return [len(self.string_value(val))]
|
|
957
|
+
|
|
958
|
+
def toChars(self, val):
|
|
959
|
+
return list(self.string_value(val))
|
|
960
|
+
|
|
961
|
+
def toString(self, val):
|
|
962
|
+
val = self.single_value(val)
|
|
963
|
+
if val == []:
|
|
964
|
+
return []
|
|
965
|
+
elif isinstance(val, self.model.dateTime) or isinstance(val, datetime.datetime):
|
|
966
|
+
if isinstance(val, self.model.dateTime):
|
|
967
|
+
result = val.value
|
|
968
|
+
else:
|
|
969
|
+
result = val.strftime("%Y-%m-%dT%H:%M:%S.%f%Z") # +/- for TZ? %f only if needed
|
|
970
|
+
elif isinstance(val, self.model.date) or isinstance(val, datetime.date):
|
|
971
|
+
if isinstance(val, self.model.date):
|
|
972
|
+
result = val.value
|
|
973
|
+
else:
|
|
974
|
+
result = val.strftime("%Y-%m-%d")
|
|
975
|
+
elif isinstance(val, self.model.time) or isinstance(val, datetime.time):
|
|
976
|
+
if isinstance(val, self.model.time):
|
|
977
|
+
result = val.value
|
|
978
|
+
else:
|
|
979
|
+
result = val.strftime("%H:%M:%S.%f%Z") # +/- for TZ? %f only if needed
|
|
980
|
+
elif isinstance(val, self.model.string):
|
|
981
|
+
result = val.value
|
|
982
|
+
elif isinstance(val, str):
|
|
983
|
+
result = val
|
|
984
|
+
elif isinstance(val, self.model.Quantity):
|
|
985
|
+
result = str(val.value.value)
|
|
986
|
+
if val.unit:
|
|
987
|
+
if val.unit.value in time_units:
|
|
988
|
+
result += " '{%s}'" % val.unit.value
|
|
989
|
+
else:
|
|
990
|
+
result += " '%s'" % val.unit.value
|
|
991
|
+
elif val.code:
|
|
992
|
+
result += " '%s'" % val.code.value
|
|
993
|
+
elif isinstance(val, self.model.boolean) or isinstance(val, bool):
|
|
994
|
+
result = "true" if self.boolean_value(val) else "false"
|
|
995
|
+
elif isinstance(val, self.model.integer) or isinstance(val, int):
|
|
996
|
+
if isinstance(val, self.model.integer):
|
|
997
|
+
result = val.value
|
|
998
|
+
else:
|
|
999
|
+
result = str(val)
|
|
1000
|
+
elif isinstance(val, self.model.decimal) or isinstance(val, decimal.Decimal):
|
|
1001
|
+
if isinstance(val, self.model.decimal):
|
|
1002
|
+
result = val.value
|
|
1003
|
+
else:
|
|
1004
|
+
result = str(val)
|
|
1005
|
+
else:
|
|
1006
|
+
result = False
|
|
1007
|
+
return [result]
|
|
1008
|
+
|
|
1009
|
+
def convertsToString(self, val):
|
|
1010
|
+
return [self.toString(val) != False]
|
|
1011
|
+
|
|
1012
|
+
def toBoolean(self, val):
|
|
1013
|
+
val = self.single_value(val)
|
|
1014
|
+
is_true = False
|
|
1015
|
+
is_false = False
|
|
1016
|
+
if val == []:
|
|
1017
|
+
pass
|
|
1018
|
+
elif isinstance(val, bool):
|
|
1019
|
+
return [val]
|
|
1020
|
+
elif isinstance(val, self.model.boolean):
|
|
1021
|
+
return [val.value == "true"]
|
|
1022
|
+
elif isinstance(val, self.model.integer) or isinstance(val, int):
|
|
1023
|
+
if isinstance(val, self.model.integer):
|
|
1024
|
+
is_true = val.value == "1"
|
|
1025
|
+
is_false = val.value == "0"
|
|
1026
|
+
else:
|
|
1027
|
+
is_true = val == 1
|
|
1028
|
+
is_false = val == 0
|
|
1029
|
+
elif isinstance(val, self.model.decimal) or isinstance(val, decimal.Decimal):
|
|
1030
|
+
if isinstance(val, self.model.decimal):
|
|
1031
|
+
is_true = val.value == "1.0"
|
|
1032
|
+
is_false = val.value == "0.0"
|
|
1033
|
+
else:
|
|
1034
|
+
is_true = str(val) == "1.0"
|
|
1035
|
+
is_false = str(val) == "0.0"
|
|
1036
|
+
elif isinstance(val, self.model.string) or isinstance(val, str):
|
|
1037
|
+
val = self.string_value(val)
|
|
1038
|
+
is_true = val.lower() in ["true", "t", "yes", "y", "1", "1.0"]
|
|
1039
|
+
is_false = val.lower() in ["false", "f", "no", "n", "0", "0.0"]
|
|
1040
|
+
if is_true or is_false:
|
|
1041
|
+
return [is_true]
|
|
1042
|
+
else:
|
|
1043
|
+
return []
|
|
1044
|
+
|
|
1045
|
+
def convertsToBoolean(self, val):
|
|
1046
|
+
if val == []:
|
|
1047
|
+
return []
|
|
1048
|
+
else:
|
|
1049
|
+
return [self.toBoolean(val) != []]
|
|
1050
|
+
|
|
1051
|
+
def toInteger(self, val):
|
|
1052
|
+
val = self.single_value(val)
|
|
1053
|
+
if val == []:
|
|
1054
|
+
return []
|
|
1055
|
+
elif isinstance(val, int):
|
|
1056
|
+
return [val]
|
|
1057
|
+
elif isinstance(val, self.model.integer):
|
|
1058
|
+
return [val.value]
|
|
1059
|
+
elif isinstance(val, self.model.boolean) or isinstance(val, bool):
|
|
1060
|
+
return [1 if self.boolean_value(val) else 0]
|
|
1061
|
+
elif isinstance(val, self.model.string) or isinstance(val, str):
|
|
1062
|
+
try:
|
|
1063
|
+
return [int(self.string_value(val))]
|
|
1064
|
+
except:
|
|
1065
|
+
return []
|
|
1066
|
+
else:
|
|
1067
|
+
return []
|
|
1068
|
+
|
|
1069
|
+
def convertsToInteger(self, val):
|
|
1070
|
+
if val == []:
|
|
1071
|
+
return []
|
|
1072
|
+
else:
|
|
1073
|
+
return [self.toInteger(val) != []]
|
|
1074
|
+
|
|
1075
|
+
def toDecimal(self, val):
|
|
1076
|
+
val = self.single_value(val)
|
|
1077
|
+
if val == []:
|
|
1078
|
+
return val
|
|
1079
|
+
elif isinstance(val, decimal.Decimal):
|
|
1080
|
+
return [val]
|
|
1081
|
+
elif isinstance(val, self.model.decimal):
|
|
1082
|
+
return [decimal.Decimal(val.value)]
|
|
1083
|
+
elif isinstance(val, self.model.integer) or isinstance(val, int):
|
|
1084
|
+
if isinstance(val, self.model.integer):
|
|
1085
|
+
return [decimal.Decimal(val.value)]
|
|
1086
|
+
else:
|
|
1087
|
+
return [decimal.Decimal(val)]
|
|
1088
|
+
elif isinstance(val, self.model.boolean) or isinstance(val, bool):
|
|
1089
|
+
return [decimal.Decimal("1.0") if self.boolean_value(val) else decimal.Decimal("0.0")]
|
|
1090
|
+
elif isinstance(val, self.model.string) or isinstance(val, str):
|
|
1091
|
+
try:
|
|
1092
|
+
return [decimal.Decimal(self.string_value(val))]
|
|
1093
|
+
except:
|
|
1094
|
+
return []
|
|
1095
|
+
else:
|
|
1096
|
+
return []
|
|
1097
|
+
|
|
1098
|
+
def convertsToDecimal(self, val):
|
|
1099
|
+
if val == []:
|
|
1100
|
+
return []
|
|
1101
|
+
else:
|
|
1102
|
+
return [self.toDecimal(val) != []]
|
|
1103
|
+
|
|
1104
|
+
def toDate(self, val):
|
|
1105
|
+
val = self.single_value(val)
|
|
1106
|
+
if val == []:
|
|
1107
|
+
return []
|
|
1108
|
+
elif isinstance(val, self.model.dateTime) or isinstance(val, datetime.datetime):
|
|
1109
|
+
if isinstance(val, self.model.dateTime):
|
|
1110
|
+
result = self.model.date(value=dateutil.parser.parse(val.value).date().isoformat())
|
|
1111
|
+
else:
|
|
1112
|
+
result = self.model.date(value=val.date().isoformat())
|
|
1113
|
+
return [result]
|
|
1114
|
+
elif isinstance(val, self.model.date):
|
|
1115
|
+
return [val]
|
|
1116
|
+
elif isinstance(val, datetime.date):
|
|
1117
|
+
return [self.model.date(value=val.isoformat())]
|
|
1118
|
+
elif isinstance(val, self.model.string) or isinstance(val, str):
|
|
1119
|
+
try:
|
|
1120
|
+
return [self.model.date(value=datetime.datetime.strptime(val,"-".join("%Y-%m-%d".split("-")[:len(val.split("-"))])).date().isoformat())]
|
|
1121
|
+
except:
|
|
1122
|
+
return []
|
|
1123
|
+
else:
|
|
1124
|
+
return []
|
|
1125
|
+
|
|
1126
|
+
def convertsToDate(self, val):
|
|
1127
|
+
if val == []:
|
|
1128
|
+
return []
|
|
1129
|
+
else:
|
|
1130
|
+
return [self.toDate(val) != []]
|
|
1131
|
+
|
|
1132
|
+
def toDateTime(self, val):
|
|
1133
|
+
val = self.single_value(val)
|
|
1134
|
+
if val == []:
|
|
1135
|
+
return []
|
|
1136
|
+
elif isinstance(val, self.model.dateTime):
|
|
1137
|
+
return [val]
|
|
1138
|
+
elif isinstance(val, datetime.datetime):
|
|
1139
|
+
return [self.model.dateTime(value=val.isoformat())]
|
|
1140
|
+
elif isinstance(val, self.model.date) or isinstance(val, datetime.date):
|
|
1141
|
+
return self.model.dateTime(value=val.value if isinstance(val, self.model.date) else val.strftime("%Y-%m-%d"))
|
|
1142
|
+
elif isinstance(val, self.model.string) or isinstance(val, str):
|
|
1143
|
+
try:
|
|
1144
|
+
if "T" in val or not "-" in val: # support for CDA dates
|
|
1145
|
+
return [self.model.dateTime(value=dateutil.parser.parse(val).isoformat())]
|
|
1146
|
+
else:
|
|
1147
|
+
return [self.model.dateTime(value=datetime.datetime.strptime(val,"-".join("%Y-%m-%d".split("-")[:len(val.split("-"))])).isoformat())]
|
|
1148
|
+
except:
|
|
1149
|
+
return []
|
|
1150
|
+
else:
|
|
1151
|
+
return []
|
|
1152
|
+
|
|
1153
|
+
def convertsToDateTime(self, val):
|
|
1154
|
+
if val == []:
|
|
1155
|
+
return []
|
|
1156
|
+
else:
|
|
1157
|
+
return [self.toDateTime(val) != []]
|
|
1158
|
+
|
|
1159
|
+
def toTime(self, val):
|
|
1160
|
+
val = self.single_value(val)
|
|
1161
|
+
if val == []:
|
|
1162
|
+
return []
|
|
1163
|
+
elif isinstance(val, self.model.time):
|
|
1164
|
+
return [self.model.time(value=val.value)]
|
|
1165
|
+
elif isinstance(val, datetime.time):
|
|
1166
|
+
return [self.model.time(value=val.isoformat())]
|
|
1167
|
+
elif isinstance(val, self.model.string) or isinstance(val, str):
|
|
1168
|
+
ms = "." in val
|
|
1169
|
+
tz = "+" in val or "-" in val
|
|
1170
|
+
parts = len(val.split(":"))
|
|
1171
|
+
form = ":".join("%H:%M:%S".split(":")[:parts]) + (".%f" if ms else "") + ("%Z" if tz else "")
|
|
1172
|
+
try:
|
|
1173
|
+
return [self.model.time(value=datetime.datetime.strptime(val, form).time().isoformat())]
|
|
1174
|
+
except:
|
|
1175
|
+
return []
|
|
1176
|
+
else:
|
|
1177
|
+
return []
|
|
1178
|
+
|
|
1179
|
+
def convertsToTime(self, val):
|
|
1180
|
+
if val == []:
|
|
1181
|
+
return []
|
|
1182
|
+
else:
|
|
1183
|
+
return [self.toTime(val) != []]
|
|
1184
|
+
|
|
1185
|
+
def toQuantity(self, val, unit):
|
|
1186
|
+
val = self.single_value(val)
|
|
1187
|
+
if val == []:
|
|
1188
|
+
return val
|
|
1189
|
+
elif isinstance(val, self.model.Quantity):
|
|
1190
|
+
if unit and (val.unit and unit != val.unit.value or val.code and unit != val.code.value):
|
|
1191
|
+
return [] # TODO implement conversions
|
|
1192
|
+
else:
|
|
1193
|
+
return val
|
|
1194
|
+
elif isinstance(val, self.model.decimal) or isinstance(val, decimal.Decimal) or isinstance(val, self.model.integer) or isinstance(val, int):
|
|
1195
|
+
if isinstance(val, self.model.integer) or isinstance(val, self.model.decimal):
|
|
1196
|
+
value = val.value
|
|
1197
|
+
else:
|
|
1198
|
+
value = str(val)
|
|
1199
|
+
return [self.model.Quantity(value=self.model.decimal(value=value), code=self.model.string(value="1"))]
|
|
1200
|
+
elif isinstance(val, self.model.boolean) or isinstance(val, bool):
|
|
1201
|
+
value = "1.0" if self.boolean_value(val) else "0.0"
|
|
1202
|
+
return [self.model.Quantity(value=self.model.decimal(value=value), code=self.model.string(value="1"))]
|
|
1203
|
+
elif isinstance(val, self.model.string) or isinstance(val, str):
|
|
1204
|
+
match = re.fullmatch("(?P<value>(\\+|-)?\\d+(\\.\\d+)?)\\s*('(?P<unit>[^']+)'|(?P<time>[a-zA-Z]+))?", val)
|
|
1205
|
+
if match:
|
|
1206
|
+
try:
|
|
1207
|
+
value = decimal.Decimal(self.string_value(match.groupdict()["value"]))
|
|
1208
|
+
except:
|
|
1209
|
+
return []
|
|
1210
|
+
time_unit = match.groupdict().get("time", None)
|
|
1211
|
+
if time_unit and time_unit not in time_units:
|
|
1212
|
+
return []
|
|
1213
|
+
unit = match.groupdict().get("unit", None)
|
|
1214
|
+
if time_unit:
|
|
1215
|
+
return [self.model.Quantity(value=self.model.decimal(value=value), unit=self.model.string(value=time_unit))]
|
|
1216
|
+
else:
|
|
1217
|
+
return [self.model.Quantity(value=self.model.decimal(value=value), code=self.model.string(value=(unit or "1")))]
|
|
1218
|
+
else:
|
|
1219
|
+
return []
|
|
1220
|
+
else:
|
|
1221
|
+
return []
|
|
1222
|
+
|
|
1223
|
+
def convertsToQuantity(self, val, unit):
|
|
1224
|
+
if val == []:
|
|
1225
|
+
return []
|
|
1226
|
+
else:
|
|
1227
|
+
return [self.toQuantity(val, unit) != []]
|
|
1228
|
+
|
|
1229
|
+
def gettype(self, val):
|
|
1230
|
+
if type(val) in self.simple_type_names:
|
|
1231
|
+
return [SimpleTypeInfo(namespace="System", name=self.simple_type_names[type(val)])]
|
|
1232
|
+
elif isinstance(val, list):
|
|
1233
|
+
pass # TODO: return type info obj
|
|
1234
|
+
return []
|
|
1235
|
+
else:
|
|
1236
|
+
elements = []
|
|
1237
|
+
value_annotation = inspect.get_annotations(type(val).__init__)
|
|
1238
|
+
for elem, type_ in value_annotation.items():
|
|
1239
|
+
if isinstance(type_, str):
|
|
1240
|
+
elements.append(ClassInfoElement(elem, type_="FHIR."+type_))
|
|
1241
|
+
else:
|
|
1242
|
+
elements.append(ClassInfoElement(elem, type_="List<%s>" % type_.__args__[0].__forward_arg__))
|
|
1243
|
+
return [ClassInfo(namespace="FHIR", name=type(val).__name__, baseType="FHIR." + type(val).__base__.__name__, element=elements)]
|
|
1244
|
+
|
|
1245
|
+
def gettype_fromspec(self, type_):
|
|
1246
|
+
parts = type_.split(".")
|
|
1247
|
+
type_class = None
|
|
1248
|
+
type_name = parts[-1]
|
|
1249
|
+
type_ns = parts[0] if len(parts) > 1 else None
|
|
1250
|
+
if type_ns == "FHIR" or type_ns is None:
|
|
1251
|
+
try:
|
|
1252
|
+
type_class = getattr(self.model, type_name)
|
|
1253
|
+
except:
|
|
1254
|
+
if type_ns is not None:
|
|
1255
|
+
raise BaseException("Unknown type %s" % type_)
|
|
1256
|
+
if type_ns == "System" or (type_ns is None and not type_class):
|
|
1257
|
+
for key, t_name in self.simple_type_names.items():
|
|
1258
|
+
if type_name == t_name:
|
|
1259
|
+
type_class = key
|
|
1260
|
+
if not type_class:
|
|
1261
|
+
raise BaseException("Unknown type %s" % type_)
|
|
1262
|
+
return type_class
|
|
1263
|
+
|
|
1264
|
+
def is_type(self, val, type_):
|
|
1265
|
+
val = self.single_value(val)
|
|
1266
|
+
if val == []:
|
|
1267
|
+
return []
|
|
1268
|
+
if isinstance(type_, str):
|
|
1269
|
+
type_class = self.gettype_fromspec(type_)
|
|
1270
|
+
if isinstance(val, bool) and type_class == int:
|
|
1271
|
+
return [False]
|
|
1272
|
+
else:
|
|
1273
|
+
return [isinstance(val, type_class)]
|
|
1274
|
+
elif isinstance(type_, list):
|
|
1275
|
+
return [False]
|
|
1276
|
+
|
|
1277
|
+
def as_type(self, val, type_):
|
|
1278
|
+
return val if self.is_type(val, type_) == [True] else []
|
|
1279
|
+
|
|
1280
|
+
def conformsTo(self, val, profile):
|
|
1281
|
+
if len(val) != 1 and len(profile) != 1:
|
|
1282
|
+
return [False]
|
|
1283
|
+
elif not isinstance(val[0], self.model.Element) and not (isinstance(profile[0], str) or isinstance(profile[0], self.model.string)):
|
|
1284
|
+
return [False]
|
|
1285
|
+
else:
|
|
1286
|
+
profile = self.string_value(profile[0])
|
|
1287
|
+
if profile.startswith("http://hl7.org/fhir/StructureDefinition/"):
|
|
1288
|
+
return [type(val[0]).__name__ == profile.rstrip("/").split("/")[-1]]
|
|
1289
|
+
else:
|
|
1290
|
+
meta = self.get(val[0], "meta") # TODO add test
|
|
1291
|
+
if not meta:
|
|
1292
|
+
return [False]
|
|
1293
|
+
profiles = self.get(meta, "profile")
|
|
1294
|
+
for p in profiles:
|
|
1295
|
+
p = self.string_value(p)
|
|
1296
|
+
if p == profile:
|
|
1297
|
+
return [True]
|
|
1298
|
+
return [False]
|
|
1299
|
+
|
|
1300
|
+
def extension(self, val, extension):
|
|
1301
|
+
if val == [] or extension == []:
|
|
1302
|
+
return []
|
|
1303
|
+
else:
|
|
1304
|
+
extension = self.string_value(self.single_value(extension, str, self.model.string))
|
|
1305
|
+
for ext in self.get(val, "extension"):
|
|
1306
|
+
if ext.url == extension:
|
|
1307
|
+
return [ext]
|
|
1308
|
+
return []
|
|
1309
|
+
|
|
1310
|
+
def resolve(self, reference, context):
|
|
1311
|
+
reference = self.string_value(reference)
|
|
1312
|
+
is_absolute = bool(urlparse(reference).netloc)
|
|
1313
|
+
roots = set()
|
|
1314
|
+
for ctx in context:
|
|
1315
|
+
while getattr(ctx, "parent_object_", []):
|
|
1316
|
+
if ctx.parent_object_.__class__.__name__ == "ResourceContainer" and reference.startswith("#"):
|
|
1317
|
+
break
|
|
1318
|
+
ctx = ctx.parent_object_
|
|
1319
|
+
if hasattr(ctx, "parent_object_"):
|
|
1320
|
+
roots.add(ctx)
|
|
1321
|
+
for root in roots:
|
|
1322
|
+
if root.gds_elementtree_node_.__class__.__module__ == "lxml.etree": # XML input
|
|
1323
|
+
parts = reference.split("/")
|
|
1324
|
+
if reference.startswith("#"):
|
|
1325
|
+
elem = root.gds_elementtree_node_.xpath(".//*[@id='{0}' or @ID='{0}']".format(reference[1:]))
|
|
1326
|
+
if elem:
|
|
1327
|
+
return [_get_module(root).node_dict[elem[0]]]
|
|
1328
|
+
else:
|
|
1329
|
+
elem = root.gds_elementtree_node_.xpath(".//*[local-name()='id' and @value='{0}']".format(reference[1:]))
|
|
1330
|
+
if elem:
|
|
1331
|
+
return [_get_module(root).node_dict[elem[0]].parent_object_]
|
|
1332
|
+
elif not is_absolute and len(parts) == 2:
|
|
1333
|
+
elem = root.gds_elementtree_node_.xpath("//*[local-name()='{0}']/*[local-name()='id' and @value='{1}']".format(parts[0], parts[1]))
|
|
1334
|
+
if elem:
|
|
1335
|
+
return [_get_module(root).node_dict[elem[0]].parent_object_]
|
|
1336
|
+
elif not is_absolute and len(parts) == 1:
|
|
1337
|
+
elem = root.gds_elementtree_node_.xpath("//*[local-name()='id' and @value='{0}']".format(reference))
|
|
1338
|
+
if elem:
|
|
1339
|
+
return [_get_module(root).node_dict[elem[0]].parent_object_]
|
|
1340
|
+
else:
|
|
1341
|
+
raise NotImplementedError("Resolve not implemented for non-local references")
|
|
1342
|
+
elif isinstance(root.gds_elementtree_node_, dict):
|
|
1343
|
+
def lookup(dict_var, type_, id):
|
|
1344
|
+
for k, v in dict_var.items():
|
|
1345
|
+
if k == "id" and v == id:
|
|
1346
|
+
if type_ is not None and dict_var["resourceType"] != type_:
|
|
1347
|
+
continue
|
|
1348
|
+
yield dict_var
|
|
1349
|
+
elif isinstance(v, list):
|
|
1350
|
+
for v_ in v:
|
|
1351
|
+
for id_val in lookup(v_, type_, id):
|
|
1352
|
+
yield id_val
|
|
1353
|
+
elif isinstance(v, dict):
|
|
1354
|
+
for id_val in lookup(v, type_, id):
|
|
1355
|
+
yield id_val
|
|
1356
|
+
parts = reference.split("/")
|
|
1357
|
+
if reference.startswith("#"):
|
|
1358
|
+
elem = next(iter(lookup(root.gds_elementtree_node_, None, reference[1:])), None)
|
|
1359
|
+
if elem:
|
|
1360
|
+
return [elem["@node"]]
|
|
1361
|
+
elif not is_absolute and len(parts) == 2:
|
|
1362
|
+
elem = next(iter(lookup(root.gds_elementtree_node_, parts[0], parts[1])), None)
|
|
1363
|
+
if elem:
|
|
1364
|
+
return [elem["@node"]]
|
|
1365
|
+
elif not is_absolute and len(parts) == 1:
|
|
1366
|
+
elem = next(iter(lookup(root.gds_elementtree_node_, None, parts[1])), None)
|
|
1367
|
+
if elem:
|
|
1368
|
+
return [elem["@node"]]
|
|
1369
|
+
else:
|
|
1370
|
+
raise NotImplementedError("Resolve not implemented for non-local references")
|
|
1371
|
+
else:
|
|
1372
|
+
raise NotImplementedError("Resolve not implemented for input format")
|
|
1373
|
+
return []
|
|
1374
|
+
|
|
1375
|
+
def single(lst):
|
|
1376
|
+
if len(lst) == 0:
|
|
1377
|
+
return None
|
|
1378
|
+
elif len(lst) == 1:
|
|
1379
|
+
return lst[0]
|
|
1380
|
+
else:
|
|
1381
|
+
raise BaseException("Single value expected, multiple found.")
|
|
1382
|
+
|
|
1383
|
+
def _get_module(var_type):
|
|
1384
|
+
mod_split = var_type.__module__.split(".")
|
|
1385
|
+
return getattr(__import__(".".join(mod_split[0:-1]), fromlist=[mod_split[-1]]), mod_split[-1])
|