tricc-oo 1.4.17__tar.gz → 1.4.19__tar.gz

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.
Files changed (51) hide show
  1. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/PKG-INFO +2 -1
  2. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/pyproject.toml +2 -1
  3. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/converters/cql_to_operation.py +24 -4
  4. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/converters/datadictionnary.py +4 -0
  5. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/converters/xml_to_tricc.py +3 -1
  6. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/models/base.py +12 -13
  7. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/models/tricc.py +5 -2
  8. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/serializers/xls_form.py +11 -119
  9. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/strategies/input/drawio.py +1 -1
  10. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/strategies/output/xls_form.py +10 -7
  11. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/strategies/output/xlsform_cdss.py +0 -4
  12. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/strategies/output/xlsform_cht.py +1 -1
  13. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/visitors/tricc.py +120 -87
  14. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo.egg-info/PKG-INFO +2 -1
  15. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo.egg-info/requires.txt +1 -0
  16. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/README.md +0 -0
  17. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/setup.cfg +0 -0
  18. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tests/build.py +0 -0
  19. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tests/test_cql.py +0 -0
  20. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tests/to_ocl.py +0 -0
  21. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/__init__.py +0 -0
  22. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/converters/__init__.py +0 -0
  23. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/converters/codesystem_to_ocl.py +0 -0
  24. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/converters/cql/cqlLexer.py +0 -0
  25. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/converters/cql/cqlListener.py +0 -0
  26. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/converters/cql/cqlParser.py +0 -0
  27. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/converters/cql/cqlVisitor.py +0 -0
  28. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/converters/drawio_type_map.py +0 -0
  29. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/converters/tricc_to_xls_form.py +0 -0
  30. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/converters/utils.py +0 -0
  31. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/models/__init__.py +0 -0
  32. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/models/calculate.py +0 -0
  33. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/models/lang.py +0 -0
  34. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/models/ocl.py +0 -0
  35. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/models/ordered_set.py +0 -0
  36. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/parsers/__init__.py +0 -0
  37. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/parsers/xml.py +0 -0
  38. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/serializers/__init__.py +0 -0
  39. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/serializers/planuml.py +0 -0
  40. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/strategies/__init__.py +0 -0
  41. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/strategies/input/__init__.py +0 -0
  42. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/strategies/input/base_input_strategy.py +0 -0
  43. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/strategies/output/base_output_strategy.py +0 -0
  44. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/strategies/output/spice.py +0 -0
  45. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/strategies/output/xlsform_cht_hf.py +0 -0
  46. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/visitors/__init__.py +0 -0
  47. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/visitors/utils.py +0 -0
  48. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo/visitors/xform_pd.py +0 -0
  49. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo.egg-info/SOURCES.txt +0 -0
  50. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo.egg-info/dependency_links.txt +0 -0
  51. {tricc_oo-1.4.17 → tricc_oo-1.4.19}/tricc_oo.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: tricc-oo
3
- Version: 1.4.17
3
+ Version: 1.4.19
4
4
  Summary: Python library that converts CDSS L2 in L3
5
5
  Project-URL: Homepage, https://github.com/SwissTPH/tricc
6
6
  Project-URL: Issues, https://github.com/SwissTPH/tricc/issues
@@ -14,6 +14,7 @@ Requires-Dist: markdownify
14
14
  Requires-Dist: pydantic
15
15
  Requires-Dist: babel
16
16
  Requires-Dist: xlsxwriter
17
+ Requires-Dist: html2text
17
18
  Requires-Dist: pandas
18
19
  Requires-Dist: polib
19
20
  Requires-Dist: strenum
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tricc-oo"
7
- version = "1.4.17"
7
+ version = "1.4.19"
8
8
  description = "Python library that converts CDSS L2 in L3"
9
9
  readme = "README.md"
10
10
 
