sapiopycommons 2024.8.7a303__py3-none-any.whl → 2024.8.19a305__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 +63 -3
- sapiopycommons/customreport/__init__.py +0 -0
- sapiopycommons/customreport/column_builder.py +60 -0
- sapiopycommons/customreport/custom_report_builder.py +125 -0
- sapiopycommons/customreport/term_builder.py +296 -0
- sapiopycommons/eln/experiment_handler.py +153 -36
- sapiopycommons/files/file_bridge_handler.py +21 -0
- sapiopycommons/files/file_util.py +25 -1
- sapiopycommons/files/file_validator.py +20 -4
- sapiopycommons/files/file_writer.py +44 -15
- sapiopycommons/general/aliases.py +77 -6
- sapiopycommons/general/popup_util.py +17 -0
- sapiopycommons/general/time_util.py +40 -0
- sapiopycommons/recordmodel/record_handler.py +36 -1
- sapiopycommons/rules/eln_rule_handler.py +23 -0
- sapiopycommons/rules/on_save_rule_handler.py +23 -0
- sapiopycommons/webhook/webhook_handlers.py +32 -3
- sapiopycommons/webhook/webservice_handlers.py +67 -0
- {sapiopycommons-2024.8.7a303.dist-info → sapiopycommons-2024.8.19a305.dist-info}/METADATA +1 -1
- {sapiopycommons-2024.8.7a303.dist-info → sapiopycommons-2024.8.19a305.dist-info}/RECORD +22 -17
- {sapiopycommons-2024.8.7a303.dist-info → sapiopycommons-2024.8.19a305.dist-info}/WHEEL +0 -0
- {sapiopycommons-2024.8.7a303.dist-info → sapiopycommons-2024.8.19a305.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
1
3
|
from sapiopylib.rest.DataMgmtService import DataMgmtServer
|
|
2
4
|
from sapiopylib.rest.pojo.datatype.DataType import DataTypeDefinition
|
|
3
5
|
from sapiopylib.rest.pojo.datatype.FieldDefinition import VeloxStringFieldDefinition, AbstractVeloxFieldDefinition, \
|
|
@@ -51,6 +53,7 @@ class PopupUtil:
|
|
|
51
53
|
:param request_context: Context that will be returned to the webhook server in the client callback result.
|
|
52
54
|
:return: A SapioWebhookResult with the popup as its client callback request.
|
|
53
55
|
"""
|
|
56
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
54
57
|
if display_name is None:
|
|
55
58
|
display_name = data_type
|
|
56
59
|
if plural_display_name is None:
|
|
@@ -97,6 +100,7 @@ class PopupUtil:
|
|
|
97
100
|
:param request_context: Context that will be returned to the webhook server in the client callback result.
|
|
98
101
|
:return: A SapioWebhookResult with the popup as its client callback request.
|
|
99
102
|
"""
|
|
103
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
100
104
|
# Get the field definitions of the data type.
|
|
101
105
|
data_type: str = record.data_type_name
|
|
102
106
|
type_man = DataMgmtServer.get_data_type_manager(context.user)
|
|
@@ -155,6 +159,7 @@ class PopupUtil:
|
|
|
155
159
|
:param request_context: Context that will be returned to the webhook server in the client callback result.
|
|
156
160
|
:return: A SapioWebhookResult with the popup as its client callback request.
|
|
157
161
|
"""
|
|
162
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
158
163
|
if max_length is None:
|
|
159
164
|
max_length = len(default_value) if default_value else 100
|
|
160
165
|
string_field = VeloxStringFieldDefinition(data_type, field_name, field_name, default_value=default_value,
|
|
@@ -191,6 +196,7 @@ class PopupUtil:
|
|
|
191
196
|
:param request_context: Context that will be returned to the webhook server in the client callback result.
|
|
192
197
|
:return: A SapioWebhookResult with the popup as its client callback request.
|
|
193
198
|
"""
|
|
199
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
194
200
|
if default_value is None:
|
|
195
201
|
default_value = max(0, min_value)
|
|
196
202
|
integer_field = VeloxIntegerFieldDefinition(data_type, field_name, field_name, default_value=default_value,
|
|
@@ -229,6 +235,7 @@ class PopupUtil:
|
|
|
229
235
|
:param request_context: Context that will be returned to the webhook server in the client callback result.
|
|
230
236
|
:return: A SapioWebhookResult with the popup as its client callback request.
|
|
231
237
|
"""
|
|
238
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
232
239
|
if default_value is None:
|
|
233
240
|
default_value = min_value
|
|
234
241
|
double_field = VeloxDoubleFieldDefinition(data_type, field_name, field_name, default_value=default_value,
|
|
@@ -260,6 +267,7 @@ class PopupUtil:
|
|
|
260
267
|
:param request_context: Context that will be returned to the webhook server in the client callback result.
|
|
261
268
|
:return: A SapioWebhookResult with the popup as its client callback request.
|
|
262
269
|
"""
|
|
270
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
263
271
|
if display_name is None:
|
|
264
272
|
display_name = data_type
|
|
265
273
|
if plural_display_name is None:
|
|
@@ -295,6 +303,7 @@ class PopupUtil:
|
|
|
295
303
|
:param request_context: Context that will be returned to the webhook server in the client callback result.
|
|
296
304
|
:return: A SapioWebhookResult with the popup as its client callback request.
|
|
297
305
|
"""
|
|
306
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
298
307
|
data_types: set[str] = {x.data_type_name for x in records}
|
|
299
308
|
if len(data_types) > 1:
|
|
300
309
|
raise SapioException("Multiple data type names encountered in records list for record table popup.")
|
|
@@ -347,6 +356,7 @@ class PopupUtil:
|
|
|
347
356
|
:param request_context: Context that will be returned to the webhook server in the client callback result.
|
|
348
357
|
:return: A SapioWebhookResult with the popup as its client callback request.
|
|
349
358
|
"""
|
|
359
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
350
360
|
data_types: set[str] = {x.data_type_name for x in records}
|
|
351
361
|
if len(data_types) > 1:
|
|
352
362
|
raise SapioException("Multiple data type names encountered in records list for record table popup.")
|
|
@@ -391,6 +401,7 @@ class PopupUtil:
|
|
|
391
401
|
:param request_context: Context that will be returned to the webhook server in the client callback result.
|
|
392
402
|
:return: A SapioWebhookResult with the popup as its client callback request.
|
|
393
403
|
"""
|
|
404
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
394
405
|
callback = ListDialogRequest(title, multi_select, options,
|
|
395
406
|
callback_context_data=request_context)
|
|
396
407
|
return SapioWebhookResult(True, client_callback_request=callback)
|
|
@@ -415,6 +426,7 @@ class PopupUtil:
|
|
|
415
426
|
:param request_context: Context that will be returned to the webhook server in the client callback result.
|
|
416
427
|
:return: A SapioWebhookResult with the popup as its client callback request.
|
|
417
428
|
"""
|
|
429
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
418
430
|
callback = OptionDialogRequest(title, msg, options, default_option, user_can_cancel,
|
|
419
431
|
callback_context_data=request_context)
|
|
420
432
|
return SapioWebhookResult(True, client_callback_request=callback)
|
|
@@ -437,6 +449,7 @@ class PopupUtil:
|
|
|
437
449
|
:param request_context: Context that will be returned to the webhook server in the client callback result.
|
|
438
450
|
:return: A SapioWebhookResult with the popup as its client callback request.
|
|
439
451
|
"""
|
|
452
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
440
453
|
return PopupUtil.option_popup(title, msg, ["OK"], 0, user_can_cancel, request_context=request_context)
|
|
441
454
|
|
|
442
455
|
@staticmethod
|
|
@@ -458,6 +471,7 @@ class PopupUtil:
|
|
|
458
471
|
:param request_context: Context that will be returned to the webhook server in the client callback result.
|
|
459
472
|
:return: A SapioWebhookResult with the popup as its client callback request.
|
|
460
473
|
"""
|
|
474
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
461
475
|
return PopupUtil.option_popup(title, msg, ["Yes", "No"], 0 if default_yes else 1, user_can_cancel,
|
|
462
476
|
request_context=request_context)
|
|
463
477
|
|
|
@@ -470,6 +484,7 @@ class PopupUtil:
|
|
|
470
484
|
|
|
471
485
|
Deprecated for PopupUtil.text_field_popup.
|
|
472
486
|
"""
|
|
487
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
473
488
|
return PopupUtil.string_field_popup(title, "", field_name, msg, len(msg), False, data_type,
|
|
474
489
|
request_context=request_context, auto_size=True)
|
|
475
490
|
|
|
@@ -481,6 +496,7 @@ class PopupUtil:
|
|
|
481
496
|
|
|
482
497
|
Deprecated for PopupUtil.option_popup.
|
|
483
498
|
"""
|
|
499
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
484
500
|
return PopupUtil.option_popup(title, msg, options, 0, user_can_cancel, request_context=request_context)
|
|
485
501
|
|
|
486
502
|
@staticmethod
|
|
@@ -490,4 +506,5 @@ class PopupUtil:
|
|
|
490
506
|
|
|
491
507
|
Deprecated for PopupUtil.ok_popup.
|
|
492
508
|
"""
|
|
509
|
+
warnings.warn("PopupUtil is deprecated as of 24.5+. Use CallbackUtil instead.", DeprecationWarning)
|
|
493
510
|
return PopupUtil.ok_popup(title, msg, False, request_context=request_context)
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import time
|
|
2
4
|
from datetime import datetime
|
|
3
5
|
|
|
4
6
|
import pytz
|
|
5
7
|
|
|
8
|
+
from sapiopycommons.general.exceptions import SapioException
|
|
9
|
+
|
|
6
10
|
__timezone = None
|
|
7
11
|
"""The default timezone. Use TimeUtil.set_default_timezone in a global context before making use of TimeUtil."""
|
|
8
12
|
|
|
@@ -137,3 +141,39 @@ class TimeUtil:
|
|
|
137
141
|
return True
|
|
138
142
|
except Exception:
|
|
139
143
|
return False
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class DateRange:
|
|
147
|
+
start: int | None
|
|
148
|
+
end: int | None
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def from_string(value: str | None) -> DateRange:
|
|
152
|
+
"""
|
|
153
|
+
Construct a DateRange object from a string. The field value of date range fields is a string of the form
|
|
154
|
+
<start timestamp>/<end timestamp>.
|
|
155
|
+
|
|
156
|
+
:param value: A date range field value.
|
|
157
|
+
:return: A DateRange object matching the input field value.
|
|
158
|
+
"""
|
|
159
|
+
if not value:
|
|
160
|
+
return DateRange(None, None)
|
|
161
|
+
values: list[str] = value.split("/")
|
|
162
|
+
return DateRange(int(values[0]), int(values[1]))
|
|
163
|
+
|
|
164
|
+
def __init__(self, start: int | None, end: int | None):
|
|
165
|
+
"""
|
|
166
|
+
:param start: The timestamp for the start of the date range.
|
|
167
|
+
:param end: The timestamp for the end of the date rate.
|
|
168
|
+
"""
|
|
169
|
+
if (start and end is None) or (end and start is None):
|
|
170
|
+
raise SapioException("Both start and end values must be present in a date range.")
|
|
171
|
+
if start and end and end < start:
|
|
172
|
+
raise SapioException(f"End timestamp {end} is earlier than the start timestamp {start}.")
|
|
173
|
+
self.start = start
|
|
174
|
+
self.end = end
|
|
175
|
+
|
|
176
|
+
def __str__(self) -> str | None:
|
|
177
|
+
if not self.start and not self.end:
|
|
178
|
+
return None
|
|
179
|
+
return f"{self.start}/{self.end}"
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from collections.abc import Iterable
|
|
2
4
|
from typing import Any
|
|
5
|
+
from weakref import WeakValueDictionary
|
|
3
6
|
|
|
4
7
|
from sapiopylib.rest.DataRecordManagerService import DataRecordManager
|
|
5
8
|
from sapiopylib.rest.User import SapioUser
|
|
@@ -36,10 +39,29 @@ class RecordHandler:
|
|
|
36
39
|
rel_man: RecordModelRelationshipManager
|
|
37
40
|
an_man: RecordModelAncestorManager
|
|
38
41
|
|
|
42
|
+
__instances: WeakValueDictionary[SapioUser, RecordHandler] = WeakValueDictionary()
|
|
43
|
+
__initialized: bool
|
|
44
|
+
|
|
45
|
+
def __new__(cls, context: SapioWebhookContext | SapioUser):
|
|
46
|
+
"""
|
|
47
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
48
|
+
"""
|
|
49
|
+
user = context if isinstance(context, SapioUser) else context.user
|
|
50
|
+
obj = cls.__instances.get(user)
|
|
51
|
+
if not obj:
|
|
52
|
+
obj = object.__new__(cls)
|
|
53
|
+
obj.__initialized = False
|
|
54
|
+
cls.__instances[user] = obj
|
|
55
|
+
return obj
|
|
56
|
+
|
|
39
57
|
def __init__(self, context: SapioWebhookContext | SapioUser):
|
|
40
58
|
"""
|
|
41
59
|
:param context: The current webhook context or a user object to send requests from.
|
|
42
60
|
"""
|
|
61
|
+
if self.__initialized:
|
|
62
|
+
return
|
|
63
|
+
self.__initialized = True
|
|
64
|
+
|
|
43
65
|
self.user = context if isinstance(context, SapioUser) else context.user
|
|
44
66
|
self.dr_man = DataRecordManager(self.user)
|
|
45
67
|
self.rec_man = RecordModelManager(self.user)
|
|
@@ -174,6 +196,19 @@ class RecordHandler:
|
|
|
174
196
|
pager.max_page = page_limit
|
|
175
197
|
return self.wrap_models(pager.get_all_at_once(), wrapper_type), pager.next_page_criteria
|
|
176
198
|
|
|
199
|
+
def query_models_by_id_and_map(self, wrapper_type: type[WrappedType], ids: Iterable[int],
|
|
200
|
+
page_limit: int | None = None) -> dict[int, WrappedType]:
|
|
201
|
+
"""
|
|
202
|
+
Shorthand for using the data record manager to query for a list of data records by record ID
|
|
203
|
+
and then converting the results into a dictionary of record ID to the record model for that ID.
|
|
204
|
+
|
|
205
|
+
:param wrapper_type: The record model wrapper to use.
|
|
206
|
+
:param ids: The list of record IDs to query.
|
|
207
|
+
:param page_limit: The maximum number of pages to query. If None, exhausts all possible pages.
|
|
208
|
+
:return: The record models for the queried records mapped in a dictionary by their record ID.
|
|
209
|
+
"""
|
|
210
|
+
return {x.record_id: x for x in self.query_models_by_id(wrapper_type, ids, page_limit)}
|
|
211
|
+
|
|
177
212
|
def query_all_models(self, wrapper_type: type[WrappedType], page_limit: int | None = None) -> list[WrappedType]:
|
|
178
213
|
"""
|
|
179
214
|
Shorthand for using the data record manager to query for all data records of a given type
|
|
@@ -503,7 +538,7 @@ class RecordHandler:
|
|
|
503
538
|
|
|
504
539
|
@staticmethod
|
|
505
540
|
def map_by_child(models: Iterable[RecordModel], child_type: type[WrappedType]) \
|
|
506
|
-
-> dict[WrappedType,
|
|
541
|
+
-> dict[WrappedType, RecordModel]:
|
|
507
542
|
"""
|
|
508
543
|
Take a list of record models and map them by their children. Essentially an inversion of map_to_child.
|
|
509
544
|
If two records share the same child, an exception will be thrown. The children must already be loaded.
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from weakref import WeakValueDictionary
|
|
4
|
+
|
|
5
|
+
from sapiopylib.rest.User import SapioUser
|
|
1
6
|
from sapiopylib.rest.pojo.DataRecord import DataRecord
|
|
2
7
|
from sapiopylib.rest.pojo.eln.SapioELNEnums import ElnBaseDataType
|
|
3
8
|
from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
|
|
@@ -33,7 +38,25 @@ class ElnRuleHandler:
|
|
|
33
38
|
"""A mapping of entry name to the lists of field maps for that entry, each grouping of field maps being mapped by
|
|
34
39
|
its data type."""
|
|
35
40
|
|
|
41
|
+
__instances: WeakValueDictionary[SapioUser, ElnRuleHandler] = WeakValueDictionary()
|
|
42
|
+
__initialized: bool
|
|
43
|
+
|
|
44
|
+
def __new__(cls, context: SapioWebhookContext):
|
|
45
|
+
if context.velox_eln_rule_result_map is None:
|
|
46
|
+
raise SapioException("No Velox ELN rule result map in context for ElnRuleHandler to parse.")
|
|
47
|
+
user = context if isinstance(context, SapioUser) else context.user
|
|
48
|
+
obj = cls.__instances.get(user)
|
|
49
|
+
if not obj:
|
|
50
|
+
obj = object.__new__(cls)
|
|
51
|
+
obj.__initialized = False
|
|
52
|
+
cls.__instances[user] = obj
|
|
53
|
+
return obj
|
|
54
|
+
|
|
36
55
|
def __init__(self, context: SapioWebhookContext):
|
|
56
|
+
if self.__initialized:
|
|
57
|
+
return
|
|
58
|
+
self.__initialized = True
|
|
59
|
+
|
|
37
60
|
if context.velox_eln_rule_result_map is None:
|
|
38
61
|
raise SapioException("No Velox ELN rule result map in context for ElnRuleHandler to parse.")
|
|
39
62
|
self.__context = context
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from weakref import WeakValueDictionary
|
|
4
|
+
|
|
5
|
+
from sapiopylib.rest.User import SapioUser
|
|
1
6
|
from sapiopylib.rest.pojo.DataRecord import DataRecord
|
|
2
7
|
from sapiopylib.rest.pojo.eln.SapioELNEnums import ElnBaseDataType
|
|
3
8
|
from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
|
|
@@ -33,7 +38,25 @@ class OnSaveRuleHandler:
|
|
|
33
38
|
"""A mapping of record IDs of records in the context.data_record_list to the field maps related to that
|
|
34
39
|
record, each grouping of field maps being mapped by its data type."""
|
|
35
40
|
|
|
41
|
+
__instances: WeakValueDictionary[SapioUser, OnSaveRuleHandler] = WeakValueDictionary()
|
|
42
|
+
__initialized: bool
|
|
43
|
+
|
|
44
|
+
def __new__(cls, context: SapioWebhookContext):
|
|
45
|
+
if context.velox_on_save_result_map is None:
|
|
46
|
+
raise SapioException("No Velox on save rule result map in context for OnSaveRuleHandler to parse.")
|
|
47
|
+
user = context if isinstance(context, SapioUser) else context.user
|
|
48
|
+
obj = cls.__instances.get(user)
|
|
49
|
+
if not obj:
|
|
50
|
+
obj = object.__new__(cls)
|
|
51
|
+
obj.__initialized = False
|
|
52
|
+
cls.__instances[user] = obj
|
|
53
|
+
return obj
|
|
54
|
+
|
|
36
55
|
def __init__(self, context: SapioWebhookContext):
|
|
56
|
+
if self.__initialized:
|
|
57
|
+
return
|
|
58
|
+
self.__initialized = True
|
|
59
|
+
|
|
37
60
|
if context.velox_on_save_result_map is None:
|
|
38
61
|
raise SapioException("No Velox on save rule result map in context for OnSaveRuleHandler to parse.")
|
|
39
62
|
self.__context = context
|
|
@@ -10,13 +10,19 @@ from sapiopylib.rest.pojo.Message import VeloxLogMessage, VeloxLogLevel
|
|
|
10
10
|
from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
|
|
11
11
|
from sapiopylib.rest.pojo.webhook.WebhookEnums import WebhookEndpointType
|
|
12
12
|
from sapiopylib.rest.pojo.webhook.WebhookResult import SapioWebhookResult
|
|
13
|
+
from sapiopylib.rest.utils.DataTypeCacheManager import DataTypeCacheManager
|
|
13
14
|
from sapiopylib.rest.utils.recordmodel.RecordModelManager import RecordModelManager, RecordModelInstanceManager, \
|
|
14
15
|
RecordModelRelationshipManager
|
|
15
16
|
from sapiopylib.rest.utils.recordmodel.ancestry import RecordModelAncestorManager
|
|
16
17
|
|
|
18
|
+
from sapiopycommons.callbacks.callback_util import CallbackUtil
|
|
19
|
+
from sapiopycommons.eln.experiment_handler import ExperimentHandler
|
|
17
20
|
from sapiopycommons.general.exceptions import SapioUserErrorException, SapioCriticalErrorException, \
|
|
18
|
-
SapioUserCancelledException
|
|
21
|
+
SapioUserCancelledException, SapioException
|
|
19
22
|
from sapiopycommons.general.sapio_links import SapioNavigationLinker
|
|
23
|
+
from sapiopycommons.recordmodel.record_handler import RecordHandler
|
|
24
|
+
from sapiopycommons.rules.eln_rule_handler import ElnRuleHandler
|
|
25
|
+
from sapiopycommons.rules.on_save_rule_handler import OnSaveRuleHandler
|
|
20
26
|
|
|
21
27
|
|
|
22
28
|
# FR-46064 - Initial port of PyWebhookUtils to sapiopycommons.
|
|
@@ -38,6 +44,12 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
|
|
|
38
44
|
# FR-46329: Add the ancestor manager to CommonsWebhookHandler.
|
|
39
45
|
an_man: RecordModelAncestorManager
|
|
40
46
|
|
|
47
|
+
dt_cache: DataTypeCacheManager
|
|
48
|
+
rec_handler: RecordHandler
|
|
49
|
+
callback: CallbackUtil
|
|
50
|
+
exp_handler: ExperimentHandler | None
|
|
51
|
+
rule_handler: OnSaveRuleHandler | ElnRuleHandler | None
|
|
52
|
+
|
|
41
53
|
def run(self, context: SapioWebhookContext) -> SapioWebhookResult:
|
|
42
54
|
self.user = context.user
|
|
43
55
|
self.context = context
|
|
@@ -50,11 +62,28 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
|
|
|
50
62
|
self.rel_man = self.rec_man.relationship_manager
|
|
51
63
|
self.an_man = RecordModelAncestorManager(self.rec_man)
|
|
52
64
|
|
|
65
|
+
self.dt_cache = DataTypeCacheManager(self.user)
|
|
66
|
+
self.rec_handler = RecordHandler(context)
|
|
67
|
+
self.callback = CallbackUtil(context)
|
|
68
|
+
if context.eln_experiment is not None:
|
|
69
|
+
self.exp_handler = ExperimentHandler(context)
|
|
70
|
+
else:
|
|
71
|
+
self.exp_handler = None
|
|
72
|
+
if self.is_on_save_rule():
|
|
73
|
+
self.rule_handler = OnSaveRuleHandler(context)
|
|
74
|
+
elif self.is_eln_rule():
|
|
75
|
+
self.rule_handler = ElnRuleHandler(context)
|
|
76
|
+
else:
|
|
77
|
+
self.rule_handler = None
|
|
78
|
+
|
|
53
79
|
# Wrap the execution of each webhook in a try/catch. If an exception occurs, handle any special sapiopycommons
|
|
54
80
|
# exceptions. Otherwise, return a generic message stating that an error occurred.
|
|
55
81
|
try:
|
|
56
82
|
self.initialize(context)
|
|
57
|
-
|
|
83
|
+
result = self.execute(context)
|
|
84
|
+
if result is None:
|
|
85
|
+
raise SapioException("Your execute function returned a None result! Don't forget your return statement!")
|
|
86
|
+
return result
|
|
58
87
|
except SapioUserErrorException as e:
|
|
59
88
|
return self.handle_user_error_exception(e)
|
|
60
89
|
except SapioCriticalErrorException as e:
|
|
@@ -138,7 +167,7 @@ class CommonsWebhookHandler(AbstractWebhookHandler):
|
|
|
138
167
|
result: SapioWebhookResult | None = self.handle_any_exception(e)
|
|
139
168
|
if result is not None:
|
|
140
169
|
return result
|
|
141
|
-
return SapioWebhookResult(False
|
|
170
|
+
return SapioWebhookResult(False)
|
|
142
171
|
|
|
143
172
|
# noinspection PyMethodMayBeStatic,PyUnusedLocal
|
|
144
173
|
def handle_any_exception(self, e: Exception) -> SapioWebhookResult | None:
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import traceback
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from flask import request
|
|
7
|
+
from sapiopylib.rest.User import SapioUser
|
|
8
|
+
from sapiopylib.rest.WebhookService import AbstractWebhookHandler
|
|
9
|
+
from sapiopylib.rest.pojo.webhook.WebhookResult import SapioWebhookResult
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AbstractWebserviceHandler(AbstractWebhookHandler):
|
|
13
|
+
"""
|
|
14
|
+
A base class for constructing "webservice" endpoints on your webhook server. These are endpoints that can be
|
|
15
|
+
communicated with by external sources without needing to format the payload JSON in the webhook context format that
|
|
16
|
+
webhook handlers expect.
|
|
17
|
+
|
|
18
|
+
The entire payload JSON is sent to the run method of this class. It is up to the run method to determine how
|
|
19
|
+
this JSON should be parsed. In order to communicate with a Sapio system, a SapioUser object must be able to be
|
|
20
|
+
defined using the payload. Functions have been provided for constructing users with various authentication methods.
|
|
21
|
+
|
|
22
|
+
Since this extends AbstractWebhookHandler, you can still register endpoints from this class in the same way you
|
|
23
|
+
would normal webhook endpoints.
|
|
24
|
+
"""
|
|
25
|
+
def post(self) -> dict[str, Any]:
|
|
26
|
+
"""
|
|
27
|
+
Internal method to be executed to translate incoming requests.
|
|
28
|
+
"""
|
|
29
|
+
# noinspection PyBroadException
|
|
30
|
+
try:
|
|
31
|
+
return self.run(request.json).to_json()
|
|
32
|
+
except Exception:
|
|
33
|
+
print('Error occurred while running webservice custom logic. See traceback.', file=sys.stderr)
|
|
34
|
+
traceback.print_exc()
|
|
35
|
+
return SapioWebhookResult(False, display_text="Error occurred during webservice execution.").to_json()
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def run(self, payload: dict[str, Any]) -> SapioWebhookResult:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
def basic_auth(self, url: str, username: str, password: str) -> SapioUser:
|
|
42
|
+
"""
|
|
43
|
+
:param url: The URL of the Sapio system that requests from this user will be sent to.
|
|
44
|
+
Must end in /webservice/api
|
|
45
|
+
:param username: The username to authenticate requests with.
|
|
46
|
+
:param password: The password to authenticate requests with.
|
|
47
|
+
:return: A SapioUser that will authenticate requests using basic auth.
|
|
48
|
+
"""
|
|
49
|
+
return SapioUser(url, self.verify_sapio_cert, self.client_timeout_seconds, username=username, password=password)
|
|
50
|
+
|
|
51
|
+
def api_token_auth(self, url: str, api_token: str) -> SapioUser:
|
|
52
|
+
"""
|
|
53
|
+
:param url: The URL of the Sapio system that requests from this user will be sent to.
|
|
54
|
+
Must end in /webservice/api
|
|
55
|
+
:param api_token: The API token to authenticate requests with.
|
|
56
|
+
:return: A SapioUser that will authenticate requests using an API token.
|
|
57
|
+
"""
|
|
58
|
+
return SapioUser(url, self.verify_sapio_cert, self.client_timeout_seconds, api_token=api_token)
|
|
59
|
+
|
|
60
|
+
def bearer_token_auth(self, url: str, bearer_token: str) -> SapioUser:
|
|
61
|
+
"""
|
|
62
|
+
:param url: The URL of the Sapio system that requests from this user will be sent to.
|
|
63
|
+
Must end in /webservice/api
|
|
64
|
+
:param bearer_token: The bearer token to authenticate requests with.
|
|
65
|
+
:return: A SapioUser that will authenticate requests using a bearer token.
|
|
66
|
+
"""
|
|
67
|
+
return SapioUser(url, self.verify_sapio_cert, self.client_timeout_seconds, bearer_token=bearer_token)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: sapiopycommons
|
|
3
|
-
Version: 2024.8.
|
|
3
|
+
Version: 2024.8.19a305
|
|
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,45 +1,50 @@
|
|
|
1
1
|
sapiopycommons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
sapiopycommons/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
sapiopycommons/callbacks/callback_util.py,sha256=
|
|
3
|
+
sapiopycommons/callbacks/callback_util.py,sha256=E9-PIO8NO2BBltd6Dy4eaha9u39V636NjM7IXR0TP_Y,63539
|
|
4
4
|
sapiopycommons/chem/IndigoMolecules.py,sha256=QqFDi9CKERj6sn_ZwVcS2xZq4imlkaTeCrpq1iNcEJA,1992
|
|
5
5
|
sapiopycommons/chem/Molecules.py,sha256=t80IsQBPJ9mwE8ZxnWomAGrZDhdsOuPvLaTPb_N6jGU,8639
|
|
6
6
|
sapiopycommons/chem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
sapiopycommons/customreport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
sapiopycommons/customreport/column_builder.py,sha256=sS_wZYOR72rs3syTNjwCVP4h8M8N0b0burkTxFQItVU,3019
|
|
9
|
+
sapiopycommons/customreport/custom_report_builder.py,sha256=o2O89OrWPm0OYS8Ux6EKZTg6hcUzfz3ZxAgnzJg1wEw,6601
|
|
10
|
+
sapiopycommons/customreport/term_builder.py,sha256=mOnm2c6EUOQVgoURmXHzqSGKuQjP2gswJmWecoeC4yw,16606
|
|
7
11
|
sapiopycommons/datatype/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
12
|
sapiopycommons/datatype/attachment_util.py,sha256=23JQ4avSmBBJdCv95LVj31x8rUCclzB_DYFBijH0NII,3708
|
|
9
13
|
sapiopycommons/eln/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
sapiopycommons/eln/experiment_handler.py,sha256=
|
|
14
|
+
sapiopycommons/eln/experiment_handler.py,sha256=9wNRjwWHekKSAA_1io5WW1iDSezDXidWKAVn0bQZkbc,66161
|
|
11
15
|
sapiopycommons/eln/experiment_report_util.py,sha256=FTLw-6SLAMeoWTOO-qhGROE9g54pZdyoQJIhiIzlwGw,7848
|
|
12
16
|
sapiopycommons/eln/plate_designer.py,sha256=FYJfhhNq8hdfuXgDYOYHy6g0m2zNwQXZWF_MTPzElDg,7184
|
|
13
17
|
sapiopycommons/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
18
|
sapiopycommons/files/complex_data_loader.py,sha256=8jgYF5iGDD6Abw8JRWLYxUWVwj4s4pH5HylyxEGFZSU,1471
|
|
15
19
|
sapiopycommons/files/file_bridge.py,sha256=njx_5Z3tvQUNW4mPazQerL8lopLAFStIByHWHJ7m5ug,6220
|
|
16
|
-
sapiopycommons/files/file_bridge_handler.py,sha256=
|
|
20
|
+
sapiopycommons/files/file_bridge_handler.py,sha256=KCk8JTojMeHWgARqVlhOBXf-OInGuc6NW2vXG5Xf-7w,14352
|
|
17
21
|
sapiopycommons/files/file_data_handler.py,sha256=3-guAdhJdeJWAFq1a27ijspkO7uMMZ6CapMCD_6o4jA,36746
|
|
18
|
-
sapiopycommons/files/file_util.py,sha256=
|
|
19
|
-
sapiopycommons/files/file_validator.py,sha256=
|
|
20
|
-
sapiopycommons/files/file_writer.py,sha256=
|
|
22
|
+
sapiopycommons/files/file_util.py,sha256=ZrgoGwHHfPdL5KHkGwlrEHJqGpttmZzRkGQCXdLjra8,28284
|
|
23
|
+
sapiopycommons/files/file_validator.py,sha256=HaZF4rqGXspkkyLPol5KAWvV7djuodzrIxg2EnNYoLw,28858
|
|
24
|
+
sapiopycommons/files/file_writer.py,sha256=96Xl8TTT46Krxe_J8rmmlEMtel4nzZB961f5Yqtl1-I,17616
|
|
21
25
|
sapiopycommons/general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
26
|
sapiopycommons/general/accession_service.py,sha256=HYgyOsH_UaoRnoury-c2yTW8SeG4OtjLemdpCzoV4R8,13484
|
|
23
|
-
sapiopycommons/general/aliases.py,sha256=
|
|
27
|
+
sapiopycommons/general/aliases.py,sha256=bSj4-oU6UDY1Yvlhr19RWVlSJf9NhfZE42y-xzYC-lg,7575
|
|
24
28
|
sapiopycommons/general/audit_log.py,sha256=YwBkm1kT6faXtipcUpoOtME3FZaUM46OtCE6KIzAROQ,9038
|
|
25
29
|
sapiopycommons/general/custom_report_util.py,sha256=cLgIR5Fn3M9uyAtgfTYRv3JRk2SKNevnsb_R5zidSYs,15557
|
|
26
30
|
sapiopycommons/general/exceptions.py,sha256=DOlLKnpCatxQF-lVCToa8ryJgusWLvip6N_1ALN00QE,1679
|
|
27
|
-
sapiopycommons/general/popup_util.py,sha256
|
|
31
|
+
sapiopycommons/general/popup_util.py,sha256=vqLSiCry4YClSKIdnk56CIKioBOtawGVmPi2iS1LOIc,31763
|
|
28
32
|
sapiopycommons/general/sapio_links.py,sha256=UlqB09wmgDgbQiB8d3mEj7rxW_GMIXz3j3RlvADNt_A,2475
|
|
29
33
|
sapiopycommons/general/storage_util.py,sha256=ovmK_jN7v09BoX07XxwShpBUC5WYQOM7dbKV_VeLXJU,8892
|
|
30
|
-
sapiopycommons/general/time_util.py,sha256=
|
|
34
|
+
sapiopycommons/general/time_util.py,sha256=sXThADCRAQDWYDD9C5CdhcKYIt3qOaVNyZfGBR7HW9A,8701
|
|
31
35
|
sapiopycommons/multimodal/multimodal.py,sha256=A1QsC8QTPmgZyPr7KtMbPRedn2Ie4WIErodUvQ9otgU,6724
|
|
32
36
|
sapiopycommons/multimodal/multimodal_data.py,sha256=zqgYHO-ULaPKV0POFWZVY9N-Sfm1RQWwdsfwFxe5DjI,15038
|
|
33
37
|
sapiopycommons/processtracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
38
|
sapiopycommons/processtracking/endpoints.py,sha256=g5h_uCVByqacYm9zWAz8TyAdRsGfaO2o0b5RSJdOaSA,10926
|
|
35
39
|
sapiopycommons/recordmodel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
-
sapiopycommons/recordmodel/record_handler.py,sha256=
|
|
40
|
+
sapiopycommons/recordmodel/record_handler.py,sha256=Xr0fRkUW5nAY4NVKwkQR81WF23POX_FtVp4pP7QMgZ0,60188
|
|
37
41
|
sapiopycommons/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
sapiopycommons/rules/eln_rule_handler.py,sha256=
|
|
39
|
-
sapiopycommons/rules/on_save_rule_handler.py,sha256=
|
|
42
|
+
sapiopycommons/rules/eln_rule_handler.py,sha256=QkK3TD9fxdw0usWik0eIRlcIDHaN3LgfR8laOElCA8A,10280
|
|
43
|
+
sapiopycommons/rules/on_save_rule_handler.py,sha256=STJ8QFeZGk5yUCPgiJ_Q0Trpn6rfXTFum0s2ECLfxHU,10047
|
|
40
44
|
sapiopycommons/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
|
-
sapiopycommons/webhook/webhook_handlers.py,sha256=
|
|
42
|
-
sapiopycommons
|
|
43
|
-
sapiopycommons-2024.8.
|
|
44
|
-
sapiopycommons-2024.8.
|
|
45
|
-
sapiopycommons-2024.8.
|
|
45
|
+
sapiopycommons/webhook/webhook_handlers.py,sha256=jwc4xu-wwl8haS5k1dENZ1UIYK9GQk74TAo3CGxMW9U,16583
|
|
46
|
+
sapiopycommons/webhook/webservice_handlers.py,sha256=1J56zFI0pWl5MHoNTznvcZumITXgAHJMluj8-2BqYEw,3315
|
|
47
|
+
sapiopycommons-2024.8.19a305.dist-info/METADATA,sha256=RW_yNejA3ktxl81ktZx88D5R4g3Iq50c2gy6Svd5YBQ,3176
|
|
48
|
+
sapiopycommons-2024.8.19a305.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
49
|
+
sapiopycommons-2024.8.19a305.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
|
|
50
|
+
sapiopycommons-2024.8.19a305.dist-info/RECORD,,
|
|
File without changes
|
{sapiopycommons-2024.8.7a303.dist-info → sapiopycommons-2024.8.19a305.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|