tricc-oo 1.4.16__tar.gz → 1.4.18__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.16/tricc_oo.egg-info → tricc_oo-1.4.18}/PKG-INFO +3 -2
  2. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/pyproject.toml +2 -1
  3. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tests/build.py +2 -1
  4. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/converters/cql_to_operation.py +7 -0
  5. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/converters/datadictionnary.py +4 -0
  6. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/converters/xml_to_tricc.py +3 -1
  7. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/models/base.py +11 -12
  8. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/models/tricc.py +5 -2
  9. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/serializers/xls_form.py +9 -116
  10. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/strategies/input/base_input_strategy.py +22 -21
  11. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/strategies/input/drawio.py +1 -1
  12. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/strategies/output/xls_form.py +10 -7
  13. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/strategies/output/xlsform_cdss.py +0 -4
  14. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/visitors/tricc.py +115 -92
  15. {tricc_oo-1.4.16 → tricc_oo-1.4.18/tricc_oo.egg-info}/PKG-INFO +2 -1
  16. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo.egg-info/requires.txt +1 -0
  17. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/README.md +0 -0
  18. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/setup.cfg +0 -0
  19. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tests/test_cql.py +0 -0
  20. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tests/to_ocl.py +0 -0
  21. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/__init__.py +0 -0
  22. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/converters/__init__.py +0 -0
  23. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/converters/codesystem_to_ocl.py +0 -0
  24. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/converters/cql/cqlLexer.py +0 -0
  25. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/converters/cql/cqlListener.py +0 -0
  26. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/converters/cql/cqlParser.py +0 -0
  27. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/converters/cql/cqlVisitor.py +0 -0
  28. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/converters/drawio_type_map.py +0 -0
  29. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/converters/tricc_to_xls_form.py +0 -0
  30. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/converters/utils.py +0 -0
  31. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/models/__init__.py +0 -0
  32. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/models/calculate.py +0 -0
  33. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/models/lang.py +0 -0
  34. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/models/ocl.py +0 -0
  35. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/models/ordered_set.py +0 -0
  36. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/parsers/__init__.py +0 -0
  37. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/parsers/xml.py +0 -0
  38. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/serializers/__init__.py +0 -0
  39. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/serializers/planuml.py +0 -0
  40. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/strategies/__init__.py +0 -0
  41. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/strategies/input/__init__.py +0 -0
  42. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/strategies/output/base_output_strategy.py +0 -0
  43. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/strategies/output/spice.py +0 -0
  44. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/strategies/output/xlsform_cht.py +0 -0
  45. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/strategies/output/xlsform_cht_hf.py +0 -0
  46. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/visitors/__init__.py +0 -0
  47. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/visitors/utils.py +0 -0
  48. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo/visitors/xform_pd.py +0 -0
  49. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo.egg-info/SOURCES.txt +0 -0
  50. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo.egg-info/dependency_links.txt +0 -0
  51. {tricc_oo-1.4.16 → tricc_oo-1.4.18}/tricc_oo.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.3
2
2
  Name: tricc-oo
3
- Version: 1.4.16
3
+ Version: 1.4.18
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.16"
7
+ version = "1.4.18"
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",
@@ -145,7 +145,7 @@ if __name__ == "__main__":
145
145
  if in_filepath is None:
146
146
  print_help()
147
147
  sys.exit(2)
148
- in_filepath_list = in_filepath.split(',')
148
+
149
149
  if not download_dir:
150
150
  download_dir = out_path
151
151
  debug_path = os.fspath(out_path + "/debug.log")
@@ -165,6 +165,7 @@ if __name__ == "__main__":
165
165
  setup_logger("default", debug_file_path, logging.INFO)
166
166
  file_content = []
167
167
  files = []
168
+ in_filepath_list = in_filepath.split(',')
168
169
  for in_filepath in in_filepath_list:
169
170
  pre, ext = os.path.splitext(in_filepath)
170
171
 
@@ -150,6 +150,13 @@ class cqlToXlsFormVisitor(cqlVisitor):
150
150
  function_name = ctx.getChild(2).getText()
151
151
  return not_clean(self._get_membership_expression(ctx, function_name))
152
152
 
