tricc-oo 1.5.13__py3-none-any.whl → 1.6.8__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.
Files changed (47) hide show
  1. tests/build.py +20 -28
  2. tests/test_build.py +260 -0
  3. tests/test_cql.py +48 -109
  4. tests/to_ocl.py +15 -17
  5. tricc_oo/__init__.py +0 -6
  6. tricc_oo/converters/codesystem_to_ocl.py +51 -40
  7. tricc_oo/converters/cql/cqlLexer.py +1 -0
  8. tricc_oo/converters/cql/cqlListener.py +1 -0
  9. tricc_oo/converters/cql/cqlParser.py +1 -0
  10. tricc_oo/converters/cql/cqlVisitor.py +1 -0
  11. tricc_oo/converters/cql_to_operation.py +129 -123
  12. tricc_oo/converters/datadictionnary.py +45 -54
  13. tricc_oo/converters/drawio_type_map.py +146 -65
  14. tricc_oo/converters/tricc_to_xls_form.py +58 -28
  15. tricc_oo/converters/utils.py +4 -4
  16. tricc_oo/converters/xml_to_tricc.py +296 -235
  17. tricc_oo/models/__init__.py +2 -1
  18. tricc_oo/models/base.py +333 -305
  19. tricc_oo/models/calculate.py +66 -51
  20. tricc_oo/models/lang.py +26 -27
  21. tricc_oo/models/ocl.py +146 -161
  22. tricc_oo/models/ordered_set.py +15 -19
  23. tricc_oo/models/tricc.py +149 -89
  24. tricc_oo/parsers/xml.py +15 -30
  25. tricc_oo/serializers/planuml.py +4 -6
  26. tricc_oo/serializers/xls_form.py +110 -153
  27. tricc_oo/strategies/input/base_input_strategy.py +28 -32
  28. tricc_oo/strategies/input/drawio.py +59 -71
  29. tricc_oo/strategies/output/base_output_strategy.py +151 -65
  30. tricc_oo/strategies/output/dhis2_form.py +908 -0
  31. tricc_oo/strategies/output/fhir_form.py +377 -0
  32. tricc_oo/strategies/output/html_form.py +224 -0
  33. tricc_oo/strategies/output/openmrs_form.py +694 -0
  34. tricc_oo/strategies/output/spice.py +106 -127
  35. tricc_oo/strategies/output/xls_form.py +322 -244
  36. tricc_oo/strategies/output/xlsform_cdss.py +627 -142
  37. tricc_oo/strategies/output/xlsform_cht.py +252 -125
  38. tricc_oo/strategies/output/xlsform_cht_hf.py +13 -24
  39. tricc_oo/visitors/tricc.py +1424 -1033
  40. tricc_oo/visitors/utils.py +16 -16
  41. tricc_oo/visitors/xform_pd.py +91 -89
  42. {tricc_oo-1.5.13.dist-info → tricc_oo-1.6.8.dist-info}/METADATA +128 -84
  43. tricc_oo-1.6.8.dist-info/RECORD +52 -0
  44. tricc_oo-1.6.8.dist-info/licenses/LICENSE +373 -0
  45. {tricc_oo-1.5.13.dist-info → tricc_oo-1.6.8.dist-info}/top_level.txt +0 -0
  46. tricc_oo-1.5.13.dist-info/RECORD +0 -46
  47. {tricc_oo-1.5.13.dist-info → tricc_oo-1.6.8.dist-info}/WHEEL +0 -0
@@ -1,11 +1,7 @@
1
1
  import json
2
2
  import os
3
3
  import logging
