sapiopycommons 2024.10.1a337__tar.gz → 2024.10.4a339__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 (69) hide show
  1. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/PKG-INFO +1 -1
  2. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/pyproject.toml +1 -1
  3. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/callbacks/callback_util.py +123 -16
  4. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/general/exceptions.py +21 -8
  5. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/webhook/webhook_handlers.py +60 -22
  6. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/.gitignore +0 -0
  7. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/LICENSE +0 -0
  8. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/README.md +0 -0
  9. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/__init__.py +0 -0
  10. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/callbacks/__init__.py +0 -0
  11. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/chem/IndigoMolecules.py +0 -0
  12. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/chem/Molecules.py +0 -0
  13. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/chem/__init__.py +0 -0
  14. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/customreport/__init__.py +0 -0
  15. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/customreport/column_builder.py +0 -0
  16. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/customreport/custom_report_builder.py +0 -0
  17. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/customreport/term_builder.py +0 -0
  18. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/datatype/__init__.py +0 -0
  19. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/datatype/attachment_util.py +0 -0
  20. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/eln/__init__.py +0 -0
  21. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/eln/experiment_handler.py +0 -0
  22. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/eln/experiment_report_util.py +0 -0
  23. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/eln/plate_designer.py +0 -0
  24. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/files/__init__.py +0 -0
  25. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/files/complex_data_loader.py +0 -0
  26. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/files/file_bridge.py +0 -0
  27. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/files/file_bridge_handler.py +0 -0
  28. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/files/file_data_handler.py +0 -0
  29. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/files/file_util.py +0 -0
  30. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/files/file_validator.py +0 -0
  31. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/files/file_writer.py +0 -0
  32. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/flowcyto/flow_cyto.py +0 -0
  33. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/flowcyto/flowcyto_data.py +0 -0
  34. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/general/__init__.py +0 -0
  35. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/general/accession_service.py +0 -0
  36. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/general/aliases.py +0 -0
  37. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/general/audit_log.py +0 -0
  38. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/general/custom_report_util.py +0 -0
  39. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/general/popup_util.py +0 -0
  40. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/general/sapio_links.py +0 -0
  41. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/general/storage_util.py +0 -0
  42. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/general/time_util.py +0 -0
  43. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/multimodal/multimodal.py +0 -0
  44. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/multimodal/multimodal_data.py +0 -0
  45. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/processtracking/__init__.py +0 -0
  46. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/processtracking/endpoints.py +0 -0
  47. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/recordmodel/__init__.py +0 -0
  48. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/recordmodel/record_handler.py +0 -0
  49. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/rules/__init__.py +0 -0
  50. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/rules/eln_rule_handler.py +0 -0
  51. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/rules/on_save_rule_handler.py +0 -0
  52. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/webhook/__init__.py +0 -0
  53. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/src/sapiopycommons/webhook/webservice_handlers.py +0 -0
  54. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/_do_not_add_init_py_here +0 -0
  55. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/accession_test.py +0 -0
  56. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/bio_reg_test.py +0 -0
  57. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/chem_test.py +0 -0
  58. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/chem_test_curation_queue.py +0 -0
  59. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/curation_queue_test.sdf +0 -0
  60. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/data_type_models.py +0 -0
  61. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/flowcyto/101_DEN084Y5_15_E01_008_clean.fcs +0 -0
  62. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/flowcyto/101_DEN084Y5_15_E03_009_clean.fcs +0 -0
  63. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/flowcyto/101_DEN084Y5_15_E05_010_clean.fcs +0 -0
  64. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/flowcyto/8_color_ICS.wsp +0 -0
  65. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/flowcyto/COVID19_W_001_O.fcs +0 -0
  66. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/flowcyto_test.py +0 -0
  67. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/kappa.chains.fasta +0 -0
  68. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/mafft_test.py +0 -0
  69. {sapiopycommons-2024.10.1a337 → sapiopycommons-2024.10.4a339}/tests/test.gb +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sapiopycommons
