sapiopycommons 2024.8.26a309__tar.gz → 2024.8.27a312__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.

Potentially problematic release.


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

Files changed (61) hide show
  1. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/PKG-INFO +1 -1
  2. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/pyproject.toml +1 -1
  3. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/callbacks/callback_util.py +37 -133
  4. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/datatype/attachment_util.py +10 -11
  5. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/eln/experiment_handler.py +48 -209
  6. sapiopycommons-2024.8.27a312/src/sapiopycommons/eln/experiment_report_util.py +214 -0
  7. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/files/complex_data_loader.py +4 -5
  8. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/files/file_bridge.py +14 -15
  9. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/files/file_bridge_handler.py +5 -27
  10. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/files/file_data_handler.py +5 -2
  11. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/files/file_util.py +5 -38
  12. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/files/file_validator.py +11 -26
  13. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/files/file_writer.py +15 -44
  14. sapiopycommons-2024.8.27a312/src/sapiopycommons/general/aliases.py +82 -0
  15. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/general/custom_report_util.py +32 -34
  16. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/general/popup_util.py +0 -17
  17. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/general/time_util.py +0 -40
  18. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/multimodal/multimodal_data.py +1 -0
  19. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/processtracking/endpoints.py +22 -22
  20. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/recordmodel/record_handler.py +77 -228
  21. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/rules/eln_rule_handler.py +25 -34
  22. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/rules/on_save_rule_handler.py +31 -34
  23. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/webhook/webhook_handlers.py +26 -90
  24. sapiopycommons-2024.8.26a309/src/sapiopycommons/customreport/column_builder.py +0 -60
  25. sapiopycommons-2024.8.26a309/src/sapiopycommons/customreport/custom_report_builder.py +0 -125
  26. sapiopycommons-2024.8.26a309/src/sapiopycommons/customreport/term_builder.py +0 -299
  27. sapiopycommons-2024.8.26a309/src/sapiopycommons/eln/experiment_report_util.py +0 -118
  28. sapiopycommons-2024.8.26a309/src/sapiopycommons/general/aliases.py +0 -226
  29. sapiopycommons-2024.8.26a309/src/sapiopycommons/general/audit_log.py +0 -196
  30. sapiopycommons-2024.8.26a309/src/sapiopycommons/general/sapio_links.py +0 -50
  31. sapiopycommons-2024.8.26a309/src/sapiopycommons/webhook/__init__.py +0 -0
  32. sapiopycommons-2024.8.26a309/src/sapiopycommons/webhook/webservice_handlers.py +0 -67
  33. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/.gitignore +0 -0
  34. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/LICENSE +0 -0
  35. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/README.md +0 -0
  36. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/__init__.py +0 -0
  37. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/callbacks/__init__.py +0 -0
  38. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/chem/IndigoMolecules.py +0 -0
  39. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/chem/Molecules.py +0 -0
  40. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/chem/__init__.py +0 -0
  41. {sapiopycommons-2024.8.26a309/src/sapiopycommons/customreport → sapiopycommons-2024.8.27a312/src/sapiopycommons/datatype}/__init__.py +0 -0
  42. {sapiopycommons-2024.8.26a309/src/sapiopycommons/datatype → sapiopycommons-2024.8.27a312/src/sapiopycommons/eln}/__init__.py +0 -0
  43. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/eln/plate_designer.py +0 -0
  44. {sapiopycommons-2024.8.26a309/src/sapiopycommons/eln → sapiopycommons-2024.8.27a312/src/sapiopycommons/files}/__init__.py +0 -0
  45. {sapiopycommons-2024.8.26a309/src/sapiopycommons/files → sapiopycommons-2024.8.27a312/src/sapiopycommons/general}/__init__.py +0 -0
  46. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/general/accession_service.py +0 -0
  47. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/general/exceptions.py +0 -0
  48. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/general/storage_util.py +0 -0
  49. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/src/sapiopycommons/multimodal/multimodal.py +0 -0
  50. {sapiopycommons-2024.8.26a309/src/sapiopycommons/general → sapiopycommons-2024.8.27a312/src/sapiopycommons/processtracking}/__init__.py +0 -0
  51. {sapiopycommons-2024.8.26a309/src/sapiopycommons/processtracking → sapiopycommons-2024.8.27a312/src/sapiopycommons/recordmodel}/__init__.py +0 -0
  52. {sapiopycommons-2024.8.26a309/src/sapiopycommons/recordmodel → sapiopycommons-2024.8.27a312/src/sapiopycommons/rules}/__init__.py +0 -0
  53. {sapiopycommons-2024.8.26a309/src/sapiopycommons/rules → sapiopycommons-2024.8.27a312/src/sapiopycommons/webhook}/__init__.py +0 -0
  54. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/tests/_do_not_add_init_py_here +0 -0
  55. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/tests/accession_test.py +0 -0
  56. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/tests/bio_reg_test.py +0 -0
  57. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/tests/chem_test.py +0 -0
  58. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/tests/data_type_models.py +0 -0
  59. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/tests/kappa.chains.fasta +0 -0
  60. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/tests/mafft_test.py +0 -0
  61. {sapiopycommons-2024.8.26a309 → sapiopycommons-2024.8.27a312}/tests/test.gb +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sapiopycommons
