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.
@@ -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])