sapiopycommons 2024.11.8a355__py3-none-any.whl → 2024.11.8a359__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sapiopycommons might be problematic. Click here for more details.
- sapiopycommons/callbacks/callback_util.py +83 -532
- sapiopycommons/chem/IndigoMolecules.py +0 -2
- sapiopycommons/chem/Molecules.py +18 -77
- sapiopycommons/datatype/attachment_util.py +10 -11
- sapiopycommons/eln/experiment_handler.py +70 -272
- sapiopycommons/files/complex_data_loader.py +4 -5
- sapiopycommons/files/file_bridge.py +24 -31
- sapiopycommons/files/file_data_handler.py +5 -2
- sapiopycommons/files/file_util.py +9 -59
- sapiopycommons/files/file_validator.py +6 -92
- sapiopycommons/files/file_writer.py +15 -44
- sapiopycommons/general/aliases.py +6 -207
- sapiopycommons/general/custom_report_util.py +37 -212
- sapiopycommons/general/exceptions.py +8 -21
- sapiopycommons/general/popup_util.py +0 -21
- sapiopycommons/general/time_util.py +2 -8
- sapiopycommons/processtracking/endpoints.py +22 -22
- sapiopycommons/recordmodel/record_handler.py +97 -481
- sapiopycommons/rules/eln_rule_handler.py +25 -34
- sapiopycommons/rules/on_save_rule_handler.py +31 -34
- sapiopycommons/webhook/webhook_handlers.py +42 -201
- {sapiopycommons-2024.11.8a355.dist-info → sapiopycommons-2024.11.8a359.dist-info}/METADATA +2 -4
- sapiopycommons-2024.11.8a359.dist-info/RECORD +38 -0
- sapiopycommons/callbacks/field_builder.py +0 -537
- sapiopycommons/customreport/__init__.py +0 -0
- sapiopycommons/customreport/column_builder.py +0 -60
- sapiopycommons/customreport/custom_report_builder.py +0 -130
- sapiopycommons/customreport/term_builder.py +0 -299
- sapiopycommons/datatype/data_fields.py +0 -61
- sapiopycommons/datatype/pseudo_data_types.py +0 -440
- sapiopycommons/eln/experiment_report_util.py +0 -653
- sapiopycommons/files/file_bridge_handler.py +0 -340
- sapiopycommons/flowcyto/flow_cyto.py +0 -77
- sapiopycommons/flowcyto/flowcyto_data.py +0 -75
- sapiopycommons/general/accession_service.py +0 -375
- sapiopycommons/general/audit_log.py +0 -189
- sapiopycommons/general/sapio_links.py +0 -50
- sapiopycommons/multimodal/multimodal.py +0 -146
- sapiopycommons/multimodal/multimodal_data.py +0 -489
- sapiopycommons/processtracking/custom_workflow_handler.py +0 -406
- sapiopycommons/sftpconnect/__init__.py +0 -0
- sapiopycommons/sftpconnect/sftp_builder.py +0 -69
- sapiopycommons/webhook/webhook_context.py +0 -39
- sapiopycommons/webhook/webservice_handlers.py +0 -67
- sapiopycommons-2024.11.8a355.dist-info/RECORD +0 -59
- {sapiopycommons-2024.11.8a355.dist-info → sapiopycommons-2024.11.8a359.dist-info}/WHEEL +0 -0
- {sapiopycommons-2024.11.8a355.dist-info → sapiopycommons-2024.11.8a359.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
import io
|
|
4
|
-
from
|
|
2
|
+
from typing import Any
|
|
5
3
|
|
|
6
|
-
from requests import ReadTimeout
|
|
7
4
|
from sapiopylib.rest.ClientCallbackService import ClientCallback
|
|
8
5
|
from sapiopylib.rest.DataMgmtService import DataMgmtServer
|
|
9
6
|
from sapiopylib.rest.User import SapioUser
|
|
@@ -12,24 +9,22 @@ from sapiopylib.rest.pojo.DataRecord import DataRecord
|
|
|
12
9
|
from sapiopylib.rest.pojo.datatype.DataType import DataTypeDefinition
|
|
13
10
|
from sapiopylib.rest.pojo.datatype.DataTypeLayout import DataTypeLayout
|
|
14
11
|
from sapiopylib.rest.pojo.datatype.FieldDefinition import AbstractVeloxFieldDefinition, VeloxStringFieldDefinition, \
|
|
15
|
-
VeloxIntegerFieldDefinition, VeloxDoubleFieldDefinition
|
|
12
|
+
VeloxIntegerFieldDefinition, VeloxDoubleFieldDefinition
|
|
16
13
|
from sapiopylib.rest.pojo.webhook.ClientCallbackRequest import OptionDialogRequest, ListDialogRequest, \
|
|
17
14
|
FormEntryDialogRequest, InputDialogCriteria, TableEntryDialogRequest, ESigningRequestPojo, \
|
|
18
|
-
DataRecordDialogRequest, InputSelectionRequest, FilePromptRequest,
|
|
19
|
-
|
|
15
|
+
DataRecordSelectionRequest, DataRecordDialogRequest, InputSelectionRequest, FilePromptRequest, \
|
|
16
|
+
MultiFilePromptRequest
|
|
20
17
|
from sapiopylib.rest.pojo.webhook.ClientCallbackResult import ESigningResponsePojo
|
|
18
|
+
from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
|
|
21
19
|
from sapiopylib.rest.pojo.webhook.WebhookEnums import FormAccessLevel, ScanToSelectCriteria, SearchType
|
|
22
20
|
from sapiopylib.rest.utils.DataTypeCacheManager import DataTypeCacheManager
|
|
23
21
|
from sapiopylib.rest.utils.FormBuilder import FormBuilder
|
|
24
22
|
from sapiopylib.rest.utils.recorddatasinks import InMemoryRecordDataSink
|
|
25
23
|
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
26
24
|
|
|
27
|
-
from sapiopycommons.
|
|
28
|
-
from sapiopycommons.general.aliases import FieldMap, SapioRecord, AliasUtil, RecordIdentifier, FieldValue, \
|
|
29
|
-
UserIdentifier
|
|
25
|
+
from sapiopycommons.general.aliases import FieldMap, SapioRecord, AliasUtil, RecordIdentifier
|
|
30
26
|
from sapiopycommons.general.custom_report_util import CustomReportUtil
|
|
31
|
-
from sapiopycommons.general.exceptions import SapioUserCancelledException, SapioException, SapioUserErrorException
|
|
32
|
-
SapioDialogTimeoutException
|
|
27
|
+
from sapiopycommons.general.exceptions import SapioUserCancelledException, SapioException, SapioUserErrorException
|
|
33
28
|
from sapiopycommons.recordmodel.record_handler import RecordHandler
|
|
34
29
|
|
|
35
30
|
|
|
@@ -37,104 +32,29 @@ class CallbackUtil:
|
|
|
37
32
|
user: SapioUser
|
|
38
33
|
callback: ClientCallback
|
|
39
34
|
dt_cache: DataTypeCacheManager
|
|
40
|
-
_original_timeout: int
|
|
41
|
-
timeout_seconds: int
|
|
42
35
|
width_pixels: int | None
|
|
43
36
|
width_percent: float | None
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
__initialized: bool
|
|
47
|
-
|
|
48
|
-
def __new__(cls, context: UserIdentifier):
|
|
49
|
-
"""
|
|
50
|
-
:param context: The current webhook context or a user object to send requests from.
|
|
51
|
-
"""
|
|
52
|
-
user = AliasUtil.to_sapio_user(context)
|
|
53
|
-
obj = cls.__instances.get(user)
|
|
54
|
-
if not obj:
|
|
55
|
-
obj = object.__new__(cls)
|
|
56
|
-
obj.__initialized = False
|
|
57
|
-
cls.__instances[user] = obj
|
|
58
|
-
return obj
|
|
59
|
-
|
|
60
|
-
def __init__(self, context: UserIdentifier):
|
|
38
|
+
def __init__(self, context: SapioWebhookContext | SapioUser):
|
|
61
39
|
"""
|
|
62
40
|
:param context: The current webhook context or a user object to send requests from.
|
|
63
41
|
"""
|
|
64
|
-
if
|
|
65
|
-
return
|
|
66
|
-
self.__initialized = True
|
|
67
|
-
|
|
68
|
-
self.user = AliasUtil.to_sapio_user(context)
|
|
42
|
+
self.user = context if isinstance(context, SapioUser) else context.user
|
|
69
43
|
self.callback = DataMgmtServer.get_client_callback(self.user)
|
|
70
44
|
self.dt_cache = DataTypeCacheManager(self.user)
|
|
71
|
-
self._original_timeout = self.user.timeout_seconds
|
|
72
|
-
self.timeout_seconds = self.user.timeout_seconds
|
|
73
45
|
self.width_pixels = None
|
|
74
46
|
self.width_percent = None
|
|
75
47
|
|
|
76
|
-
def set_dialog_width(self, width_pixels: int | None
|
|
48
|
+
def set_dialog_width(self, width_pixels: int | None, width_percent: float | None):
|
|
77
49
|
"""
|
|
78
50
|
Set the width that dialogs will appear as for those dialogs that support specifying their width.
|
|
79
51
|
|
|
80
52
|
:param width_pixels: The number of pixels wide that dialogs will appear as.
|
|
81
|
-
:param width_percent: The percentage
|
|
82
|
-
will appear as.
|
|
53
|
+
:param width_percent: The percentage of the client's screen width that dialogs will appear as.
|
|
83
54
|
"""
|
|
84
|
-
if width_pixels is not None and width_percent is not None:
|
|
85
|
-
raise SapioException("Cannot set both width_pixels and width_percent at once.")
|
|
86
55
|
self.width_pixels = width_pixels
|
|
87
56
|
self.width_percent = width_percent
|
|
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
|
-
|
|
100
|
-
def toaster_popup(self, message: str, title: str = "", popup_type: PopupType = PopupType.Info) -> None:
|
|
101
|
-
"""
|
|
102
|
-
Display a toaster popup in the bottom right corner of the user's screen.
|
|
103
|
-
|
|
104
|
-
:param message: The message to display in the toaster.
|
|
105
|
-
:param title: The title to display at the top of the toaster.
|
|
106
|
-
:param popup_type: The popup type to use for the toaster. This controls the color that the toaster appears with.
|
|
107
|
-
Info is blue, Success is green, Warning is yellow, and Error is red
|
|
108
|
-
"""
|
|
109
|
-
self.callback.display_popup(DisplayPopupRequest(title, message, popup_type))
|
|
110
|
-
|
|
111
|
-
def display_info(self, message: str) -> None:
|
|
112
|
-
"""
|
|
113
|
-
Display an info message to the user in a dialog. Repeated calls to this function will append the new messages
|
|
114
|
-
to the same dialog if it is still opened by the user.
|
|
115
|
-
|
|
116
|
-
:param message: The message to display to the user.
|
|
117
|
-
"""
|
|
118
|
-
self.callback.display_info(message)
|
|
119
|
-
|
|
120
|
-
def display_warning(self, message: str) -> None:
|
|
121
|
-
"""
|
|
122
|
-
Display a warning message to the user in a dialog. Repeated calls to this function will append the new messages
|
|
123
|
-
to the same dialog if it is still opened by the user.
|
|
124
|
-
|
|
125
|
-
:param message: The message to display to the user.
|
|
126
|
-
"""
|
|
127
|
-
self.callback.display_warning(message)
|
|
128
|
-
|
|
129
|
-
def display_error(self, message: str) -> None:
|
|
130
|
-
"""
|
|
131
|
-
Display an error message to the user in a dialog. Repeated calls to this function will append the new messages
|
|
132
|
-
to the same dialog if it is still opened by the user.
|
|
133
|
-
|
|
134
|
-
:param message: The message to display to the user.
|
|
135
|
-
"""
|
|
136
|
-
self.callback.display_error(message)
|
|
137
|
-
|
|
57
|
+
|
|
138
58
|
def option_dialog(self, title: str, msg: str, options: list[str], default_option: int = 0,
|
|
139
59
|
user_can_cancel: bool = False) -> str:
|
|
140
60
|
"""
|
|
@@ -149,15 +69,8 @@ class CallbackUtil:
|
|
|
149
69
|
SapioUserCancelledException is thrown.
|
|
150
70
|
:return: The name of the button that the user selected.
|
|
151
71
|
"""
|
|
152
|
-
request = OptionDialogRequest(title, msg, options, default_option, user_can_cancel
|
|
153
|
-
|
|
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
|
|
72
|
+
request = OptionDialogRequest(title, msg, options, default_option, user_can_cancel)
|
|
73
|
+
response: int | None = self.callback.show_option_dialog(request)
|
|
161
74
|
if response is None:
|
|
162
75
|
raise SapioUserCancelledException()
|
|
163
76
|
return options[response]
|
|
@@ -196,27 +109,17 @@ class CallbackUtil:
|
|
|
196
109
|
"""
|
|
197
110
|
return self.option_dialog(title, msg, ["Yes", "No"], 0 if default_yes else 1, False) == "Yes"
|
|
198
111
|
|
|
199
|
-
def list_dialog(self, title: str, options: list[str], multi_select: bool = False
|
|
200
|
-
preselected_values: list[str] | None = None) -> list[str]:
|
|
112
|
+
def list_dialog(self, title: str, options: list[str], multi_select: bool = False) -> list[str]:
|
|
201
113
|
"""
|
|
202
114
|
Create a list dialog with the given options for the user to choose from.
|
|
203
115
|
|
|
204
116
|
:param title: The title of the dialog.
|
|
205
117
|
:param options: The list options that the user has to choose from.
|
|
206
118
|
:param multi_select: Whether the user is able to select multiple options from the list.
|
|
207
|
-
:param preselected_values: A list of values that will already be selected when the list dialog is created. The
|
|
208
|
-
user can unselect these values if they want to.
|
|
209
119
|
:return: The list of options that the user selected.
|
|
210
120
|
"""
|
|
211
|
-
request = ListDialogRequest(title, multi_select, options
|
|
212
|
-
|
|
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
|
|
121
|
+
request = ListDialogRequest(title, multi_select, options)
|
|
122
|
+
response: list[str] | None = self.callback.show_list_dialog(request)
|
|
220
123
|
if response is None:
|
|
221
124
|
raise SapioUserCancelledException()
|
|
222
125
|
return response
|
|
@@ -258,6 +161,8 @@ class CallbackUtil:
|
|
|
258
161
|
builder = FormBuilder(data_type, display_name, plural_display_name)
|
|
259
162
|
for field_def in fields:
|
|
260
163
|
field_name = field_def.data_field_name
|
|
164
|
+
if values and hasattr(field_def, "default_value"):
|
|
165
|
+
field_def.default_value = values.get(field_name)
|
|
261
166
|
column: int = 0
|
|
262
167
|
span: int = 4
|
|
263
168
|
if column_positions and field_name in column_positions:
|
|
@@ -266,15 +171,8 @@ class CallbackUtil:
|
|
|
266
171
|
span = position[1]
|
|
267
172
|
builder.add_field(field_def, column, span)
|
|
268
173
|
|
|
269
|
-
request = FormEntryDialogRequest(title, msg, builder.get_temporary_data_type()
|
|
270
|
-
|
|
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
|
|
174
|
+
request = FormEntryDialogRequest(title, msg, builder.get_temporary_data_type())
|
|
175
|
+
response: FieldMap | None = self.callback.show_form_entry_dialog(request)
|
|
278
176
|
if response is None:
|
|
279
177
|
raise SapioUserCancelledException()
|
|
280
178
|
return response
|
|
@@ -311,39 +209,32 @@ class CallbackUtil:
|
|
|
311
209
|
type_def: DataTypeDefinition = self.dt_cache.get_data_type(data_type)
|
|
312
210
|
field_defs: dict[str, AbstractVeloxFieldDefinition] = self.dt_cache.get_fields_for_type(data_type)
|
|
313
211
|
|
|
314
|
-
# Make everything visible, because presumably the caller gave a field name because they want it to be seen.
|
|
315
|
-
modifier = FieldModifier(visible=True, editable=editable)
|
|
316
|
-
|
|
317
212
|
# Build the form using only those fields that are desired.
|
|
318
|
-
values: dict[str, FieldValue] = {}
|
|
319
213
|
builder = FormBuilder(data_type, type_def.display_name, type_def.plural_display_name)
|
|
320
214
|
for field_name in fields:
|
|
321
215
|
field_def = field_defs.get(field_name)
|
|
322
216
|
if field_def is None:
|
|
323
217
|
raise SapioException(f"No field of name \"{field_name}\" in field definitions of type \"{data_type}\"")
|
|
324
|
-
|
|
218
|
+
if editable is not None:
|
|
219
|
+
field_def.editable = editable
|
|
220
|
+
field_def.visible = True
|
|
221
|
+
if hasattr(field_def, "default_value"):
|
|
222
|
+
field_def.default_value = record.get_field_value(field_name)
|
|
325
223
|
column: int = 0
|
|
326
224
|
span: int = 4
|
|
327
225
|
if column_positions and field_name in column_positions:
|
|
328
226
|
position = column_positions.get(field_name)
|
|
329
227
|
column = position[0]
|
|
330
228
|
span = position[1]
|
|
331
|
-
builder.add_field(
|
|
332
|
-
|
|
333
|
-
request = FormEntryDialogRequest(title, msg, builder.get_temporary_data_type()
|
|
334
|
-
|
|
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
|
|
229
|
+
builder.add_field(field_def, column, span)
|
|
230
|
+
|
|
231
|
+
request = FormEntryDialogRequest(title, msg, builder.get_temporary_data_type())
|
|
232
|
+
response: FieldMap | None = self.callback.show_form_entry_dialog(request)
|
|
342
233
|
if response is None:
|
|
343
234
|
raise SapioUserCancelledException()
|
|
344
235
|
return response
|
|
345
236
|
|
|
346
|
-
def input_dialog(self, title: str, msg: str, field: AbstractVeloxFieldDefinition) ->
|
|
237
|
+
def input_dialog(self, title: str, msg: str, field: AbstractVeloxFieldDefinition) -> Any:
|
|
347
238
|
"""
|
|
348
239
|
Create an input dialog where the user must input data for a singular field.
|
|
349
240
|
|
|
@@ -352,15 +243,8 @@ class CallbackUtil:
|
|
|
352
243
|
:param field: The definition for a field that the user must provide input to.
|
|
353
244
|
:return: The response value from the user for the given field.
|
|
354
245
|
"""
|
|
355
|
-
request = InputDialogCriteria(title, msg, field,
|
|
356
|
-
|
|
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
|
|
246
|
+
request = InputDialogCriteria(title, msg, field, self.width_pixels, self.width_percent)
|
|
247
|
+
response: Any | None = self.callback.show_input_dialog(request)
|
|
364
248
|
if response is None:
|
|
365
249
|
raise SapioUserCancelledException()
|
|
366
250
|
return response
|
|
@@ -375,7 +259,7 @@ class CallbackUtil:
|
|
|
375
259
|
:param field_name: The name and display name of the string field.
|
|
376
260
|
:param default_value: The default value to place into the string field, if any.
|
|
377
261
|
:param max_length: The max length of the string value. If not provided, uses the length of the default value.
|
|
378
|
-
If neither this
|
|
262
|
+
If neither this or a default value are not provided, defaults to 100 characters.
|
|
379
263
|
:param editable: Whether the field is editable by the user.
|
|
380
264
|
:param kwargs: Any additional keyword arguments to pass to the field definition.
|
|
381
265
|
:return: The string that the user input into the dialog.
|
|
@@ -436,8 +320,6 @@ class CallbackUtil:
|
|
|
436
320
|
msg: str,
|
|
437
321
|
fields: list[AbstractVeloxFieldDefinition],
|
|
438
322
|
values: list[FieldMap],
|
|
439
|
-
group_by: str | None = None,
|
|
440
|
-
image_data: list[bytes] | None = None,
|
|
441
323
|
*,
|
|
442
324
|
data_type: str = "Default",
|
|
443
325
|
display_name: str | None = None,
|
|
@@ -451,10 +333,6 @@ class CallbackUtil:
|
|
|
451
333
|
:param fields: The definitions of the fields to display as table columns. Fields will be displayed in the order
|
|
452
334
|
they are provided in this list.
|
|
453
335
|
:param values: The values to set for each row of the table.
|
|
454
|
-
:param group_by: If provided, the created table dialog will be grouped by the field with this name by default.
|
|
455
|
-
The user may remove this grouping if they want to.
|
|
456
|
-
:param image_data: The bytes to the images that should be displayed in the rows of the table. Each element in
|
|
457
|
-
the image data list corresponds to the element at the same index in the values list.
|
|
458
336
|
:param data_type: The data type name for the temporary data type that will be created for this table.
|
|
459
337
|
:param display_name: The display name for the temporary data type. If not provided, defaults to the data type
|
|
460
338
|
name.
|
|
@@ -468,24 +346,12 @@ class CallbackUtil:
|
|
|
468
346
|
if plural_display_name is None:
|
|
469
347
|
plural_display_name = display_name + "s"
|
|
470
348
|
|
|
471
|
-
# Key fields display their columns in order before all non-key fields.
|
|
472
|
-
# Unmark key fields so that the column order is respected exactly as the caller provides it.
|
|
473
|
-
modifier = FieldModifier(key_field=False)
|
|
474
|
-
|
|
475
349
|
builder = FormBuilder(data_type, display_name, plural_display_name)
|
|
476
|
-
for
|
|
477
|
-
builder.add_field(
|
|
478
|
-
|
|
479
|
-
request = TableEntryDialogRequest(title, msg, builder.get_temporary_data_type(), values
|
|
480
|
-
|
|
481
|
-
width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
|
|
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
|
|
350
|
+
for column in fields:
|
|
351
|
+
builder.add_field(column)
|
|
352
|
+
|
|
353
|
+
request = TableEntryDialogRequest(title, msg, builder.get_temporary_data_type(), values)
|
|
354
|
+
response: list[FieldMap] | None = self.callback.show_table_entry_dialog(request)
|
|
489
355
|
if response is None:
|
|
490
356
|
raise SapioUserCancelledException()
|
|
491
357
|
return response
|
|
@@ -495,14 +361,11 @@ class CallbackUtil:
|
|
|
495
361
|
msg: str,
|
|
496
362
|
fields: list[str],
|
|
497
363
|
records: list[SapioRecord],
|
|
498
|
-
editable: bool | None = True
|
|
499
|
-
group_by: str | None = None,
|
|
500
|
-
image_data: list[bytes] | None = None) -> list[FieldMap]:
|
|
364
|
+
editable: bool | None = True) -> list[FieldMap]:
|
|
501
365
|
"""
|
|
502
366
|
Create a table dialog where the user may input data into the fields of the table. The table is constructed from
|
|
503
|
-
a given list of records
|
|
504
|
-
|
|
505
|
-
the given records.
|
|
367
|
+
a given list of records. Provided field names must match fields on the definition of the data type of the given
|
|
368
|
+
records. The fields that are displayed will have their default value be that of the fields on the given records.
|
|
506
369
|
|
|
507
370
|
Makes webservice calls to get the data type definition and fields of the given records if they weren't
|
|
508
371
|
previously cached.
|
|
@@ -514,15 +377,9 @@ class CallbackUtil:
|
|
|
514
377
|
they are provided in this list.
|
|
515
378
|
:param editable: If true, all fields are displayed as editable. If false, all fields are displayed as
|
|
516
379
|
uneditable. If none, only those fields that are defined as editable by the data designer will be editable.
|
|
517
|
-
:param group_by: If provided, the created table dialog will be grouped by the field with this name by default.
|
|
518
|
-
The user may remove this grouping if they want to.
|
|
519
|
-
:param image_data: The bytes to the images that should be displayed in the rows of the table. Each element in
|
|
520
|
-
the image data list corresponds to the element at the same index in the records list.
|
|
521
380
|
:return: A list of dictionaries mapping the data field names of the given field definitions to the response
|
|
522
381
|
value from the user for that field for each row.
|
|
523
382
|
"""
|
|
524
|
-
if not records:
|
|
525
|
-
raise SapioException("No records provided.")
|
|
526
383
|
data_types: set[str] = {x.data_type_name for x in records}
|
|
527
384
|
if len(data_types) > 1:
|
|
528
385
|
raise SapioException("Multiple data type names encountered in records list for record table popup.")
|
|
@@ -533,193 +390,22 @@ class CallbackUtil:
|
|
|
533
390
|
type_def: DataTypeDefinition = self.dt_cache.get_data_type(data_type)
|
|
534
391
|
field_defs: dict[str, AbstractVeloxFieldDefinition] = self.dt_cache.get_fields_for_type(data_type)
|
|
535
392
|
|
|
536
|
-
# Key fields display their columns in order before all non-key fields.
|
|
537
|
-
# Unmark key fields so that the column order is respected exactly as the caller provides it.
|
|
538
|
-
# Also make everything visible, because presumably the caller gave a field name because they want it to be seen.
|
|
539
|
-
modifier = FieldModifier(visible=True, key_field=False, editable=editable)
|
|
540
|
-
|
|
541
393
|
# Build the form using only those fields that are desired.
|
|
542
394
|
builder = FormBuilder(data_type, type_def.display_name, type_def.plural_display_name)
|
|
543
395
|
for field_name in fields:
|
|
544
396
|
field_def = field_defs.get(field_name)
|
|
545
397
|
if field_def is None:
|
|
546
398
|
raise SapioException(f"No field of name \"{field_name}\" in field definitions of type \"{data_type}\"")
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
finally:
|
|
558
|
-
self.user.timeout_seconds = self._original_timeout
|
|
559
|
-
if response is None:
|
|
560
|
-
raise SapioUserCancelledException()
|
|
561
|
-
return response
|
|
562
|
-
|
|
563
|
-
def multi_type_table_dialog(self,
|
|
564
|
-
title: str,
|
|
565
|
-
msg: str,
|
|
566
|
-
fields: list[(str, str) | AbstractVeloxFieldDefinition],
|
|
567
|
-
row_contents: list[list[SapioRecord | FieldMap]],
|
|
568
|
-
*,
|
|
569
|
-
default_modifier: FieldModifier | None = None,
|
|
570
|
-
field_modifiers: dict[str, FieldModifier] | None = None,
|
|
571
|
-
data_type: str = "Default",
|
|
572
|
-
display_name: str | None = None,
|
|
573
|
-
plural_display_name: str | None = None) -> list[FieldMap]:
|
|
574
|
-
"""
|
|
575
|
-
Create a table dialog where the user may input data into the fields of the table. The table is constructed from
|
|
576
|
-
a given list of records of multiple data types or field maps. Provided field names must match with field names
|
|
577
|
-
from the data type definition of the given records. The fields that are displayed will have their default value
|
|
578
|
-
be that of the fields on the given records or field maps.
|
|
579
|
-
|
|
580
|
-
Makes webservice calls to get the data type field definitions of the given records if they weren't
|
|
581
|
-
previously cached.
|
|
582
|
-
|
|
583
|
-
:param title: The title of the dialog.
|
|
584
|
-
:param msg: The message to display in the dialog.
|
|
585
|
-
:param fields: A list of objects representing the fields in the table. This could either be a two-element tuple
|
|
586
|
-
where the first element is a data type name and the second is a field name, or it could be a field
|
|
587
|
-
definition. If it is the former, a query will be made to find the field definition matching tht data type.
|
|
588
|
-
The data type names of the fields must match the data type names of the records in the row contents.
|
|
589
|
-
See the description of row_contents for what to do if you want to construct a field that pulls from a field
|
|
590
|
-
map.
|
|
591
|
-
If two fields share the same field name, an exception will be thrown. This is even true in the case where
|
|
592
|
-
the data type name of the fields is different. If you wish to display two fields from two data types with
|
|
593
|
-
the same name, then you must provide a FieldModifier for at least one of the fields where prepend_data_type
|
|
594
|
-
is True in order to make that field's name unique again. Note that if you do this for a field, the mapping
|
|
595
|
-
of record to field name will use the unedited field name, but the return results of this function will
|
|
596
|
-
use the edited field name in the results dictionary for a row.
|
|
597
|
-
:param row_contents: A list where each element is another list representing the records or a field map that will
|
|
598
|
-
be used to populate the columns of the table. If the data type of a given record doesn't match any of the
|
|
599
|
-
data type names of the given fields, then it will not be used.
|
|
600
|
-
This list can contain up to one field map, which are fields not tied to a record. This is so that you can
|
|
601
|
-
create abstract field definition not tied to a specific record in the system. If you want to define an
|
|
602
|
-
abstract field that pulls from the field map in the row contents, then you must set the data type name to
|
|
603
|
-
Default.
|
|
604
|
-
If a record of a given data type appears more than once in one of the inner-lists of the row contents, or
|
|
605
|
-
there is more than one field map, then an exception will be thrown, as there is no way of distinguishing
|
|
606
|
-
which record should be used for a field, and not all fields could have their values combined across multiple
|
|
607
|
-
records.
|
|
608
|
-
The row contents may have an inner-list which is missing a record of a data type that matches one of the
|
|
609
|
-
fields. In this case, the field value for that row under that column will be null.
|
|
610
|
-
The inner-list does not need to be sorted in any way, as this function will map the inner-list contents to
|
|
611
|
-
fields as necessary.
|
|
612
|
-
The inner-list may contain null elements; these will simply be discarded by this function.
|
|
613
|
-
:param default_modifier: A default field modifier that will be applied to the given fields. This can be used to
|
|
614
|
-
make field definitions from the system behave differently than their system values. If this value is None,
|
|
615
|
-
then a default field modifier is created that causes all specified fields to be both visible and not key
|
|
616
|
-
fields. (Key fields get displayed first before any non-key fields in tables, so the key field setting is
|
|
617
|
-
disabled by default in order to have the columns in the table respect the order of the fields as they are
|
|
618
|
-
provided to this function.)
|
|
619
|
-
:param field_modifiers: A mapping of data field name to field modifier for changes that should be applied to
|
|
620
|
-
the matching field. If a data field name is not present in the provided dict, or the provided dictionary is
|
|
621
|
-
None, then the default modifier will be used.
|
|
622
|
-
:param data_type: The data type name for the temporary data type that will be created for this table.
|
|
623
|
-
:param display_name: The display name for the temporary data type. If not provided, defaults to the data type
|
|
624
|
-
name.
|
|
625
|
-
:param plural_display_name: The plural display name for the temporary data type. If not provided, defaults to
|
|
626
|
-
the display name + "s".
|
|
627
|
-
:return: A list of dictionaries mapping the data field names of the given field definitions to the response
|
|
628
|
-
value from the user for that field for each row.
|
|
629
|
-
"""
|
|
630
|
-
# Set the default modifier to make all fields visible and not key if no default was provided.
|
|
631
|
-
if default_modifier is None:
|
|
632
|
-
default_modifier = FieldModifier(visible=True, key_field=False)
|
|
633
|
-
# To make things simpler, treat null field modifiers as an empty dict.
|
|
634
|
-
if field_modifiers is None:
|
|
635
|
-
field_modifiers = {}
|
|
636
|
-
|
|
637
|
-
# Construct the final fields list from the possible field objects.
|
|
638
|
-
final_fields: list[AbstractVeloxFieldDefinition] = []
|
|
639
|
-
# Keep track of whether any given field name appears more than once, as two fields could have the same
|
|
640
|
-
# field name but different data types. In this case, the user should provide a field modifier or field
|
|
641
|
-
# definition that changes one of the field names.
|
|
642
|
-
field_names: list[str] = []
|
|
643
|
-
for field in fields:
|
|
644
|
-
# Find the field definition for this field object.
|
|
645
|
-
if isinstance(field, tuple):
|
|
646
|
-
field_def: AbstractVeloxFieldDefinition = self.dt_cache.get_fields_for_type(field[0]).get(field[1])
|
|
647
|
-
elif isinstance(field, AbstractVeloxFieldDefinition):
|
|
648
|
-
field_def: AbstractVeloxFieldDefinition = field
|
|
649
|
-
else:
|
|
650
|
-
raise SapioException("Unrecognized field object.")
|
|
651
|
-
|
|
652
|
-
# Locate the modifier for this field and store the modified field.
|
|
653
|
-
name: str = field_def.data_field_name
|
|
654
|
-
modifier: FieldModifier = field_modifiers.get(name, default_modifier)
|
|
655
|
-
field_def: AbstractVeloxFieldDefinition = modifier.modify_field(field_def)
|
|
656
|
-
final_fields.append(field_def)
|
|
657
|
-
|
|
658
|
-
# Verify that this field name isn't a duplicate.
|
|
659
|
-
# The field name may have changed due to the modifier.
|
|
660
|
-
name: str = field_def.data_field_name
|
|
661
|
-
if name in field_names:
|
|
662
|
-
raise SapioException(f"The field name \"{name}\" appears more than once in the given fields. "
|
|
663
|
-
f"If you have provided two fields with the same name but different data types, "
|
|
664
|
-
f"consider providing a FieldModifier where prepend_data_type is true for one of "
|
|
665
|
-
f"the fields so that the field names will be different.")
|
|
666
|
-
field_names.append(name)
|
|
667
|
-
|
|
668
|
-
# Get the values for each row.
|
|
669
|
-
values: list[dict[str, FieldValue]] = []
|
|
670
|
-
for row in row_contents:
|
|
671
|
-
# The final values for this row:
|
|
672
|
-
row_values: dict[str, FieldValue] = {}
|
|
673
|
-
|
|
674
|
-
# Map the records for this row by their data type. If a field map is provided, its data type is Default.
|
|
675
|
-
row_records: dict[str, SapioRecord | FieldMap] = {}
|
|
676
|
-
for rec in row:
|
|
677
|
-
# Toss out null elements.
|
|
678
|
-
if rec is None:
|
|
679
|
-
continue
|
|
680
|
-
# Map records to their data type name. Map field maps to Default.
|
|
681
|
-
dt: str = "Default" if isinstance(rec, dict) else rec.data_type_name
|
|
682
|
-
# Warn if the same data type name appears more than once.
|
|
683
|
-
if dt in row_records:
|
|
684
|
-
raise SapioException(f"The data type \"{dt}\" appears more than once in the given row contents.")
|
|
685
|
-
row_records[dt] = rec
|
|
686
|
-
|
|
687
|
-
# Get the field values from the above records.
|
|
688
|
-
for field in final_fields:
|
|
689
|
-
# Find the object that corresponds to this field given its data type name.
|
|
690
|
-
record: SapioRecord | FieldMap | None = row_records.get(field.data_type_name)
|
|
691
|
-
# This could be either a record, a field map, or null. Convert any records to field maps.
|
|
692
|
-
if not isinstance(record, dict) and record is not None:
|
|
693
|
-
record: FieldMap | None = AliasUtil.to_field_map_lists([record])[0]
|
|
694
|
-
|
|
695
|
-
# Find out if this field had its data type prepended to it. If this is the case, then we need to find
|
|
696
|
-
# the true data field name before retrieving the value from the field map.
|
|
697
|
-
name: str = field.data_field_name
|
|
698
|
-
if field_modifiers.get(name, default_modifier).prepend_data_type is True:
|
|
699
|
-
name = name.split(".")[1]
|
|
700
|
-
|
|
701
|
-
# Set the value for this particular field.
|
|
702
|
-
row_values[field.data_field_name] = record.get(name) if record else None
|
|
703
|
-
values.append(row_values)
|
|
704
|
-
|
|
705
|
-
if display_name is None:
|
|
706
|
-
display_name = data_type
|
|
707
|
-
if plural_display_name is None:
|
|
708
|
-
plural_display_name = display_name + "s"
|
|
709
|
-
|
|
710
|
-
builder = FormBuilder(data_type, display_name, plural_display_name)
|
|
711
|
-
for field in final_fields:
|
|
712
|
-
builder.add_field(field)
|
|
713
|
-
|
|
714
|
-
request = TableEntryDialogRequest(title, msg, builder.get_temporary_data_type(), values,
|
|
715
|
-
width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
|
|
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
|
|
399
|
+
if editable is not None:
|
|
400
|
+
field_def.editable = editable
|
|
401
|
+
field_def.visible = True
|
|
402
|
+
# Key fields display their columns in order before all non-key fields.
|
|
403
|
+
# Unmark key fields so that the column order is respected exactly as the caller provides it.
|
|
404
|
+
field_def.key_field = False
|
|
405
|
+
builder.add_field(field_def)
|
|
406
|
+
|
|
407
|
+
request = TableEntryDialogRequest(title, msg, builder.get_temporary_data_type(), field_map_list)
|
|
408
|
+
response: list[FieldMap] | None = self.callback.show_table_entry_dialog(request)
|
|
723
409
|
if response is None:
|
|
724
410
|
raise SapioUserCancelledException()
|
|
725
411
|
return response
|
|
@@ -767,15 +453,8 @@ class CallbackUtil:
|
|
|
767
453
|
raise SapioException(f"The data type \"{data_type}\" does not have a layout by the name "
|
|
768
454
|
f"\"{layout_name}\" in the system.")
|
|
769
455
|
|
|
770
|
-
request = DataRecordDialogRequest(title, record, layout, minimized, access_level, plugin_path_list
|
|
771
|
-
|
|
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
|
|
456
|
+
request = DataRecordDialogRequest(title, record, layout, minimized, access_level, plugin_path_list)
|
|
457
|
+
response: bool = self.callback.data_record_form_view_dialog(request)
|
|
779
458
|
if not response:
|
|
780
459
|
raise SapioUserCancelledException()
|
|
781
460
|
|
|
@@ -785,8 +464,7 @@ class CallbackUtil:
|
|
|
785
464
|
values: list[FieldMap],
|
|
786
465
|
multi_select: bool = True,
|
|
787
466
|
*,
|
|
788
|
-
|
|
789
|
-
display_name: str | None = None,
|
|
467
|
+
display_name: str = "Default",
|
|
790
468
|
plural_display_name: str | None = None) -> list[FieldMap]:
|
|
791
469
|
"""
|
|
792
470
|
Create a selection dialog for a list of field maps for the user to choose from. Requires that the caller
|
|
@@ -797,37 +475,24 @@ class CallbackUtil:
|
|
|
797
475
|
they are provided in this list.
|
|
798
476
|
:param values: The values to set for each row of the table.
|
|
799
477
|
:param multi_select: Whether the user is able to select multiple rows from the list.
|
|
800
|
-
:param
|
|
801
|
-
:param display_name: The display name for the temporary data type. If not provided, defaults to the data type
|
|
802
|
-
name.
|
|
478
|
+
:param display_name: The display name for the temporary data type that will be created.
|
|
803
479
|
:param plural_display_name: The plural display name for the temporary data type. If not provided, defaults to
|
|
804
480
|
the display name + "s".
|
|
805
481
|
:return: A list of field maps corresponding to the chosen input field maps.
|
|
806
482
|
"""
|
|
807
|
-
if display_name is None:
|
|
808
|
-
display_name = data_type
|
|
809
483
|
if plural_display_name is None:
|
|
810
484
|
plural_display_name = display_name + "s"
|
|
811
485
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
request = TempTableSelectionRequest(builder.get_temporary_data_type(), msg, values,
|
|
817
|
-
multi_select=multi_select)
|
|
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
|
|
486
|
+
# Build the form using only those fields that are desired.
|
|
487
|
+
request = DataRecordSelectionRequest(display_name, plural_display_name,
|
|
488
|
+
fields, values, msg, multi_select)
|
|
489
|
+
response: list[FieldMap] | None = self.callback.show_data_record_selection_dialog(request)
|
|
825
490
|
if response is None:
|
|
826
491
|
raise SapioUserCancelledException()
|
|
827
492
|
return response
|
|
828
493
|
|
|
829
494
|
def record_selection_dialog(self, msg: str, fields: list[str], records: list[SapioRecord],
|
|
830
|
-
multi_select: bool = True) -> list[
|
|
495
|
+
multi_select: bool = True) -> list[FieldMap]:
|
|
831
496
|
"""
|
|
832
497
|
Create a record selection dialog for a list of records for the user to choose from. Provided field names must
|
|
833
498
|
match fields on the definition of the data type of the given records.
|
|
@@ -840,10 +505,8 @@ class CallbackUtil:
|
|
|
840
505
|
they are provided in this list.
|
|
841
506
|
:param records: The records to display as rows in the table.
|
|
842
507
|
:param multi_select: Whether the user is able to select multiple records from the list.
|
|
843
|
-
:return: A list of the
|
|
508
|
+
:return: A list of field maps corresponding to the chosen input records.
|
|
844
509
|
"""
|
|
845
|
-
if not records:
|
|
846
|
-
raise SapioException("No records provided.")
|
|
847
510
|
data_types: set[str] = {x.data_type_name for x in records}
|
|
848
511
|
if len(data_types) > 1:
|
|
849
512
|
raise SapioException("Multiple data type names encountered in records list for record table popup.")
|
|
@@ -858,28 +521,21 @@ class CallbackUtil:
|
|
|
858
521
|
type_def: DataTypeDefinition = self.dt_cache.get_data_type(data_type)
|
|
859
522
|
field_defs: dict[str, AbstractVeloxFieldDefinition] = self.dt_cache.get_fields_for_type(data_type)
|
|
860
523
|
|
|
861
|
-
# Key fields display their columns in order before all non-key fields.
|
|
862
|
-
# Unmark key fields so that the column order is respected exactly as the caller provides it.
|
|
863
|
-
# Also make everything visible, because presumably the caller give a field name because they want it to be seen.
|
|
864
|
-
modifier = FieldModifier(visible=True, key_field=False)
|
|
865
|
-
|
|
866
524
|
# Build the form using only those fields that are desired.
|
|
867
|
-
|
|
525
|
+
field_def_list: list = []
|
|
868
526
|
for field_name in fields:
|
|
869
527
|
field_def = field_defs.get(field_name)
|
|
870
528
|
if field_def is None:
|
|
871
529
|
raise SapioException(f"No field of name \"{field_name}\" in field definitions of type \"{data_type}\"")
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
finally:
|
|
882
|
-
self.user.timeout_seconds = self._original_timeout
|
|
530
|
+
field_def.visible = True
|
|
531
|
+
# Key fields display their columns in order before all non-key fields.
|
|
532
|
+
# Unmark key fields so that the column order is respected exactly as the caller provides it.
|
|
533
|
+
field_def.key_field = False
|
|
534
|
+
field_def_list.append(field_def)
|
|
535
|
+
|
|
536
|
+
request = DataRecordSelectionRequest(type_def.display_name, type_def.plural_display_name,
|
|
537
|
+
field_def_list, field_map_list, msg, multi_select)
|
|
538
|
+
response: list[FieldMap] | None = self.callback.show_data_record_selection_dialog(request)
|
|
883
539
|
if response is None:
|
|
884
540
|
raise SapioUserCancelledException()
|
|
885
541
|
# Map the field maps in the response back to the record they come from, returning the chosen record instead of
|
|
@@ -946,7 +602,7 @@ class CallbackUtil:
|
|
|
946
602
|
|
|
947
603
|
# If CustomReportCriteria was provided, it must be wrapped as a CustomReport.
|
|
948
604
|
if isinstance(custom_search, CustomReportCriteria):
|
|
949
|
-
custom_search: CustomReport = CustomReport(False,
|
|
605
|
+
custom_search: CustomReport = CustomReport(False, None, custom_search)
|
|
950
606
|
# If a string was provided, locate the report criteria for the predefined search in the system matching this
|
|
951
607
|
# name.
|
|
952
608
|
if isinstance(custom_search, str):
|
|
@@ -955,13 +611,7 @@ class CallbackUtil:
|
|
|
955
611
|
request = InputSelectionRequest(data_type, msg, search_types, only_key_fields, record_blacklist,
|
|
956
612
|
record_whitelist, preselected_records, custom_search, scan_criteria,
|
|
957
613
|
multi_select)
|
|
958
|
-
|
|
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
|
|
614
|
+
response: list[DataRecord] | None = self.callback.show_input_selection_dialog(request)
|
|
965
615
|
if response is None:
|
|
966
616
|
raise SapioUserCancelledException()
|
|
967
617
|
return RecordHandler(self.user).wrap_models(response, wrapper_type)
|
|
@@ -985,21 +635,14 @@ class CallbackUtil:
|
|
|
985
635
|
for field in additional_fields:
|
|
986
636
|
builder.add_field(field)
|
|
987
637
|
temp_dt = builder.get_temporary_data_type()
|
|
988
|
-
request = ESigningRequestPojo(title, msg, show_comment, temp_dt
|
|
989
|
-
|
|
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
|
|
638
|
+
request = ESigningRequestPojo(title, msg, show_comment, temp_dt)
|
|
639
|
+
response: ESigningResponsePojo | None = self.callback.show_esign_dialog(request)
|
|
997
640
|
if response is None:
|
|
998
641
|
raise SapioUserCancelledException()
|
|
999
642
|
return response
|
|
1000
643
|
|
|
1001
644
|
def request_file(self, title: str, exts: list[str] | None = None,
|
|
1002
|
-
show_image_editor: bool = False, show_camera_button: bool = False) ->
|
|
645
|
+
show_image_editor: bool = False, show_camera_button: bool = False) -> (str, bytes):
|
|
1003
646
|
"""
|
|
1004
647
|
Request a single file from the user.
|
|
1005
648
|
|
|
@@ -1024,13 +667,7 @@ class CallbackUtil:
|
|
|
1024
667
|
return sink.consume_data(chunk, io_obj)
|
|
1025
668
|
|
|
1026
669
|
request = FilePromptRequest(title, show_image_editor, ",".join(exts), show_camera_button)
|
|
1027
|
-
|
|
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
|
|
670
|
+
file_path: str | None = self.callback.show_file_dialog(request, do_consume)
|
|
1034
671
|
if file_path is None:
|
|
1035
672
|
raise SapioUserCancelledException()
|
|
1036
673
|
|
|
@@ -1038,7 +675,7 @@ class CallbackUtil:
|
|
|
1038
675
|
return file_path, sink.data
|
|
1039
676
|
|
|
1040
677
|
def request_files(self, title: str, exts: list[str] | None = None,
|
|
1041
|
-
show_image_editor: bool = False, show_camera_button: bool = False)
|
|
678
|
+
show_image_editor: bool = False, show_camera_button: bool = False):
|
|
1042
679
|
"""
|
|
1043
680
|
Request multiple files from the user.
|
|
1044
681
|
|
|
@@ -1055,13 +692,7 @@ class CallbackUtil:
|
|
|
1055
692
|
exts: list[str] = []
|
|
1056
693
|
|
|
1057
694
|
request = MultiFilePromptRequest(title, show_image_editor, ",".join(exts), show_camera_button)
|
|
1058
|
-
|
|
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
|
|
695
|
+
file_paths: list[str] | None = self.callback.show_multi_file_dialog(request)
|
|
1065
696
|
if not file_paths:
|
|
1066
697
|
raise SapioUserCancelledException()
|
|
1067
698
|
|
|
@@ -1075,7 +706,7 @@ class CallbackUtil:
|
|
|
1075
706
|
return ret_dict
|
|
1076
707
|
|
|
1077
708
|
@staticmethod
|
|
1078
|
-
def __verify_file(file_path: str, file_bytes: bytes, allowed_extensions: list[str])
|
|
709
|
+
def __verify_file(file_path: str, file_bytes: bytes, allowed_extensions: list[str]):
|
|
1079
710
|
"""
|
|
1080
711
|
Verify that the provided file was read (i.e. the file path and file bytes aren't None or empty) and that it
|
|
1081
712
|
has the correct file extension. Raises a user error exception if something about the file is incorrect.
|
|
@@ -1089,7 +720,7 @@ class CallbackUtil:
|
|
|
1089
720
|
if len(allowed_extensions) != 0:
|
|
1090
721
|
matches: bool = False
|
|
1091
722
|
for ext in allowed_extensions:
|
|
1092
|
-
if file_path.endswith("." + ext
|
|
723
|
+
if file_path.endswith("." + ext):
|
|
1093
724
|
matches = True
|
|
1094
725
|
break
|
|
1095
726
|
if matches is False:
|
|
@@ -1103,85 +734,5 @@ class CallbackUtil:
|
|
|
1103
734
|
:param file_name: The name of the file.
|
|
1104
735
|
:param file_data: The data of the file, provided as either a string or as a bytes array.
|
|
1105
736
|
"""
|
|
1106
|
-
data = io.
|
|
737
|
+
data = io.StringIO(file_data) if isinstance(file_data, str) else io.BytesIO(file_data)
|
|
1107
738
|
self.callback.send_file(file_name, False, data)
|
|
1108
|
-
|
|
1109
|
-
def write_zip_file(self, zip_name: str, files: dict[str, str | bytes]) -> None:
|
|
1110
|
-
"""
|
|
1111
|
-
Send a collection of files to the user in a zip file.
|
|
1112
|
-
|
|
1113
|
-
:param zip_name: The name of the zip file.
|
|
1114
|
-
:param files: A dictionary of the files to add to the zip file.
|
|
1115
|
-
"""
|
|
1116
|
-
data = io.BytesIO(FileUtil.zip_files(files))
|
|
1117
|
-
self.callback.send_file(zip_name, False, data)
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
class FieldModifier:
|
|
1121
|
-
"""
|
|
1122
|
-
A FieldModifier can be used to update the settings of a field definition from the system.
|
|
1123
|
-
"""
|
|
1124
|
-
prepend_data_type: bool
|
|
1125
|
-
display_name: str | None
|
|
1126
|
-
required: bool | None
|
|
1127
|
-
editable: bool | None
|
|
1128
|
-
visible: bool | None
|
|
1129
|
-
key_field: bool | None
|
|
1130
|
-
column_width: int | None
|
|
1131
|
-
|
|
1132
|
-
def __init__(self, *, prepend_data_type: bool = False,
|
|
1133
|
-
display_name: str | None = None, required: bool | None = None, editable: bool | None = None,
|
|
1134
|
-
visible: bool | None = None, key_field: bool | None = None, column_width: int | None = None):
|
|
1135
|
-
"""
|
|
1136
|
-
If any values are given as None then that value will not be changed on the given field.
|
|
1137
|
-
|
|
1138
|
-
:param prepend_data_type: If true, prepends the data type name of the field to the data field name. For example,
|
|
1139
|
-
if a field has a data type name X and a data field name Y, then the field name would become "X.Y". This is
|
|
1140
|
-
useful for cases where you have the same field name on two different data types and want to distinguish one
|
|
1141
|
-
or both of them.
|
|
1142
|
-
:param display_name: Change the display name.
|
|
1143
|
-
:param required: Change the required status.
|
|
1144
|
-
:param editable: Change the editable status.
|
|
1145
|
-
:param visible: Change the visible status.
|
|
1146
|
-
:param key_field: Change the key field status.
|
|
1147
|
-
:param column_width: Change the column width.
|
|
1148
|
-
"""
|
|
1149
|
-
self.prepend_data_type = prepend_data_type
|
|
1150
|
-
self.display_name = display_name
|
|
1151
|
-
self.required = required
|
|
1152
|
-
self.editable = editable
|
|
1153
|
-
self.visible = visible
|
|
1154
|
-
self.key_field = key_field
|
|
1155
|
-
self.column_width = column_width
|
|
1156
|
-
|
|
1157
|
-
def modify_field(self, field: AbstractVeloxFieldDefinition) -> AbstractVeloxFieldDefinition:
|
|
1158
|
-
"""
|
|
1159
|
-
Apply modifications to a given field.
|
|
1160
|
-
|
|
1161
|
-
:param field: The field to modify.
|
|
1162
|
-
:return: A copy of the input field with the modifications applied.
|
|
1163
|
-
"""
|
|
1164
|
-
field = copy_field(field)
|
|
1165
|
-
if self.prepend_data_type is True:
|
|
1166
|
-
field._data_field_name = field.data_field_name + "." + field.data_field_name
|
|
1167
|
-
if self.display_name is not None:
|
|
1168
|
-
field.display_name = self.display_name
|
|
1169
|
-
if self.required is not None:
|
|
1170
|
-
field.required = self.required
|
|
1171
|
-
if self.editable is not None:
|
|
1172
|
-
field.editable = self.editable
|
|
1173
|
-
if self.visible is not None:
|
|
1174
|
-
field.visible = self.visible
|
|
1175
|
-
if self.key_field is not None:
|
|
1176
|
-
field.key_field = self.key_field
|
|
1177
|
-
if self.column_width is not None:
|
|
1178
|
-
field.default_table_column_width = self.column_width
|
|
1179
|
-
return field
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
def copy_field(field: AbstractVeloxFieldDefinition) -> AbstractVeloxFieldDefinition:
|
|
1183
|
-
"""
|
|
1184
|
-
Create a copy of a given field definition. This is used to modify field definitions from the server for existing
|
|
1185
|
-
data types without also modifying the field definition in the cache.
|
|
1186
|
-
"""
|
|
1187
|
-
return FieldDefinitionParser.to_field_definition(field.to_json())
|