sapiopycommons 2024.8.28a315__tar.gz → 2024.8.29a317__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.
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/PKG-INFO +1 -1
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/pyproject.toml +1 -1
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/callbacks/callback_util.py +37 -133
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/datatype/attachment_util.py +10 -11
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/eln/experiment_handler.py +48 -209
- sapiopycommons-2024.8.29a317/src/sapiopycommons/eln/experiment_report_util.py +214 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/files/complex_data_loader.py +4 -5
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/files/file_bridge.py +14 -15
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/files/file_bridge_handler.py +5 -27
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/files/file_data_handler.py +5 -2
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/files/file_util.py +5 -38
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/files/file_validator.py +11 -26
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/files/file_writer.py +15 -44
- sapiopycommons-2024.8.29a317/src/sapiopycommons/general/aliases.py +82 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/general/custom_report_util.py +32 -34
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/general/popup_util.py +0 -17
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/general/time_util.py +0 -40
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/multimodal/multimodal_data.py +1 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/processtracking/endpoints.py +22 -22
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/recordmodel/record_handler.py +77 -228
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/rules/eln_rule_handler.py +25 -34
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/rules/on_save_rule_handler.py +31 -34
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/webhook/webhook_handlers.py +26 -90
- sapiopycommons-2024.8.28a315/src/sapiopycommons/customreport/column_builder.py +0 -60
- sapiopycommons-2024.8.28a315/src/sapiopycommons/customreport/custom_report_builder.py +0 -125
- sapiopycommons-2024.8.28a315/src/sapiopycommons/customreport/term_builder.py +0 -299
- sapiopycommons-2024.8.28a315/src/sapiopycommons/eln/experiment_report_util.py +0 -118
- sapiopycommons-2024.8.28a315/src/sapiopycommons/general/aliases.py +0 -226
- sapiopycommons-2024.8.28a315/src/sapiopycommons/general/audit_log.py +0 -196
- sapiopycommons-2024.8.28a315/src/sapiopycommons/general/sapio_links.py +0 -50
- sapiopycommons-2024.8.28a315/src/sapiopycommons/webhook/__init__.py +0 -0
- sapiopycommons-2024.8.28a315/src/sapiopycommons/webhook/webservice_handlers.py +0 -67
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/.gitignore +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/LICENSE +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/README.md +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/__init__.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/callbacks/__init__.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/chem/IndigoMolecules.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/chem/Molecules.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/chem/__init__.py +0 -0
- {sapiopycommons-2024.8.28a315/src/sapiopycommons/customreport → sapiopycommons-2024.8.29a317/src/sapiopycommons/datatype}/__init__.py +0 -0
- {sapiopycommons-2024.8.28a315/src/sapiopycommons/datatype → sapiopycommons-2024.8.29a317/src/sapiopycommons/eln}/__init__.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/eln/plate_designer.py +0 -0
- {sapiopycommons-2024.8.28a315/src/sapiopycommons/eln → sapiopycommons-2024.8.29a317/src/sapiopycommons/files}/__init__.py +0 -0
- {sapiopycommons-2024.8.28a315/src/sapiopycommons/files → sapiopycommons-2024.8.29a317/src/sapiopycommons/general}/__init__.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/general/accession_service.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/general/exceptions.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/general/storage_util.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/src/sapiopycommons/multimodal/multimodal.py +0 -0
- {sapiopycommons-2024.8.28a315/src/sapiopycommons/general → sapiopycommons-2024.8.29a317/src/sapiopycommons/processtracking}/__init__.py +0 -0
- {sapiopycommons-2024.8.28a315/src/sapiopycommons/processtracking → sapiopycommons-2024.8.29a317/src/sapiopycommons/recordmodel}/__init__.py +0 -0
- {sapiopycommons-2024.8.28a315/src/sapiopycommons/recordmodel → sapiopycommons-2024.8.29a317/src/sapiopycommons/rules}/__init__.py +0 -0
- {sapiopycommons-2024.8.28a315/src/sapiopycommons/rules → sapiopycommons-2024.8.29a317/src/sapiopycommons/webhook}/__init__.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/tests/_do_not_add_init_py_here +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/tests/accession_test.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/tests/bio_reg_test.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/tests/chem_test.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/tests/data_type_models.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/tests/kappa.chains.fasta +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/tests/mafft_test.py +0 -0
- {sapiopycommons-2024.8.28a315 → sapiopycommons-2024.8.29a317}/tests/test.gb +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: sapiopycommons
|
|
3
|
-
Version: 2024.8.
|
|
3
|
+
Version: 2024.8.29a317
|
|
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>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import io
|
|
4
|
-
from
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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()
|
|
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
|
-
|
|
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()
|
|
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) ->
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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[
|
|
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
|
|
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,
|
|
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) ->
|
|
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)
|
|
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])
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
49
|
+
context.data_record_manager.set_attachment_data(attachment, file_name, stream)
|
|
51
50
|
|
|
52
51
|
@staticmethod
|
|
53
|
-
def create_attachment(context:
|
|
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.
|