4
- from collections import OrderedDict
5
- from pydantic import BaseModel
6
4
  from tricc_oo.models import (
7
- TriccOperation,
8
- TriccOperator,
9
5
  TriccNodeSelect,
10
6
  TriccNodeSelectOne,
11
7
  TriccNodeAcceptDiagnostic,
@@ -17,7 +13,7 @@ from tricc_oo.models import (
17
13
  OrderedSet,
18
14
  TriccNodeNote,
19
15
  TriccNodeDiagnosis,
20
- TriccNodeSelectOption
16
+ TriccNodeSelectOption,
21
17
  )
22
18
 
23
19
  from tricc_oo.converters.datadictionnary import lookup_codesystems_code
@@ -27,58 +23,51 @@ from tricc_oo.visitors.tricc import (
27
23
  walktrhough_tricc_node_processed_stached,
28
24
  is_ready_to_process,
29
25
  process_reference,
30
- get_node_expressions
31
26
  )
32
27
 
33
28
  from tricc_oo.strategies.output.base_output_strategy import BaseOutPutStrategy
34
29
 
35
30
  logger = logging.getLogger("default")
36
31
 
37
-
38
-
39
32
 
40
-
41
- class spiceCondition():
33
+ class spiceCondition:
42
34
  eq: str = None
43
35
  targetId: str = None
44
- visibility: str = 'visible' # other option gone
45
-
46
- def __init__(self, eq=None, targetId=None, visibility='visible'):
36
+ visibility: str = "visible" # other option gone
37
+
38
+ def __init__(self, eq=None, targetId=None, visibility="visible"):
47
39
  self.eq = eq
48
40
  self.targetId = targetId
49
41
  self.visibility = visibility
50
42
 
51
-
52
43
  def __repr__(self):
53
44
  self.__str__()
45
+
54
46
  def to_dict(self):
55
47
  # Create a dictionary with only the required attributes
56
- return {
57
- "eq": self.eq,
58
- "targetId": self.targetId,
59
- "visibility": self.visibility
60
- }
48
+ return {"eq": self.eq, "targetId": self.targetId, "visibility": self.visibility}
61
49
 
62
50
 
63
- class spiceOption():
51
+ class spiceOption:
64
52
  id: str = None
65
53
  name: str = None
54
+
66
55
  def __init__(self, id=None, name=None):
67
56
  self.id = id
68
57
  self.name = name
69
58
 
70
-
71
59
  def __repr__(self):
72
60
  self.__str__()
73
-
61
+
74
62
  def to_dict(self):
75
63
  # Create a dictionary with only the required attributes
76
64
  return {
77
65
  "id": self.id,
78
66
  "name": self.name,
79
67
  }
80
-
81
- class SpiceQuestion():
68
+
69
+
70
+ class SpiceQuestion:
82
71
  condition: list = []
83
72
  errorMessage: str = None
84
73
  family: str = None
@@ -92,180 +81,171 @@ class SpiceQuestion():
92
81
  isBooleanAnswer: bool = False
93
82
  isSummary: str = None
94
83
  optionsList: list = []
95
- optionType: str = 'boolean'
84
+ optionType: str = "boolean"
96
85
  orderId: str = None
97
86
  readOnly: bool = True
98
87
  title: str = None
99
88
  viewType: str = None
100
- isInfo: str = 'gone'
101
- visibility: str = 'visible'
89
+ isInfo: str = "gone"
90
+ visibility: str = "visible"
102
91
  noOfDays: int = None
103
-
92
+
104
93
  def to_dict(self):
105
94
  """
106
95
  Returns a dictionary representation of the object,
107
96
  including only attributes that are not None or empty lists.
108
97
  """
109
- return {
110
- key: value for key, value in self.__dict__.items()
111
- if value is not None and value != []
112
- }
113
-
98
+ return {key: value for key, value in self.__dict__.items() if value is not None and value != []}
99
+
114
100
 
115
101
  def get_node_options(node):
116
- options = []
102
+ options = []
117
103
  if isinstance(node, TriccNodeSelect):
118
- for k,o in node.options.items():
119
- options.append(
120
- spiceOption(
121
- id=o.name,
122
- name=o.label
123
- ).to_dict()
124
- )
104
+ for k, o in node.options.items():
105
+ options.append(spiceOption(id=o.name, name=o.label).to_dict())
125
106
  return options
126
-
107
+
108
+
127
109
  def get_node_conditions(node):
128
- conditions = []
110
+ conditions = []
129
111
  if isinstance(node, TriccNodeSelect):
130
- for k,o in node.options.items():
112
+ for k, o in node.options.items():
131
113
  for n in o.next_nodes:
132
114
  conditions.append(
133
115
  spiceCondition(
134
116
  eq=o.name,
135
117
  targetId=n.name,
136
- visibility='visible' # other option gone
118
+ visibility="visible", # other option gone
137
119
  ).to_dict()
138
120
  )
139
121
  return conditions
140
-
122
+
123
+
141
124
  def get_option_type(node):
142
125
  if isinstance(node, (TriccNodeSelectYesNo, TriccNodeAcceptDiagnostic)):
143
- return 'boolean'
126
+ return "boolean"
144
127
  elif isinstance(node, TriccNodeSelect):
145
- #FIXME no other value than boolean found
146
- return 'string'
128
+ # FIXME no other value than boolean found
129
+ return "string"
130
+
147
131
 
148
132
  def get_node_view_type(node, concept):
149
133
  if isinstance(node, TriccNodeSelectOne):
150
- return 'SingleSelectionView'
134
+ return "SingleSelectionView"
151
135
  elif isinstance(node, TriccNodeSelectMultiple):
152
- #FIXME
153
- return 'Spinner'
136
+ # FIXME
137
+ return "Spinner"
154
138
  elif isinstance(node, TriccNodeText):
155
- return 'EditText'
156
- elif is_no_of_day(node,concept):
157
- return 'NoOfDaysView'
139
+ return "EditText"
140
+ elif is_no_of_day(node, concept):
141
+ return "NoOfDaysView"
158
142
  elif isinstance(node, TriccNodeNote):
159
- #FIXME
160
- return 'InformationLabel'
143
+ # FIXME
144
+ return "InformationLabel"
161
145
  elif isinstance(node, TriccNodeDiagnosis):
162
- #FIXME
163
- return 'InformationLabel'
164
-
146
+ # FIXME
147
+ return "InformationLabel"
148
+
165
149
 
166
-
167
-
168
150
  def is_no_of_day(node, concept):
169
151
  return (
170
- isinstance(node, TriccNodeInteger)
152
+ isinstance(node, TriccNodeInteger)
171
153
  and concept
172
- and any(
173
- [
174
- True for p in concept.property
175
- if p.valueString == 'nbDays' and p.code == 'archetype'
176
- ]
177
- )
154
+ and any([True for p in concept.property if p.valueString == "nbDays" and p.code == "archetype"])
178
155
  )
179
-
156
+
157
+
180
158
  def to_spice_question(node, codesystems):
181
159
  concept = lookup_codesystems_code(codesystems, node.name)
182
- view_type = get_node_view_type(node,concept)
160
+ view_type = get_node_view_type(node, concept)
183
161
  if view_type:
184
-
162
+
185
163
  q = SpiceQuestion()
186
- q.condition=get_node_conditions(node)
187
- q.errorMessage=getattr(node, 'constraint_message', None)
188
- q.family= (node.group or node.activity).name
189
- q.titleSummary= (node.group or node.activity).label
190
- q.fieldName= node.label
191
- q.isEnabled= True
192
- q.isMandatory= True
193
- q.isNeededDefault= True
194
- q.isBooleanAnswer= isinstance(node, TriccNodeSelectYesNo)
195
- q.isSummary=any([True for p in concept.property if p.valueBoolean == True and p.code == 'isSummary']) if concept and concept.property else None
196
- q.optionsList= get_node_options(node)
197
- q.optionType= get_option_type(node)
198
- q.orderId= node.path_len
199
- q.readOnly= isinstance(node, TriccNodeNote)
200
- q.title= node.label
201
- q.isInfo= 'gone'
202
- q.visibility = 'gone' if node.prev_nodes else 'visible'
164
+ q.condition = get_node_conditions(node)
165
+ q.errorMessage = getattr(node, "constraint_message", None)
166
+ q.family = (node.group or node.activity).name
167
+ q.titleSummary = (node.group or node.activity).label
168
+ q.fieldName = node.label
169
+ q.isEnabled = True
170
+ q.isMandatory = True
171
+ q.isNeededDefault = True
172
+ q.isBooleanAnswer = isinstance(node, TriccNodeSelectYesNo)
173
+ q.isSummary = (
174
+ any([True for p in concept.property if p.valueBoolean is True and p.code == "isSummary"])
175
+ if concept and concept.property
176
+ else None
177
+ )
178
+ q.optionsList = get_node_options(node)
179
+ q.optionType = get_option_type(node)
180
+ q.orderId = node.path_len
181
+ q.readOnly = isinstance(node, TriccNodeNote)
182
+ q.title = node.label
183
+ q.isInfo = "gone"
184
+ q.visibility = "gone" if node.prev_nodes else "visible"
203
185
  q.viewType = view_type
204
186
  q.noOfDays = node.default if is_no_of_day(node, concept) else None
205
187
  return q
206
188
  elif not isinstance(node, TriccNodeSelectOption):
207
189
  logger.warning(f"unsuported question type {node.get_name()} ")
208
190
 
209
-
210
191
 
211
192
  class SpiceStrategy(BaseOutPutStrategy):
212
193
  form_layout = []
213
194
  groups = []
195
+
214
196
  def __init__(self, project, output_path):
215
197
  super().__init__(project, output_path)
216
198
  self.form_layout = []
217
199
  self.groups = []
218
200
 
219
-
220
201
  def process_base(self, start_pages, **kwargs):
221
202
  # for each node, check if condition is required issubclass(TriccNodeDisplayModel)
222
203
  # process name
223
204
  pass
224
-
205
+
225
206
  def process_relevance(self, start_pages, **kwargs):
226
-
207
+
227
208
  pass
228
-
209
+
229
210
  def process_calculate(self, start_pages, **kwargs):
230
211
  # call the strategy specific code
231
212
  pass
232
-
233
213
 
234
214
  def do_clean(self):
235
215
  self.form_layout = []
236
216
  self.groups = []
237
217
 
238
-
239
218
  def get_kwargs(self):
240
219
  return {
241
220
  "form_layout": self.form_layout,
242
221
  "group": self.groups,
243
-
244
222
  }
245
223
 
246
224
  def generate_export(self, node, processed_nodes, calculates, **kwargs):
247
225
  # Export logic to JSON format
248
- if is_ready_to_process(node,processed_nodes, strict=True) and process_reference(node, processed_nodes, calculates, replace_reference=False, codesystems= kwargs.get('codesystems', None)) :
249
- if node not in processed_nodes :
226
+ if is_ready_to_process(node, processed_nodes, strict=True) and process_reference(
227
+ node,
228
+ processed_nodes,
229
+ calculates,
230
+ replace_reference=False,
231
+ codesystems=kwargs.get("codesystems", None),
232
+ ):
233
+ if node not in processed_nodes:
250
234
  self.start_group(cur_group=node.group or node.activity)
251
235
  if not isinstance(node, TriccNodeActivity):
252
236
  q = to_spice_question(node, self.project.code_systems)
253
237
  if q:
254
- self.form_layout.append(
255
- q.to_dict()
256
- )
238
+ self.form_layout.append(q.to_dict())
257
239
  return True
258
240
 
259
241
  def export(self, start_pages, version):
260
242
  # Save the JSON output to a file
261
243
  file_name = f"{start_pages['main'].root.form_id}.json"
262
244
  output_path = os.path.join(self.output_path, file_name)
263
-
264
-
265
-
266
- with open(output_path, 'w') as json_file:
245
+
246
+ with open(output_path, "w") as json_file:
267
247
  json.dump({"formLayout": self.form_layout}, json_file, indent=4)
268
-
248
+
269
249
  logger.info(f"JSON form exported to {output_path}")
270
250
 
271
251
  def process_export(self, start_pages, **kwargs):
@@ -276,15 +256,14 @@ class SpiceStrategy(BaseOutPutStrategy):
276
256
  if cur_group.get_name() not in self.groups:
277
257
  self.groups.append(cur_group.get_name())
278
258
  self.form_layout.append(
279
- {
259
+ {
280
260
  "familyOrder": len(self.groups),
281
261
  "id": cur_group.name,
282
262
  "title": cur_group.label,
283
- "viewType": "CardView"
263
+ "viewType": "CardView",
284
264
  }
285
265
  )
286
266
 
287
-
288
267
  def activity_export(self, activity, processed_nodes=OrderedSet(), **kwargs):
289
268
  stashed_nodes = OrderedSet()
290
269
  calculates = []
@@ -292,7 +271,7 @@ class SpiceStrategy(BaseOutPutStrategy):
292
271
 
293
272
  path_len = 0
294
273
  # keep the vesrions on the group id, max version
295
-
274
+
296
275
  self.start_group(cur_group)
297
276
  walktrhough_tricc_node_processed_stached(
298
277
  activity.root,
@@ -301,10 +280,10 @@ class SpiceStrategy(BaseOutPutStrategy):
301
280
  stashed_nodes,
302
281
  path_len,
303
282
  cur_group=activity.root.group,
304
- calculates = calculates,
305
- **self.get_kwargs()
283
+ calculates=calculates,
284
+ **self.get_kwargs(),
306
285
  )
307
- ## MANAGE STASHED NODES
286
+ # MANAGE STASHED NODES
308
287
  prev_stashed_nodes = stashed_nodes.copy()
309
288
  loop_count = 0
310
289
  len_prev_processed_nodes = 0
@@ -338,28 +317,28 @@ class SpiceStrategy(BaseOutPutStrategy):
338
317
  processed_nodes,
339
318
  stashed_nodes,
340
319
  path_len,
341
- groups=groups,
342
320
  cur_group=s_node.group,
343
- calculates = calculates,
344
- **self.get_kwargs()
321
+ calculates=calculates,
322
+ **self.get_kwargs(),
345
323
  )
346
-
324
+
347
325
  return processed_nodes
348
326
 
349
327
  # Handle stashed nodes (if needed)
328
+
350
329
  def tricc_operation_equal(self, ref_expressions):
351
330
  return {
352
- "eq": str(ref_expressions[1]),
353
- "targetId": str(ref_expressions[0]),
354
- "visibility": "visible"
331
+ "eq": str(ref_expressions[1]),
332
+ "targetId": str(ref_expressions[0]),
333
+ "visibility": "visible",
355
334
  }
356
335
 
357
336
  def tricc_operation_in(self, ref_expressions):
358
337
  return {
359
- "eq": str(ref_expressions[0]),
360
- "targetId": str(ref_expressions[1]),
361
- "visibility": "visible"
338
+ "eq": str(ref_expressions[0]),
339
+ "targetId": str(ref_expressions[1]),
340
+ "visibility": "visible",
362
341
  }
363
-
364
- def tricc_operation_in(self, ref_expressions):
365
- return ref_expressions[0].replace('visible', 'gone')
342
+
343
+ # def tricc_operation_in(self, ref_expressions):
344
+ # return ref_expressions[0].replace("visible", "gone")