sapiopycommons 2025.6.30a571__py3-none-any.whl → 2025.7.1a575__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.

Potentially problematic release.


This version of sapiopycommons might be problematic. Click here for more details.

Files changed (59) hide show
  1. sapiopycommons/ai/tool_of_tools.py +917 -0
  2. sapiopycommons/callbacks/callback_util.py +665 -332
  3. sapiopycommons/callbacks/field_builder.py +2 -0
  4. sapiopycommons/chem/IndigoMolecules.py +29 -1
  5. sapiopycommons/chem/Molecules.py +3 -3
  6. sapiopycommons/customreport/auto_pagers.py +26 -1
  7. sapiopycommons/customreport/term_builder.py +1 -1
  8. sapiopycommons/datatype/pseudo_data_types.py +349 -326
  9. sapiopycommons/eln/experiment_cache.py +188 -0
  10. sapiopycommons/eln/experiment_handler.py +408 -767
  11. sapiopycommons/eln/experiment_report_util.py +11 -6
  12. sapiopycommons/eln/experiment_step_factory.py +476 -0
  13. sapiopycommons/eln/plate_designer.py +7 -2
  14. sapiopycommons/eln/step_creation.py +236 -0
  15. sapiopycommons/files/file_util.py +7 -5
  16. sapiopycommons/general/accession_service.py +2 -2
  17. sapiopycommons/general/aliases.py +3 -1
  18. sapiopycommons/general/audit_log.py +7 -0
  19. sapiopycommons/general/custom_report_util.py +12 -0
  20. sapiopycommons/general/data_structure_util.py +115 -0
  21. sapiopycommons/processtracking/custom_workflow_handler.py +11 -1
  22. sapiopycommons/processtracking/endpoints.py +27 -0
  23. sapiopycommons/recordmodel/record_handler.py +657 -317
  24. sapiopycommons/rules/eln_rule_handler.py +8 -1
  25. sapiopycommons/rules/on_save_rule_handler.py +8 -1
  26. sapiopycommons/webhook/webhook_handlers.py +3 -0
  27. sapiopycommons/webhook/webservice_handlers.py +2 -2
  28. {sapiopycommons-2025.6.30a571.dist-info → sapiopycommons-2025.7.1a575.dist-info}/METADATA +2 -2
  29. sapiopycommons-2025.7.1a575.dist-info/RECORD +70 -0
  30. sapiopycommons/ai/api/fielddefinitions/proto/fields_pb2.py +0 -43
  31. sapiopycommons/ai/api/fielddefinitions/proto/fields_pb2.pyi +0 -31
  32. sapiopycommons/ai/api/fielddefinitions/proto/fields_pb2_grpc.py +0 -24
  33. sapiopycommons/ai/api/fielddefinitions/proto/velox_field_def_pb2.py +0 -123
  34. sapiopycommons/ai/api/fielddefinitions/proto/velox_field_def_pb2.pyi +0 -598
  35. sapiopycommons/ai/api/fielddefinitions/proto/velox_field_def_pb2_grpc.py +0 -24
  36. sapiopycommons/ai/api/plan/proto/step_output_pb2.py +0 -45
  37. sapiopycommons/ai/api/plan/proto/step_output_pb2.pyi +0 -42
  38. sapiopycommons/ai/api/plan/proto/step_output_pb2_grpc.py +0 -24
  39. sapiopycommons/ai/api/plan/proto/step_pb2.py +0 -43
  40. sapiopycommons/ai/api/plan/proto/step_pb2.pyi +0 -43
  41. sapiopycommons/ai/api/plan/proto/step_pb2_grpc.py +0 -24
  42. sapiopycommons/ai/api/plan/script/proto/script_pb2.py +0 -53
  43. sapiopycommons/ai/api/plan/script/proto/script_pb2.pyi +0 -99
  44. sapiopycommons/ai/api/plan/script/proto/script_pb2_grpc.py +0 -153
  45. sapiopycommons/ai/api/plan/tool/proto/entry_pb2.py +0 -57
  46. sapiopycommons/ai/api/plan/tool/proto/entry_pb2.pyi +0 -96
  47. sapiopycommons/ai/api/plan/tool/proto/entry_pb2_grpc.py +0 -24
  48. sapiopycommons/ai/api/plan/tool/proto/tool_pb2.py +0 -67
  49. sapiopycommons/ai/api/plan/tool/proto/tool_pb2.pyi +0 -220
  50. sapiopycommons/ai/api/plan/tool/proto/tool_pb2_grpc.py +0 -154
  51. sapiopycommons/ai/api/session/proto/sapio_conn_info_pb2.py +0 -39
  52. sapiopycommons/ai/api/session/proto/sapio_conn_info_pb2.pyi +0 -32
  53. sapiopycommons/ai/api/session/proto/sapio_conn_info_pb2_grpc.py +0 -24
  54. sapiopycommons/ai/protobuf_utils.py +0 -454
  55. sapiopycommons/ai/test_client.py +0 -197
  56. sapiopycommons/ai/tool_service_base.py +0 -799
  57. sapiopycommons-2025.6.30a571.dist-info/RECORD +0 -92
  58. {sapiopycommons-2025.6.30a571.dist-info → sapiopycommons-2025.7.1a575.dist-info}/WHEEL +0 -0
  59. {sapiopycommons-2025.6.30a571.dist-info → sapiopycommons-2025.7.1a575.dist-info}/licenses/LICENSE +0 -0
