sapiopycommons 2024.8.19a305__tar.gz → 2024.8.26a307__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.19a305 → sapiopycommons-2024.8.26a307}/PKG-INFO +1 -1
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/pyproject.toml +1 -1
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/callbacks/callback_util.py +14 -15
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/customreport/term_builder.py +5 -2
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/datatype/attachment_util.py +6 -14
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/eln/experiment_handler.py +24 -17
- sapiopycommons-2024.8.26a307/src/sapiopycommons/eln/experiment_report_util.py +118 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/files/complex_data_loader.py +4 -3
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/files/file_bridge.py +14 -13
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/files/file_bridge_handler.py +8 -7
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/files/file_data_handler.py +2 -5
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/files/file_validator.py +7 -7
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/general/aliases.py +54 -1
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/general/audit_log.py +19 -23
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/general/custom_report_util.py +34 -32
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/general/sapio_links.py +4 -2
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/multimodal/multimodal_data.py +0 -1
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/processtracking/endpoints.py +22 -22
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/recordmodel/record_handler.py +119 -65
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/rules/eln_rule_handler.py +5 -3
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/rules/on_save_rule_handler.py +5 -3
- sapiopycommons-2024.8.19a305/src/sapiopycommons/eln/experiment_report_util.py +0 -214
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/.gitignore +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/LICENSE +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/README.md +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/__init__.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/callbacks/__init__.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/chem/IndigoMolecules.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/chem/Molecules.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/chem/__init__.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/customreport/__init__.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/customreport/column_builder.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/customreport/custom_report_builder.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/datatype/__init__.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/eln/__init__.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/eln/plate_designer.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/files/__init__.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/files/file_util.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/files/file_writer.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/general/__init__.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/general/accession_service.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/general/exceptions.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/general/popup_util.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/general/storage_util.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/general/time_util.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/multimodal/multimodal.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/processtracking/__init__.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/recordmodel/__init__.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/rules/__init__.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/webhook/__init__.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/webhook/webhook_handlers.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/src/sapiopycommons/webhook/webservice_handlers.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/tests/_do_not_add_init_py_here +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/tests/accession_test.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/tests/bio_reg_test.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/tests/chem_test.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/tests/data_type_models.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/tests/kappa.chains.fasta +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/tests/mafft_test.py +0 -0
- {sapiopycommons-2024.8.19a305 → sapiopycommons-2024.8.26a307}/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.26a307
|
|
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,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import io
|
|
4
|
-
from typing import Any
|
|
5
4
|
from weakref import WeakValueDictionary
|
|
6
5
|
|
|
7
6
|
from sapiopylib.rest.ClientCallbackService import ClientCallback
|
|
@@ -18,7 +17,6 @@ from sapiopylib.rest.pojo.webhook.ClientCallbackRequest import OptionDialogReque
|
|
|
18
17
|
DataRecordDialogRequest, InputSelectionRequest, FilePromptRequest, MultiFilePromptRequest, \
|
|
19
18
|
TempTableSelectionRequest, DisplayPopupRequest, PopupType
|
|
20
19
|
from sapiopylib.rest.pojo.webhook.ClientCallbackResult import ESigningResponsePojo
|
|
21
|
-
from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
|
|
22
20
|
from sapiopylib.rest.pojo.webhook.WebhookEnums import FormAccessLevel, ScanToSelectCriteria, SearchType
|
|
23
21
|
from sapiopylib.rest.utils.DataTypeCacheManager import DataTypeCacheManager
|
|
24
22
|
from sapiopylib.rest.utils.FormBuilder import FormBuilder
|
|
@@ -26,7 +24,8 @@ from sapiopylib.rest.utils.recorddatasinks import InMemoryRecordDataSink
|
|
|
26
24
|
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
27
25
|
|
|
28
26
|
from sapiopycommons.files.file_util import FileUtil
|
|
29
|
-
from sapiopycommons.general.aliases import FieldMap, SapioRecord, AliasUtil, RecordIdentifier
|
|
27
|
+
from sapiopycommons.general.aliases import FieldMap, SapioRecord, AliasUtil, RecordIdentifier, FieldValue, \
|
|
28
|
+
UserIdentifier
|
|
30
29
|
from sapiopycommons.general.custom_report_util import CustomReportUtil
|
|
31
30
|
from sapiopycommons.general.exceptions import SapioUserCancelledException, SapioException, SapioUserErrorException
|
|
32
31
|
from sapiopycommons.recordmodel.record_handler import RecordHandler
|
|
@@ -42,11 +41,11 @@ class CallbackUtil:
|
|
|
42
41
|
__instances: WeakValueDictionary[SapioUser, CallbackUtil] = WeakValueDictionary()
|
|
43
42
|
__initialized: bool
|
|
44
43
|
|
|
45
|
-
def __new__(cls, context:
|
|
44
|
+
def __new__(cls, context: UserIdentifier):
|
|
46
45
|
"""
|
|
47
46
|
:param context: The current webhook context or a user object to send requests from.
|
|
48
47
|
"""
|
|
49
|
-
user =
|
|
48
|
+
user = AliasUtil.to_sapio_user(context)
|
|
50
49
|
obj = cls.__instances.get(user)
|
|
51
50
|
if not obj:
|
|
52
51
|
obj = object.__new__(cls)
|
|
@@ -54,7 +53,7 @@ class CallbackUtil:
|
|
|
54
53
|
cls.__instances[user] = obj
|
|
55
54
|
return obj
|
|
56
55
|
|
|
57
|
-
def __init__(self, context:
|
|
56
|
+
def __init__(self, context: UserIdentifier):
|
|
58
57
|
"""
|
|
59
58
|
:param context: The current webhook context or a user object to send requests from.
|
|
60
59
|
"""
|
|
@@ -62,7 +61,7 @@ class CallbackUtil:
|
|
|
62
61
|
return
|
|
63
62
|
self.__initialized = True
|
|
64
63
|
|
|
65
|
-
self.user =
|
|
64
|
+
self.user = AliasUtil.to_sapio_user(context)
|
|
66
65
|
self.callback = DataMgmtServer.get_client_callback(self.user)
|
|
67
66
|
self.dt_cache = DataTypeCacheManager(self.user)
|
|
68
67
|
self.width_pixels = None
|
|
@@ -281,7 +280,7 @@ class CallbackUtil:
|
|
|
281
280
|
modifier = FieldModifier(visible=True, editable=editable)
|
|
282
281
|
|
|
283
282
|
# Build the form using only those fields that are desired.
|
|
284
|
-
values: dict[str,
|
|
283
|
+
values: dict[str, FieldValue] = {}
|
|
285
284
|
builder = FormBuilder(data_type, type_def.display_name, type_def.plural_display_name)
|
|
286
285
|
for field_name in fields:
|
|
287
286
|
field_def = field_defs.get(field_name)
|
|
@@ -303,7 +302,7 @@ class CallbackUtil:
|
|
|
303
302
|
raise SapioUserCancelledException()
|
|
304
303
|
return response
|
|
305
304
|
|
|
306
|
-
def input_dialog(self, title: str, msg: str, field: AbstractVeloxFieldDefinition) ->
|
|
305
|
+
def input_dialog(self, title: str, msg: str, field: AbstractVeloxFieldDefinition) -> FieldValue:
|
|
307
306
|
"""
|
|
308
307
|
Create an input dialog where the user must input data for a singular field.
|
|
309
308
|
|
|
@@ -314,7 +313,7 @@ class CallbackUtil:
|
|
|
314
313
|
"""
|
|
315
314
|
request = InputDialogCriteria(title, msg, field,
|
|
316
315
|
width_in_pixels=self.width_pixels, width_percentage=self.width_percent)
|
|
317
|
-
response:
|
|
316
|
+
response: FieldValue | None = self.callback.show_input_dialog(request)
|
|
318
317
|
if response is None:
|
|
319
318
|
raise SapioUserCancelledException()
|
|
320
319
|
return response
|
|
@@ -606,10 +605,10 @@ class CallbackUtil:
|
|
|
606
605
|
field_names.append(name)
|
|
607
606
|
|
|
608
607
|
# Get the values for each row.
|
|
609
|
-
values: list[dict[str,
|
|
608
|
+
values: list[dict[str, FieldValue]] = []
|
|
610
609
|
for row in row_contents:
|
|
611
610
|
# The final values for this row:
|
|
612
|
-
row_values: dict[str,
|
|
611
|
+
row_values: dict[str, FieldValue] = {}
|
|
613
612
|
|
|
614
613
|
# Map the records for this row by their data type. If a field map is provided, its data type is Default.
|
|
615
614
|
row_records: dict[str, SapioRecord | FieldMap] = {}
|
|
@@ -901,7 +900,7 @@ class CallbackUtil:
|
|
|
901
900
|
return response
|
|
902
901
|
|
|
903
902
|
def request_file(self, title: str, exts: list[str] | None = None,
|
|
904
|
-
show_image_editor: bool = False, show_camera_button: bool = False) ->
|
|
903
|
+
show_image_editor: bool = False, show_camera_button: bool = False) -> tuple[str, bytes]:
|
|
905
904
|
"""
|
|
906
905
|
Request a single file from the user.
|
|
907
906
|
|
|
@@ -934,7 +933,7 @@ class CallbackUtil:
|
|
|
934
933
|
return file_path, sink.data
|
|
935
934
|
|
|
936
935
|
def request_files(self, title: str, exts: list[str] | None = None,
|
|
937
|
-
show_image_editor: bool = False, show_camera_button: bool = False):
|
|
936
|
+
show_image_editor: bool = False, show_camera_button: bool = False) -> dict[str, bytes]:
|
|
938
937
|
"""
|
|
939
938
|
Request multiple files from the user.
|
|
940
939
|
|
|
@@ -965,7 +964,7 @@ class CallbackUtil:
|
|
|
965
964
|
return ret_dict
|
|
966
965
|
|
|
967
966
|
@staticmethod
|
|
968
|
-
def __verify_file(file_path: str, file_bytes: bytes, allowed_extensions: list[str]):
|
|
967
|
+
def __verify_file(file_path: str, file_bytes: bytes, allowed_extensions: list[str]) -> None:
|
|
969
968
|
"""
|
|
970
969
|
Verify that the provided file was read (i.e. the file path and file bytes aren't None or empty) and that it
|
|
971
970
|
has the correct file extension. Raises a user error exception if something about the file is incorrect.
|
|
@@ -18,7 +18,7 @@ AND = CompositeTermOperation.AND_OPERATOR
|
|
|
18
18
|
OR = CompositeTermOperation.OR_OPERATOR
|
|
19
19
|
|
|
20
20
|
# Forms that field term values can take.
|
|
21
|
-
TermValue = str | int | float | bool | Iterable
|
|
21
|
+
TermValue = str | int | float | bool | Iterable | None
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class TermBuilder:
|
|
@@ -281,9 +281,12 @@ class TermBuilder:
|
|
|
281
281
|
"""
|
|
282
282
|
# If the given value is already a string, then nothing needs to be done with it.
|
|
283
283
|
if not isinstance(value, str):
|
|
284
|
+
# If the given value is None, then use an empty string for the search instead.
|
|
285
|
+
if value is None:
|
|
286
|
+
value = ""
|
|
284
287
|
# If the given value is an iterable object, then the return value is the contents of that iterable
|
|
285
288
|
# in a comma separated list surrounded by curly braces.
|
|
286
|
-
|
|
289
|
+
elif isinstance(value, Iterable):
|
|
287
290
|
# When converting a list of values to a string, values in the list which are already strings should be
|
|
288
291
|
# put in quotation marks so that strings that contain commas do not get split up. All other value
|
|
289
292
|
# types can be simply converted to a string, though.
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import io
|
|
2
2
|
|
|
3
3
|
from sapiopylib.rest.DataMgmtService import DataMgmtServer
|
|
4
|
-
from sapiopylib.rest.User import SapioUser
|
|
5
|
-
from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
|
|
6
4
|
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
7
5
|
|
|
8
|
-
from sapiopycommons.general.aliases import AliasUtil, SapioRecord
|
|
6
|
+
from sapiopycommons.general.aliases import AliasUtil, SapioRecord, UserIdentifier
|
|
9
7
|
from sapiopycommons.general.exceptions import SapioException
|
|
10
8
|
from sapiopycommons.recordmodel.record_handler import RecordHandler
|
|
11
9
|
|
|
@@ -13,7 +11,7 @@ from sapiopycommons.recordmodel.record_handler import RecordHandler
|
|
|
13
11
|
# FR-46064 - Initial port of PyWebhookUtils to sapiopycommons.
|
|
14
12
|
class AttachmentUtil:
|
|
15
13
|
@staticmethod
|
|
16
|
-
def get_attachment_bytes(context:
|
|
14
|
+
def get_attachment_bytes(context: UserIdentifier, attachment: SapioRecord) -> bytes:
|
|
17
15
|
"""
|
|
18
16
|
Get the data bytes for the given attachment record. Makes a webservice call to retrieve the data.
|
|
19
17
|
|
|
@@ -22,10 +20,7 @@ class AttachmentUtil:
|
|
|
22
20
|
:return: The bytes for the attachment's file data.
|
|
23
21
|
"""
|
|
24
22
|
attachment = AliasUtil.to_data_record(attachment)
|
|
25
|
-
|
|
26
|
-
dr_man = context.data_record_manager
|
|
27
|
-
else:
|
|
28
|
-
dr_man = DataMgmtServer.get_data_record_manager(context)
|
|
23
|
+
dr_man = DataMgmtServer.get_data_record_manager(AliasUtil.to_sapio_user(context))
|
|
29
24
|
with io.BytesIO() as data_sink:
|
|
30
25
|
def consume_data(chunk: bytes):
|
|
31
26
|
data_sink.write(chunk)
|
|
@@ -36,7 +31,7 @@ class AttachmentUtil:
|
|
|
36
31
|
return file_bytes
|
|
37
32
|
|
|
38
33
|
@staticmethod
|
|
39
|
-
def set_attachment_bytes(context:
|
|
34
|
+
def set_attachment_bytes(context: UserIdentifier, attachment: SapioRecord,
|
|
40
35
|
file_name: str, file_bytes: bytes) -> None:
|
|
41
36
|
"""
|
|
42
37
|
Set the attachment data for a given attachment record. Makes a webservice call to set the data.
|
|
@@ -50,15 +45,12 @@ class AttachmentUtil:
|
|
|
50
45
|
raise SapioException("Provided record cannot have its attachment data set, as it does not exist in the "
|
|
51
46
|
"system yet.")
|
|
52
47
|
attachment = AliasUtil.to_data_record(attachment)
|
|
53
|
-
|
|
54
|
-
dr_man = context.data_record_manager
|
|
55
|
-
else:
|
|
56
|
-
dr_man = DataMgmtServer.get_data_record_manager(context)
|
|
48
|
+
dr_man = DataMgmtServer.get_data_record_manager(AliasUtil.to_sapio_user(context))
|
|
57
49
|
with io.BytesIO(file_bytes) as stream:
|
|
58
50
|
dr_man.set_attachment_data(attachment, file_name, stream)
|
|
59
51
|
|
|
60
52
|
@staticmethod
|
|
61
|
-
def create_attachment(context:
|
|
53
|
+
def create_attachment(context: UserIdentifier, file_name: str, file_bytes: bytes,
|
|
62
54
|
wrapper_type: type[WrappedType]) -> WrappedType:
|
|
63
55
|
"""
|
|
64
56
|
Create an attachment data type and initialize its attachment bytes at the same time.
|
|
@@ -23,7 +23,8 @@ from sapiopylib.rest.utils.recordmodel.RecordModelManager import RecordModelInst
|
|
|
23
23
|
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
24
24
|
from sapiopylib.rest.utils.recordmodel.properties import Child
|
|
25
25
|
|
|
26
|
-
from sapiopycommons.general.aliases import AliasUtil, SapioRecord, ExperimentIdentifier,
|
|
26
|
+
from sapiopycommons.general.aliases import AliasUtil, SapioRecord, ExperimentIdentifier, UserIdentifier, \
|
|
27
|
+
DataTypeIdentifier, RecordModel
|
|
27
28
|
from sapiopycommons.general.exceptions import SapioException
|
|
28
29
|
|
|
29
30
|
Step = str | ElnEntryStep
|
|
@@ -87,8 +88,7 @@ class ExperimentHandler:
|
|
|
87
88
|
__instances: WeakValueDictionary[str, ExperimentHandler] = WeakValueDictionary()
|
|
88
89
|
__initialized: bool
|
|
89
90
|
|
|
90
|
-
def __new__(cls, context:
|
|
91
|
-
experiment: ExperimentIdentifier | SapioRecord | None = None):
|
|
91
|
+
def __new__(cls, context: UserIdentifier, experiment: ExperimentIdentifier | SapioRecord | None = None):
|
|
92
92
|
"""
|
|
93
93
|
:param context: The current webhook context or a user object to send requests from.
|
|
94
94
|
:param experiment: If an experiment is provided that is separate from the experiment that is in the context,
|
|
@@ -106,8 +106,7 @@ class ExperimentHandler:
|
|
|
106
106
|
cls.__instances[key] = obj
|
|
107
107
|
return obj
|
|
108
108
|
|
|
109
|
-
def __init__(self, context:
|
|
110
|
-
experiment: ExperimentIdentifier | SapioRecord | None = None):
|
|
109
|
+
def __init__(self, context: UserIdentifier, experiment: ExperimentIdentifier | SapioRecord | None = None):
|
|
111
110
|
"""
|
|
112
111
|
Initialization will throw an exception if there is no ELN Experiment in the provided context and no experiment
|
|
113
112
|
is provided.
|
|
@@ -146,8 +145,7 @@ class ExperimentHandler:
|
|
|
146
145
|
self.__steps.update({entry.entry_name: ElnEntryStep(self.__protocol, entry)})
|
|
147
146
|
|
|
148
147
|
@staticmethod
|
|
149
|
-
def __parse_params(context:
|
|
150
|
-
experiment: ExperimentIdentifier | SapioRecord | None = None) \
|
|
148
|
+
def __parse_params(context: UserIdentifier, experiment: ExperimentIdentifier | SapioRecord | None = None) \
|
|
151
149
|
-> tuple[SapioUser, SapioWebhookContext | None, ElnExperiment]:
|
|
152
150
|
if isinstance(context, SapioWebhookContext):
|
|
153
151
|
user = context.user
|
|
@@ -332,11 +330,7 @@ class ExperimentHandler:
|
|
|
332
330
|
:return: The data record for this experiment. None if it has no record.
|
|
333
331
|
"""
|
|
334
332
|
if not hasattr(self, "_ExperimentHandler__exp_record"):
|
|
335
|
-
|
|
336
|
-
dt = self.__eln_exp.experiment_data_type_name
|
|
337
|
-
results = drm.query_data_records_by_id(dt, [self.__eln_exp.experiment_record_id]).result_list
|
|
338
|
-
# PR-46504: Set the exp_record to None if there are no results.
|
|
339
|
-
self.__exp_record = results[0] if results else None
|
|
333
|
+
self.__exp_record = self.__protocol.get_record()
|
|
340
334
|
if self.__exp_record is None and exception_on_none:
|
|
341
335
|
raise SapioException(f"Experiment record not found for experiment with ID {self.__exp_id}.")
|
|
342
336
|
return self.__exp_record
|
|
@@ -536,7 +530,7 @@ class ExperimentHandler:
|
|
|
536
530
|
ret_list.append(step)
|
|
537
531
|
return ret_list
|
|
538
532
|
|
|
539
|
-
def get_all_steps(self, data_type:
|
|
533
|
+
def get_all_steps(self, data_type: DataTypeIdentifier | None = None) -> list[ElnEntryStep]:
|
|
540
534
|
"""
|
|
541
535
|
Get a list of every entry in the experiment. Optionally filter the returned entries by a data type.
|
|
542
536
|
|
|
@@ -552,8 +546,7 @@ class ExperimentHandler:
|
|
|
552
546
|
all_steps: list[ElnEntryStep] = self.__protocol.get_sorted_step_list()
|
|
553
547
|
if data_type is None:
|
|
554
548
|
return all_steps
|
|
555
|
-
|
|
556
|
-
data_type: str = data_type.get_wrapper_data_type_name()
|
|
549
|
+
data_type: str = AliasUtil.to_data_type_name(data_type)
|
|
557
550
|
return [x for x in all_steps if data_type in x.get_data_type_names()]
|
|
558
551
|
|
|
559
552
|
def get_step_records(self, step: Step) -> list[DataRecord]:
|
|
@@ -605,6 +598,10 @@ class ExperimentHandler:
|
|
|
605
598
|
The records may be provided as either DataRecords, PyRecordModels, or WrappedRecordModels.
|
|
606
599
|
"""
|
|
607
600
|
step = self.__to_eln_step(step)
|
|
601
|
+
dt: str = AliasUtil.to_singular_data_type_name(records)
|
|
602
|
+
if dt != step.get_data_type_names()[0]:
|
|
603
|
+
raise SapioException(f"Cannot add {dt} records to entry {step.get_name()} of type "
|
|
604
|
+
f"{step.get_data_type_names()[0]}.")
|
|
608
605
|
step.add_records(AliasUtil.to_data_records(records))
|
|
609
606
|
|
|
610
607
|
def remove_step_records(self, step: Step, records: Iterable[SapioRecord]) -> None:
|
|
@@ -623,6 +620,10 @@ class ExperimentHandler:
|
|
|
623
620
|
The records may be provided as either DataRecords, PyRecordModels, or WrappedRecordModels.
|
|
624
621
|
"""
|
|
625
622
|
step = self.__to_eln_step(step)
|
|
623
|
+
dt: str = AliasUtil.to_singular_data_type_name(records)
|
|
624
|
+
if dt != step.get_data_type_names()[0]:
|
|
625
|
+
raise SapioException(f"Cannot remove {dt} records from entry {step.get_name()} of type "
|
|
626
|
+
f"{step.get_data_type_names()[0]}.")
|
|
626
627
|
step.remove_records(AliasUtil.to_data_records(records))
|
|
627
628
|
|
|
628
629
|
def set_step_records(self, step: Step, records: Iterable[SapioRecord]) -> None:
|
|
@@ -646,6 +647,10 @@ class ExperimentHandler:
|
|
|
646
647
|
The records may be provided as either DataRecords, PyRecordModels, or WrappedRecordModels.
|
|
647
648
|
"""
|
|
648
649
|
step = self.__to_eln_step(step)
|
|
650
|
+
dt: str = AliasUtil.to_singular_data_type_name(records)
|
|
651
|
+
if dt != step.get_data_type_names()[0]:
|
|
652
|
+
raise SapioException(f"Cannot set {dt} records for entry {step.get_name()} of type "
|
|
653
|
+
f"{step.get_data_type_names()[0]}.")
|
|
649
654
|
step.set_records(AliasUtil.to_data_records(records))
|
|
650
655
|
|
|
651
656
|
# FR-46496 - Provide alias of set_step_records for use with form entries.
|
|
@@ -733,8 +738,10 @@ class ExperimentHandler:
|
|
|
733
738
|
dt: str = step.get_data_type_names()[0]
|
|
734
739
|
if not ElnBaseDataType.is_eln_type(dt):
|
|
735
740
|
raise SapioException("The provided step is not an ELN data type entry.")
|
|
736
|
-
|
|
737
|
-
|
|
741
|
+
record_dt: str = AliasUtil.to_singular_data_type_name(records)
|
|
742
|
+
if record_dt != dt:
|
|
743
|
+
raise SapioException(f"Cannot remove {dt} records from entry {step.get_name()} of type "
|
|
744
|
+
f"{step.get_data_type_names()[0]}.")
|
|
738
745
|
# If any rows were provided as data records, turn them into record models before deleting them, as otherwise
|
|
739
746
|
# this function would need to make a webservice call to do the deletion.
|
|
740
747
|
data_records: list[DataRecord] = []
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from sapiopylib.rest.User import SapioUser
|
|
2
|
+
from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
|
|
3
|
+
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
4
|
+
|
|
5
|
+
from sapiopycommons.customreport.custom_report_builder import CustomReportBuilder
|
|
6
|
+
from sapiopycommons.customreport.term_builder import TermBuilder
|
|
7
|
+
from sapiopycommons.general.aliases import SapioRecord, UserIdentifier, AliasUtil
|
|
8
|
+
from sapiopycommons.general.custom_report_util import CustomReportUtil
|
|
9
|
+
from sapiopycommons.recordmodel.record_handler import RecordHandler
|
|
10
|
+
|
|
11
|
+
_NOTEBOOK_ID = "EXPERIMENTID"
|
|
12
|
+
_RECORD_ID = "RECORDID"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# FR-46908 - Provide a utility class that holds experiment related custom reports e.g. getting all the experiments
|
|
16
|
+
# that given records were used in or getting all records of a datatype used in given experiments.
|
|
17
|
+
class ExperimentReportUtil:
|
|
18
|
+
@staticmethod
|
|
19
|
+
def map_records_to_experiment_ids(context: UserIdentifier, records: list[SapioRecord]) \
|
|
20
|
+
-> dict[SapioRecord, list[int]]:
|
|
21
|
+
"""
|
|
22
|
+
Return a dictionary mapping each record to a list of ids of experiments that they were used in.
|
|
23
|
+
If a record wasn't used in any experiments then it will be mapped to an empty list.
|
|
24
|
+
|
|
25
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
26
|
+
:param records: a list of records of the same data type.
|
|
27
|
+
:return: a dictionary mapping each record to a list of ids of each experiment it was used in.
|
|
28
|
+
"""
|
|
29
|
+
if not records:
|
|
30
|
+
return {}
|
|
31
|
+
|
|
32
|
+
user: SapioUser = AliasUtil.to_sapio_user(context)
|
|
33
|
+
data_type_name: str = AliasUtil.to_singular_data_type_name(records)
|
|
34
|
+
|
|
35
|
+
record_ids = [record.record_id for record in records]
|
|
36
|
+
rows = ExperimentReportUtil.__get_record_experiment_relation_rows(user, data_type_name, record_ids=record_ids)
|
|
37
|
+
|
|
38
|
+
id_to_record: dict[int, SapioRecord] = RecordHandler.map_by_id(records)
|
|
39
|
+
record_to_exps: dict[SapioRecord, set[int]] = {record: set() for record in records}
|
|
40
|
+
for row in rows:
|
|
41
|
+
record_id: int = row[_RECORD_ID]
|
|
42
|
+
exp_id: int = row[_NOTEBOOK_ID]
|
|
43
|
+
record = id_to_record[record_id]
|
|
44
|
+
record_to_exps[record].add(exp_id)
|
|
45
|
+
|
|
46
|
+
return {record: list(exps) for record, exps in record_to_exps.items()}
|
|
47
|
+
|
|
48
|
+
@staticmethod
|
|
49
|
+
def map_experiments_to_records_of_type(context: UserIdentifier, exp_ids: list[int],
|
|
50
|
+
wrapper_type: type[WrappedType]) -> dict[int, list[WrappedType]]:
|
|
51
|
+
"""
|
|
52
|
+
Return a dictionary mapping each experiment id to a list of records of the given type that were used in each experiment.
|
|
53
|
+
If an experiment didn't use any records of the given type then it will be mapped to an empty list.
|
|
54
|
+
|
|
55
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
56
|
+
:param exp_ids: a list of experiment ids. These are specifically the Notebook Experiment ids which can be found in the title of the experiment.
|
|
57
|
+
:param wrapper_type: The record model wrapper to use, corresponds to which data type we will query for.
|
|
58
|
+
:return: a dictionary mapping each experiment id to a list of records of the given type that were used in that experiment.
|
|
59
|
+
"""
|
|
60
|
+
if not exp_ids:
|
|
61
|
+
return {}
|
|
62
|
+
|
|
63
|
+
user = AliasUtil.to_sapio_user(context)
|
|
64
|
+
record_handler = RecordHandler(user)
|
|
65
|
+
data_type_name: str = wrapper_type.get_wrapper_data_type_name()
|
|
66
|
+
|
|
67
|
+
rows = ExperimentReportUtil.__get_record_experiment_relation_rows(user, data_type_name, exp_ids=exp_ids)
|
|
68
|
+
record_ids: set[int] = {row[_RECORD_ID] for row in rows}
|
|
69
|
+
records = record_handler.query_models_by_id(wrapper_type, record_ids)
|
|
70
|
+
|
|
71
|
+
id_to_record: dict[int, WrappedType] = RecordHandler.map_by_id(records)
|
|
72
|
+
exp_to_records: dict[int, set[SapioRecord]] = {exp: set() for exp in exp_ids}
|
|
73
|
+
for row in rows:
|
|
74
|
+
record_id: int = row[_RECORD_ID]
|
|
75
|
+
exp_id: int = row[_NOTEBOOK_ID]
|
|
76
|
+
record = id_to_record[record_id]
|
|
77
|
+
exp_to_records[exp_id].add(record)
|
|
78
|
+
|
|
79
|
+
return {exp: list(records) for exp, records in exp_to_records.items()}
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def __get_record_experiment_relation_rows(user: SapioUser, data_type_name: str, record_ids: list[int] | None = None,
|
|
83
|
+
exp_ids: list[int] | None = None) -> list[dict[str, int]]:
|
|
84
|
+
"""
|
|
85
|
+
Return a list of dicts mapping \"RECORDID\" to the record id and \"EXPERIMENTID\" to the experiment id.
|
|
86
|
+
At least one of record_ids and exp_ids should be provided.
|
|
87
|
+
"""
|
|
88
|
+
assert (record_ids or exp_ids)
|
|
89
|
+
|
|
90
|
+
if record_ids:
|
|
91
|
+
records_term = TermBuilder.is_term(data_type_name, "RECORDID", record_ids)
|
|
92
|
+
else:
|
|
93
|
+
# Get all records of the given type
|
|
94
|
+
records_term = TermBuilder.all_records_term(data_type_name)
|
|
95
|
+
|
|
96
|
+
if exp_ids:
|
|
97
|
+
exp_term = TermBuilder.is_term("NOTEBOOKEXPERIMENT", "EXPERIMENTID", exp_ids)
|
|
98
|
+
else:
|
|
99
|
+
# Get all experiments
|
|
100
|
+
exp_term = TermBuilder.gte_term("NOTEBOOKEXPERIMENT", "EXPERIMENTID", "0")
|
|
101
|
+
|
|
102
|
+
root_term = TermBuilder.and_terms(records_term, exp_term)
|
|
103
|
+
|
|
104
|
+
# Join records on the experiment entry records that correspond to them.
|
|
105
|
+
records_entry_join = TermBuilder.compare_is_term("EXPERIMENTENTRYRECORD", "RECORDID", data_type_name, "RECORDID")
|
|
106
|
+
# Join entry records on the experiment entries they are in.
|
|
107
|
+
experiment_entry_enb_entry_join = TermBuilder.compare_is_term("ENBENTRY", "ENTRYID", "EXPERIMENTENTRYRECORD", "ENTRYID")
|
|
108
|
+
# Join entries on the experiments they are in.
|
|
109
|
+
enb_entry_experiment_join = TermBuilder.compare_is_term("NOTEBOOKEXPERIMENT", "EXPERIMENTID", "ENBENTRY", "EXPERIMENTID")
|
|
110
|
+
|
|
111
|
+
report_builder = CustomReportBuilder(data_type_name)
|
|
112
|
+
report_builder.set_root_term(root_term)
|
|
113
|
+
report_builder.add_column("RECORDID", FieldType.LONG, data_type=data_type_name)
|
|
114
|
+
report_builder.add_column("EXPERIMENTID", FieldType.LONG, data_type="NOTEBOOKEXPERIMENT")
|
|
115
|
+
report_builder.add_join(records_entry_join)
|
|
116
|
+
report_builder.add_join(experiment_entry_enb_entry_join)
|
|
117
|
+
report_builder.add_join(enb_entry_experiment_join)
|
|
118
|
+
return CustomReportUtil.run_custom_report(user, report_builder.build_report_criteria())
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import io
|
|
2
2
|
|
|
3
3
|
from sapiopylib.rest.User import SapioUser
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
from sapiopycommons.general.aliases import UserIdentifier, AliasUtil
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class CDL:
|
|
8
9
|
@staticmethod
|
|
9
|
-
def load_cdl(context:
|
|
10
|
+
def load_cdl(context: UserIdentifier, config_name: str, file_name: str, file_data: bytes | str) \
|
|
10
11
|
-> list[int]:
|
|
11
12
|
"""
|
|
12
13
|
Create data records from a file using one of the complex data loader (CDL) configurations in the system.
|
|
@@ -22,7 +23,7 @@ class CDL:
|
|
|
22
23
|
"configName": config_name,
|
|
23
24
|
"fileName": file_name
|
|
24
25
|
}
|
|
25
|
-
user: SapioUser =
|
|
26
|
+
user: SapioUser = AliasUtil.to_sapio_user(context)
|
|
26
27
|
with io.BytesIO(file_data.encode() if isinstance(file_data, str) else file_data) as data_stream:
|
|
27
28
|
response = user.post_data_stream(sub_path, params=params, data_stream=data_stream)
|
|
28
29
|
user.raise_for_status(response)
|
|
@@ -4,13 +4,14 @@ import urllib.parse
|
|
|
4
4
|
|
|
5
5
|
from requests import Response
|
|
6
6
|
from sapiopylib.rest.User import SapioUser
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
from sapiopycommons.general.aliases import UserIdentifier, AliasUtil
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
# FR-46064 - Initial port of PyWebhookUtils to sapiopycommons.
|
|
11
12
|
class FileBridge:
|
|
12
13
|
@staticmethod
|
|
13
|
-
def read_file(context:
|
|
14
|
+
def read_file(context: UserIdentifier, bridge_name: str, file_path: str,
|
|
14
15
|
base64_decode: bool = True) -> bytes:
|
|
15
16
|
"""
|
|
16
17
|
Read a file from FileBridge.
|
|
@@ -27,7 +28,7 @@ class FileBridge:
|
|
|
27
28
|
params = {
|
|
28
29
|
'Filepath': f"bridge://{bridge_name}/{file_path}"
|
|
29
30
|
}
|
|
30
|
-
user: SapioUser =
|
|
31
|
+
user: SapioUser = AliasUtil.to_sapio_user(context)
|
|
31
32
|
response = user.get(sub_path, params)
|
|
32
33
|
user.raise_for_status(response)
|
|
33
34
|
|
|
@@ -37,7 +38,7 @@ class FileBridge:
|
|
|
37
38
|
return ret_val
|
|
38
39
|
|
|
39
40
|
@staticmethod
|
|
40
|
-
def write_file(context:
|
|
41
|
+
def write_file(context: UserIdentifier, bridge_name: str, file_path: str,
|
|
41
42
|
file_data: bytes | str) -> None:
|
|
42
43
|
"""
|
|
43
44
|
Write a file to FileBridge.
|
|
@@ -53,13 +54,13 @@ class FileBridge:
|
|
|
53
54
|
params = {
|
|
54
55
|
'Filepath': f"bridge://{bridge_name}/{file_path}"
|
|
55
56
|
}
|
|
56
|
-
user: SapioUser =
|
|
57
|
+
user: SapioUser = AliasUtil.to_sapio_user(context)
|
|
57
58
|
with io.BytesIO(file_data.encode() if isinstance(file_data, str) else file_data) as data_stream:
|
|
58
59
|
response = user.post_data_stream(sub_path, params=params, data_stream=data_stream)
|
|
59
60
|
user.raise_for_status(response)
|
|
60
61
|
|
|
61
62
|
@staticmethod
|
|
62
|
-
def list_directory(context:
|
|
63
|
+
def list_directory(context: UserIdentifier, bridge_name: str,
|
|
63
64
|
file_path: str | None = "") -> list[str]:
|
|
64
65
|
"""
|
|
65
66
|
List the contents of a FileBridge directory.
|
|
@@ -74,7 +75,7 @@ class FileBridge:
|
|
|
74
75
|
params = {
|
|
75
76
|
'Filepath': f"bridge://{bridge_name}/{file_path}"
|
|
76
77
|
}
|
|
77
|
-
user: SapioUser =
|
|
78
|
+
user: SapioUser = AliasUtil.to_sapio_user(context)
|
|
78
79
|
response: Response = user.get(sub_path, params=params)
|
|
79
80
|
user.raise_for_status(response)
|
|
80
81
|
|
|
@@ -83,7 +84,7 @@ class FileBridge:
|
|
|
83
84
|
return [urllib.parse.unquote(value)[path_length:] for value in response_body]
|
|
84
85
|
|
|
85
86
|
@staticmethod
|
|
86
|
-
def create_directory(context:
|
|
87
|
+
def create_directory(context: UserIdentifier, bridge_name: str, file_path: str) -> None:
|
|
87
88
|
"""
|
|
88
89
|
Create a new directory in FileBridge.
|
|
89
90
|
|
|
@@ -97,12 +98,12 @@ class FileBridge:
|
|
|
97
98
|
params = {
|
|
98
99
|
'Filepath': f"bridge://{bridge_name}/{file_path}"
|
|
99
100
|
}
|
|
100
|
-
user: SapioUser =
|
|
101
|
+
user: SapioUser = AliasUtil.to_sapio_user(context)
|
|
101
102
|
response = user.post(sub_path, params=params)
|
|
102
103
|
user.raise_for_status(response)
|
|
103
104
|
|
|
104
105
|
@staticmethod
|
|
105
|
-
def delete_file(context:
|
|
106
|
+
def delete_file(context: UserIdentifier, bridge_name: str, file_path: str) -> None:
|
|
106
107
|
"""
|
|
107
108
|
Delete an existing file in FileBridge.
|
|
108
109
|
|
|
@@ -115,12 +116,12 @@ class FileBridge:
|
|
|
115
116
|
params = {
|
|
116
117
|
'Filepath': f"bridge://{bridge_name}/{file_path}"
|
|
117
118
|
}
|
|
118
|
-
user: SapioUser =
|
|
119
|
+
user: SapioUser = AliasUtil.to_sapio_user(context)
|
|
119
120
|
response = user.delete(sub_path, params=params)
|
|
120
121
|
user.raise_for_status(response)
|
|
121
122
|
|
|
122
123
|
@staticmethod
|
|
123
|
-
def delete_directory(context:
|
|
124
|
+
def delete_directory(context: UserIdentifier, bridge_name: str, file_path: str) -> None:
|
|
124
125
|
"""
|
|
125
126
|
Delete an existing directory in FileBridge.
|
|
126
127
|
|
|
@@ -133,6 +134,6 @@ class FileBridge:
|
|
|
133
134
|
params = {
|
|
134
135
|
'Filepath': f"bridge://{bridge_name}/{file_path}"
|
|
135
136
|
}
|
|
136
|
-
user: SapioUser =
|
|
137
|
+
user: SapioUser = AliasUtil.to_sapio_user(context)
|
|
137
138
|
response = user.delete(sub_path, params=params)
|
|
138
139
|
user.raise_for_status(response)
|
|
@@ -3,9 +3,10 @@ from __future__ import annotations
|
|
|
3
3
|
from abc import abstractmethod, ABC
|
|
4
4
|
from weakref import WeakValueDictionary
|
|
5
5
|
|
|
6
|
-
from sapiopycommons.files.file_bridge import FileBridge
|
|
7
6
|
from sapiopylib.rest.User import SapioUser
|
|
8
|
-
|
|
7
|
+
|
|
8
|
+
from sapiopycommons.files.file_bridge import FileBridge
|
|
9
|
+
from sapiopycommons.general.aliases import AliasUtil, UserIdentifier
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class FileBridgeHandler:
|
|
@@ -27,11 +28,11 @@ class FileBridgeHandler:
|
|
|
27
28
|
__instances: WeakValueDictionary[str, FileBridgeHandler] = WeakValueDictionary()
|
|
28
29
|
__initialized: bool
|
|
29
30
|
|
|
30
|
-
def __new__(cls, context:
|
|
31
|
+
def __new__(cls, context: UserIdentifier, bridge_name: str):
|
|
31
32
|
"""
|
|
32
33
|
:param context: The current webhook context or a user object to send requests from.
|
|
33
34
|
"""
|
|
34
|
-
user =
|
|
35
|
+
user = AliasUtil.to_sapio_user(context)
|
|
35
36
|
key = f"{user.__hash__()}:{bridge_name}"
|
|
36
37
|
obj = cls.__instances.get(key)
|
|
37
38
|
if not obj:
|
|
@@ -40,7 +41,7 @@ class FileBridgeHandler:
|
|
|
40
41
|
cls.__instances[key] = obj
|
|
41
42
|
return obj
|
|
42
43
|
|
|
43
|
-
def __init__(self, context:
|
|
44
|
+
def __init__(self, context: UserIdentifier, bridge_name: str):
|
|
44
45
|
"""
|
|
45
46
|
:param context: The current webhook context or a user object to send requests from.
|
|
46
47
|
:param bridge_name: The name of the bridge to communicate with. This is the "connection name" in the
|
|
@@ -50,7 +51,7 @@ class FileBridgeHandler:
|
|
|
50
51
|
return
|
|
51
52
|
self.__initialized = True
|
|
52
53
|
|
|
53
|
-
self.user =
|
|
54
|
+
self.user = AliasUtil.to_sapio_user(context)
|
|
54
55
|
self.__bridge = bridge_name
|
|
55
56
|
self.__file_cache = {}
|
|
56
57
|
self.__files = {}
|
|
@@ -327,7 +328,7 @@ class Directory(FileBridgeObject):
|
|
|
327
328
|
return {x: y for x, y in self.contents.items() if not y.is_file()}
|
|
328
329
|
|
|
329
330
|
|
|
330
|
-
def split_path(file_path: str) ->
|
|
331
|
+
def split_path(file_path: str) -> tuple[str, str]:
|
|
331
332
|
"""
|
|
332
333
|
:param file_path: A file path where directories are separated the "/" characters.
|
|
333
334
|
:return: A tuple of two strings that splits the path on its last slash. The first string is the name of the
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from typing import Any, Callable, Iterable
|
|
3
3
|
|
|
4
|
-
from sapiopycommons.general.exceptions import SapioException
|
|
5
|
-
|
|
6
|
-
from sapiopycommons.recordmodel.record_handler import RecordHandler
|
|
7
|
-
|
|
8
4
|
from sapiopycommons.general.aliases import SapioRecord
|
|
9
|
-
|
|
5
|
+
from sapiopycommons.general.exceptions import SapioException
|
|
10
6
|
from sapiopycommons.general.time_util import TimeUtil
|
|
7
|
+
from sapiopycommons.recordmodel.record_handler import RecordHandler
|
|
11
8
|
|
|
12
9
|
FilterList = Iterable[int] | range | Callable[[int, dict[str, Any]], bool] | None
|
|
13
10
|
"""A FilterList is an object used to determine if a row in the file data should be skipped over. This can take the
|