153
+ def visitInvocationExpressionTerm(self, ctx):
154
+ result = super().visitInvocationExpressionTerm(ctx)
155
+ if isinstance(result, list) and all(isinstance(x, TriccStatic) for x in result):
156
+ value = '.'.join([x.value for x in result])
157
+ logger.warning(f"guessed reference for '{value}'")
158
+ return TriccReference(value)
159
+ return result
153
160
 
154
161
  def visitBetweenExpression(self, ctx):
155
162
  ref = self.visit(ctx.expression(0))
@@ -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]
@@ -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,12 +49,17 @@ 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
65
  elif isinstance(relevance_expression, TriccOperation):
@@ -628,118 +636,3 @@ def get_input_calc_line(node, replace_dots=True):
628
636
  ]
629
637
 
630
638
 
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
- ]
@@ -73,29 +73,30 @@ class BaseInputStrategy:
73
73
  # setting the activity/group to main
74
74
  prev_bridge = root
75
75
  prev_process = None
76
- for process in sorted_pages:
77
- nodes = {page.id: page for page in sorted_pages[process]}
78
- if prev_process:
79
- prev_bridge = get_activity_wait(
80
- prev_bridge,
81
- sorted_pages[prev_process],
82
- nodes.values(),
83
- activity=app,
84
- )
85
- else:
86
- for a in nodes:
87
- set_prev_next_node(
76
+ for process in self.processes:
77
+ if process in sorted_pages:
78
+ nodes = {page.id: page for page in sorted_pages[process]}
79
+ if prev_process:
80
+ prev_bridge = get_activity_wait(
88
81
  prev_bridge,
89
- a,
90
- edge_only=True
82
+ sorted_pages[prev_process],
83
+ nodes.values(),
84
+ activity=app,
91
85
  )
92
- app.nodes[prev_bridge.id] = prev_bridge
93
-
94
- for n in nodes.values():
95
- n.activity = app
96
- n.group = app
97
- app.nodes[n.id] = n
98
- prev_process = process
86
+ else:
87
+ for a in nodes:
88
+ set_prev_next_node(
89
+ prev_bridge,
90
+ a,
91
+ edge_only=True
92
+ )
93
+ app.nodes[prev_bridge.id] = prev_bridge
94
+
95
+ for n in nodes.values():
96
+ n.activity = app
97
+ n.group = app
98
+ app.nodes[n.id] = n
99
+ prev_process = process
99
100
 
100
101
 
101
102
  return app
@@ -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
@@ -657,7 +657,7 @@ def process_operation_reference(operation, node, processed_nodes, calculates, us
657
657
  if codesystems:
658
658
  concept = lookup_codesystems_code(codesystems, ref)
659
659
  if not concept:
660
- logger.critical(f"reference {ref} not found in the project")
660
+ logger.critical(f"reference {ref} not found in the project for{str(node)} ")
661
661
  exit(1)
662
662
  else:
663
663
  if warn:
@@ -768,7 +768,13 @@ def walktrhough_tricc_node_processed_stached(node, callback, processed_nodes, st
768
768
  references = node.get_references()
769
769
  if references:
770
770
  path_len = max(path_len, *[0,*[getattr(n,'path_len',0) + 1 for n in references]])
771
- node.path_len = max(node.path_len, path_len)
771
+ node.path_len = max(node.path_len, path_len)
772
+ prev_process = process[0] if process else None
773
+ if isinstance(node, TriccNodeActivity) and getattr(node.root, 'process', None):
774
+ if process is None:
775
+ process = [node.root.process]
776
+ else:
777
+ process[0] = node.root.process
772
778
  if (
773
779
  callback(
774
780
  node,
@@ -802,11 +808,6 @@ def walktrhough_tricc_node_processed_stached(node, callback, processed_nodes, st
802
808
  if not recursive:
803
809
  reorder_node_list(stashed_nodes, node.group, processed_nodes)
804
810
  if isinstance(node, (TriccNodeActivityStart, TriccNodeMainStart)):
805
- if getattr(node, 'process', None):
806
- if process is None:
807
- process = [node.process]
808
- else:
809
- process[0] = node.process
810
811
  if recursive:
811
812
  for gp in node.activity.groups.values():
812
813
  walktrhough_tricc_node_processed_stached(
@@ -851,11 +852,6 @@ def walktrhough_tricc_node_processed_stached(node, callback, processed_nodes, st
851
852
  if node.root not in processed_nodes:
852
853
  if node.root is not None:
853
854
  node.root.path_len = max(path_len, node.root.path_len)
854
- if getattr(node.root, 'process', None):
855
- if process is None:
856
- process = [node.root.process]
857
- else:
858
- process[0] = node.root.process
859
855
  if recursive:
860
856
  walktrhough_tricc_node_processed_stached(node.root, callback, processed_nodes, stashed_nodes, path_len,
861
857
  recursive, warn = warn,node_path = node_path.copy(),**kwargs)
@@ -897,6 +893,8 @@ def walktrhough_tricc_node_processed_stached(node, callback, processed_nodes, st
897
893
 
898
894
 
899
895
  else:
896
+ if prev_process and process and prev_process != process[0]:
897
+ process[0] = prev_process
900
898
  if node not in processed_nodes and node not in stashed_nodes:
901
899
  if node not in stashed_nodes:
902
900
  stashed_nodes.insert_at_bottom(node)
@@ -1146,23 +1144,34 @@ def check_stashed_loop(stashed_nodes, prev_stashed_nodes, processed_nodes, len_p
1146
1144
  es_node.activity.instance if hasattr(es_node,'activity') else '',
1147
1145
  es_node.__class__,
1148
1146
  es_node.get_name()))
1147
+ for es_node in [node for node_list in looped.values() for node in node_list if isinstance(node, TriccReference)]:
1148
+ logger.info("looped node {}:{}|{} {}".format(
1149
+ es_node.activity.get_name() if hasattr(es_node,'activity') else '' ,
1150
+ es_node.activity.instance if hasattr(es_node,'activity') else '',
1151
+ es_node.__class__,
1152
+ es_node.get_name()))
1153
+ for es_node in [node for node_list in waited.values() for node in node_list if isinstance(node, TriccReference)]:
1154
+ logger.info("waited node {}:{}|{} {}".format(
1155
+ es_node.activity.get_name() if hasattr(es_node,'activity') else '' ,
1156
+ es_node.activity.instance if hasattr(es_node,'activity') else '',
1157
+ es_node.__class__,
1158
+ es_node.get_name()))
1149
1159
  logger.info("looped nodes")
1150
1160
  for dep_list in looped:
1151
1161
  for d in looped[dep_list]:
1152
- if d.get_name() == dep_list:
1153
- logger.critical("[{}] depends on itself".format(
1154
- dep_list,
1162
+ if str(d) in looped:
1163
+ logger.critical("[{}] depends on [{}]".format(
1164
+ dep_list, str(d)
1165
+ ))
1166
+ else:
1167
+ logger.error("[{}] depends on [{}]".format(
1168
+ dep_list, str(d)
1155
1169
  ))
1156
- logger.error("[{}] depends on [{}]".format(
1157
- dep_list, str(d)
1158
- ))
1159
1170
  if dep_list in waited:
1160
1171
  for d in waited[dep_list]:
1161
1172
  logger.warning("[{}] depends on [{}]".format(
1162
1173
  dep_list, str(d)
1163
1174
  ))
1164
-
1165
- #reverse_walkthrough(es_node, es_node, print_trace, processed_nodes, stashed_nodes)
1166
1175
  logger.info("waited nodes")
1167
1176
  for dep_list in waited:
1168
1177
  if dep_list not in looped:
@@ -1179,7 +1188,6 @@ def check_stashed_loop(stashed_nodes, prev_stashed_nodes, processed_nodes, len_p
1179
1188
  loop_count = 0
1180
1189
  return loop_count
1181
1190
 
1182
-
1183
1191
  def add_to_tree(tree, n, d):
1184
1192
  n_str = str(n)
1185
1193
  if n_str not in tree:
@@ -1189,12 +1197,16 @@ def add_to_tree(tree, n, d):
1189
1197
  return tree
1190
1198
 
1191
1199
 
1192
- def get_all_dependant(loop, stashed_nodes, processed_nodes, depth=0, waited=None , looped=None):
1200
+ def get_all_dependant(loop, stashed_nodes, processed_nodes, depth=0, waited=None , looped=None, path = None):
1201
+ if path is None:
1202
+ path =[]
1193
1203
  if looped is None:
1194
1204
  looped = {}
1195
1205
  if waited is None:
1196
1206
  waited = {}
1197
1207
  for n in loop:
1208
+ cur_path = path.copy()
1209
+ cur_path.append(n)
1198
1210
  dependant = OrderedSet()
1199
1211
  i=0
1200
1212
  #logger.critical(f"{i}: {n.__class__}::{n.get_name()}::{getattr(n,'instance','')}::{process_reference(n, processed_nodes, [])}")
@@ -1206,28 +1218,30 @@ def get_all_dependant(loop, stashed_nodes, processed_nodes, depth=0, waited=None
1206
1218
  if not isinstance(dependant, list):
1207
1219
  pass
1208
1220
  for d in dependant:
1221
+ if d in path:
1222
+ logger.warning(f"loop {str(d)} already in path {'::'.join(map(path, str))} ")
1209
1223
  if isinstance(d, TriccNodeSelectOption):
1210
1224
  d = d.select
1211
- if d not in waited and d not in looped:
1212
- if isinstance(d, TriccReference):
1213
- if not any(n.name == d.value for n in processed_nodes):
1214
- if not any(n.name == d.value for n in stashed_nodes):
1215
- waited = add_to_tree(waited, n, d)
1216
- else :
1217
- looped = add_to_tree(looped, n, d)
1218
-
1219
- elif d not in processed_nodes:
1220
- if d in stashed_nodes:
1221
- looped = add_to_tree(looped, n, d)
1225
+
1226
+ if isinstance(d, TriccReference):
1227
+ if not any(n.name == d.value for n in processed_nodes):
1228
+ if not any(n.name == d.value for n in stashed_nodes):
1229
+ waited = add_to_tree(waited, n, d)
1222
1230
  else :
1223
- waited = add_to_tree(waited, n, d)
1231
+ looped = add_to_tree(looped, n, d)
1232
+
1233
+ elif d not in processed_nodes:
1234
+ if d in stashed_nodes:
1235
+ looped = add_to_tree(looped, n, d)
1236
+ else :
1237
+ waited = add_to_tree(waited, n, d)
1224
1238
  if depth < MAX_DRILL:
1225
- return get_all_dependant(waited, stashed_nodes, processed_nodes, depth+1, waited , looped)
1239
+ return get_all_dependant(looped, stashed_nodes, processed_nodes, depth+1, waited , looped, path=cur_path)
1226
1240
 
1227
1241
  return waited, looped
1228
1242
 
1229
1243
 
1230
- MAX_DRILL = 1
1244
+ MAX_DRILL = 3
1231
1245
 
1232
1246
  def get_last_end_node(processed_nodes, process=None):
1233
1247
  end_name = 'tricc_end_'
@@ -1550,61 +1564,10 @@ def get_node_expression( in_node, processed_nodes, is_calculate=False, is_prev=F
1550
1564
  if expression is None:
1551
1565
  expression = get_prev_node_expression(node, processed_nodes=processed_nodes, is_calculate=is_calculate, process=process)
1552
1566
  # in_node not in processed_nodes is need for calculates that can but run after the end of the activity
1553
-
1554
-
1555
- if isinstance(node, TriccNodeActivity):
1556
-
1557
-
1558
- if node.base_instance is not None and not is_prev:
1559
- activity = node
1560
- expression_inputs = []
1561
- past_instances = [
1562
- n for n in processed_nodes if getattr(n.base_instance, 'id', None) == node.base_instance.id
1563
- ]
1564
- for past_instance in past_instances:
1565
- add_sub_expression(
1566
- expression_inputs,
1567
- get_node_expression(
1568
- past_instance,
1569
- processed_nodes=processed_nodes,
1570
- is_calculate=False,
1571
- is_prev=True,
1572
- process=process
1573
- )
1574
- )
1575
-
1576
- if isinstance(node.applicability,(TriccStatic,TriccOperation, TriccReference)):
1577
- if expression:
1578
- expression = and_join([node.applicability, expression])
1579
- else:
1580
- expression = node.applicability
1581
- if expression and expression_inputs:
1582
- add_sub_expression(expression_inputs, expression)
1583
- expression = nand_join(expression, or_join(expression_inputs))
1584
- elif expression_inputs:
1585
- expression = negate_term(or_join(expression_inputs))
1586
- if not is_prev:
1587
- end_expressions = []
1588
- f_end_expression = get_end_expression(processed_nodes)
1589
- if f_end_expression:
1590
- end_expressions.append(f_end_expression)
1591
- if process[0] in PROCESSES:
1592
- for p in PROCESSES[PROCESSES.index(process[0])+1:]:
1593
- p_end_expression = get_end_expression(processed_nodes, p)
1594
- if p_end_expression:
1595
- end_expressions.append(p_end_expression)
1596
- if node.applicability:
1597
- end_expressions.append(node.applicability)
1598
- if end_expressions:
1599
- if expression:
1600
- end_expressions.append(expression)
1601
- if len(end_expressions) == 1:
1602
- expression = end_expressions[0]
1603
- else:
1604
- expression = and_join(end_expressions)
1605
-
1606
-
1607
-
1567
+ #if isinstance(node, TriccNodeActivitiy) and not prev:
1568
+ # expression = get_applicability_expression(node, processed_nodes, process, expression)
1569
+ # expression = get_prev_instance_skip_expression(node, processed_nodes, process, expression)
1570
+ # expression = get_process_skip_expression(node, processed_nodes, process, expression)
1608
1571
  if negate:
1609
1572
  if negate_expression is not None:
1610
1573
  return negate_expression
@@ -1615,7 +1578,67 @@ def get_node_expression( in_node, processed_nodes, is_calculate=False, is_prev=F
1615
1578
  # exit(1)
1616
1579
  else:
1617
1580
  return expression
1581
+
1582
+ def get_applicability_expression(node, processed_nodes, process, expression=None):
1583
+ if isinstance(node.applicability,(TriccStatic,TriccOperation, TriccReference)):
1584
+ if expression:
1585
+ expression = and_join([node.applicability, expression])
1586
+ else:
1587
+ expression = node.applicability
1588
+
1589
+ return expression
1590
+
1618
1591
 
1592
+
1593
+
1594
+
1595
+ def get_prev_instance_skip_expression(node, processed_nodes, process, expression=None):
1596
+ if node.base_instance is not None:
1597
+ activity = node
1598
+ expression_inputs = []
1599
+ past_instances = [
1600
+ n for n in processed_nodes if getattr(n.base_instance, 'id', None) == node.base_instance.id
1601
+ ]
1602
+ for past_instance in past_instances:
1603
+ add_sub_expression(
1604
+ expression_inputs,
1605
+ get_node_expression(
1606
+ past_instance,
1607
+ processed_nodes=processed_nodes,
1608
+ is_calculate=False,
1609
+ is_prev=True,
1610
+ process=process
1611
+ )
1612
+ )
1613
+ if expression and expression_inputs:
1614
+ add_sub_expression(expression_inputs, expression)
1615
+ expression = nand_join(expression, or_join(expression_inputs))
1616
+ elif expression_inputs:
1617
+ expression = negate_term(or_join(expression_inputs))
1618
+ return expression
1619
+
1620
+
1621
+ # end def
1622
+ def get_process_skip_expression(node, processed_nodes, process, expression=None):
1623
+
1624
+ end_expressions = []
1625
+ f_end_expression = get_end_expression(processed_nodes)
1626
+ if f_end_expression:
1627
+ end_expressions.append(f_end_expression)
1628
+ if process[0] in PROCESSES:
1629
+ for p in PROCESSES[PROCESSES.index(process[0])+1:]:
1630
+ p_end_expression = get_end_expression(processed_nodes, p)
1631
+ if p_end_expression:
1632
+ end_expressions.append(p_end_expression)
1633
+ if end_expressions:
1634
+ if expression:
1635
+ end_expressions.append(expression)
1636
+ if len(end_expressions) == 1:
1637
+ expression = end_expressions[0]
1638
+ else:
1639
+ expression = and_join(end_expressions)
1640
+ return expression
1641
+
1619
1642
  def get_end_expression(processed_nodes, process=None):
1620
1643
  end_node = get_last_end_node(processed_nodes, process)
1621
1644
  if end_node:
@@ -2033,7 +2056,7 @@ def get_rhombus_terms( node, processed_nodes, is_calculate=False, negate=False,
2033
2056
  ]
2034
2057
  )
2035
2058
  else:
2036
- if left_term is not None and re.search(" (\+)|(\-)|(or)|(and) ", expression):
2059
+ if left_term is not None and re.search(" (+)|(-)|(or)|(and) ", expression):
2037
2060
  expression = "({0}){1}".format(expression, left_term)
2038
2061
  else:
2039
2062
  expression = "{0}{1}".format(expression, left_term)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tricc-oo
3
- Version: 1.4.16
3
+ Version: 1.4.18
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