@@ -1,454 +0,0 @@
1
- from typing import Any
2
-
3
- from sapiopylib.rest.pojo.Sort import SortDirection
4
- from sapiopylib.rest.pojo.datatype.FieldDefinition import AbstractVeloxFieldDefinition, FieldType, \
5
- VeloxBooleanFieldDefinition, VeloxDateFieldDefinition, VeloxAccessionFieldDefinition, VeloxActionFieldDefinition, \
6
- VeloxChildLinkFieldDefinition, VeloxDateRangeFieldDefinition, VeloxDoubleFieldDefinition, \
7
- VeloxEnumFieldDefinition, VeloxIdentifierFieldDefinition, VeloxIntegerFieldDefinition, \
8
- VeloxLongFieldDefinition, VeloxMultiParentFieldDefinition, VeloxParentFieldDefinition, \
9
- VeloxPickListFieldDefinition, VeloxSelectionFieldDefinition, VeloxShortFieldDefinition, \
10
- VeloxStringFieldDefinition, VeloxSideLinkFieldDefinition, VeloxActionStringFieldDefinition, FieldValidator, \
11
- ListMode, SapioDoubleFormat, SapioStringFormat
12
-
13
- from sapiopycommons.ai.api.fielddefinitions.proto.fields_pb2 import FieldValuePbo
14
- from sapiopycommons.ai.api.fielddefinitions.proto.velox_field_def_pb2 import FieldTypePbo, SortDirectionPbo, \
15
- DoubleFormatPbo, StringFormatPbo, FieldValidatorPbo, VeloxFieldDefPbo, BooleanPropertiesPbo, DatePropertiesPbo, \
16
- DoublePropertiesPbo, IntegerPropertiesPbo, LongPropertiesPbo, SelectionPropertiesPbo, StringPropertiesPbo, \
17
- SideLinkPropertiesPbo, ShortPropertiesPbo, PickListPropertiesPbo, ParentLinkPropertiesPbo, MultiParentPropertiesPbo, \
18
- IdentifierPropertiesPbo, FileBlobPropertiesPbo, EnumPropertiesPbo, DateRangePropertiesPbo, ChildLinkPropertiesPbo, \
19
- ActionStringPropertiesPbo, ActionPropertiesPbo, AccessionPropertiesPbo, SelectionDependentFieldEntryPbo, \
20
- EnumDependentFieldEntryPbo, BooleanDependentFieldEntryPbo
21
- from sapiopycommons.ai.api.plan.tool.proto.entry_pb2 import DataTypePbo
22
- from sapiopycommons.general.aliases import FieldValue
23
-
24
-
25
- class ProtobufUtils:
26
- @staticmethod
27
- def content_type_str(content_type: DataTypePbo) -> str:
28
- """
29
- Convert a DataTypePbo enum to its string representation.
30
-
31
- :param content_type: The DataTypePbo enum value.
32
- :return: The string representation of the DataTypePbo.
33
- """
34
- match content_type:
35
- case DataTypePbo.BINARY:
36
- return "binary"
37
- case DataTypePbo.CSV:
38
- return "csv"
39
- case DataTypePbo.IMAGE:
40
- return "image"
41
- case DataTypePbo.JSON:
42
- return "json"
43
- case DataTypePbo.TEXT:
44
- return "text"
45
- case _:
46
- raise Exception(f"Unexpected content type: {content_type}")
47
-
48
- @staticmethod
49
- def field_type_to_pbo(field_type: FieldType) -> FieldTypePbo:
50
- """
51
- Convert a FieldType enum to its corresponding FieldTypePbo.
52
-
53
- :param field_type: The FieldType enum value.
54
- :return: The corresponding FieldTypePbo.
55
- """
56
- match field_type:
57
- case FieldType.ACTION:
58
- return FieldTypePbo.ACTION
59
- case FieldType.ACTION_STRING:
60
- return FieldTypePbo.ACTION_STRING
61
- case FieldType.AUTO_ACCESSION:
62
- return FieldTypePbo.AUTO_ACCESSION
63
- case FieldType.BOOLEAN:
64
- return FieldTypePbo.BOOLEAN
65
- case FieldType.CHILDLINK:
66
- return FieldTypePbo.CHILDLINK
67
- case FieldType.DATE:
68
- return FieldTypePbo.DATE
69
- case FieldType.DATE_RANGE:
70
- return FieldTypePbo.DATE_RANGE
71
- case FieldType.DOUBLE:
72
- return FieldTypePbo.DOUBLE
73
- case FieldType.ENUM:
74
- return FieldTypePbo.ENUM
75
- # case FieldType.FILE_BLOB:
76
- # return FieldTypePbo.FILE_BLOB
77
- case FieldType.IDENTIFIER:
78
- return FieldTypePbo.IDENTIFIER
79
- case FieldType.INTEGER:
80
- return FieldTypePbo.INTEGER
81
- case FieldType.LINK:
82
- return FieldTypePbo.LINK
83
- case FieldType.LONG:
84
- return FieldTypePbo.LONG
85
- case FieldType.MULTIPARENTLINK:
86
- return FieldTypePbo.MULTIPARENTLINK
87
- case FieldType.PARENTLINK:
88
- return FieldTypePbo.PARENTLINK
89
- case FieldType.PICKLIST:
90
- return FieldTypePbo.PICKLIST
91
- case FieldType.SELECTION:
92
- return FieldTypePbo.SELECTION
93
- case FieldType.SHORT:
94
- return FieldTypePbo.SHORT
95
- case FieldType.SIDE_LINK:
96
- return FieldTypePbo.SIDE_LINK
97
- case FieldType.STRING:
98
- return FieldTypePbo.STRING
99
- case _:
100
- return FieldTypePbo.FIELD_TYPE_UNSPECIFIED
101
-
102
- @staticmethod
103
- def sort_direction_to_pbo(sort_direction: SortDirection | None) -> SortDirectionPbo:
104
- """
105
- Convert a SortDirection enum to its corresponding SortDirectionPbo.
106
-
107
- :param sort_direction: The SortDirection enum value.
108
- :return: The corresponding SortDirectionPbo.
109
- """
110
- if sort_direction is None or sort_direction == SortDirection.NONE:
111
- return SortDirectionPbo.SORT_DIRECTION_NONE
112
- elif sort_direction == SortDirection.ASCENDING:
113
- return SortDirectionPbo.SORT_DIRECTION_ASCENDING
114
- elif sort_direction == SortDirection.DESCENDING:
115
- return SortDirectionPbo.SORT_DIRECTION_DESCENDING
116
- else:
117
- return SortDirectionPbo.SORT_DIRECTION_UNSPECIFIED
118
-
119
- @staticmethod
120
- def double_format_to_pbo(double_format: SapioDoubleFormat | None) -> DoubleFormatPbo:
121
- """
122
- Convert a SapioDoubleFormat enum to its corresponding DoubleFormatPbo.
123
-
124
- :param double_format: The SapioDoubleFormat enum value.
125
- :return: The corresponding DoubleFormatPbo.
126
- """
127
- if double_format is None:
128
- return DoubleFormatPbo.DOUBLE_FORMAT_UNSPECIFIED
129
- elif double_format == SapioDoubleFormat.CURRENCY:
130
- return DoubleFormatPbo.DOUBLE_FORMAT_CURRENCY
131
- elif double_format == SapioDoubleFormat.PERCENTAGE:
132
- return DoubleFormatPbo.DOUBLE_FORMAT_PERCENTAGE
133
- else:
134
- return DoubleFormatPbo.DOUBLE_FORMAT_UNSPECIFIED
135
-
136
- @staticmethod
137
- def string_format_to_pbo(string_format: SapioStringFormat | None) -> StringFormatPbo:
138
- """
139
- Convert a SapioStringFormat enum to its corresponding StringFormatPbo.
140
-
141
- :param string_format: The SapioStringFormat enum value.
142
- :return: The corresponding StringFormatPbo.
143
- """
144
- if string_format is None:
145
- return StringFormatPbo.STRING_FORMAT_UNSPECIFIED
146
- elif string_format == SapioStringFormat.EMAIL:
147
- return StringFormatPbo.STRING_FORMAT_EMAIL
148
- elif string_format == SapioStringFormat.PHONE:
149
- return StringFormatPbo.STRING_FORMAT_PHONE
150
- else:
151
- return StringFormatPbo.STRING_FORMAT_UNSPECIFIED
152
-
153
- @staticmethod
154
- def field_validator_to_pbo(validator: FieldValidator | None) -> FieldValidatorPbo | None:
155
- """
156
- Convert a FieldValidator object to its corresponding FieldValidatorPbo.
157
-
158
- :param validator: The FieldValidator object.
159
- :return: The corresponding FieldValidatorPbo or None if validator is None.
160
- """
161
- if validator is None:
162
- return None
163
- return FieldValidatorPbo(
164
- validation_regex=validator.validation_regex,
165
- error_message=validator.error_message
166
- )
167
-
168
- @staticmethod
169
- def list_mode_to_str(list_mode: ListMode, field: VeloxSelectionFieldDefinition) -> str | None:
170
- """
171
- Convert a ListMode enum to its string representation.
172
-
173
- :param list_mode: The ListMode enum value.
174
- :param field: The VeloxSelectionFieldDefinition object.
175
- :return: The string representation of the ListMode or None if list_mode is None.
176
- """
177
- if list_mode is None:
178
- return None
179
- list_mode_str = list_mode.list_mode_name
180
- if list_mode == ListMode.LIST:
181
- list_mode_str += field.pick_list_name or ""
182
- elif list_mode == ListMode.PLUGIN:
183
- list_mode_str += field.plugin_name or ""
184
- elif list_mode == ListMode.REPORT:
185
- list_mode_str += field.custom_report_name or ""
186
- return list_mode_str
187
-
188
- @staticmethod
189
- def field_def_to_pbo(field: AbstractVeloxFieldDefinition) -> VeloxFieldDefPbo:
190
- """
191
- Convert a AbstractVeloxFieldDefinition object to its corresponding VeloxFieldDefPbo.
192
-
193
- :param field: The AbstractVeloxFieldDefinition object.
194
- :return: The corresponding VeloxFieldDefPbo.
195
- """
196
- accession_properties: AccessionPropertiesPbo | None = None
197
- action_properties: ActionPropertiesPbo | None = None
198
- action_string_properties: ActionStringPropertiesPbo | None = None
199
- boolean_properties: BooleanPropertiesPbo | None = None
200
- child_link_properties: ChildLinkPropertiesPbo | None = None
201
- date_properties: DatePropertiesPbo | None = None
202
- date_range_properties: DateRangePropertiesPbo | None = None
203
- double_properties: DoublePropertiesPbo | None = None
204
- enum_properties: EnumPropertiesPbo | None = None
205
- file_blob_properties: FileBlobPropertiesPbo | None = None
206
- identifier_properties: IdentifierPropertiesPbo | None = None
207
- integer_properties: IntegerPropertiesPbo | None = None
208
- long_properties: LongPropertiesPbo | None = None
209
- multi_parent_properties: MultiParentPropertiesPbo | None = None
210
- parent_link_properties: ParentLinkPropertiesPbo | None = None
211
- picklist_properties: PickListPropertiesPbo | None = None
212
- selection_properties: SelectionPropertiesPbo | None = None
213
- short_properties: ShortPropertiesPbo | None = None
214
- side_link_properties: SideLinkPropertiesPbo | None = None
215
- string_properties: StringPropertiesPbo | None = None
216
-
217
- if isinstance(field, VeloxAccessionFieldDefinition):
218
- accession_properties = AccessionPropertiesPbo(
219
- unique_value=field.unique_value,
220
- # index_for_search # Missing in FieldDefinition.py
221
- link_out=field.link_out,
222
- link_out_url=field.link_out_url,
223
- sequence_key=field.sequence_key,
224
- prefix=field.prefix,
225
- suffix=field.suffix,
226
- number_of_digits=field.number_of_digits,
227
- starting_value=field.starting_value
228
- )
229
- elif isinstance(field, VeloxActionFieldDefinition):
230
- action_properties = ActionPropertiesPbo(
231
- # icon_name # Missing in FieldDefinition.py
232
- # icon_color # Missing in FieldDefinition.py
233
- # background_color # Missing in FieldDefinition.py
234
- # font_color # Missing in FieldDefinition.py
235
- # action_plugin_path # Missing in FieldDefinition.py
236
- )
237
- elif isinstance(field, VeloxActionStringFieldDefinition):
238
- action_string_properties = ActionStringPropertiesPbo(
239
- default_value=field.default_value,
240
- max_length=field.max_length,
241
- unique_value=field.unique_value,
242
- icon_name=field.icon_name,
243
- action_plugin_path=field.action_plugin_path,
244
- field_validator=ProtobufUtils.field_validator_to_pbo(field.field_validator),
245
- direct_edit=field.direct_edit
246
- )
247
- elif isinstance(field, VeloxBooleanFieldDefinition):
248
- boolean_properties = BooleanPropertiesPbo(
249
- default_value=field.default_value,
250
- is_process_todo_item=field.process_todo_item,
251
- dependent_fields=[BooleanDependentFieldEntryPbo(k, v) for k, v in field.get_dependent_field_map().items()],
252
- is_hide_disabled_fields=field.hide_disabled_fields
253
- )
254
- elif isinstance(field, VeloxChildLinkFieldDefinition):
255
- child_link_properties = ChildLinkPropertiesPbo(
256
- # default_value # Missing in FieldDefinition.py
257
- )
258
- elif isinstance(field, VeloxDateFieldDefinition):
259
- date_properties = DatePropertiesPbo(
260
- default_value=field.default_value,
261
- static_date=field.static_date,
262
- date_time_format=field.date_time_format
263
- )
264
- elif isinstance(field, VeloxDateRangeFieldDefinition):
265
- date_range_properties = DateRangePropertiesPbo(
266
- default_value=field.default_value,
267
- is_static=field.static_date,
268
- date_time_format=field.date_time_format
269
- )
270
- elif isinstance(field, VeloxDoubleFieldDefinition):
271
- double_properties = DoublePropertiesPbo(
272
- min_value=field.min_value,
273
- max_value=field.max_value,
274
- default_value=field.default_value,
275
- precision=field.precision,
276
- double_format=ProtobufUtils.double_format_to_pbo(field.double_format),
277
- # color_ranges # Missing in FieldDefinition.py
278
- )
279
- # elif isinstance(field, VeloxFileBlobFieldDefinition): # Assuming this exists
280
- # file_blob_properties = FileBlobPropertiesPbo()
281
- elif isinstance(field, VeloxEnumFieldDefinition):
282
- enum_properties = EnumPropertiesPbo(
283
- default_value=field.default_value,
284
- values=field.values if field.values is not None else [],
285
- # color_mapping # Missing in FieldDefinition.py
286
- # auto_clear_field_list # Missing in FieldDefinition.py
287
- dependent_fields=[EnumDependentFieldEntryPbo(k, v) for k,v in field.get_dependent_field_map().items()],
288
- is_hide_disabled_fields=field.hide_disabled_fields
289
- )
290
- elif isinstance(field, VeloxIdentifierFieldDefinition):
291
- identifier_properties = IdentifierPropertiesPbo(
292
- # default_value # Missing in FieldDefinition.py
293
- )
294
- elif isinstance(field, VeloxIntegerFieldDefinition):
295
- integer_properties = IntegerPropertiesPbo(
296
- min_value=field.min_value,
297
- max_value=field.max_value,
298
- default_value=field.default_value,
299
- unique_value=field.unique_value,
300
- # color_ranges # Missing in FieldDefinition.py
301
- )
302
- elif isinstance(field, VeloxLongFieldDefinition):
303
- long_properties = LongPropertiesPbo(
304
- min_value=field.min_value,
305
- max_value=field.max_value,
306
- default_value=field.default_value,
307
- unique_value=field.unique_value,
308
- # color_ranges # Missing in FieldDefinition.py
309
- )
310
- elif isinstance(field, VeloxMultiParentFieldDefinition):
311
- multi_parent_properties = MultiParentPropertiesPbo()
312
- elif isinstance(field, VeloxParentFieldDefinition):
313
- parent_link_properties = ParentLinkPropertiesPbo(
314
- # default_value # Missing in FieldDefinition.py
315
- )
316
- elif isinstance(field, VeloxPickListFieldDefinition):
317
- picklist_properties = PickListPropertiesPbo(
318
- default_value=field.default_value,
319
- pick_list_name=field.pick_list_name,
320
- direct_edit=field.direct_edit,
321
- # link_out # Missing in FieldDefinition.py
322
- # link_out_url # Missing in FieldDefinition.py
323
- # index_for_search # Missing in FieldDefinition.py
324
- # field_validator # Missing in FieldDefinition.py
325
- # color_mapping # Missing in FieldDefinition.py
326
- # auto_clear_field_list # Missing in FieldDefinition.py
327
- # process_detail_map # Missing in FieldDefinition.py
328
- dependent_fields=[SelectionDependentFieldEntryPbo(k, v) for k,v in field.get_dependent_field_map().items()],
329
- is_hide_disabled_fields=field.hide_disabled_fields
330
- )
331
- elif isinstance(field, VeloxSelectionFieldDefinition):
332
- list_mode_str = ProtobufUtils.list_mode_to_str(field.list_mode, field)
333
- selection_properties = SelectionPropertiesPbo(
334
- default_value=field.default_value,
335
- list_mode=list_mode_str,
336
- # auto_sort # Missing in FieldDefinition.py
337
- direct_edit=field.direct_edit,
338
- unique_value=field.unique_value,
339
- # link_out # Missing in FieldDefinition.py
340
- # link_out_url # Missing in FieldDefinition.py
341
- multi_select=field.multi_select,
342
- # index_for_search # Missing in FieldDefinition.py
343
- # is_auto_size # Missing in FieldDefinition.py
344
- # field_validator # Missing in FieldDefinition.py
345
- static_list_values=field.static_list_values if field.static_list_values is not None else [],
346
- # color_mapping # Missing in FieldDefinition.py
347
- # auto_clear_field_list # Missing in FieldDefinition.py
348
- # process_detail_map # Missing in FieldDefinition.py
349
- dependent_fields=[SelectionDependentFieldEntryPbo(k, v) for k,v in field.get_dependent_field_map().items()],
350
- is_hide_disabled_fields=field.hide_disabled_fields
351
- )
352
- elif isinstance(field, VeloxShortFieldDefinition):
353
- short_properties = ShortPropertiesPbo(
354
- min_value=field.min_value,
355
- max_value=field.max_value,
356
- default_value=field.default_value,
357
- unique_value=field.unique_value,
358
- # color_ranges # Missing in FieldDefinition.py
359
- )
360
- elif isinstance(field, VeloxSideLinkFieldDefinition):
361
- side_link_properties = SideLinkPropertiesPbo(
362
- linked_data_type_name=field.linked_data_type_name,
363
- default_value=field.default_value,
364
- # show_in_knowledge_graph # Missing in FieldDefinition.py
365
- # knowledge_graph_display_name # Missing in FieldDefinition.py
366
- )
367
- elif isinstance(field, VeloxStringFieldDefinition):
368
- string_properties = StringPropertiesPbo(
369
- default_value=field.default_value,
370
- max_length=field.max_length,
371
- num_lines=field.num_lines,
372
- unique_value=field.unique_value,
373
- # index_for_search # Missing in FieldDefinition.py
374
- html_editor=field.html_editor,
375
- link_out=field.link_out,
376
- link_out_url=field.link_out_url,
377
- string_format=ProtobufUtils.string_format_to_pbo(field.string_format),
378
- is_auto_size=field.auto_size,
379
- field_validator=ProtobufUtils.field_validator_to_pbo(field.field_validator),
380
- # preserve_padding # Missing in FieldDefinition.py
381
- )
382
- # else: # Handle unknown types or just let it pass with no specific properties
383
- # print(f"Warning: Unhandled field type for properties mapping: {type(field)}")
384
-
385
- return VeloxFieldDefPbo(
386
- data_field_type=ProtobufUtils.field_type_to_pbo(field.data_field_type),
387
- data_field_name=field.data_field_name,
388
- display_name=field.display_name,
389
- description=field.description,
390
- required=field.required,
391
- editable=field.editable,
392
- visible=field.visible,
393
- identifier=field.identifier,
394
- identifier_order=field.identifier_order,
395
- sort_direction=ProtobufUtils.sort_direction_to_pbo(field.sort_direction),
396
- sort_order=field.sort_order,
397
- tag=field.tag,
398
- # approve_edit # Missing in FieldDefinition.py
399
- # workflow_only_editing # Missing in FieldDefinition.py
400
- # font_size # Missing in FieldDefinition.py
401
- # bold_font # Missing in FieldDefinition.py
402
- # italic_font # Missing in FieldDefinition.py
403
- # text_decoration # Missing in FieldDefinition.py
404
- is_key_field=field.key_field,
405
- key_field_order=field.key_field_order,
406
- # is_removable # Missing in FieldDefinition.py
407
- is_system_field=field.system_field,
408
- # is_restricted # Missing in FieldDefinition.py
409
- is_audit_logged=field.audit_logged,
410
- # is_active # Missing in FieldDefinition.py
411
- # is_for_plugin_use_only # Missing in FieldDefinition.py
412
- default_table_column_width=field.default_table_column_width,
413
-
414
- accession_properties=accession_properties,
415
- action_properties=action_properties,
416
- action_string_properties=action_string_properties,
417
- boolean_properties=boolean_properties,
418
- child_link_properties=child_link_properties,
419
- date_properties=date_properties,
420
- date_range_properties=date_range_properties,
421
- double_properties=double_properties,
422
- enum_properties=enum_properties,
423
- file_blob_properties=file_blob_properties,
424
- identifier_properties=identifier_properties,
425
- integer_properties=integer_properties,
426
- long_properties=long_properties,
427
- multi_parent_properties=multi_parent_properties,
428
- parent_link_properties=parent_link_properties,
429
- picklist_properties=picklist_properties,
430
- selection_properties=selection_properties,
431
- short_properties=short_properties,
432
- side_link_properties=side_link_properties,
433
- string_properties=string_properties,
434
- )
435
-
436
- @staticmethod
437
- def field_pbo_to_value(field: Any, value: FieldValuePbo) -> FieldValue:
438
- """
439
- Convert a StepRecordFieldValue to its corresponding Python value based on the field type.
440
-
441
- :param field: The VeloxFieldDefPbo or FieldTypePbo object for the given value.
442
- :param value: The StepRecordFieldValue object.
443
- :return: The corresponding Python value.
444
- """
445
- field_type = field.data_field_type if hasattr(field, "data_field_type") else field
446
- match field_type:
447
- case FieldTypePbo.BOOLEAN:
448
- return value.bool_value
449
- case FieldTypePbo.DOUBLE:
450
- return value.double_value
451
- case FieldTypePbo.ENUM | FieldTypePbo.INTEGER | FieldTypePbo.LONG | FieldTypePbo.SHORT:
452
- return value.int_value
453
- case _:
454
- return value.string_value
@@ -1,197 +0,0 @@
1
- import json
2
- from typing import Mapping, Any
3
-
4
- import grpc
5
-
6
- from sapiopycommons.ai.api.fielddefinitions.proto.fields_pb2 import FieldValuePbo
7
- from sapiopycommons.ai.api.plan.tool.proto.entry_pb2 import DataTypePbo, StepBinaryContainerPbo, StepCsvRowPbo, \
8
- StepCsvHeaderRowPbo, StepCsvContainerPbo, StepJsonContainerPbo, StepImageContainerPbo, StepTextContainerPbo, \
9
- StepItemContainerPbo, StepInputBatchPbo
10
- from sapiopycommons.ai.api.plan.tool.proto.tool_pb2 import ProcessStepResponsePbo, ProcessStepRequestPbo
11
- from sapiopycommons.ai.api.plan.tool.proto.tool_pb2_grpc import ToolServiceStub
12
- from sapiopycommons.ai.api.session.proto.sapio_conn_info_pb2 import SapioConnectionInfoPbo, SapioUserSecretTypePbo
13
-
14
-
15
- class TestOutput:
16
- """
17
- A class for holding the output of a TestClient that calls a ToolService. TestOutput objects an be
18
- printed to show the output of the tool in a human-readable format.
19
- """
20
- binary_output: list[bytes]
21
- csv_output: list[dict[str, Any]]
22
- json_output: list[Any]
23
- image_output: list[bytes]
24
- text_output: list[str]
25
-
26
- new_records: list[Mapping[str, FieldValuePbo]]
27
-
28
- logs: list[str]
29
-
30
- def __init__(self):
31
- self.binary_output = []
32
- self.csv_output = []
33
- self.json_output = []
34
- self.image_output = []
35
- self.text_output = []
36
- self.new_records = []
37
- self.logs = []
38
-
39
- def __str__(self):
40
- ret_val: str = ""
41
- ret_val += f"Binary Output: {len(self.binary_output)} item(s)\n"
42
- for binary in self.binary_output:
43
- ret_val += f"\t{len(binary)} byte(s)\n"
44
- ret_val += f"\t{binary[:50]}...\n"
45
- ret_val += f"CSV Output: {len(self.csv_output)} item(s)\n"
46
- if self.csv_output:
47
- ret_val += f"\tHeaders: {', '.join(self.csv_output[0].keys())}\n"
48
- for i, csv_row in enumerate(self.csv_output):
49
- ret_val += f"\t{i}: {', '.join(f'{v}' for k, v in csv_row.items())}\n"
50
- ret_val += f"JSON Output: {len(self.json_output)} item(s)\n"
51
- if self.json_output:
52
- ret_val += f"\t{json.dumps(self.json_output, indent=2)}\n"
53
- ret_val += f"Image Output: {len(self.image_output)} item(s)\n"
54
- for image in self.image_output:
55
- ret_val += f"\t{len(image)} bytes\n"
56
- ret_val += f"\t{image[:50]}...\n"
57
- ret_val += f"Text Output: {len(self.text_output)} item(s)\n"
58
- for text in self.text_output:
59
- ret_val += f"\t{text}...\n"
60
- ret_val += f"New Records: {len(self.new_records)} item(s)\n"
61
- for record in self.new_records:
62
- ret_val += f"\t{json.dumps(record, indent=2)}\n"
63
- ret_val += f"Logs: {len(self.logs)} item(s)\n"
64
- for log in self.logs:
65
- ret_val += f"\t{log}\n"
66
- return ret_val
67
-
68
-
69
- class TestClient:
70
- """
71
- A client for testing a ToolService. This client can be used to send requests to a tool and receive
72
- responses.
73
- """
74
- server_url: str
75
- tool_name: str
76
- connection: SapioConnectionInfoPbo
77
- request_inputs: list[Any]
78
-
79
- def __init__(self, server_url: str, tool_name: str):
80
- """
81
- :param server_url: The URL of the gRPC server to connect to.
82
- :param tool_name: The name of the tool to call on the server.
83
- """
84
- self.create_user()
85
- self.server_url = server_url
86
- self.tool_name = tool_name
87
- self.request_inputs = []
88
-
89
- def create_user(self):
90
- """
91
- Create a SapioConnectionInfoPbo object with test credentials. This method can be overridden to
92
- create a user with specific credentials for testing.
93
- """
94
- self.connection = SapioConnectionInfoPbo()
95
- self.connection.username = "Testing"
96
- self.connection.webservice_url = "https://localhost:8080/webservice/api"
97
- self.connection.app_guid = "1234567890"
98
- self.connection.secret_type = SapioUserSecretTypePbo.PASSWORD
99
- self.connection.rmi_host.append("Testing")
100
- self.connection.rmi_port = 9001
101
- self.connection.secret = "password"
102
-
103
- def add_input_input(self, input_data: list[bytes]) -> None:
104
- """
105
- Add a binary input to the the next request.
106
- """
107
- self._add_input(DataTypePbo.BINARY, StepBinaryContainerPbo(items=input_data))
108
-
109
- def add_csv_input(self, input_data: list[dict[str, Any]]) -> None:
110
- """
111
- Add a CSV input to the next request.
112
- """
113
- csv_items = []
114
- for row in input_data:
115
- csv_items.append(StepCsvRowPbo(cells=[str(value) for value in row.values()]))
116
- header = StepCsvHeaderRowPbo(cells=list(input_data[0].keys()))
117
- self._add_input(DataTypePbo.CSV, StepCsvContainerPbo(header=header, items=csv_items))
118
-
119
- def add_json_input(self, input_data: list[dict[str, Any]]) -> None:
120
- """
121
- Add a JSON input to the next request.
122
- """
123
- self._add_input(DataTypePbo.JSON, StepJsonContainerPbo(items=[json.dumps(x) for x in input_data]))
124
-
125
- def add_image_input(self, input_data: list[bytes], image_format: str = "png") -> None:
126
- """
127
- Add an image input to the next request.
128
- """
129
- self._add_input(DataTypePbo.IMAGE, StepImageContainerPbo(items=input_data, image_format=image_format))
130
-
131
- def add_text_input(self, input_data: list[str]) -> None:
132
- """
133
- Add a text input to the next request.
134
- """
135
- self._add_input(DataTypePbo.TEXT, StepTextContainerPbo(items=input_data))
136
-
137
- def _add_input(self, data_type: DataTypePbo, items: Any) -> None:
138
- """
139
- Helper method for adding inputs to the next request.
140
- """
141
- match data_type:
142
- case DataTypePbo.BINARY:
143
- container = StepItemContainerPbo(dataType=data_type, binary_container=items)
144
- case DataTypePbo.CSV:
145
- container = StepItemContainerPbo(dataType=data_type, csv_container=items)
146
- case DataTypePbo.JSON:
147
- container = StepItemContainerPbo(dataType=data_type, json_container=items)
148
- case DataTypePbo.IMAGE:
149
- container = StepItemContainerPbo(dataType=data_type, image_container=items)
150
- case DataTypePbo.TEXT:
151
- container = StepItemContainerPbo(dataType=data_type, text_container=items)
152
- case _:
153
- raise ValueError(f"Unsupported data type: {data_type}")
154
- self.request_inputs.append(container)
155
-
156
- def send_request(self) -> TestOutput:
157
- """
158
- Send the request to the tool service. This will send all the inputs that have been added using the
159
- add_X_input functions.
160
-
161
- :return: A TestOutput object containing the results of the tool service call.
162
- """
163
- with grpc.insecure_channel(self.server_url) as channel:
164
- stub = ToolServiceStub(channel)
165
-
166
- response: ProcessStepResponsePbo = stub.ProcessData(
167
- ProcessStepRequestPbo(
168
- sapio_user=self.connection,
169
- tool_name=self.tool_name,
170
- input=[
171
- StepInputBatchPbo(is_partial=False, item_container=item)
172
- for item in self.request_inputs
173
- ]
174
- )
175
- )
176
-
177
- results = TestOutput()
178
-
179
- for item in response.output:
180
- container = item.item_container
181
-
182
- results.binary_output.extend(container.binary_container.items)
183
- for header in container.csv_container.header.cells:
184
- output_row: dict[str, Any] = {}
185
- for i, row in enumerate(container.csv_container.items):
186
- output_row[header] = row.cells[i]
187
- results.csv_output.append(output_row)
188
- results.json_output.extend([json.loads(x) for x in container.json_container.items])
189
- results.image_output.extend(container.image_container.items)
190
- results.text_output.extend(container.text_container.items)
191
-
192
- for record in response.new_records:
193
- results.new_records.append(record.fields)
194
-
195
- results.logs.extend(response.log)
196
-
197
- return results