3
- Version: 2024.8.26a309
3
+ Version: 2024.8.27a312
4
4
  Summary: Official Sapio Python API Utilities Package
5
5
  Project-URL: Homepage, https://github.com/sapiosciences
6
6
  Author-email: Jonathan Steck <jsteck@sapiosciences.com>, Yechen Qiao <yqiao@sapiosciences.com>
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sapiopycommons"
7
- version='2024.08.26a309'
7
+ version='2024.08.27a312'
8
8
  authors = [
9
9
  { name="Jonathan Steck", email="jsteck@sapiosciences.com" },
10
10
  { name="Yechen Qiao", email="yqiao@sapiosciences.com" },
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import io
4
- from weakref import WeakValueDictionary
4
+ from typing import Any
5
5
 
6
6
  from sapiopylib.rest.ClientCallbackService import ClientCallback
7
7
  from sapiopylib.rest.DataMgmtService import DataMgmtServer
@@ -15,17 +15,16 @@ from sapiopylib.rest.pojo.datatype.FieldDefinition import AbstractVeloxFieldDefi
15
15
  from sapiopylib.rest.pojo.webhook.ClientCallbackRequest import OptionDialogRequest, ListDialogRequest, \
16
16
  FormEntryDialogRequest, InputDialogCriteria, TableEntryDialogRequest, ESigningRequestPojo, \
17
17
  DataRecordDialogRequest, InputSelectionRequest, FilePromptRequest, MultiFilePromptRequest, \
18
- TempTableSelectionRequest, DisplayPopupRequest, PopupType
18
+ TempTableSelectionRequest
19
19
  from sapiopylib.rest.pojo.webhook.ClientCallbackResult import ESigningResponsePojo
20
+ from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
20
21
  from sapiopylib.rest.pojo.webhook.WebhookEnums import FormAccessLevel, ScanToSelectCriteria, SearchType
21
22
  from sapiopylib.rest.utils.DataTypeCacheManager import DataTypeCacheManager
22
23
  from sapiopylib.rest.utils.FormBuilder import FormBuilder
23
24
  from sapiopylib.rest.utils.recorddatasinks import InMemoryRecordDataSink
24
25
  from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
25
26
 
26
- from sapiopycommons.files.file_util import FileUtil
27
- from sapiopycommons.general.aliases import FieldMap, SapioRecord, AliasUtil, RecordIdentifier, FieldValue, \
28
- UserIdentifier
27
+ from sapiopycommons.general.aliases import FieldMap, SapioRecord, AliasUtil, RecordIdentifier
29
28
  from sapiopycommons.general.custom_report_util import CustomReportUtil
30
29
  from sapiopycommons.general.exceptions import SapioUserCancelledException, SapioException, SapioUserErrorException
31
30
  from sapiopycommons.recordmodel.record_handler import RecordHandler
@@ -38,86 +37,26 @@ class CallbackUtil:
38
37
  width_pixels: int | None
39
38
  width_percent: float | None
40
39
 
41
- __instances: WeakValueDictionary[SapioUser, CallbackUtil] = WeakValueDictionary()
42
- __initialized: bool
43
-
44
- def __new__(cls, context: UserIdentifier):
45
- """
46
- :param context: The current webhook context or a user object to send requests from.
47
- """
48
- user = AliasUtil.to_sapio_user(context)
49
- obj = cls.__instances.get(user)
50
- if not obj:
51
- obj = object.__new__(cls)
52
- obj.__initialized = False
53
- cls.__instances[user] = obj
54
- return obj
55
-
56
- def __init__(self, context: UserIdentifier):
40
+ def __init__(self, context: SapioWebhookContext | SapioUser):
57
41
  """
58
42
  :param context: The current webhook context or a user object to send requests from.
59
43
  """
60
- if self.__initialized:
61
- return
62
- self.__initialized = True
63
-
64
- self.user = AliasUtil.to_sapio_user(context)
44
+ self.user = context if isinstance(context, SapioUser) else context.user
65
45
  self.callback = DataMgmtServer.get_client_callback(self.user)
66
46
  self.dt_cache = DataTypeCacheManager(self.user)
67
47
  self.width_pixels = None
68
48
  self.width_percent = None
69
49
 
70
- def set_dialog_width(self, width_pixels: int | None = None, width_percent: float | None = None):
50
+ def set_dialog_width(self, width_pixels: int | None, width_percent: float | None):
71
51
  """
72
52
  Set the width that dialogs will appear as for those dialogs that support specifying their width.
73
53
 
74
54
  :param width_pixels: The number of pixels wide that dialogs will appear as.
75
- :param width_percent: The percentage (as a value between 0 and 1) of the client's screen width that dialogs
76
- will appear as.
55
+ :param width_percent: The percentage of the client's screen width that dialogs will appear as.
77
56
  """
78
- if width_pixels is not None and width_percent is not None:
79
- raise SapioException("Cannot set both width_pixels and width_percent at once.")
80
57
  self.width_pixels = width_pixels
81
58
  self.width_percent = width_percent
82
-
83
- def toaster_popup(self, message: str, title: str = "", popup_type: PopupType = PopupType.Info) -> None:
84
- """
85
- Display a toaster popup in the bottom right corner of the user's screen.
86
-
87
- :param message: The message to display in the toaster.
88
- :param title: The title to display at the top of the toaster.
89
- :param popup_type: The popup type to use for the toaster. This controls the color that the toaster appears with.
90
- Info is blue, Success is green, Warning is yellow, and Error is red
91
- """
92
- self.callback.display_popup(DisplayPopupRequest(title, message, popup_type))
93
-
94
- def display_info(self, message: str) -> None:
95
- """
96
- Display an info message to the user in a dialog. Repeated calls to this function will append the new messages
97
- to the same dialog if it is still opened by the user.
98
-
99
- :param message: The message to display to the user.
100
- """
101
- self.callback.display_info(message)
102
-
103
- def display_warning(self, message: str) -> None:
104
- """
105
- Display a warning message to the user in a dialog. Repeated calls to this function will append the new messages
106
- to the same dialog if it is still opened by the user.
107
-
108
- :param message: The message to display to the user.
109
- """
110
- self.callback.display_warning(message)
111
-
112
- def display_error(self, message: str) -> None:
113
- """
114
- Display an error message to the user in a dialog. Repeated calls to this function will append the new messages
115
- to the same dialog if it is still opened by the user.
116
-
117
- :param message: The message to display to the user.
118
- """
119
- self.callback.display_error(message)
120
-
59
+
121
60
  def option_dialog(self, title: str, msg: str, options: list[str], default_option: int = 0,
122
61
  user_can_cancel: bool = False) -> str:
123
62
  """
@@ -132,8 +71,7 @@ class CallbackUtil:
132
71
  SapioUserCancelledException is thrown.
133
72
  :return: The name of the button that the user selected.
134
73
  """
135
- request = OptionDialogRequest(title, msg, options, default_option, user_can_cancel,
136
- width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
74
+ request = OptionDialogRequest(title, msg, options, default_option, user_can_cancel)
137
75
  response: int | None = self.callback.show_option_dialog(request)
138
76
  if response is None:
139
77
  raise SapioUserCancelledException()
@@ -173,20 +111,16 @@ class CallbackUtil:
173
111
  """
174
112
  return self.option_dialog(title, msg, ["Yes", "No"], 0 if default_yes else 1, False) == "Yes"
175
113
 
176
- def list_dialog(self, title: str, options: list[str], multi_select: bool = False,
177
- preselected_values: list[str] | None = None) -> list[str]:
114
+ def list_dialog(self, title: str, options: list[str], multi_select: bool = False) -> list[str]:
178
115
  """
179
116
  Create a list dialog with the given options for the user to choose from.
180
117
 
181
118
  :param title: The title of the dialog.
182
119
  :param options: The list options that the user has to choose from.
183
120
  :param multi_select: Whether the user is able to select multiple options from the list.
184
- :param preselected_values: A list of values that will already be selected when the list dialog is created. The
185
- user can unselect these values if they want to.
186
121
  :return: The list of options that the user selected.
187
122
  """
188
- request = ListDialogRequest(title, multi_select, options, preselected_values,
189
- width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
123
+ request = ListDialogRequest(title, multi_select, options)
190
124
  response: list[str] | None = self.callback.show_list_dialog(request)
191
125
  if response is None:
192
126
  raise SapioUserCancelledException()
@@ -229,6 +163,8 @@ class CallbackUtil:
229
163
  builder = FormBuilder(data_type, display_name, plural_display_name)
230
164
  for field_def in fields:
231
165
  field_name = field_def.data_field_name
166
+ if values and hasattr(field_def, "default_value"):
167
+ field_def.default_value = values.get(field_name)
232
168
  column: int = 0
233
169
  span: int = 4
234
170
  if column_positions and field_name in column_positions:
@@ -237,8 +173,7 @@ class CallbackUtil:
237
173
  span = position[1]
238
174
  builder.add_field(field_def, column, span)
239
175
 
240
- request = FormEntryDialogRequest(title, msg, builder.get_temporary_data_type(), values,
241
- width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
176
+ request = FormEntryDialogRequest(title, msg, builder.get_temporary_data_type())
242
177
  response: FieldMap | None = self.callback.show_form_entry_dialog(request)
243
178
  if response is None:
244
179
  raise SapioUserCancelledException()
@@ -280,13 +215,13 @@ class CallbackUtil:
280
215
  modifier = FieldModifier(visible=True, editable=editable)
281
216
 
282
217
  # Build the form using only those fields that are desired.
283
- values: dict[str, FieldValue] = {}
284
218
  builder = FormBuilder(data_type, type_def.display_name, type_def.plural_display_name)
285
219
  for field_name in fields:
286
220
  field_def = field_defs.get(field_name)
287
221
  if field_def is None:
288
222
  raise SapioException(f"No field of name \"{field_name}\" in field definitions of type \"{data_type}\"")
289
- values[field_name] = record.get_field_value(field_name)
223
+ if hasattr(field_def, "default_value"):
224
+ field_def.default_value = record.get_field_value(field_name)
290
225
  column: int = 0
291
226
  span: int = 4
292
227
  if column_positions and field_name in column_positions:
@@ -295,14 +230,13 @@ class CallbackUtil:
295
230
  span = position[1]
296
231
  builder.add_field(modifier.modify_field(field_def), column, span)
297
232
 
298
- request = FormEntryDialogRequest(title, msg, builder.get_temporary_data_type(), values,
299
- width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
233
+ request = FormEntryDialogRequest(title, msg, builder.get_temporary_data_type())
300
234
  response: FieldMap | None = self.callback.show_form_entry_dialog(request)
301
235
  if response is None:
302
236
  raise SapioUserCancelledException()
303
237
  return response
304
238
 
305
- def input_dialog(self, title: str, msg: str, field: AbstractVeloxFieldDefinition) -> FieldValue:
239
+ def input_dialog(self, title: str, msg: str, field: AbstractVeloxFieldDefinition) -> Any:
306
240
  """
307
241
  Create an input dialog where the user must input data for a singular field.
308
242
 
@@ -311,9 +245,8 @@ class CallbackUtil:
311
245
  :param field: The definition for a field that the user must provide input to.
312
246
  :return: The response value from the user for the given field.
313
247
  """
314
- request = InputDialogCriteria(title, msg, field,
315
- width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
316
- response: FieldValue | None = self.callback.show_input_dialog(request)
248
+ request = InputDialogCriteria(title, msg, field, self.width_pixels, self.width_percent)
249
+ response: Any | None = self.callback.show_input_dialog(request)
317
250
  if response is None:
318
251
  raise SapioUserCancelledException()
319
252
  return response
@@ -389,8 +322,6 @@ class CallbackUtil:
389
322
  msg: str,
390
323
  fields: list[AbstractVeloxFieldDefinition],
391
324
  values: list[FieldMap],
392
- group_by: str | None = None,
393
- image_data: list[bytes] | None = None,
394
325
  *,
395
326
  data_type: str = "Default",
396
327
  display_name: str | None = None,
@@ -404,10 +335,6 @@ class CallbackUtil:
404
335
  :param fields: The definitions of the fields to display as table columns. Fields will be displayed in the order
405
336
  they are provided in this list.
406
337
  :param values: The values to set for each row of the table.
407
- :param group_by: If provided, the created table dialog will be grouped by the field with this name by default.
408
- The user may remove this grouping if they want to.
409
- :param image_data: The bytes to the images that should be displayed in the rows of the table. Each element in
410
- the image data list corresponds to the element at the same index in the values list.
411
338
  :param data_type: The data type name for the temporary data type that will be created for this table.
412
339
  :param display_name: The display name for the temporary data type. If not provided, defaults to the data type
413
340
  name.
@@ -429,9 +356,7 @@ class CallbackUtil:
429
356
  for field in fields:
430
357
  builder.add_field(modifier.modify_field(field))
431
358
 
432
- request = TableEntryDialogRequest(title, msg, builder.get_temporary_data_type(), values,
433
- record_image_data_list=image_data, group_by_field=group_by,
434
- width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
359
+ request = TableEntryDialogRequest(title, msg, builder.get_temporary_data_type(), values)
435
360
  response: list[FieldMap] | None = self.callback.show_table_entry_dialog(request)
436
361
  if response is None:
437
362
  raise SapioUserCancelledException()
@@ -442,9 +367,7 @@ class CallbackUtil:
442
367
  msg: str,
443
368
  fields: list[str],
444
369
  records: list[SapioRecord],
445
- editable: bool | None = True,
446
- group_by: str | None = None,
447
- image_data: list[bytes] | None = None) -> list[FieldMap]:
370
+ editable: bool | None = True) -> list[FieldMap]:
448
371
  """
449
372
  Create a table dialog where the user may input data into the fields of the table. The table is constructed from
450
373
  a given list of records of a singular type. Provided field names must match fields on the definition of the data
@@ -461,10 +384,6 @@ class CallbackUtil:
461
384
  they are provided in this list.
462
385
  :param editable: If true, all fields are displayed as editable. If false, all fields are displayed as
463
386
  uneditable. If none, only those fields that are defined as editable by the data designer will be editable.
464
- :param group_by: If provided, the created table dialog will be grouped by the field with this name by default.
465
- The user may remove this grouping if they want to.
466
- :param image_data: The bytes to the images that should be displayed in the rows of the table. Each element in
467
- the image data list corresponds to the element at the same index in the records list.
468
387
  :return: A list of dictionaries mapping the data field names of the given field definitions to the response
469
388
  value from the user for that field for each row.
470
389
  """
@@ -491,9 +410,7 @@ class CallbackUtil:
491
410
  raise SapioException(f"No field of name \"{field_name}\" in field definitions of type \"{data_type}\"")
492
411
  builder.add_field(modifier.modify_field(field_def))
493
412
 
494
- request = TableEntryDialogRequest(title, msg, builder.get_temporary_data_type(), field_map_list,
495
- record_image_data_list=image_data, group_by_field=group_by,
496
- width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
413
+ request = TableEntryDialogRequest(title, msg, builder.get_temporary_data_type(), field_map_list)
497
414
  response: list[FieldMap] | None = self.callback.show_table_entry_dialog(request)
498
415
  if response is None:
499
416
  raise SapioUserCancelledException()
@@ -605,10 +522,10 @@ class CallbackUtil:
605
522
  field_names.append(name)
606
523
 
607
524
  # Get the values for each row.
608
- values: list[dict[str, FieldValue]] = []
525
+ values: list[dict[str, Any]] = []
609
526
  for row in row_contents:
610
527
  # The final values for this row:
611
- row_values: dict[str, FieldValue] = {}
528
+ row_values: dict[str, Any] = {}
612
529
 
613
530
  # Map the records for this row by their data type. If a field map is provided, its data type is Default.
614
531
  row_records: dict[str, SapioRecord | FieldMap] = {}
@@ -650,8 +567,7 @@ class CallbackUtil:
650
567
  for field in final_fields:
651
568
  builder.add_field(field)
652
569
 
653
- request = TableEntryDialogRequest(title, msg, builder.get_temporary_data_type(), values,
654
- width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
570
+ request = TableEntryDialogRequest(title, msg, builder.get_temporary_data_type(), values)
655
571
  response: list[FieldMap] | None = self.callback.show_table_entry_dialog(request)
656
572
  if response is None:
657
573
  raise SapioUserCancelledException()
@@ -700,8 +616,7 @@ class CallbackUtil:
700
616
  raise SapioException(f"The data type \"{data_type}\" does not have a layout by the name "
701
617
  f"\"{layout_name}\" in the system.")
702
618
 
703
- request = DataRecordDialogRequest(title, record, layout, minimized, access_level, plugin_path_list,
704
- width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
619
+ request = DataRecordDialogRequest(title, record, layout, minimized, access_level, plugin_path_list)
705
620
  response: bool = self.callback.data_record_form_view_dialog(request)
706
621
  if not response:
707
622
  raise SapioUserCancelledException()
@@ -748,7 +663,7 @@ class CallbackUtil:
748
663
  return response
749
664
 
750
665
  def record_selection_dialog(self, msg: str, fields: list[str], records: list[SapioRecord],
751
- multi_select: bool = True) -> list[SapioRecord]:
666
+ multi_select: bool = True) -> list[FieldMap]:
752
667
  """
753
668
  Create a record selection dialog for a list of records for the user to choose from. Provided field names must
754
669
  match fields on the definition of the data type of the given records.
@@ -761,7 +676,7 @@ class CallbackUtil:
761
676
  they are provided in this list.
762
677
  :param records: The records to display as rows in the table.
763
678
  :param multi_select: Whether the user is able to select multiple records from the list.
764
- :return: A list of the selected records.
679
+ :return: A list of field maps corresponding to the chosen input records.
765
680
  """
766
681
  data_types: set[str] = {x.data_type_name for x in records}
767
682
  if len(data_types) > 1:
@@ -859,7 +774,7 @@ class CallbackUtil:
859
774
 
860
775
  # If CustomReportCriteria was provided, it must be wrapped as a CustomReport.
861
776
  if isinstance(custom_search, CustomReportCriteria):
862
- custom_search: CustomReport = CustomReport(False, [], custom_search)
777
+ custom_search: CustomReport = CustomReport(False, None, custom_search)
863
778
  # If a string was provided, locate the report criteria for the predefined search in the system matching this
864
779
  # name.
865
780
  if isinstance(custom_search, str):
@@ -892,15 +807,14 @@ class CallbackUtil:
892
807
  for field in additional_fields:
893
808
  builder.add_field(field)
894
809
  temp_dt = builder.get_temporary_data_type()
895
- request = ESigningRequestPojo(title, msg, show_comment, temp_dt,
896
- width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
810
+ request = ESigningRequestPojo(title, msg, show_comment, temp_dt)
897
811
  response: ESigningResponsePojo | None = self.callback.show_esign_dialog(request)
898
812
  if response is None:
899
813
  raise SapioUserCancelledException()
900
814
  return response
901
815
 
902
816
  def request_file(self, title: str, exts: list[str] | None = None,
903
- show_image_editor: bool = False, show_camera_button: bool = False) -> tuple[str, bytes]:
817
+ show_image_editor: bool = False, show_camera_button: bool = False) -> (str, bytes):
904
818
  """
905
819
  Request a single file from the user.
906
820
 
@@ -933,7 +847,7 @@ class CallbackUtil:
933
847
  return file_path, sink.data
934
848
 
935
849
  def request_files(self, title: str, exts: list[str] | None = None,
936
- show_image_editor: bool = False, show_camera_button: bool = False) -> dict[str, bytes]:
850
+ show_image_editor: bool = False, show_camera_button: bool = False):
937
851
  """
938
852
  Request multiple files from the user.
939
853
 
@@ -964,7 +878,7 @@ class CallbackUtil:
964
878
  return ret_dict
965
879
 
966
880
  @staticmethod
967
- def __verify_file(file_path: str, file_bytes: bytes, allowed_extensions: list[str]) -> None:
881
+ def __verify_file(file_path: str, file_bytes: bytes, allowed_extensions: list[str]):
968
882
  """
969
883
  Verify that the provided file was read (i.e. the file path and file bytes aren't None or empty) and that it
970
884
  has the correct file extension. Raises a user error exception if something about the file is incorrect.
@@ -978,7 +892,7 @@ class CallbackUtil:
978
892
  if len(allowed_extensions) != 0:
979
893
  matches: bool = False
980
894
  for ext in allowed_extensions:
981
- if file_path.endswith("." + ext.lstrip(".")):
895
+ if file_path.endswith("." + ext):
982
896
  matches = True
983
897
  break
984
898
  if matches is False:
@@ -992,19 +906,9 @@ class CallbackUtil:
992
906
  :param file_name: The name of the file.
993
907
  :param file_data: The data of the file, provided as either a string or as a bytes array.
994
908
  """
995
- data = io.BytesIO(file_data.encode() if isinstance(file_data, str) else file_data)
909
+ data = io.StringIO(file_data) if isinstance(file_data, str) else io.BytesIO(file_data)
996
910
  self.callback.send_file(file_name, False, data)
997
911
 
998
- def write_zip_file(self, zip_name: str, files: dict[str, str | bytes]) -> None:
999
- """
1000
- Send a collection of files to the user in a zip file.
1001
-
1002
- :param zip_name: The name of the zip file.
1003
- :param files: A dictionary of the files to add to the zip file.
1004
- """
1005
- data = io.BytesIO(FileUtil.zip_files(files))
1006
- self.callback.send_file(zip_name, False, data)
1007
-
1008
912
 
1009
913
  class FieldModifier:
1010
914
  """
@@ -1,9 +1,10 @@
1
1
  import io
2
2
 
3
- from sapiopylib.rest.DataMgmtService import DataMgmtServer
3
+ from sapiopylib.rest.User import SapioUser
4
+ from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
4
5
  from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
5
6
 
6
- from sapiopycommons.general.aliases import AliasUtil, SapioRecord, UserIdentifier
7
+ from sapiopycommons.general.aliases import AliasUtil, SapioRecord
7
8
  from sapiopycommons.general.exceptions import SapioException
8
9
  from sapiopycommons.recordmodel.record_handler import RecordHandler
9
10
 
@@ -11,32 +12,31 @@ from sapiopycommons.recordmodel.record_handler import RecordHandler
11
12
  # FR-46064 - Initial port of PyWebhookUtils to sapiopycommons.
12
13
  class AttachmentUtil:
13
14
  @staticmethod
14
- def get_attachment_bytes(context: UserIdentifier, attachment: SapioRecord) -> bytes:
15
+ def get_attachment_bytes(context: SapioWebhookContext, attachment: SapioRecord) -> bytes:
15
16
  """
16
17
  Get the data bytes for the given attachment record. Makes a webservice call to retrieve the data.
17
18
 
18
- :param context: The current webhook context or a user object to send requests from.
19
+ :param context: The current webhook context.
19
20
  :param attachment: The attachment record.
20
21
  :return: The bytes for the attachment's file data.
21
22
  """
22
23
  attachment = AliasUtil.to_data_record(attachment)
23
- dr_man = DataMgmtServer.get_data_record_manager(AliasUtil.to_sapio_user(context))
24
24
  with io.BytesIO() as data_sink:
25
25
  def consume_data(chunk: bytes):
26
26
  data_sink.write(chunk)
27
- dr_man.get_attachment_data(attachment, consume_data)
27
+ context.data_record_manager.get_attachment_data(attachment, consume_data)
28
28
  data_sink.flush()
29
29
  data_sink.seek(0)
30
30
  file_bytes = data_sink.read()
31
31
  return file_bytes
32
32
 
33
33
  @staticmethod
34
- def set_attachment_bytes(context: UserIdentifier, attachment: SapioRecord,
34
+ def set_attachment_bytes(context: SapioWebhookContext, attachment: SapioRecord,
35
35
  file_name: str, file_bytes: bytes) -> None:
36
36
  """
37
37
  Set the attachment data for a given attachment record. Makes a webservice call to set the data.
38
38
 
39
- :param context: The current webhook context or a user object to send requests from.
39
+ :param context: The current webhook context.
40
40
  :param attachment: The attachment record. Must be an existing data record that is an attachment type.
41
41
  :param file_name: The name of the attachment.
42
42
  :param file_bytes: The bytes of the attachment data.
@@ -45,12 +45,11 @@ class AttachmentUtil:
45
45
  raise SapioException("Provided record cannot have its attachment data set, as it does not exist in the "
46
46
  "system yet.")
47
47
  attachment = AliasUtil.to_data_record(attachment)
48
- dr_man = DataMgmtServer.get_data_record_manager(AliasUtil.to_sapio_user(context))
49
48
  with io.BytesIO(file_bytes) as stream:
50
- dr_man.set_attachment_data(attachment, file_name, stream)
49
+ context.data_record_manager.set_attachment_data(attachment, file_name, stream)
51
50
 
52
51
  @staticmethod
53
- def create_attachment(context: UserIdentifier, file_name: str, file_bytes: bytes,
52
+ def create_attachment(context: SapioWebhookContext | SapioUser, file_name: str, file_bytes: bytes,
54
53
  wrapper_type: type[WrappedType]) -> WrappedType:
55
54
  """
56
55
  Create an attachment data type and initialize its attachment bytes at the same time.