3
- Version: 2024.10.1a337
3
+ Version: 2024.10.4a339
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.10.01a337'
7
+ version='2024.10.04a339'
8
8
  authors = [
9
9
  { name="Jonathan Steck", email="jsteck@sapiosciences.com" },
10
10
  { name="Yechen Qiao", email="yqiao@sapiosciences.com" },
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import io
4
4
  from weakref import WeakValueDictionary
5
5
 
6
+ from requests import ReadTimeout
6
7
  from sapiopylib.rest.ClientCallbackService import ClientCallback
7
8
  from sapiopylib.rest.DataMgmtService import DataMgmtServer
8
9
  from sapiopylib.rest.User import SapioUser
@@ -27,7 +28,8 @@ from sapiopycommons.files.file_util import FileUtil
27
28
  from sapiopycommons.general.aliases import FieldMap, SapioRecord, AliasUtil, RecordIdentifier, FieldValue, \
28
29
  UserIdentifier
29
30
  from sapiopycommons.general.custom_report_util import CustomReportUtil
30
- from sapiopycommons.general.exceptions import SapioUserCancelledException, SapioException, SapioUserErrorException
31
+ from sapiopycommons.general.exceptions import SapioUserCancelledException, SapioException, SapioUserErrorException, \
32
+ SapioDialogTimeoutException
31
33
  from sapiopycommons.recordmodel.record_handler import RecordHandler
32
34
 
33
35
 
@@ -35,6 +37,8 @@ class CallbackUtil:
35
37
  user: SapioUser
36
38
  callback: ClientCallback
37
39
  dt_cache: DataTypeCacheManager
40
+ _original_timeout: int
41
+ timeout_seconds: int
38
42
  width_pixels: int | None
39
43
  width_percent: float | None
40
44
 
@@ -64,6 +68,8 @@ class CallbackUtil:
64
68
  self.user = AliasUtil.to_sapio_user(context)
65
69
  self.callback = DataMgmtServer.get_client_callback(self.user)
66
70
  self.dt_cache = DataTypeCacheManager(self.user)
71
+ self._original_timeout = self.user.timeout_seconds
72
+ self.timeout_seconds = self.user.timeout_seconds
67
73
  self.width_pixels = None
68
74
  self.width_percent = None
69
75
 
@@ -80,6 +86,17 @@ class CallbackUtil:
80
86
  self.width_pixels = width_pixels
81
87
  self.width_percent = width_percent
82
88
 
89
+ def set_dialog_timeout(self, timeout: int):
90
+ """
91
+ Alter the timeout time used for callback requests that create dialogs for the user to interact with. By default,
92
+ a CallbackUtil will use the timeout time of the SapioUser provided to it. By altering this, a different timeout
93
+ time is used.
94
+
95
+ :param timeout: The number of seconds that must elapse before a SapioDialogTimeoutException is thrown by
96
+ any callback that creates a dialog for the user to interact with.
97
+ """
98
+ self.timeout_seconds = timeout
99
+
83
100
  def toaster_popup(self, message: str, title: str = "", popup_type: PopupType = PopupType.Info) -> None:
84
101
  """
85
102
  Display a toaster popup in the bottom right corner of the user's screen.
@@ -134,7 +151,13 @@ class CallbackUtil:
134
151
  """
135
152
  request = OptionDialogRequest(title, msg, options, default_option, user_can_cancel,
136
153
  width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
137
- response: int | None = self.callback.show_option_dialog(request)
154
+ try:
155
+ self.user.timeout_seconds = self.timeout_seconds
156
+ response: int | None = self.callback.show_option_dialog(request)
157
+ except ReadTimeout:
158
+ raise SapioDialogTimeoutException()
159
+ finally:
160
+ self.user.timeout_seconds = self._original_timeout
138
161
  if response is None:
139
162
  raise SapioUserCancelledException()
140
163
  return options[response]
@@ -187,7 +210,13 @@ class CallbackUtil:
187
210
  """
188
211
  request = ListDialogRequest(title, multi_select, options, preselected_values,
189
212
  width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
190
- response: list[str] | None = self.callback.show_list_dialog(request)
213
+ try:
214
+ self.user.timeout_seconds = self.timeout_seconds
215
+ response: list[str] | None = self.callback.show_list_dialog(request)
216
+ except ReadTimeout:
217
+ raise SapioDialogTimeoutException()
218
+ finally:
219
+ self.user.timeout_seconds = self._original_timeout
191
220
  if response is None:
192
221
  raise SapioUserCancelledException()
193
222
  return response
@@ -239,7 +268,13 @@ class CallbackUtil:
239
268
 
240
269
  request = FormEntryDialogRequest(title, msg, builder.get_temporary_data_type(), values,
241
270
  width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
242
- response: FieldMap | None = self.callback.show_form_entry_dialog(request)
271
+ try:
272
+ self.user.timeout_seconds = self.timeout_seconds
273
+ response: FieldMap | None = self.callback.show_form_entry_dialog(request)
274
+ except ReadTimeout:
275
+ raise SapioDialogTimeoutException()
276
+ finally:
277
+ self.user.timeout_seconds = self._original_timeout
243
278
  if response is None:
244
279
  raise SapioUserCancelledException()
245
280
  return response
@@ -297,7 +332,13 @@ class CallbackUtil:
297
332
 
298
333
  request = FormEntryDialogRequest(title, msg, builder.get_temporary_data_type(), values,
299
334
  width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
300
- response: FieldMap | None = self.callback.show_form_entry_dialog(request)
335
+ try:
336
+ self.user.timeout_seconds = self.timeout_seconds
337
+ response: FieldMap | None = self.callback.show_form_entry_dialog(request)
338
+ except ReadTimeout:
339
+ raise SapioDialogTimeoutException()
340
+ finally:
341
+ self.user.timeout_seconds = self._original_timeout
301
342
  if response is None:
302
343
  raise SapioUserCancelledException()
303
344
  return response
@@ -313,7 +354,13 @@ class CallbackUtil:
313
354
  """
314
355
  request = InputDialogCriteria(title, msg, field,
315
356
  width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
316
- response: FieldValue | None = self.callback.show_input_dialog(request)
357
+ try:
358
+ self.user.timeout_seconds = self.timeout_seconds
359
+ response: FieldValue | None = self.callback.show_input_dialog(request)
360
+ except ReadTimeout:
361
+ raise SapioDialogTimeoutException()
362
+ finally:
363
+ self.user.timeout_seconds = self._original_timeout
317
364
  if response is None:
318
365
  raise SapioUserCancelledException()
319
366
  return response
@@ -432,7 +479,13 @@ class CallbackUtil:
432
479
  request = TableEntryDialogRequest(title, msg, builder.get_temporary_data_type(), values,
433
480
  record_image_data_list=image_data, group_by_field=group_by,
434
481
  width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
435
- response: list[FieldMap] | None = self.callback.show_table_entry_dialog(request)
482
+ try:
483
+ self.user.timeout_seconds = self.timeout_seconds
484
+ response: list[FieldMap] | None = self.callback.show_table_entry_dialog(request)
485
+ except ReadTimeout:
486
+ raise SapioDialogTimeoutException()
487
+ finally:
488
+ self.user.timeout_seconds = self._original_timeout
436
489
  if response is None:
437
490
  raise SapioUserCancelledException()
438
491
  return response
@@ -496,7 +549,13 @@ class CallbackUtil:
496
549
  request = TableEntryDialogRequest(title, msg, builder.get_temporary_data_type(), field_map_list,
497
550
  record_image_data_list=image_data, group_by_field=group_by,
498
551
  width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
499
- response: list[FieldMap] | None = self.callback.show_table_entry_dialog(request)
552
+ try:
553
+ self.user.timeout_seconds = self.timeout_seconds
554
+ response: list[FieldMap] | None = self.callback.show_table_entry_dialog(request)
555
+ except ReadTimeout:
556
+ raise SapioDialogTimeoutException()
557
+ finally:
558
+ self.user.timeout_seconds = self._original_timeout
500
559
  if response is None:
501
560
  raise SapioUserCancelledException()
502
561
  return response
@@ -654,7 +713,13 @@ class CallbackUtil:
654
713
 
655
714
  request = TableEntryDialogRequest(title, msg, builder.get_temporary_data_type(), values,
656
715
  width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
657
- response: list[FieldMap] | None = self.callback.show_table_entry_dialog(request)
716
+ try:
717
+ self.user.timeout_seconds = self.timeout_seconds
718
+ response: list[FieldMap] | None = self.callback.show_table_entry_dialog(request)
719
+ except ReadTimeout:
720
+ raise SapioDialogTimeoutException()
721
+ finally:
722
+ self.user.timeout_seconds = self._original_timeout
658
723
  if response is None:
659
724
  raise SapioUserCancelledException()
660
725
  return response
@@ -704,7 +769,13 @@ class CallbackUtil:
704
769
 
705
770
  request = DataRecordDialogRequest(title, record, layout, minimized, access_level, plugin_path_list,
706
771
  width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
707
- response: bool = self.callback.data_record_form_view_dialog(request)
772
+ try:
773
+ self.user.timeout_seconds = self.timeout_seconds
774
+ response: bool = self.callback.data_record_form_view_dialog(request)
775
+ except ReadTimeout:
776
+ raise SapioDialogTimeoutException()
777
+ finally:
778
+ self.user.timeout_seconds = self._original_timeout
708
779
  if not response:
709
780
  raise SapioUserCancelledException()
710
781
 
@@ -744,7 +815,13 @@ class CallbackUtil:
744
815
 
745
816
  request = TempTableSelectionRequest(builder.get_temporary_data_type(), msg, values,
746
817
  multi_select=multi_select)
747
- response: list[FieldMap] | None = self.callback.show_temp_table_selection_dialog(request)
818
+ try:
819
+ self.user.timeout_seconds = self.timeout_seconds
820
+ response: list[FieldMap] | None = self.callback.show_temp_table_selection_dialog(request)
821
+ except ReadTimeout:
822
+ raise SapioDialogTimeoutException()
823
+ finally:
824
+ self.user.timeout_seconds = self._original_timeout
748
825
  if response is None:
749
826
  raise SapioUserCancelledException()
750
827
  return response
@@ -796,7 +873,13 @@ class CallbackUtil:
796
873
 
797
874
  request = TempTableSelectionRequest(builder.get_temporary_data_type(), msg, field_map_list,
798
875
  multi_select=multi_select)
799
- response: list[FieldMap] | None = self.callback.show_temp_table_selection_dialog(request)
876
+ try:
877
+ self.user.timeout_seconds = self.timeout_seconds
878
+ response: list[FieldMap] | None = self.callback.show_temp_table_selection_dialog(request)
879
+ except ReadTimeout:
880
+ raise SapioDialogTimeoutException()
881
+ finally:
882
+ self.user.timeout_seconds = self._original_timeout
800
883
  if response is None:
801
884
  raise SapioUserCancelledException()
802
885
  # Map the field maps in the response back to the record they come from, returning the chosen record instead of
@@ -872,7 +955,13 @@ class CallbackUtil:
872
955
  request = InputSelectionRequest(data_type, msg, search_types, only_key_fields, record_blacklist,
873
956
  record_whitelist, preselected_records, custom_search, scan_criteria,
874
957
  multi_select)
875
- response: list[DataRecord] | None = self.callback.show_input_selection_dialog(request)
958
+ try:
959
+ self.user.timeout_seconds = self.timeout_seconds
960
+ response: list[DataRecord] | None = self.callback.show_input_selection_dialog(request)
961
+ except ReadTimeout:
962
+ raise SapioDialogTimeoutException()
963
+ finally:
964
+ self.user.timeout_seconds = self._original_timeout
876
965
  if response is None:
877
966
  raise SapioUserCancelledException()
878
967
  return RecordHandler(self.user).wrap_models(response, wrapper_type)
@@ -898,7 +987,13 @@ class CallbackUtil:
898
987
  temp_dt = builder.get_temporary_data_type()
899
988
  request = ESigningRequestPojo(title, msg, show_comment, temp_dt,
900
989
  width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
901
- response: ESigningResponsePojo | None = self.callback.show_esign_dialog(request)
990
+ try:
991
+ self.user.timeout_seconds = self.timeout_seconds
992
+ response: ESigningResponsePojo | None = self.callback.show_esign_dialog(request)
993
+ except ReadTimeout:
994
+ raise SapioDialogTimeoutException()
995
+ finally:
996
+ self.user.timeout_seconds = self._original_timeout
902
997
  if response is None:
903
998
  raise SapioUserCancelledException()
904
999
  return response
@@ -929,7 +1024,13 @@ class CallbackUtil:
929
1024
  return sink.consume_data(chunk, io_obj)
930
1025
 
931
1026
  request = FilePromptRequest(title, show_image_editor, ",".join(exts), show_camera_button)
932
- file_path: str | None = self.callback.show_file_dialog(request, do_consume)
1027
+ try:
1028
+ self.user.timeout_seconds = self.timeout_seconds
1029
+ file_path: str | None = self.callback.show_file_dialog(request, do_consume)
1030
+ except ReadTimeout:
1031
+ raise SapioDialogTimeoutException()
1032
+ finally:
1033
+ self.user.timeout_seconds = self._original_timeout
933
1034
  if file_path is None:
934
1035
  raise SapioUserCancelledException()
935
1036
 
@@ -954,7 +1055,13 @@ class CallbackUtil:
954
1055
  exts: list[str] = []
955
1056
 
956
1057
  request = MultiFilePromptRequest(title, show_image_editor, ",".join(exts), show_camera_button)
957
- file_paths: list[str] | None = self.callback.show_multi_file_dialog(request)
1058
+ try:
1059
+ self.user.timeout_seconds = self.timeout_seconds
1060
+ file_paths: list[str] | None = self.callback.show_multi_file_dialog(request)
1061
+ except ReadTimeout:
1062
+ raise SapioDialogTimeoutException()
1063
+ finally:
1064
+ self.user.timeout_seconds = self._original_timeout
958
1065
  if not file_paths:
959
1066
  raise SapioUserCancelledException()
960
1067
 
@@ -3,34 +3,47 @@ class SapioException(Exception):
3
3
  """
4
4
  A generic exception thrown by sapiopycommons methods. Typically caused by programmer error, but may also be from
5
5
  extremely edge case user errors. For expected user errors, use SapioUserErrorException.
6
+
7
+ CommonsWebhookHandler's default behavior for this and any other exception that doesn't extend SapioException is
8
+ to return a generic toaster message saying that an unexpected error has occurred.
6
9
  """
7
10
  pass
8
11
 
9
12
 
10
- # CommonsWebhookHandler catches this exception and returns "User Cancelled."
11
13
  class SapioUserCancelledException(SapioException):
12
14
  """
13
15
  An exception thrown when the user cancels a client callback.
16
+
17
+ CommonsWebhookHandler's default behavior is to simply end the webhook session with a true result without logging
18
+ the exception.
19
+ """
20
+ pass
21
+
22
+
23
+ class SapioDialogTimeoutException(SapioException):
24
+ """
25
+ An exception thrown when the user leaves a client callback open for too long.
26
+
27
+ CommonsWebhookHandler's default behavior is to display an OK popup notifying the user that the dialog has timed out.
14
28
  """
15
29
  pass
16
30
 
17
31
 
18
- # CommonsWebhookHandler catches this exception and returns the text to the user as display text in a webhook result.
19
32
  class SapioUserErrorException(SapioException):
20
33
  """
21
34
  An exception caused by user error (e.g. user provided a CSV when an XLSX was expected), which promises to return a
22
- user-friendly message explaining the error that should be displayed to the user. It is the responsibility of the
23
- programmer to catch any such exceptions and return the value in e.args[0] as text for the user to see (such as
24
- through the display text of a webhook result).
35
+ user-friendly message explaining the error that should be displayed to the user.
36
+
37
+ CommonsWebhookHandler's default behavior is to return the error message in a toaster popup.
25
38
  """
26
39
  pass
27
40
 
28
41
 
29
- # CommonsWebhookHandler catches this exception and returns the text in a display_error client callback.
30
42
  class SapioCriticalErrorException(SapioException):
31
43
  """
32
44
  A critical exception caused by user error, which promises to return a user-friendly message explaining the error
33
- that should be displayed to the user. It is the responsibility of the programmer to catch any such exceptions and
34
- return the value in e.args[0] as text for the user to see (such as through a dialog form client callback request).
45
+ that should be displayed to the user.
46
+
47
+ CommonsWebhookHandler's default behavior is to return the error message in a display_error callback.
35
48
  """
36
49
  pass
@@ -18,7 +18,7 @@ from sapiopylib.rest.utils.recordmodel.ancestry import RecordModelAncestorManage
18
18
  from sapiopycommons.callbacks.callback_util import CallbackUtil
19
19
  from sapiopycommons.eln.experiment_handler import ExperimentHandler
20
20
  from sapiopycommons.general.exceptions import SapioUserErrorException, SapioCriticalErrorException, \
21
- SapioUserCancelledException, SapioException
21
+ SapioUserCancelledException, SapioException, SapioDialogTimeoutException
22
22
  from sapiopycommons.general.sapio_links import SapioNavigationLinker
23
23
  from sapiopycommons.recordmodel.record_handler import RecordHandler
24
24
  from sapiopycommons.rules.eln_rule_handler import ElnRuleHandler
@@ -90,6 +90,8 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
90
90
  return self.handle_critical_error_exception(e)
91
91
  except SapioUserCancelledException as e:
92
92
  return self.handle_user_cancelled_exception(e)
93
+ except SapioDialogTimeoutException as e:
94
+ return self.handle_dialog_timeout_exception(e)
93
95
  except Exception as e:
94
96
  return self.handle_unexpected_exception(e)
95
97
 
@@ -111,11 +113,12 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
111
113
  # the run method and into their own functions.
112
114
  def handle_user_error_exception(self, e: SapioUserErrorException) -> SapioWebhookResult:
113
115
  """
114
- Handle a SapioUserErrorException. Default behavior returns the error message as display text in a webhook
115
- result.
116
+ Handle a SapioUserErrorException.
117
+
118
+ Default behavior returns a false result and the error message as display text in a webhook result.
116
119
 
117
120
  :param e: The exception that was raised.
118
- :return: A SapioWebhookResult reporting the exception to the user.
121
+ :return: A SapioWebhookResult to end the webhook session with.
119
122
  """
120
123
  result: SapioWebhookResult | None = self.handle_any_exception(e)
121
124
  if result is not None:
@@ -125,50 +128,84 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
125
128
 
126
129
  def handle_critical_error_exception(self, e: SapioCriticalErrorException) -> SapioWebhookResult:
127
130
  """
128
- Handle a SapioCriticalErrorException. Default behavior makes a display_error client callback.
131
+ Handle a SapioCriticalErrorException.
132
+
133
+ Default behavior makes a display_error client callback with the error message and returns a false result.
129
134
 
130
135
  :param e: The exception that was raised.
131
- :return: A SapioWebhookResult reporting the exception to the user.
136
+ :return: A SapioWebhookResult to end the webhook session with.
132
137
  """
133
138
  result: SapioWebhookResult | None = self.handle_any_exception(e)
134
139
  if result is not None:
135
140
  return result
136
141
  self.log_error(traceback.format_exc())
142
+ # This error be thrown by endpoints that can't send client callbacks. If that happens, fall back onto sending
143
+ # display text instead.
137
144
  if self.can_send_client_callback():
138
- DataMgmtServer.get_client_callback(self.user).display_error(e.args[0])
145
+ self.callback.display_error(e.args[0])
146
+ else:
147
+ return SapioWebhookResult(False, e.args[0])
139
148
  return SapioWebhookResult(False)
140
149
 
141
- def handle_unexpected_exception(self, e: Exception) -> SapioWebhookResult:
150
+ def handle_user_cancelled_exception(self, e: SapioUserCancelledException) -> SapioWebhookResult:
142
151
  """
143
- Handle a generic exception which isn't a SapioUserErrorException or SapioCriticalErrorException. Default
144
- behavior returns a generic error message as display text informing the user to contact Sapio support.
152
+ Handle a SapioUserCancelledException.
153
+
154
+ Default behavior simply ends the webhook session with a true result (since the user cancelling is a valid
155
+ action).
145
156
 
146
157
  :param e: The exception that was raised.
147
- :return: A SapioWebhookResult reporting the exception to the user.
158
+ :return: A SapioWebhookResult to end the webhook session with.
148
159
  """
149
160
  result: SapioWebhookResult | None = self.handle_any_exception(e)
150
161
  if result is not None:
151
162
  return result
152
- msg: str = traceback.format_exc()
153
- self.log_error(msg)
154
- # FR-47079: Also log all unexpected exception messages to the webhook execution log within the platform.
155
- self.log_error_to_webhook_execution_log(msg)
156
- return SapioWebhookResult(False, display_text="Unexpected error occurred during webhook execution. "
157
- "Please contact Sapio support.")
163
+ return SapioWebhookResult(True)
158
164
 
159
- def handle_user_cancelled_exception(self, e: SapioUserCancelledException) -> SapioWebhookResult:
165
+ def handle_dialog_timeout_exception(self, e: SapioDialogTimeoutException) -> SapioWebhookResult:
160
166
  """
161
- Handle a SapioUserCancelledException. Default behavior returns "User Cancelled" as display text in a webhook
162
- result.
167
+ Handle a SapioDialogTimeoutException.
168
+
169
+ Default behavior displays an OK popup notifying the user that the dialog has timed out and returns a false
170
+ webhook result.
163
171
 
164
172
  :param e: The exception that was raised.
165
- :return: A SapioWebhookResult with display text saying the user cancelled the request.
173
+ :return: A SapioWebhookResult to end the webhook session with.
166
174
  """
167
175
  result: SapioWebhookResult | None = self.handle_any_exception(e)
168
176
  if result is not None:
169
177
  return result
178
+ # This dialog could time out too! Ignore it if it does.
179
+ # No need to check can_send_client_callback() here, as this exception should only be thrown by endpoints that
180
+ # are capable of sending callbacks.
181
+ try:
182
+ self.callback.ok_dialog("Notice", "You have remained idle for too long and this dialog has timed out. "
183
+ "Close and re-initiate it to continue.")
184
+ except SapioDialogTimeoutException:
185
+ pass
170
186
  return SapioWebhookResult(False)
171
187
 
188
+ def handle_unexpected_exception(self, e: Exception) -> SapioWebhookResult:
189
+ """
190
+ Handle a generic exception which isn't one of the handled Sapio exceptions.
191
+
192
+ Default behavior returns a false webhook result with a generic error message as display text informing the user
193
+ to contact Sapio support. Additionally, the stace trace of the exception that was thrown is logged to the
194
+ execution log for the webhook call in the system.
195
+
196
+ :param e: The exception that was raised.
197
+ :return: A SapioWebhookResult to end the webhook session with.
198
+ """
199
+ result: SapioWebhookResult | None = self.handle_any_exception(e)
200
+ if result is not None:
201
+ return result
202
+ msg: str = traceback.format_exc()
203
+ self.log_error(msg)
204
+ # FR-47079: Also log all unexpected exception messages to the webhook execution log within the platform.
205
+ self.log_error_to_webhook_execution_log(msg)
206
+ return SapioWebhookResult(False, display_text="Unexpected error occurred during webhook execution. "
207
+ "Please contact Sapio support.")
208
+
172
209
  # noinspection PyMethodMayBeStatic,PyUnusedLocal
173
210
  def handle_any_exception(self, e: Exception) -> SapioWebhookResult | None:
174
211
  """
@@ -177,7 +214,8 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
177
214
 
178
215
  :param e: The exception that was raised.
179
216
  :return: An optional SapioWebhookResult. May return a custom message to the client that wouldn't have been
180
- sent by one of the normal exception handlers, or may return None if no result needs returned.
217
+ sent by one of the normal exception handlers, or may return None if no result needs returned. It a result is
218
+ returned, then the default behavior of other exception handlers is skipped.
181
219
  """
182
220
  return None
183
221