sapiopycommons 2025.9.5rc727__py3-none-any.whl → 2025.9.8a728__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/ai/converter_service_base.py +132 -0
- sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2.py +43 -0
- sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2.pyi +31 -0
- sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2_grpc.py +24 -0
- sapiopycommons/ai/protoapi/fielddefinitions/velox_field_def_pb2.py +123 -0
- sapiopycommons/ai/protoapi/fielddefinitions/velox_field_def_pb2.pyi +598 -0
- sapiopycommons/ai/protoapi/fielddefinitions/velox_field_def_pb2_grpc.py +24 -0
- sapiopycommons/ai/protoapi/plan/converter/converter_pb2.py +51 -0
- sapiopycommons/ai/protoapi/plan/converter/converter_pb2.pyi +63 -0
- sapiopycommons/ai/protoapi/plan/converter/converter_pb2_grpc.py +149 -0
- sapiopycommons/ai/protoapi/plan/item/item_container_pb2.py +55 -0
- sapiopycommons/ai/protoapi/plan/item/item_container_pb2.pyi +90 -0
- sapiopycommons/ai/protoapi/plan/item/item_container_pb2_grpc.py +24 -0
- sapiopycommons/ai/protoapi/plan/script/script_pb2.py +59 -0
- sapiopycommons/ai/protoapi/plan/script/script_pb2.pyi +102 -0
- sapiopycommons/ai/protoapi/plan/script/script_pb2_grpc.py +153 -0
- sapiopycommons/ai/protoapi/plan/step_output_pb2.py +45 -0
- sapiopycommons/ai/protoapi/plan/step_output_pb2.pyi +42 -0
- sapiopycommons/ai/protoapi/plan/step_output_pb2_grpc.py +24 -0
- sapiopycommons/ai/protoapi/plan/step_pb2.py +43 -0
- sapiopycommons/ai/protoapi/plan/step_pb2.pyi +43 -0
- sapiopycommons/ai/protoapi/plan/step_pb2_grpc.py +24 -0
- sapiopycommons/ai/protoapi/plan/tool/entry_pb2.py +41 -0
- sapiopycommons/ai/protoapi/plan/tool/entry_pb2.pyi +35 -0
- sapiopycommons/ai/protoapi/plan/tool/entry_pb2_grpc.py +24 -0
- sapiopycommons/ai/protoapi/plan/tool/tool_pb2.py +75 -0
- sapiopycommons/ai/protoapi/plan/tool/tool_pb2.pyi +237 -0
- sapiopycommons/ai/protoapi/plan/tool/tool_pb2_grpc.py +154 -0
- sapiopycommons/ai/protoapi/session/sapio_conn_info_pb2.py +39 -0
- sapiopycommons/ai/protoapi/session/sapio_conn_info_pb2.pyi +32 -0
- sapiopycommons/ai/protoapi/session/sapio_conn_info_pb2_grpc.py +24 -0
- sapiopycommons/ai/protobuf_utils.py +504 -0
- sapiopycommons/ai/server.py +106 -0
- sapiopycommons/ai/test_client.py +356 -0
- sapiopycommons/ai/tool_service_base.py +961 -0
- sapiopycommons/callbacks/callback_util.py +16 -26
- sapiopycommons/eln/experiment_handler.py +5 -12
- sapiopycommons/files/temp_files.py +49 -0
- sapiopycommons/flowcyto/flow_cyto.py +24 -2
- sapiopycommons/general/accession_service.py +28 -2
- sapiopycommons/multimodal/multimodal.py +24 -2
- sapiopycommons/recordmodel/record_handler.py +0 -4
- sapiopycommons/rules/eln_rule_handler.py +0 -3
- sapiopycommons/rules/on_save_rule_handler.py +0 -3
- sapiopycommons/webhook/webservice_handlers.py +1 -1
- {sapiopycommons-2025.9.5rc727.dist-info → sapiopycommons-2025.9.8a728.dist-info}/METADATA +2 -2
- {sapiopycommons-2025.9.5rc727.dist-info → sapiopycommons-2025.9.8a728.dist-info}/RECORD +49 -16
- sapiopycommons/ai/tool_of_tools.py +0 -917
- sapiopycommons/files/assay_plate_reader.py +0 -93
- sapiopycommons/files/file_text_converter.py +0 -207
- {sapiopycommons-2025.9.5rc727.dist-info → sapiopycommons-2025.9.8a728.dist-info}/WHEEL +0 -0
- {sapiopycommons-2025.9.5rc727.dist-info → sapiopycommons-2025.9.8a728.dist-info}/licenses/LICENSE +0 -0
|
@@ -1765,11 +1765,8 @@ class CallbackUtil:
|
|
|
1765
1765
|
blank_result_handling = BlankResultHandling.REPEAT
|
|
1766
1766
|
def not_blank_func(r: list[DataRecord]) -> bool:
|
|
1767
1767
|
return bool(r)
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
not_blank_func, blank_result_handling,
|
|
1771
|
-
repeat_message, cancel_message)
|
|
1772
|
-
return self.rec_handler.wrap_models(response, wrapper_type)
|
|
1768
|
+
return self.__send_dialog_blank_results(request, self.callback.show_input_selection_dialog, not_blank_func,
|
|
1769
|
+
blank_result_handling, repeat_message, cancel_message)
|
|
1773
1770
|
|
|
1774
1771
|
# FR-47690: Deprecated the require_authentication parameter.
|
|
1775
1772
|
# noinspection PyUnusedLocal
|
|
@@ -1815,8 +1812,7 @@ class CallbackUtil:
|
|
|
1815
1812
|
return response
|
|
1816
1813
|
|
|
1817
1814
|
def request_file(self, title: str, exts: Iterable[str] | None = None,
|
|
1818
|
-
show_image_editor: bool = False, show_camera_button: bool = False,
|
|
1819
|
-
*, enforce_file_extensions: bool = True) -> tuple[str, bytes]:
|
|
1815
|
+
show_image_editor: bool = False, show_camera_button: bool = False) -> tuple[str, bytes]:
|
|
1820
1816
|
"""
|
|
1821
1817
|
Request a single file from the user.
|
|
1822
1818
|
|
|
@@ -1826,8 +1822,6 @@ class CallbackUtil:
|
|
|
1826
1822
|
:param show_image_editor: Whether the user will see an image editor when image is uploaded in this file prompt.
|
|
1827
1823
|
:param show_camera_button: Whether the user will be able to use camera to take a picture as an upload request,
|
|
1828
1824
|
rather than selecting an existing file.
|
|
1829
|
-
:param enforce_file_extensions: If true, then the file extensions provided in the exts parameter will be
|
|
1830
|
-
enforced. If false, then the user may upload any file type.
|
|
1831
1825
|
:return: The file name and bytes of the uploaded file.
|
|
1832
1826
|
"""
|
|
1833
1827
|
# If no extensions were provided, use an empty list for the extensions instead.
|
|
@@ -1847,12 +1841,11 @@ class CallbackUtil:
|
|
|
1847
1841
|
file_path: str = self.__send_dialog(request, self.callback.show_file_dialog, data_sink=do_consume)
|
|
1848
1842
|
|
|
1849
1843
|
# Verify that each of the file given matches the expected extension(s).
|
|
1850
|
-
self.__verify_file(file_path, sink.data, exts
|
|
1844
|
+
self.__verify_file(file_path, sink.data, exts)
|
|
1851
1845
|
return file_path, sink.data
|
|
1852
1846
|
|
|
1853
1847
|
def request_files(self, title: str, exts: Iterable[str] | None = None,
|
|
1854
|
-
show_image_editor: bool = False, show_camera_button: bool = False,
|
|
1855
|
-
*, enforce_file_extensions: bool = True) -> dict[str, bytes]:
|
|
1848
|
+
show_image_editor: bool = False, show_camera_button: bool = False) -> dict[str, bytes]:
|
|
1856
1849
|
"""
|
|
1857
1850
|
Request multiple files from the user.
|
|
1858
1851
|
|
|
@@ -1862,8 +1855,6 @@ class CallbackUtil:
|
|
|
1862
1855
|
:param show_image_editor: Whether the user will see an image editor when image is uploaded in this file prompt.
|
|
1863
1856
|
:param show_camera_button: Whether the user will be able to use camera to take a picture as an upload request,
|
|
1864
1857
|
rather than selecting an existing file.
|
|
1865
|
-
:param enforce_file_extensions: If true, then the file extensions provided in the exts parameter will be
|
|
1866
|
-
enforced. If false, then the user may upload any file type.
|
|
1867
1858
|
:return: A dictionary of file name to file bytes for each file the user uploaded.
|
|
1868
1859
|
"""
|
|
1869
1860
|
# If no extensions were provided, use an empty list for the extensions instead.
|
|
@@ -1879,7 +1870,7 @@ class CallbackUtil:
|
|
|
1879
1870
|
for file_path in file_paths:
|
|
1880
1871
|
sink = InMemoryRecordDataSink(self.user)
|
|
1881
1872
|
sink.consume_client_callback_file_path_data(file_path)
|
|
1882
|
-
self.__verify_file(file_path, sink.data, exts
|
|
1873
|
+
self.__verify_file(file_path, sink.data, exts)
|
|
1883
1874
|
ret_dict.update({file_path: sink.data})
|
|
1884
1875
|
|
|
1885
1876
|
return ret_dict
|
|
@@ -1896,17 +1887,16 @@ class CallbackUtil:
|
|
|
1896
1887
|
"""
|
|
1897
1888
|
if file_path is None or len(file_path) == 0 or file_bytes is None or len(file_bytes) == 0:
|
|
1898
1889
|
raise SapioUserErrorException("Empty file provided or file unable to be read.")
|
|
1899
|
-
if
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
+ (",".join(allowed_extensions)))
|
|
1890
|
+
if allowed_extensions:
|
|
1891
|
+
matches: bool = False
|
|
1892
|
+
for ext in allowed_extensions:
|
|
1893
|
+
# FR-47690: Changed to a case-insensitive match.
|
|
1894
|
+
if file_path.casefold().endswith("." + ext.lstrip(".").casefold()):
|
|
1895
|
+
matches = True
|
|
1896
|
+
break
|
|
1897
|
+
if matches is False:
|
|
1898
|
+
raise SapioUserErrorException("Unsupported file type. Expecting the following extension(s): "
|
|
1899
|
+
+ (",".join(allowed_extensions)))
|
|
1910
1900
|
|
|
1911
1901
|
def write_file(self, file_name: str, file_data: str | bytes) -> None:
|
|
1912
1902
|
"""
|
|
@@ -206,11 +206,12 @@ class ExperimentHandler:
|
|
|
206
206
|
else:
|
|
207
207
|
user = context
|
|
208
208
|
context = None
|
|
209
|
+
if context is not None and context.eln_experiment is not None and experiment is None:
|
|
210
|
+
experiment = context.eln_experiment
|
|
209
211
|
# FR-46495 - Allow the init function of ExperimentHandler to take in an ElnExperiment that is separate from the
|
|
210
212
|
# context.
|
|
211
213
|
# CR-37038 - Allow other experiment object types to be provided. Convert them all down to ElnExperiment.
|
|
212
|
-
|
|
213
|
-
if experiment is not None:
|
|
214
|
+
if (context is None or context.eln_experiment is None) and experiment is not None:
|
|
214
215
|
eln_manager = DataMgmtServer.get_eln_manager(user)
|
|
215
216
|
# If this object is already an ElnExperiment, do nothing.
|
|
216
217
|
if isinstance(experiment, ElnExperiment):
|
|
@@ -226,19 +227,13 @@ class ExperimentHandler:
|
|
|
226
227
|
raise SapioException(f"No experiment with notebook ID {notebook_id} located in the system.")
|
|
227
228
|
# If this object is a record, assume it is an experiment record that we can query the system with.
|
|
228
229
|
else:
|
|
229
|
-
record_id: int = AliasUtil.
|
|
230
|
+
record_id: int = AliasUtil.to_record_ids([experiment])[0]
|
|
230
231
|
experiment: ElnExperiment = eln_manager.get_eln_experiment_by_record_id(record_id)
|
|
231
232
|
if not experiment:
|
|
232
233
|
raise SapioException(f"No experiment with record ID {record_id} located in the system.")
|
|
233
|
-
elif context is not None and context.eln_experiment is not None:
|
|
234
|
-
experiment = context.eln_experiment
|
|
235
|
-
|
|
236
234
|
if experiment is None:
|
|
237
235
|
raise SapioException("Cannot initialize ExperimentHandler. No ELN Experiment found in the provided "
|
|
238
236
|
"parameters.")
|
|
239
|
-
elif not isinstance(experiment, ElnExperiment):
|
|
240
|
-
raise SapioException("Cannot initialize ExperimentHandler. The experiment variable is not an "
|
|
241
|
-
"ElnExperiment!")
|
|
242
237
|
|
|
243
238
|
return user, context, experiment
|
|
244
239
|
|
|
@@ -1430,9 +1425,7 @@ class ExperimentHandler:
|
|
|
1430
1425
|
:return: The map of options for the input step.
|
|
1431
1426
|
"""
|
|
1432
1427
|
step: ElnEntryStep = self.get_step(step)
|
|
1433
|
-
|
|
1434
|
-
# properly checking its cache of entry options.
|
|
1435
|
-
if step.get_id() not in self._step_options:
|
|
1428
|
+
if step not in self._step_options:
|
|
1436
1429
|
self._step_options.update(ExperimentReportUtil.get_experiment_entry_options(self.user,
|
|
1437
1430
|
self.get_all_steps()))
|
|
1438
1431
|
return self._step_options[step.get_id()]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import tempfile
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# FR-47422: Created class.
|
|
7
|
+
class TempFileHandler:
|
|
8
|
+
"""
|
|
9
|
+
A utility class to manage temporary files and directories.
|
|
10
|
+
"""
|
|
11
|
+
directories: list[str]
|
|
12
|
+
files: list[str]
|
|
13
|
+
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
self.directories = []
|
|
16
|
+
self.files = []
|
|
17
|
+
|
|
18
|
+
def create_temp_directory(self) -> str:
|
|
19
|
+
"""
|
|
20
|
+
:return: The path to a newly created temporary directory.
|
|
21
|
+
"""
|
|
22
|
+
directory: str = tempfile.mkdtemp()
|
|
23
|
+
self.directories.append(directory)
|
|
24
|
+
return directory
|
|
25
|
+
|
|
26
|
+
def create_temp_file(self, data: str | bytes, suffix: str = "") -> str:
|
|
27
|
+
"""
|
|
28
|
+
:param data: The data to write to the temporary file.
|
|
29
|
+
:param suffix: An optional suffix for the temporary file.
|
|
30
|
+
:return: The path to a newly created temporary file containing the provided data.
|
|
31
|
+
"""
|
|
32
|
+
mode: str = 'w' if isinstance(data, str) else 'wb'
|
|
33
|
+
with tempfile.NamedTemporaryFile(mode=mode, suffix=suffix, delete=False) as tmp_file:
|
|
34
|
+
tmp_file.write(data)
|
|
35
|
+
file_path: str = tmp_file.name
|
|
36
|
+
self.files.append(file_path)
|
|
37
|
+
return file_path
|
|
38
|
+
|
|
39
|
+
def cleanup(self) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Delete all temporary files and directories created by this handler.
|
|
42
|
+
"""
|
|
43
|
+
for directory in self.directories:
|
|
44
|
+
if os.path.exists(directory):
|
|
45
|
+
shutil.rmtree(directory)
|
|
46
|
+
|
|
47
|
+
for file_path in self.files:
|
|
48
|
+
if os.path.exists(file_path):
|
|
49
|
+
os.remove(file_path)
|
|
@@ -4,16 +4,38 @@ from weakref import WeakValueDictionary
|
|
|
4
4
|
|
|
5
5
|
from databind.json import dumps
|
|
6
6
|
from sapiopylib.rest.User import SapioUser
|
|
7
|
-
from sapiopylib.rest.utils.singletons import SapioContextManager
|
|
8
7
|
|
|
9
8
|
from sapiopycommons.flowcyto.flowcyto_data import FlowJoWorkspaceInputJson, UploadFCSInputJson, \
|
|
10
9
|
ComputeFlowStatisticsInputJson
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
class FlowCytoManager
|
|
12
|
+
class FlowCytoManager:
|
|
14
13
|
"""
|
|
15
14
|
This manager includes flow cytometry analysis tools that would require FlowCyto license to use.
|
|
16
15
|
"""
|
|
16
|
+
_user: SapioUser
|
|
17
|
+
|
|
18
|
+
__instances: WeakValueDictionary[SapioUser, FlowCytoManager] = WeakValueDictionary()
|
|
19
|
+
__initialized: bool
|
|
20
|
+
|
|
21
|
+
def __new__(cls, user: SapioUser):
|
|
22
|
+
"""
|
|
23
|
+
Observes singleton pattern per record model manager object.
|
|
24
|
+
|
|
25
|
+
:param user: The user that will make the webservice request to the application.
|
|
26
|
+
"""
|
|
27
|
+
obj = cls.__instances.get(user)
|
|
28
|
+
if not obj:
|
|
29
|
+
obj = object.__new__(cls)
|
|
30
|
+
obj.__initialized = False
|
|
31
|
+
cls.__instances[user] = obj
|
|
32
|
+
return obj
|
|
33
|
+
|
|
34
|
+
def __init__(self, user: SapioUser):
|
|
35
|
+
if self.__initialized:
|
|
36
|
+
return
|
|
37
|
+
self._user = user
|
|
38
|
+
self.__initialized = True
|
|
17
39
|
|
|
18
40
|
def create_flowjo_workspace(self, workspace_input: FlowJoWorkspaceInputJson) -> int:
|
|
19
41
|
"""
|
|
@@ -5,7 +5,6 @@ from typing import Any
|
|
|
5
5
|
from weakref import WeakValueDictionary
|
|
6
6
|
|
|
7
7
|
from sapiopylib.rest.User import SapioUser
|
|
8
|
-
from sapiopylib.rest.utils.singletons import SapioContextManager
|
|
9
8
|
|
|
10
9
|
_STR_JAVA_TYPE = "java.lang.String"
|
|
11
10
|
_INT_JAVA_TYPE = "java.lang.Integer"
|
|
@@ -275,10 +274,37 @@ class AccessionServiceDescriptor:
|
|
|
275
274
|
}
|
|
276
275
|
|
|
277
276
|
|
|
278
|
-
class AccessionService
|
|
277
|
+
class AccessionService:
|
|
279
278
|
"""
|
|
280
279
|
Provides Sapio Foundations Accession Service functionalities.
|
|
281
280
|
"""
|
|
281
|
+
_user: SapioUser
|
|
282
|
+
|
|
283
|
+
__instances: WeakValueDictionary[SapioUser, AccessionService] = WeakValueDictionary()
|
|
284
|
+
__initialized: bool
|
|
285
|
+
|
|
286
|
+
@property
|
|
287
|
+
def user(self) -> SapioUser:
|
|
288
|
+
return self._user
|
|
289
|
+
|
|
290
|
+
def __new__(cls, user: SapioUser):
|
|
291
|
+
"""
|
|
292
|
+
Observes singleton pattern per record model manager object.
|
|
293
|
+
|
|
294
|
+
:param user: The user that will make the webservice request to the application.
|
|
295
|
+
"""
|
|
296
|
+
obj = cls.__instances.get(user)
|
|
297
|
+
if not obj:
|
|
298
|
+
obj = object.__new__(cls)
|
|
299
|
+
obj.__initialized = False
|
|
300
|
+
cls.__instances[user] = obj
|
|
301
|
+
return obj
|
|
302
|
+
|
|
303
|
+
def __init__(self, user: SapioUser):
|
|
304
|
+
if self.__initialized:
|
|
305
|
+
return
|
|
306
|
+
self._user = user
|
|
307
|
+
self.__initialized = True
|
|
282
308
|
|
|
283
309
|
def accession_with_config(self, data_type_name: str, data_field_name: str, num_ids: int) -> list[str]:
|
|
284
310
|
"""
|
|
@@ -7,13 +7,35 @@ from weakref import WeakValueDictionary
|
|
|
7
7
|
from databind.json import dumps, loads
|
|
8
8
|
from sapiopylib.rest.User import SapioUser
|
|
9
9
|
from sapiopylib.rest.pojo.DataRecord import DataRecord
|
|
10
|
-
from sapiopylib.rest.utils.singletons import SapioContextManager
|
|
11
10
|
|
|
12
11
|
from sapiopycommons.general.exceptions import SapioException
|
|
13
12
|
from sapiopycommons.multimodal.multimodal_data import *
|
|
14
13
|
|
|
15
14
|
|
|
16
|
-
class MultiModalManager
|
|
15
|
+
class MultiModalManager:
|
|
16
|
+
_user: SapioUser
|
|
17
|
+
|
|
18
|
+
__instances: WeakValueDictionary[SapioUser, MultiModalManager] = WeakValueDictionary()
|
|
19
|
+
__initialized: bool
|
|
20
|
+
|
|
21
|
+
def __new__(cls, user: SapioUser):
|
|
22
|
+
"""
|
|
23
|
+
Observes singleton pattern per record model manager object.
|
|
24
|
+
|
|
25
|
+
:param user: The user that will make the webservice request to the application.
|
|
26
|
+
"""
|
|
27
|
+
obj = cls.__instances.get(user)
|
|
28
|
+
if not obj:
|
|
29
|
+
obj = object.__new__(cls)
|
|
30
|
+
obj.__initialized = False
|
|
31
|
+
cls.__instances[user] = obj
|
|
32
|
+
return obj
|
|
33
|
+
|
|
34
|
+
def __init__(self, user:SapioUser):
|
|
35
|
+
if self.__initialized:
|
|
36
|
+
return
|
|
37
|
+
self._user = user
|
|
38
|
+
self.__initialized = True
|
|
17
39
|
|
|
18
40
|
def load_image_data(self, request: ImageDataRequestPojo) -> list[str]:
|
|
19
41
|
"""
|
|
@@ -100,10 +100,6 @@ class RecordHandler:
|
|
|
100
100
|
PyRecordModel instead of a WrappedRecordModel.
|
|
101
101
|
:return: The record model for the input.
|
|
102
102
|
"""
|
|
103
|
-
# PR-47792: Set the wrapper_type to None if a str was provided instead of a type[WrappedType]. The type hints
|
|
104
|
-
# say this shouldn't be done anyway, but using this as a safeguard against user error.
|
|
105
|
-
if isinstance(wrapper_type, str):
|
|
106
|
-
wrapper_type = None
|
|
107
103
|
if wrapper_type is not None:
|
|
108
104
|
self.__verify_data_type(record, wrapper_type)
|
|
109
105
|
if isinstance(record, PyRecordModel):
|
|
@@ -184,7 +184,4 @@ class ElnRuleHandler:
|
|
|
184
184
|
instead of a model wrapper, then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
185
185
|
"""
|
|
186
186
|
dt: str = AliasUtil.to_data_type_name(wrapper_type)
|
|
187
|
-
# PR-47792: Set the wrapper_type to None if a str was provided instead of a type[WrappedType].
|
|
188
|
-
if isinstance(wrapper_type, str):
|
|
189
|
-
wrapper_type = None
|
|
190
187
|
return self._rec_handler.wrap_models(self.get_records(dt, entry), wrapper_type)
|
|
@@ -180,7 +180,4 @@ class OnSaveRuleHandler:
|
|
|
180
180
|
instead of a model wrapper, then the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
181
181
|
"""
|
|
182
182
|
dt: str = AliasUtil.to_data_type_name(wrapper_type)
|
|
183
|
-
# PR-47792: Set the wrapper_type to None if a str was provided instead of a type[WrappedType].
|
|
184
|
-
if isinstance(wrapper_type, str):
|
|
185
|
-
wrapper_type = None
|
|
186
183
|
return self._rec_handler.wrap_models(self.get_records(dt, record_id), wrapper_type)
|
|
@@ -140,7 +140,7 @@ class AbstractWebserviceHandler(AbstractWebhookHandler):
|
|
|
140
140
|
# Get the login credentials from the headers.
|
|
141
141
|
auth: str = headers.get("Authorization")
|
|
142
142
|
if auth and auth.startswith("Basic "):
|
|
143
|
-
credentials: list[str] = b64decode(auth.split("Basic ")[1]).decode().split(":")
|
|
143
|
+
credentials: list[str] = b64decode(auth.split("Basic ")[1]).decode().split(":", 1)
|
|
144
144
|
user = self.basic_auth(url, credentials[0], credentials[1])
|
|
145
145
|
elif auth and auth.startswith("Bearer "):
|
|
146
146
|
user = self.bearer_token_auth(url, auth.split("Bearer ")[1])
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: sapiopycommons
|
|
3
|
-
Version: 2025.9.
|
|
3
|
+
Version: 2025.9.8a728
|
|
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>
|
|
@@ -17,7 +17,7 @@ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
|
17
17
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
18
|
Requires-Python: >=3.10
|
|
19
19
|
Requires-Dist: databind>=4.5
|
|
20
|
-
Requires-Dist: sapiopylib>=2025.
|
|
20
|
+
Requires-Dist: sapiopylib>=2025.4.17.264
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
|
|
23
23
|
|
|
@@ -1,8 +1,42 @@
|
|
|
1
1
|
sapiopycommons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
sapiopycommons/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
sapiopycommons/ai/
|
|
3
|
+
sapiopycommons/ai/converter_service_base.py,sha256=TMSyEekbbqMk9dRuAtLlSJ1sA1H8KpyCDlSOeqGFMWI,5115
|
|
4
|
+
sapiopycommons/ai/protobuf_utils.py,sha256=cBjbxoFAwU02kNUxEce95WnMU2CMuDD-qFaeWgvQJMQ,24599
|
|
5
|
+
sapiopycommons/ai/server.py,sha256=jvmAcs4y8qp0d483wCAUgPlSBSoUUQvCjv_OvQXEGUs,4274
|
|
6
|
+
sapiopycommons/ai/test_client.py,sha256=iPhn7cvKNLmDAXrjpmIkZpW2pDWlUhJZHDLHJbEoWsg,15673
|
|
7
|
+
sapiopycommons/ai/tool_service_base.py,sha256=AVW6Yf0l_l4x__dosIZyAJun4UmZAZaBJRYzNru_fD4,45806
|
|
8
|
+
sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2.py,sha256=8tKXwLXcqFGdQHHSEBSi6Fd7dcaCFoOqmhjzqhenb_M,2372
|
|
9
|
+
sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2.pyi,sha256=FwtXmNAf7iYGEFm4kbqb04v77jNHbZg18ZmEDhle_bU,1444
|
|
10
|
+
sapiopycommons/ai/protoapi/fielddefinitions/fields_pb2_grpc.py,sha256=uO25bcnfGqXpP4ggUur54Nr73Wj-DGWftExzLNcxdHI,931
|
|
11
|
+
sapiopycommons/ai/protoapi/fielddefinitions/velox_field_def_pb2.py,sha256=in9iHiLPYcnLWoLeqy4nWSI0jZGHD_bMhEFRIRtJPuo,20864
|
|
12
|
+
sapiopycommons/ai/protoapi/fielddefinitions/velox_field_def_pb2.pyi,sha256=U5zXrbBxsWilLTsRWJd1TqjdjLKFsr3enF9OJ8GfyWw,34028
|
|
13
|
+
sapiopycommons/ai/protoapi/fielddefinitions/velox_field_def_pb2_grpc.py,sha256=Vj6qDKvsHgl25iBi3UjtTuGxihekgqCuufExvnJKzQI,940
|
|
14
|
+
sapiopycommons/ai/protoapi/plan/step_output_pb2.py,sha256=EBNCzLUDwwCqDCh35zSfFdfq0RP8WrmTMXEzEPu_1_E,2655
|
|
15
|
+
sapiopycommons/ai/protoapi/plan/step_output_pb2.pyi,sha256=yuxOYnDZ9DRuu-TLzaKOW_B4LUiYxTrNc2AbssXg4kE,2022
|
|
16
|
+
sapiopycommons/ai/protoapi/plan/step_output_pb2_grpc.py,sha256=ebWLIfOFeVE2WuUIThMBerVweH-1phviGX195UTwYyg,924
|
|
17
|
+
sapiopycommons/ai/protoapi/plan/step_pb2.py,sha256=mKTm_syaX99GzhWtGIPkxMTsfcsvW0QbRqjv06eHSM0,2433
|
|
18
|
+
sapiopycommons/ai/protoapi/plan/step_pb2.pyi,sha256=QPIcsjcUvEGQkdZMUMiVzFFNDl8yOUe_qJtf5XEp5Ck,2062
|
|
19
|
+
sapiopycommons/ai/protoapi/plan/step_pb2_grpc.py,sha256=1CBna5NBBxPwQhrkN8-Fim_j3FGmOfDo5C4c8sIBV8Q,917
|
|
20
|
+
sapiopycommons/ai/protoapi/plan/converter/converter_pb2.py,sha256=rZYBRfR0umwDYvBdYnzxR1VZSutRqunhd3QsdtQXiCM,3593
|
|
21
|
+
sapiopycommons/ai/protoapi/plan/converter/converter_pb2.pyi,sha256=_35yHfKTJH3SMdA5_c6qF6OZG6UwFWXpNh6dwRFDKkk,4250
|
|
22
|
+
sapiopycommons/ai/protoapi/plan/converter/converter_pb2_grpc.py,sha256=6T5FCmT_vEFSywUVlAlWWfBDPaILb0Dq8yCGuO_Q-BU,6448
|
|
23
|
+
sapiopycommons/ai/protoapi/plan/item/item_container_pb2.py,sha256=VIXmIw8-9jtH7peJZ16BEmGDfaVjjxTKhmlfcHWT82M,3863
|
|
24
|
+
sapiopycommons/ai/protoapi/plan/item/item_container_pb2.pyi,sha256=bbPNQDwfFDd7_S7yU99Co1O7sRhxjle-RM0_nK8jgjc,4246
|
|
25
|
+
sapiopycommons/ai/protoapi/plan/item/item_container_pb2_grpc.py,sha256=1NzBWBUINC0Rk-NGYZ-97BVKvVUxcn_I44IjQO2h-nQ,932
|
|
26
|
+
sapiopycommons/ai/protoapi/plan/script/script_pb2.py,sha256=swyahlxM7fKm-RqtgMd1Czqd1JrbXRw3sYqFTkjbbyM,5144
|
|
27
|
+
sapiopycommons/ai/protoapi/plan/script/script_pb2.pyi,sha256=GqlqLf9UX_B5vCR9cm-VoYLHFJNmZjJafcPJ6EDa8Qw,6192
|
|
28
|
+
sapiopycommons/ai/protoapi/plan/script/script_pb2_grpc.py,sha256=ginPYpsiI6U6dB6gfWHJM8LWjSHUgcqz_gR7Dmdsyck,6864
|
|
29
|
+
sapiopycommons/ai/protoapi/plan/tool/entry_pb2.py,sha256=yNyyyVvz94ewjGaHw3t0pUP-KH7ACg5hLC0QzrsFPis,2130
|
|
30
|
+
sapiopycommons/ai/protoapi/plan/tool/entry_pb2.pyi,sha256=2vI0WSy0KGFHDewMSRyX5rUkmmmSaMBLvFO7txqA6Ug,2354
|
|
31
|
+
sapiopycommons/ai/protoapi/plan/tool/entry_pb2_grpc.py,sha256=i24BfJEt7LtyROxHVEyS9RLYqLZKPvyJMKRFZj6NUTg,923
|
|
32
|
+
sapiopycommons/ai/protoapi/plan/tool/tool_pb2.py,sha256=Gy6YjakkicKi4JthUFOxOyPLen-af7XI-ijinVs7EOY,8181
|
|
33
|
+
sapiopycommons/ai/protoapi/plan/tool/tool_pb2.pyi,sha256=x33Ko2KC5_lw8omA1hYRGoUWvDitxNdloxswTxaMaqc,16713
|
|
34
|
+
sapiopycommons/ai/protoapi/plan/tool/tool_pb2_grpc.py,sha256=CLSGEgSpN4D6wcE-P_5lzb2Nttjr4XjumxGUrZQSip0,6915
|
|
35
|
+
sapiopycommons/ai/protoapi/session/sapio_conn_info_pb2.py,sha256=MhWzTyJz3xZgpdW8_LCVKuzKXT0cv6iHMRB-UNCUNzM,2094
|
|
36
|
+
sapiopycommons/ai/protoapi/session/sapio_conn_info_pb2.pyi,sha256=vLYA8Tkzq2AwgVadoUp5vAg4HgGlgga0kzeS3e_XkCQ,1621
|
|
37
|
+
sapiopycommons/ai/protoapi/session/sapio_conn_info_pb2_grpc.py,sha256=imcciy_kbmm7OV_W3jYZ53R6GQ6Yh-eUcVW0A9GkWdg,931
|
|
4
38
|
sapiopycommons/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
sapiopycommons/callbacks/callback_util.py,sha256=
|
|
39
|
+
sapiopycommons/callbacks/callback_util.py,sha256=OuPJ1o6jcDQ7qV-dxrjAkJerGbVI9_9P-xu0r3ODaMM,153008
|
|
6
40
|
sapiopycommons/callbacks/field_builder.py,sha256=rnIP-RJafk3mZlAx1eJ8a0eSW9Ps_L6_WadCmusnENw,38772
|
|
7
41
|
sapiopycommons/chem/IndigoMolecules.py,sha256=7ucCaRMLu1zfH2uPIvXwRTSdpNcS03O1P9p_O-5B4xQ,5110
|
|
8
42
|
sapiopycommons/chem/Molecules.py,sha256=mVqPn32MPMjF0iZas-5MFkS-upIdoW5OB72KKZmJRJA,12523
|
|
@@ -18,26 +52,25 @@ sapiopycommons/datatype/data_fields.py,sha256=pczUlEcE0TeHEDU0Gkvu7voacSLPXCB7l9
|
|
|
18
52
|
sapiopycommons/datatype/pseudo_data_types.py,sha256=lAJDnFuStrUP0mK5AuYlFvLerwjEB-ABd6Z4qlCrwJA,40637
|
|
19
53
|
sapiopycommons/eln/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
54
|
sapiopycommons/eln/experiment_cache.py,sha256=Zv4IcsAl95ftO2ul3DRc_Hyno0AfC3OvFV7RYb72ITo,9560
|
|
21
|
-
sapiopycommons/eln/experiment_handler.py,sha256=
|
|
55
|
+
sapiopycommons/eln/experiment_handler.py,sha256=kMQuccmGyPzc8wYKsds6AZ_VjH9WLjtUY6hsSWXJx6s,98786
|
|
22
56
|
sapiopycommons/eln/experiment_report_util.py,sha256=GLpgwSEPuUqnY1v4oJ1ao60Va-YcgXh7E-cH9YnVeAg,37256
|
|
23
57
|
sapiopycommons/eln/experiment_step_factory.py,sha256=qw9UfLslVzB6dEIZPOZ85XHKpld81RhD4-csM6TgQNg,26099
|
|
24
58
|
sapiopycommons/eln/experiment_tags.py,sha256=7-fpOiSqrjbXmWIJhEhaxMgLsVCPAtKqH8xRzpDVKoE,356
|
|
25
59
|
sapiopycommons/eln/plate_designer.py,sha256=XFazSvhTbSy47t80-jc2tyx_-fQ_IUjKd18JQKEFcsY,13939
|
|
26
60
|
sapiopycommons/eln/step_creation.py,sha256=CFkGC-SxwAQpQlcs_obqLAVgmsNxKSGMqMtO_E6IVmw,10171
|
|
27
61
|
sapiopycommons/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
sapiopycommons/files/assay_plate_reader.py,sha256=3c2PQiiAbc2QJU9ZfNLzcTmvJrUwsbkIHO7R6R52xGU,3020
|
|
29
62
|
sapiopycommons/files/complex_data_loader.py,sha256=T39veNhvYl6j_uZjIIJ8Mk5Aa7otR5RB-g8XlAdkksA,1421
|
|
30
63
|
sapiopycommons/files/file_bridge.py,sha256=vKbqxPexi15epr_-_qLrEfYoxNxB031mXN92iVtOMqE,9511
|
|
31
64
|
sapiopycommons/files/file_bridge_handler.py,sha256=SEYDIQhSCmjI6qyLdDJE8JVKSd0WYvF7JvAq_Ahp9Do,25503
|
|
32
65
|
sapiopycommons/files/file_data_handler.py,sha256=f96MlkMuQhUCi4oLnzJK5AiuElCp5jLI8_sJkZVwpws,36779
|
|
33
|
-
sapiopycommons/files/file_text_converter.py,sha256=Gaj_divTiKXWd6flDOgrxNXpcn9fDWqxX6LUG0joePk,7516
|
|
34
66
|
sapiopycommons/files/file_util.py,sha256=djouyGjsYgWzjz2OBRnSeMDgj6NrsJUm1a2J93J8Wco,31915
|
|
35
67
|
sapiopycommons/files/file_validator.py,sha256=ryg22-93csmRO_Pv0ZpWphNkB74xWZnHyJ23K56qLj0,28761
|
|
36
68
|
sapiopycommons/files/file_writer.py,sha256=hACVl0duCjP28gJ1NPljkjagNCLod0ygUlPbvUmRDNM,17605
|
|
37
|
-
sapiopycommons/
|
|
69
|
+
sapiopycommons/files/temp_files.py,sha256=Puv59qtGwiXVJnTm4YuyeZPKw_leXDW906Uz_xbIt6A,1542
|
|
70
|
+
sapiopycommons/flowcyto/flow_cyto.py,sha256=vs9WhXXKz3urpjL8QKSk56B-NSmQR3O3x_WFBKoeO10,3227
|
|
38
71
|
sapiopycommons/flowcyto/flowcyto_data.py,sha256=mYKFuLbtpJ-EsQxLGtu4tNHVlygTxKixgJxJqD68F58,2596
|
|
39
72
|
sapiopycommons/general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
|
-
sapiopycommons/general/accession_service.py,sha256=
|
|
73
|
+
sapiopycommons/general/accession_service.py,sha256=3e__bVs7CYZ1CduLlGA9plnK7nCtdy7GXjCrNObPFgo,13484
|
|
41
74
|
sapiopycommons/general/aliases.py,sha256=VwnWf_P803pcteoAIs0DkLScVChCS5XNgryTp8FzaNc,14696
|
|
42
75
|
sapiopycommons/general/audit_log.py,sha256=sQAMcJx0cNkgZm7nTZSaGPxWvHG0_x6dBtU0jESavb4,9131
|
|
43
76
|
sapiopycommons/general/custom_report_util.py,sha256=9elLEUSgfM0gli8nRPz1uYkhaXN4Vnx3piSiNHv5IBs,19156
|
|
@@ -49,24 +82,24 @@ sapiopycommons/general/popup_util.py,sha256=HKILegU1uCL_6abNlNL0Wn3xgX2JNa_kJeq7
|
|
|
49
82
|
sapiopycommons/general/sapio_links.py,sha256=YkcVKNLrSGoM7tCCXBAsIbIxylctwdcEyhePrRMODe0,2859
|
|
50
83
|
sapiopycommons/general/storage_util.py,sha256=ovmK_jN7v09BoX07XxwShpBUC5WYQOM7dbKV_VeLXJU,8892
|
|
51
84
|
sapiopycommons/general/time_util.py,sha256=jU1urPoZRv6evNucR0-288EyT4PrsDpCr-H1-7BKq9A,12363
|
|
52
|
-
sapiopycommons/multimodal/multimodal.py,sha256=
|
|
85
|
+
sapiopycommons/multimodal/multimodal.py,sha256=PFaGJPbKvW__tnxb8KkgkJZOKjQdgxF_kGfD5chet1s,6779
|
|
53
86
|
sapiopycommons/multimodal/multimodal_data.py,sha256=0BeVPr9HaC0hNTF1v1phTIKGruvNnwerHsD994qJKBg,15099
|
|
54
87
|
sapiopycommons/processtracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
88
|
sapiopycommons/processtracking/custom_workflow_handler.py,sha256=eYKdYlwo8xx-6AkB_iPUBNV9yDoNvW2h_Sm3i8JpmRU,25844
|
|
56
89
|
sapiopycommons/processtracking/endpoints.py,sha256=5AJLbhRKQsOeeOdQa888xcCJZD5aavxD-DHZ36Qob_M,12548
|
|
57
90
|
sapiopycommons/recordmodel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
58
|
-
sapiopycommons/recordmodel/record_handler.py,sha256=
|
|
91
|
+
sapiopycommons/recordmodel/record_handler.py,sha256=WxmgrWQ3nX3eVZSHJY7e8fj7CI7azSyEyovmYcy9098,95021
|
|
59
92
|
sapiopycommons/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
60
|
-
sapiopycommons/rules/eln_rule_handler.py,sha256=
|
|
61
|
-
sapiopycommons/rules/on_save_rule_handler.py,sha256=
|
|
93
|
+
sapiopycommons/rules/eln_rule_handler.py,sha256=MnE-eSl1kNfaXWFi9elTOC9V2fdUzrwWTvCHUprC8_I,11388
|
|
94
|
+
sapiopycommons/rules/on_save_rule_handler.py,sha256=fkNIlslAZZ0BUrRiwecyvf42JBR8FpCCQ6DBNKXP2jE,11155
|
|
62
95
|
sapiopycommons/samples/aliquot.py,sha256=mWOJUqaQh0t3HklNuGdmuV7D5zzXs6fpLwtDdM6_XTo,3018
|
|
63
96
|
sapiopycommons/sftpconnect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
64
97
|
sapiopycommons/sftpconnect/sftp_builder.py,sha256=lFK3FeXk-sFLefW0hqY8WGUQDeYiGaT6yDACzT_zFgQ,3015
|
|
65
98
|
sapiopycommons/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
99
|
sapiopycommons/webhook/webhook_context.py,sha256=D793uLsb1691SalaPnBUk3rOSxn_hYLhdvkaIxjNXss,1909
|
|
67
100
|
sapiopycommons/webhook/webhook_handlers.py,sha256=7o_wXOruhT9auNh8OfhJAh4WhhiPKij67FMBSpGPICc,39939
|
|
68
|
-
sapiopycommons/webhook/webservice_handlers.py,sha256=
|
|
69
|
-
sapiopycommons-2025.9.
|
|
70
|
-
sapiopycommons-2025.9.
|
|
71
|
-
sapiopycommons-2025.9.
|
|
72
|
-
sapiopycommons-2025.9.
|
|
101
|
+
sapiopycommons/webhook/webservice_handlers.py,sha256=cvW6Mk_110BzYqkbk63Kg7jWrltBCDALOlkJRu8h4VQ,14300
|
|
102
|
+
sapiopycommons-2025.9.8a728.dist-info/METADATA,sha256=6IMqQ6KDSTEcXMpbfbwpBSC_eGnhn-336JCdZkvNmR0,3142
|
|
103
|
+
sapiopycommons-2025.9.8a728.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
104
|
+
sapiopycommons-2025.9.8a728.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
|
|
105
|
+
sapiopycommons-2025.9.8a728.dist-info/RECORD,,
|