@@ -19,6 +19,7 @@ dependencies = [
19
19
  "pydantic",
20
20
  "babel",
21
21
  "xlsxwriter",
22
+ "html2text",
22
23
  "pandas",
23
24
  "polib",
24
25
  "strenum",
@@ -33,6 +33,7 @@ FUNCTION_MAP = {
33
33
  class cqlToXlsFormVisitor(cqlVisitor):
34
34
  def __init__(self):
35
35
  self.xlsform_rows = []
36
+ self.errors= []
36
37
 
37
38
  def resolve_scv(self, arg):
38
39
 
@@ -42,9 +43,15 @@ class cqlToXlsFormVisitor(cqlVisitor):
42
43
  # if no code or not found return None
43
44
  if arg.startswith('"') and arg.endswith('"'):
44
45
  return TriccReference(arg[1:-1])
45
- else:
46
+ elif arg.lower() in ['true', 'false']:
47
+ return TriccStatic(arg.lower() == 'true')
48
+ elif arg != 'runner':
49
+ self.errors.append(f"'{arg}' will be poccessed as reference ")
46
50
  return TriccReference(arg)
47
51
 
52
+ else:
53
+ return 'runner'
54
+
48
55
  def translate(self, arg, type=ANY):
49
56
  return self.resolve_scv(arg) or str(arg)
50
57
 
@@ -150,6 +157,13 @@ class cqlToXlsFormVisitor(cqlVisitor):
150
157
  function_name = ctx.getChild(2).getText()
151
158
  return not_clean(self._get_membership_expression(ctx, function_name))
152
159
 
160
+ def visitInvocationExpressionTerm(self, ctx):
161
+ result = super().visitInvocationExpressionTerm(ctx)
162
+ if isinstance(result, list) and all(isinstance(x, TriccStatic) for x in result):
163
+ value = '.'.join([x.value for x in result])
164
+ logger.warning(f"guessed reference for '{value}'")
165
+ return TriccReference(value)
166
+ return result
153
167
 
154
168
  def visitBetweenExpression(self, ctx):
155
169
  ref = self.visit(ctx.expression(0))
@@ -361,13 +375,13 @@ class CQLErrorListener(ErrorListener):
361
375
  self.errors.append(error)
362
376
 
363
377
  def transform_cql_to_operation(cql_input, context=None):
364
- cql_input = f"""
378
+ lib_input = f"""
365
379
  library runner
366
380
 
367
381
  define "calc":
368
382
  {cql_input.replace('−', '-')}
369
383
  """
370
- input_stream = InputStream(chr(10).join(cql_input.split('\n')))
384
+ input_stream = InputStream(chr(10).join(lib_input.split('\n')))
371
385
  lexer = cqlLexer(input_stream)
372
386
  stream = CommonTokenStream(lexer)
373
387
  parser = cqlParser(stream)
@@ -383,12 +397,18 @@ def transform_cql_to_operation(cql_input, context=None):
383
397
  # Check for errors
384
398
  if error_listener.errors:
385
399
  for error in error_listener.errors:
386
- print(f"CQL Grammar Error: {error}")
400
+ print(f"CQL Grammar Error: {error} \n in:\n {cql_input}")
387
401
  return None # Or handle errors as appropriate for your use case
388
402
 
389
403
  # If no errors, proceed with visitor
390
404
  visitor = cqlToXlsFormVisitor()
405
+
391
406
  visitor.visit(tree)
407
+ if visitor.errors:
408
+ logger.warning(f"while visiting cql: \n{cql_input}")
409
+ for e in visitor.errors:
410
+ logger.warning(e)
411
+
392
412
  return visitor.xlsform_rows[0]['calculation']
393
413
 
394
414
  def transform_cql_lib_to_operations(cql_input):
@@ -16,6 +16,10 @@ logger = logging.getLogger("default")
16
16
 
17
17
 
18
18
  def lookup_codesystems_code(codesystems, ref):
19
+ if ref.startswith('final.'):
20
+ concept = lookup_codesystems_code(codesystems, ref[6:])
21
+ if concept:
22
+ return concept
19
23
  for code_system in codesystems.values():
20
24
  for concept in code_system.concept or []:
21
25
  if concept.code == ref:
@@ -35,6 +35,7 @@ DISPLAY_ATTRIBUTES = [
35
35
  'help'
36
36
  ]
37
37
  logger = logging.getLogger("default")
38
+ import html2text
38
39
 
39
40
 
40
41
 
@@ -211,6 +212,7 @@ def process_edges(diagram, media_path, activity, nodes):
211
212
  ):
212
213
  if isinstance(nodes[edge.source], TriccNodeRhombus):
213
214
  edge.source = nodes[edge.source].path.id
215
+ edge.source_external_id = None
214
216
  processed = True
215
217
  elif label.lower() in (TRICC_YES_LABEL) or label == "":
216
218
  # do nothinbg for yes
@@ -219,7 +221,7 @@ def process_edges(diagram, media_path, activity, nodes):
219
221
  calc = process_factor_edge(edge, nodes)
220
222
  elif label.lower() in TRICC_NO_LABEL:
221
223
  calc = process_exclusive_edge(edge, nodes)
222
- elif any(reserved in label.lower() for reserved in ([str(o) for o in list(TriccOperator)] + list(OPERATION_LIST.keys()) + ['$this'])):
224
+ elif any(reserved in html2text.html2text(label.lower()) for reserved in ([str(o) for o in list(TriccOperator)] + list(OPERATION_LIST.keys()) + ['$this'])):
223
225
  # manage comment
224
226
  calc = process_condition_edge(edge, nodes)
225
227
  else:
@@ -118,10 +118,6 @@ class TriccBaseModel(BaseModel):
118
118
 
119
119
  def make_instance(self, nb_instance, **kwargs):
120
120
  instance = self.copy()
121
- # change the id to avoid collision of name
122
- instance.id = generate_id(f"{self.id}{nb_instance}")
123
- instance.instance = int(nb_instance)
124
- instance.base_instance = self
125
121
  attr_dict = self.to_dict()
126
122
  for attr, value in attr_dict.items():
127
123
  if not attr.startswith('_') and value is not None:
@@ -132,6 +128,13 @@ class TriccBaseModel(BaseModel):
132
128
  setattr(instance, attr, value)
133
129
  except Exception as e:
134
130
  logger.warning(f"Warning: Could not copy attribute {attr}: {e}")
131
+
132
+ # change the id to avoid collision of name
133
+ instance.id = generate_id(f"{self.id}{nb_instance}")
134
+ instance.instance = int(nb_instance)
135
+ instance.base_instance = self
136
+
137
+
135
138
  # assign the defualt group
136
139
  # if activity is not None and self.group == activity.base_instance:
137
140
  # instance.group = instance
@@ -269,7 +272,7 @@ class TriccNodeBaseModel(TriccBaseModel):
269
272
  if self.name is None:
270
273
  self.name = get_rand_name(self.id)
271
274
  def get_references(self):
272
- return OrderedSet([self])
275
+ return OrderedSet()
273
276
 
274
277
  class TriccStatic(BaseModel):
275
278
  value: Union[str, float, int, bool]
@@ -302,7 +305,7 @@ class TriccStatic(BaseModel):
302
305
  return str(self.value)
303
306
 
304
307
  def __repr__(self):
305
- return "TriccStatic:"+str(type(self.value))+':' +str(self.value)
308
+ return self.__class__.__name__+":"+str(type(self.value))+':' +str(self.value)
306
309
 
307
310
  def get_references(self):
308
311
  return OrderedSet()
@@ -706,17 +709,13 @@ def nand_join(left, right):
706
709
  right_issue = right is None or right == ''
707
710
  left_neg = left == False or left == 0 or left == '0' or left == TriccStatic(False)
708
711
  right_neg = right == False or right == 0 or right == '0' or right == TriccStatic(False)
709
- if issubclass(left.__class__, TriccNodeBaseModel):
710
- left = get_export_name(left)
711
- if issubclass(right.__class__, TriccNodeBaseModel):
712
- right = get_export_name(right)
713
712
  if left_issue and right_issue:
714
713
  logger.critical("and with both terms empty")
715
714
  elif left_issue:
716
715
  logger.debug('and with empty left term')
717
- return negate_term(right)
716
+ return not_clean(right)
718
717
  elif left == '1' or left == 1 or left == TriccStatic(True):
719
- return negate_term(right)
718
+ return not_clean(right)
720
719
  elif right_issue :
721
720
  logger.debug('and with empty right term')
722
721
  return TriccStatic(False)
@@ -725,7 +724,7 @@ def nand_join(left, right):
725
724
  elif right_neg:
726
725
  return left
727
726
  else:
728
- return and_join([left, negate_term(right)])
727
+ return and_join([left, not_clean(right)])
729
728
 
730
729
 
731
730
  TriccGroup.update_forward_refs()
@@ -96,7 +96,9 @@ class TriccNodeActivity(TriccNodeBaseModel):
96
96
  return self.instances[instance_nb]
97
97
  else:
98
98
  instance = super().make_instance(instance_nb, activity=None)
99
- self.instances[instance_nb] = instance
99
+ base_instance = (self.base_instance or self)
100
+ base_instance.instances[instance_nb] = instance
101
+ instance.base_instance = base_instance
100
102
  # instance.base_instance = self
101
103
  # we duplicate all the related nodes (not the calculate, duplication is manage in calculate version code)
102
104
  nodes = {}
@@ -124,7 +126,7 @@ class TriccNodeActivity(TriccNodeBaseModel):
124
126
  if node in self.calculates and instance_node:
125
127
  instance.calculates.append(instance_node)
126
128
  # update parents
127
- for node in list(filter(lambda p_node: hasattr(p_node, 'parent'),list(instance.nodes.values()))):
129
+ for node in list(filter(lambda p_node: getattr(p_node, 'parent', None) is not None, list(instance.nodes.values()))):
128
130
  new_parent = list(filter(lambda p_node: p_node.base_instance == node.parent,list(instance.nodes.values())))
129
131
  if new_parent:
130
132
  node.parent = new_parent[0]
@@ -179,6 +181,7 @@ class TriccNodeActivity(TriccNodeBaseModel):
179
181
  if issubclass(node_instance.__class__, TriccRhombusMixIn):
180
182
  old_path = node_origin.path
181
183
  if old_path is not None:
184
+ node_instance.path = None
182
185
  for n in node_instance.activity.nodes.values():
183
186
  if n.base_instance.id == old_path.id:
184
187
  node_instance.path = n
@@ -18,6 +18,9 @@ from tricc_oo.visitors.tricc import (
18
18
  add_calculate,
19
19
  TRICC_TRUE_VALUE,
20
20
  TRICC_FALSE_VALUE,
21
+ get_applicability_expression,
22
+ get_prev_instance_skip_expression,
23
+ get_process_skip_expression,
21
24
  )
22
25
 
23
26
  logger = logging.getLogger("default")
@@ -32,7 +35,7 @@ BOOLEAN_MAP = {
32
35
 
33
36
 
34
37
  def start_group(
35
- strategy, cur_group, groups, df_survey, df_calculate, relevance=False, **kwargs
38
+ strategy, cur_group, groups, df_survey, df_calculate, processed_nodes, process, relevance=False, **kwargs
36
39
  ):
37
40
  name = get_export_name(cur_group)
38
41
 
@@ -46,20 +49,24 @@ def start_group(
46
49
  relevance = (
47
50
  relevance and cur_group.relevance is not None and cur_group.relevance != ""
48
51
  )
52
+
49
53
 
50
54
  group_calc_required = (
51
55
  False and relevance and not is_activity and len(relevance) > 100
52
56
  )
53
57
 
54
58
  relevance_expression = cur_group.relevance
59
+ relevance_expression = get_applicability_expression(cur_group, processed_nodes, process, relevance_expression)
60
+ relevance_expression = get_prev_instance_skip_expression(cur_group, processed_nodes, process, relevance_expression)
61
+ relevance_expression = get_process_skip_expression(cur_group, processed_nodes, process, relevance_expression)
62
+
55
63
  if not relevance:
56
64
  relevance_expression = ""
57
- elif isinstance(relevance_expression, TriccOperation):
65
+ elif isinstance(relevance_expression, (TriccOperation, TriccStatic)):
58
66
  relevance_expression = strategy.get_tricc_operation_expression(
59
67
  relevance_expression
60
68
  )
61
- elif isinstance(relevance_expression, TriccStatic):
62
- relevance_expression = str(relevance_expression.value)
69
+
63
70
 
64
71
  # elif is_activity:
65
72
  # relevance_expression = TRICC_CALC_EXPRESSION.format(get_export_name(cur_group.root))
@@ -628,118 +635,3 @@ def get_input_calc_line(node, replace_dots=True):
628
635
  ]
629
636
 
630
637
 
631
- def get_diagnostic_start_group_line():
632
- label = langs.get_trads("List of diagnostics", force_dict=True)
633
- empty = langs.get_trads("", force_dict=True)
634
- return [
635
- "begin group",
636
- "l_diag_list25",
637
- *list(label.values()),
638
- *list(empty.values()), # hint
639
- *list(empty.values()), # help
640
- "", # default
641
- "field-list", #'appearance',
642
- "", #'constraint',
643
- *list(empty.values()), #'constraint_message'
644
- "", #'relevance'
645
- "", #'disabled'
646
- "", #'required'
647
- *list(empty.values()), #'required message'
648
- "", #'read only'
649
- "", #'expression'
650
- "", #'repeat_count'
651
- "", #'image'
652
- "",
653
- ]
654
-
655
-
656
- def get_diagnostic_add_line(diags, df_choice):
657
- for diag in diags:
658
- df_choice.loc[len(df_choice)] = [
659
- "tricc_diag_add",
660
- get_export_name(diag),
661
- *list(langs.get_trads(diag.label, True).values()),
662
- "", # filter
663
- "", # min y
664
- "", # max Y
665
- "", # l
666
- "", # m
667
- "", # s
668
- ]
669
- label = langs.get_trads("Add a missing diagnostic", force_dict=True)
670
- empty = langs.get_trads("", force_dict=True)
671
- return [
672
- "select_multiple tricc_diag_add",
673
- "new_diag",
674
- *list(label.values()),
675
- *list(empty.values()), # hint
676
- *list(empty.values()), # help
677
- "", # default
678
- "minimal", #'appearance',
679
- "", #'constraint',
680
- *list(empty.values()), #'constraint_message',
681
- "", #'relevance'
682
- "", #'disabled'
683
- "", #'required'
684
- *list(empty.values()), #'required message'
685
- "", #'read only'
686
- "", #'expression'
687
- "", #'repeat_count'
688
- "", #'image'
689
- "",
690
- ]
691
-
692
-
693
- def get_diagnostic_none_line(diags):
694
- relevance = ""
695
- for diag in diags:
696
- relevance += TRICC_CALC_EXPRESSION.format(get_export_name(diag)) + " or "
697
- label = langs.get_trads(
698
- "Aucun diagnostic trouvé par l'outil mais cela ne veut pas dire que le patient est en bonne santé",
699
- force_dict=True,
700
- )
701
- empty = langs.get_trads("", force_dict=True)
702
- return [
703
- "note",
704
- "l_diag_none25",
705
- *list(label.values()),
706
- *list(empty.values()),
707
- *list(empty.values()),
708
- "", # default
709
- "", #'appearance',
710
- "", #'constraint',
711
- *list(empty.values()),
712
- f"not({relevance[:-4]})", #'relevance'
713
- "", #'disabled'
714
- "", #'required'
715
- *list(empty.values()),
716
- "", #'read only'
717
- "", #'expression'
718
- "", #'repeat_count'
719
- "", #'image' TRICC_NEGATE
720
- "",
721
- ]
722
-
723
-
724
- def get_diagnostic_stop_group_line():
725
- label = langs.get_trads("", force_dict=True)
726
- return [
727
- "end group",
728
- "l_diag_list25",
729
- *list(label.values()),
730
- *list(label.values()),
731
- *list(label.values()), # help
732
- "", # default
733
- "", #'appearance',
734
- "", #'constraint',
735
- *list(label.values()),
736
- "", #'relevance'
737
- "", #'disabled'
738
- "", #'required'
739
- *list(label.values()),
740
- "", #'read only'
741
- "", #'expression'
742
- "", #'repeat_count'
743
- "", #'image'
744
- "",
745
- ]
@@ -126,8 +126,8 @@ class DrawioStrategy(BaseInputStrategy):
126
126
 
127
127
  node_edge = list(
128
128
  filter(lambda x: (
129
- (x.source_external_id and x.source_external_id == node.external_id) or
130
129
  ( x.source and x.source == node.id) or
130
+ (not x.source and x.source_external_id and x.source_external_id == node.external_id) or
131
131
  x.source == node
132
132
  ), page.edges)
133
133
  )
@@ -85,7 +85,7 @@ class XLSFormStrategy(BaseOutPutStrategy):
85
85
 
86
86
 
87
87
  def clean_coalesce(self, expression):
88
- if re.match(r"^coalesce\(\${[^}]+},''\)$", expression):
88
+ if re.match(r"^coalesce\(\${[^}]+},''\)$", str(expression)):
89
89
  return expression[9:-4]
90
90
  return expression
91
91
 
@@ -174,8 +174,9 @@ class XLSFormStrategy(BaseOutPutStrategy):
174
174
  cur_group = activity
175
175
  groups[activity.id] = 0
176
176
  path_len = 0
177
+ process = ['main']
177
178
  # keep the vesrions on the group id, max version
178
- start_group(self, cur_group=cur_group, groups=groups, **self.get_kwargs())
179
+ start_group(self, cur_group=cur_group, groups=groups, processed_nodes=processed_nodes, process=process, **self.get_kwargs())
179
180
  walktrhough_tricc_node_processed_stached(
180
181
  activity.root,
181
182
  self.generate_export,
@@ -183,6 +184,7 @@ class XLSFormStrategy(BaseOutPutStrategy):
183
184
  stashed_nodes,
184
185
  path_len,
185
186
  cur_group=activity.root.group,
187
+ process=process,
186
188
  recursive=False,
187
189
  **self.get_kwargs()
188
190
  )
@@ -218,6 +220,8 @@ class XLSFormStrategy(BaseOutPutStrategy):
218
220
  self,
219
221
  cur_group=s_node.group,
220
222
  groups=groups,
223
+ processed_nodes=processed_nodes,
224
+ process=process,
221
225
  relevance=True,
222
226
  **self.get_kwargs()
223
227
  )
@@ -314,9 +318,8 @@ class XLSFormStrategy(BaseOutPutStrategy):
314
318
  save_calc = save_calc.iloc[0]
315
319
  if save_calc["name"] != drop_calc["name"]:
316
320
  self.df_survey.replace(
317
- "\$\{" + drop_calc["name"] + "\}",
318
- "\$\{" + save_calc["name"] + "\}",
319
- regex=True,
321
+ "${" + drop_calc["name"] + "}",
322
+ "${" + save_calc["name"] + "}",
320
323
  )
321
324
  else:
322
325
  logger.critical(
@@ -325,7 +328,7 @@ class XLSFormStrategy(BaseOutPutStrategy):
325
328
  )
326
329
  )
327
330
  for index, empty_calc in df_empty_calc.iterrows():
328
- self.df_survey.replace("\$\{" + empty_calc["name"] + "\}", "1", regex=True)
331
+ self.df_survey.replace("${" + empty_calc["name"] + "}", "1", regex=True)
329
332
 
330
333
  # TODO try to reinject calc to reduce complexity
331
334
  for i, c in self.df_calculate[
@@ -334,7 +337,7 @@ class XLSFormStrategy(BaseOutPutStrategy):
334
337
  real_calc = re.find(r"^number\((.+)\)$", c["calculation"])
335
338
  if real_calc is not None and real_calc != "":
336
339
  self.df_survey[~self.df_survey["name"] == c["name"]].replace(
337
- real_calc, "\$\{" + c["name"] + "\}"
340
+ real_calc, "${" + c["name"] + "}"
338
341
  )
339
342
 
340
343
  df_duplicate = self.df_survey[
@@ -1,10 +1,6 @@
1
1
  import logging
2
2
  from tricc_oo.models.tricc import TriccNodeActivity
3
3
  from tricc_oo.models.calculate import TriccNodeProposedDiagnosis, TriccNodeInput
4
- from tricc_oo.serializers.xls_form import (
5
- get_diagnostic_none_line,
6
- get_diagnostic_start_group_line,
7
- get_diagnostic_stop_group_line)
8
4
  from tricc_oo.converters.tricc_to_xls_form import get_export_name
9
5
  from tricc_oo.strategies.output.xls_form import XLSFormStrategy
10
6
  from tricc_oo.models.lang import SingletonLangClass
@@ -117,7 +117,7 @@ class XLSFormCHTStrategy(XLSFormCDSSStrategy):
117
117
  # pause
118
118
  ends = []
119
119
  for p in self.project.pages.values():
120
- p_ends = list(filter(lambda x: issubclass(x.__class__, TriccNodeEnd) and getattr(x, 'hint', None) is not None, p.nodes.values() ))
120
+ p_ends = list(filter(lambda x: issubclass(x.__class__, TriccNodeEnd) and getattr(x, 'process', '') == 'pause', p.nodes.values() ))
121
121
  if p_ends:
122
122
  ends += p_ends
123
123
  if ends:
@@ -120,7 +120,7 @@ def process_calculate(node,processed_nodes, stashed_nodes, calculates, used_calc
120
120
  node_name = node.name if not isinstance(node, TriccNodeEnd) else node.get_reference()
121
121
  last_version = get_last_version(node_name, processed_nodes) if issubclass(node.__class__, (TriccNodeDisplayModel, TriccNodeDisplayCalculateBase, TriccNodeEnd)) and not isinstance(node, TriccNodeSelectOption) else None
122
122
  #last_version = processed_nodes.find_prev(node, lambda item: hasattr(item, 'name') and item.name == node.name)
123
- if last_version:
123
+ if last_version and getattr(node, 'process', '') != 'pause':
124
124
  # 0-100 for manually specified instance. 100-200 for auto instance
125
125
  node.version = last_version.version + 1
126
126
  last_version.last = False
@@ -526,12 +526,15 @@ def process_reference(node, processed_nodes, calculates, used_calculates=None,
526
526
  node.reference = list(modified_expression.get_references())
527
527
  node.expression_reference = modified_expression
528
528
  elif getattr(node, 'reference', None):
529
- if isinstance(node.reference, list):
529
+ reference = node.reference
530
+ if isinstance(reference, list):
531
+ if isinstance(node, TriccNodeWait):
532
+ reference = [TriccOperation(TriccOperator.ISTRUE,[n]) for n in reference]
530
533
  if len(node.reference) == 1 :
531
- operation = node.reference[0]
534
+ operation = reference[0]
532
535
  else:
533
536
  operation = and_join(
534
- node.reference
537
+ reference
535
538
  )
536
539
  modified_expression = process_operation_reference(
537
540
  operation,
@@ -657,7 +660,7 @@ def process_operation_reference(operation, node, processed_nodes, calculates, us
657
660
  if codesystems:
658
661
  concept = lookup_codesystems_code(codesystems, ref)
659
662
  if not concept:
660
- logger.critical(f"reference {ref} not found in the project")
663
+ logger.critical(f"reference {ref} not found in the project for{str(node)} ")
661
664
  exit(1)
662
665
  else:
663
666
  if warn:
@@ -1144,23 +1147,34 @@ def check_stashed_loop(stashed_nodes, prev_stashed_nodes, processed_nodes, len_p
1144
1147
  es_node.activity.instance if hasattr(es_node,'activity') else '',
1145
1148
  es_node.__class__,
1146
1149
  es_node.get_name()))
1150
+ for es_node in [node for node_list in looped.values() for node in node_list if isinstance(node, TriccReference)]:
1151
+ logger.info("looped node {}:{}|{} {}".format(
1152
+ es_node.activity.get_name() if hasattr(es_node,'activity') else '' ,
1153
+ es_node.activity.instance if hasattr(es_node,'activity') else '',
1154
+ es_node.__class__,
1155
+ es_node.get_name()))
1156
+ for es_node in [node for node_list in waited.values() for node in node_list if isinstance(node, TriccReference)]:
1157
+ logger.info("waited node {}:{}|{} {}".format(
1158
+ es_node.activity.get_name() if hasattr(es_node,'activity') else '' ,
1159
+ es_node.activity.instance if hasattr(es_node,'activity') else '',
1160
+ es_node.__class__,
1161
+ es_node.get_name()))
1147
1162
  logger.info("looped nodes")
1148
1163
  for dep_list in looped:
1149
1164
  for d in looped[dep_list]:
1150
- if d.get_name() == dep_list:
1151
- logger.critical("[{}] depends on itself".format(
1152
- dep_list,
1165
+ if str(d) in looped:
1166
+ logger.critical("[{}] depends on [{}]".format(
1167
+ dep_list, str(d)
1168
+ ))
1169
+ else:
1170
+ logger.error("[{}] depends on [{}]".format(
1171
+ dep_list, str(d)
1153
1172
  ))
1154
- logger.error("[{}] depends on [{}]".format(
1155
- dep_list, str(d)
1156
- ))
1157
1173
  if dep_list in waited:
1158
1174
  for d in waited[dep_list]:
1159
1175
  logger.warning("[{}] depends on [{}]".format(
1160
1176
  dep_list, str(d)
1161
1177
  ))
1162
-
1163
- #reverse_walkthrough(es_node, es_node, print_trace, processed_nodes, stashed_nodes)
1164
1178
  logger.info("waited nodes")
1165
1179
  for dep_list in waited:
1166
1180
  if dep_list not in looped:
@@ -1177,7 +1191,6 @@ def check_stashed_loop(stashed_nodes, prev_stashed_nodes, processed_nodes, len_p
1177
1191
  loop_count = 0
1178
1192
  return loop_count
1179
1193
 
1180
-
1181
1194
  def add_to_tree(tree, n, d):
1182
1195
  n_str = str(n)
1183
1196
  if n_str not in tree:
@@ -1187,12 +1200,16 @@ def add_to_tree(tree, n, d):
1187
1200
  return tree
1188
1201
 
1189
1202
 
1190
- def get_all_dependant(loop, stashed_nodes, processed_nodes, depth=0, waited=None , looped=None):
1203
+ def get_all_dependant(loop, stashed_nodes, processed_nodes, depth=0, waited=None , looped=None, path = None):
1204
+ if path is None:
1205
+ path =[]
1191
1206
  if looped is None:
1192
1207
  looped = {}
1193
1208
  if waited is None:
1194
1209
  waited = {}
1195
1210
  for n in loop:
1211
+ cur_path = path.copy()
1212
+ cur_path.append(n)
1196
1213
  dependant = OrderedSet()
1197
1214
  i=0
1198
1215
  #logger.critical(f"{i}: {n.__class__}::{n.get_name()}::{getattr(n,'instance','')}::{process_reference(n, processed_nodes, [])}")
@@ -1204,28 +1221,30 @@ def get_all_dependant(loop, stashed_nodes, processed_nodes, depth=0, waited=None
1204
1221
  if not isinstance(dependant, list):
1205
1222
  pass
1206
1223
  for d in dependant:
1224
+ if d in path:
1225
+ logger.warning(f"loop {str(d)} already in path {'::'.join(map(path, str))} ")
1207
1226
  if isinstance(d, TriccNodeSelectOption):
1208
1227
  d = d.select
1209
- if d not in waited and d not in looped:
1210
- if isinstance(d, TriccReference):
1211
- if not any(n.name == d.value for n in processed_nodes):
1212
- if not any(n.name == d.value for n in stashed_nodes):
1213
- waited = add_to_tree(waited, n, d)
1214
- else :
1215
- looped = add_to_tree(looped, n, d)
1216
-
1217
- elif d not in processed_nodes:
1218
- if d in stashed_nodes:
1219
- looped = add_to_tree(looped, n, d)
1228
+
1229
+ if isinstance(d, TriccReference):
1230
+ if not any(n.name == d.value for n in processed_nodes):
1231
+ if not any(n.name == d.value for n in stashed_nodes):
1232
+ waited = add_to_tree(waited, n, d)
1220
1233
  else :
1221
- waited = add_to_tree(waited, n, d)
1234
+ looped = add_to_tree(looped, n, d)
1235
+
1236
+ elif d not in processed_nodes:
1237
+ if d in stashed_nodes:
1238
+ looped = add_to_tree(looped, n, d)
1239
+ else :
1240
+ waited = add_to_tree(waited, n, d)
1222
1241
  if depth < MAX_DRILL:
1223
- return get_all_dependant(waited, stashed_nodes, processed_nodes, depth+1, waited , looped)
1242
+ return get_all_dependant(looped, stashed_nodes, processed_nodes, depth+1, waited , looped, path=cur_path)
1224
1243
 
1225
1244
  return waited, looped
1226
1245
 
1227
1246
 
1228
- MAX_DRILL = 1
1247
+ MAX_DRILL = 3
1229
1248
 
1230
1249
  def get_last_end_node(processed_nodes, process=None):
1231
1250
  end_name = 'tricc_end_'
@@ -1452,7 +1471,7 @@ def get_node_expression( in_node, processed_nodes, is_calculate=False, is_prev=F
1452
1471
  expression = None
1453
1472
  negate_expression = None
1454
1473
  node = in_node
1455
- if isinstance(node, (TriccNodeActivityStart,TriccNodeMainStart, TriccNodeActivityEnd)):
1474
+ if isinstance(node, (TriccNodeActivityStart,TriccNodeMainStart)):
1456
1475
  if is_prev and is_calculate:
1457
1476
  expression = get_node_expression(
1458
1477
  node.activity,
@@ -1462,7 +1481,9 @@ def get_node_expression( in_node, processed_nodes, is_calculate=False, is_prev=F
1462
1481
  negate=negate,
1463
1482
  process=process
1464
1483
  )
1465
- elif isinstance(node, (TriccNodeActivityStart)):
1484
+ if isinstance(node, TriccNodeMainStart):
1485
+ expression = get_applicability_expression(node.activity, processed_nodes, process, expression )
1486
+ elif isinstance(node, (TriccNodeActivityStart)):
1466
1487
  return None
1467
1488
 
1468
1489
  elif isinstance(node, TriccNodeWait):
@@ -1522,7 +1543,7 @@ def get_node_expression( in_node, processed_nodes, is_calculate=False, is_prev=F
1522
1543
  # else:
1523
1544
  expression = node.expression_reference
1524
1545
  elif not is_prev and hasattr(node, 'relevance') and isinstance(node.relevance, TriccOperation):
1525
- expression = node.relevance
1546
+ expression = node.relevance
1526
1547
  elif is_prev and isinstance(node, TriccNodeSelectOption):
1527
1548
  if negate:
1528
1549
  negate_expression = get_selected_option_expression(node, negate)
@@ -1548,61 +1569,10 @@ def get_node_expression( in_node, processed_nodes, is_calculate=False, is_prev=F
1548
1569
  if expression is None:
1549
1570
  expression = get_prev_node_expression(node, processed_nodes=processed_nodes, is_calculate=is_calculate, process=process)
1550
1571
  # in_node not in processed_nodes is need for calculates that can but run after the end of the activity
1551
-
1552
-
1553
- if isinstance(node, TriccNodeActivity):
1554
-
1555
-
1556
- if node.base_instance is not None and not is_prev:
1557
- activity = node
1558
- expression_inputs = []
1559
- past_instances = [
1560
- n for n in processed_nodes if getattr(n.base_instance, 'id', None) == node.base_instance.id
1561
- ]
1562
- for past_instance in past_instances:
1563
- add_sub_expression(
1564
- expression_inputs,
1565
- get_node_expression(
1566
- past_instance,
1567
- processed_nodes=processed_nodes,
1568
- is_calculate=False,
1569
- is_prev=True,
1570
- process=process
1571
- )
1572
- )
1573
-
1574
- if isinstance(node.applicability,(TriccStatic,TriccOperation, TriccReference)):
1575
- if expression:
1576
- expression = and_join([node.applicability, expression])
1577
- else:
1578
- expression = node.applicability
1579
- if expression and expression_inputs:
1580
- add_sub_expression(expression_inputs, expression)
1581
- expression = nand_join(expression, or_join(expression_inputs))
1582
- elif expression_inputs:
1583
- expression = negate_term(or_join(expression_inputs))
1584
- if not is_prev:
1585
- end_expressions = []
1586
- f_end_expression = get_end_expression(processed_nodes)
1587
- if f_end_expression:
1588
- end_expressions.append(f_end_expression)
1589
- if process[0] in PROCESSES:
1590
- for p in PROCESSES[PROCESSES.index(process[0])+1:]:
1591
- p_end_expression = get_end_expression(processed_nodes, p)
1592
- if p_end_expression:
1593
- end_expressions.append(p_end_expression)
1594
- if node.applicability:
1595
- end_expressions.append(node.applicability)
1596
- if end_expressions:
1597
- if expression:
1598
- end_expressions.append(expression)
1599
- if len(end_expressions) == 1:
1600
- expression = end_expressions[0]
1601
- else:
1602
- expression = and_join(end_expressions)
1603
-
1604
-
1605
-
1572
+ #if isinstance(node, TriccNodeActivitiy) and not prev:
1573
+ # expression = get_applicability_expression(node, processed_nodes, process, expression)
1574
+ # expression = get_prev_instance_skip_expression(node, processed_nodes, process, expression)
1575
+ # expression = get_process_skip_expression(node, processed_nodes, process, expression)
1606
1576
  if negate:
1607
1577
  if negate_expression is not None:
1608
1578
  return negate_expression
@@ -1613,7 +1583,70 @@ def get_node_expression( in_node, processed_nodes, is_calculate=False, is_prev=F
1613
1583
  # exit(1)
1614
1584
  else:
1615
1585
  return expression
1586
+
1587
+ def get_applicability_expression(node, processed_nodes, process, expression=None):
1588
+ if isinstance(node.applicability,(TriccStatic,TriccOperation, TriccReference)):
1589
+ if expression:
1590
+ expression = and_join([node.applicability, expression])
1591
+ else:
1592
+ expression = node.applicability
1593
+
1594
+ return expression
1595
+
1596
+
1597
+
1598
+
1599
+
1600
+ def get_prev_instance_skip_expression(node, processed_nodes, process, expression=None):
1601
+ if node.base_instance is not None:
1602
+ activity = node
1603
+ expression_inputs = []
1604
+ past_instances = [
1605
+ n for n in processed_nodes if getattr(n.base_instance, 'id', None) == node.base_instance.id
1606
+ ]
1607
+ for past_instance in past_instances:
1608
+ add_sub_expression(
1609
+ expression_inputs,
1610
+ get_node_expression(
1611
+ past_instance,
1612
+ processed_nodes=processed_nodes,
1613
+ is_calculate=False,
1614
+ is_prev=True,
1615
+ process=process
1616
+ )
1617
+ )
1618
+ if expression and expression_inputs:
1619
+ add_sub_expression(expression_inputs, expression)
1620
+ expression = nand_join(expression, or_join(expression_inputs))
1621
+ elif expression_inputs:
1622
+ expression = negate_term(or_join(expression_inputs))
1623
+ return expression
1616
1624
 
1625
+
1626
+ # end def
1627
+ def get_process_skip_expression(node, processed_nodes, process, expression=None):
1628
+
1629
+ end_expressions = []
1630
+ f_end_expression = get_end_expression(processed_nodes)
1631
+ if f_end_expression:
1632
+ end_expressions.append(f_end_expression)
1633
+ b_end_expression = get_end_expression(processed_nodes, 'pause')
1634
+ if b_end_expression:
1635
+ end_expressions.append(b_end_expression)
1636
+ if process[0] in PROCESSES:
1637
+ for p in PROCESSES[PROCESSES.index(process[0])+1:]:
1638
+ p_end_expression = get_end_expression(processed_nodes, p)
1639
+ if p_end_expression:
1640
+ end_expressions.append(p_end_expression)
1641
+ if end_expressions:
1642
+ if expression:
1643
+ end_expressions.append(expression)
1644
+ if len(end_expressions) == 1:
1645
+ expression = end_expressions[0]
1646
+ else:
1647
+ expression = and_join(end_expressions)
1648
+ return expression
1649
+
1617
1650
  def get_end_expression(processed_nodes, process=None):
1618
1651
  end_node = get_last_end_node(processed_nodes, process)
1619
1652
  if end_node:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tricc-oo
3
- Version: 1.4.17
3
+ Version: 1.4.19
4
4
  Summary: Python library that converts CDSS L2 in L3
5
5
  Project-URL: Homepage, https://github.com/SwissTPH/tricc
6
6
  Project-URL: Issues, https://github.com/SwissTPH/tricc/issues
@@ -14,6 +14,7 @@ Requires-Dist: markdownify
14
14
  Requires-Dist: pydantic
15
15
  Requires-Dist: babel
16
16
  Requires-Dist: xlsxwriter
17
+ Requires-Dist: html2text
17
18
  Requires-Dist: pandas
18
19
  Requires-Dist: polib
19
20
  Requires-Dist: strenum
@@ -3,6 +3,7 @@ markdownify
3
3
  pydantic
4
4
  babel
5
5
  xlsxwriter
6
+ html2text
6
7
  pandas
7
8
  polib
8
9
  strenum
File without changes
File without changes
File without changes
File without changes
File without changes