sapiopycommons 2025.3.6a454__py3-none-any.whl → 2025.3.10a455__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 +366 -1220
- sapiopycommons/chem/Molecules.py +2 -0
- sapiopycommons/datatype/data_fields.py +1 -1
- sapiopycommons/eln/experiment_handler.py +1 -2
- sapiopycommons/eln/experiment_report_util.py +7 -7
- sapiopycommons/files/file_bridge.py +0 -76
- sapiopycommons/files/file_bridge_handler.py +110 -325
- sapiopycommons/files/file_data_handler.py +2 -2
- sapiopycommons/files/file_util.py +11 -36
- sapiopycommons/files/file_validator.py +5 -6
- sapiopycommons/files/file_writer.py +1 -1
- sapiopycommons/flowcyto/flow_cyto.py +1 -1
- sapiopycommons/general/accession_service.py +1 -1
- sapiopycommons/general/aliases.py +28 -48
- sapiopycommons/general/audit_log.py +2 -2
- sapiopycommons/general/custom_report_util.py +1 -24
- sapiopycommons/general/exceptions.py +2 -41
- sapiopycommons/general/popup_util.py +2 -2
- sapiopycommons/multimodal/multimodal.py +0 -1
- sapiopycommons/processtracking/custom_workflow_handler.py +3 -3
- sapiopycommons/recordmodel/record_handler.py +3 -5
- sapiopycommons/webhook/webhook_handlers.py +55 -445
- {sapiopycommons-2025.3.6a454.dist-info → sapiopycommons-2025.3.10a455.dist-info}/METADATA +1 -1
- {sapiopycommons-2025.3.6a454.dist-info → sapiopycommons-2025.3.10a455.dist-info}/RECORD +26 -29
- sapiopycommons/customreport/auto_pagers.py +0 -270
- sapiopycommons/general/directive_util.py +0 -86
- sapiopycommons/samples/aliquot.py +0 -48
- {sapiopycommons-2025.3.6a454.dist-info → sapiopycommons-2025.3.10a455.dist-info}/WHEEL +0 -0
- {sapiopycommons-2025.3.6a454.dist-info → sapiopycommons-2025.3.10a455.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,62 +1,59 @@
|
|
|
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=2pGr14f_FVCqeF6dsbYP1e4cOsaUTQyQ7XVwZuUkM_k,67952
|
|
4
4
|
sapiopycommons/callbacks/field_builder.py,sha256=p2XacN99MuKk3ite8GAqstUMpixqugul2CsC4gB83-o,38620
|
|
5
5
|
sapiopycommons/chem/IndigoMolecules.py,sha256=slM2y39zZFHc468c366EqR8T-GYJ24UnM9HWAqWFEwQ,3900
|
|
6
|
-
sapiopycommons/chem/Molecules.py,sha256=
|
|
6
|
+
sapiopycommons/chem/Molecules.py,sha256=9B4sbwbvYs50XHRn0TZiu-D1Oa3pKrI9qE5vNW8zv-U,12464
|
|
7
7
|
sapiopycommons/chem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
sapiopycommons/customreport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
sapiopycommons/customreport/auto_pagers.py,sha256=9G0yo288ODzpJ5jzdMi8GknB1iWwbK_5_LGuoDUBoA8,13967
|
|
10
9
|
sapiopycommons/customreport/column_builder.py,sha256=0RO53e9rKPZ07C--KcepN6_tpRw_FxF3O9vdG0ilKG8,3014
|
|
11
10
|
sapiopycommons/customreport/custom_report_builder.py,sha256=BlTxZ4t1sfZA2Ciur1EfYvkZxHxJ7ADwYNAe2zwiN0c,7176
|
|
12
11
|
sapiopycommons/customreport/term_builder.py,sha256=PNp71NF1vFxidk5v6uQNi9oQR9KJIk8WfhyntvvZN-U,18573
|
|
13
12
|
sapiopycommons/datatype/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
13
|
sapiopycommons/datatype/attachment_util.py,sha256=_l2swuP8noIGAl4bwzBUEhr6YlN_OVZl3-gi1XqFHYA,3364
|
|
15
|
-
sapiopycommons/datatype/data_fields.py,sha256=
|
|
14
|
+
sapiopycommons/datatype/data_fields.py,sha256=g8Ib6LH8ikNu9AzeVJs8Z2qS8A-cplACeFU7vYguNEY,4063
|
|
16
15
|
sapiopycommons/datatype/pseudo_data_types.py,sha256=6TG7aJxgmUZ8FQkWBcgmbK5oy7AFFNtKOPpi1w1OOYA,27657
|
|
17
16
|
sapiopycommons/eln/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
sapiopycommons/eln/experiment_handler.py,sha256=
|
|
19
|
-
sapiopycommons/eln/experiment_report_util.py,sha256=
|
|
17
|
+
sapiopycommons/eln/experiment_handler.py,sha256=ZSx0uZy-2OtH_ArHy2OVwoNI3BYQLXSHGBmjviZl1Fw,69283
|
|
18
|
+
sapiopycommons/eln/experiment_report_util.py,sha256=bs9zSUgRo40q2VYtjDCVPsT-D1umdNfl5s4oZrY4uuc,36597
|
|
20
19
|
sapiopycommons/eln/plate_designer.py,sha256=FYJfhhNq8hdfuXgDYOYHy6g0m2zNwQXZWF_MTPzElDg,7184
|
|
21
20
|
sapiopycommons/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
21
|
sapiopycommons/files/complex_data_loader.py,sha256=T39veNhvYl6j_uZjIIJ8Mk5Aa7otR5RB-g8XlAdkksA,1421
|
|
23
|
-
sapiopycommons/files/file_bridge.py,sha256=
|
|
24
|
-
sapiopycommons/files/file_bridge_handler.py,sha256=
|
|
25
|
-
sapiopycommons/files/file_data_handler.py,sha256=
|
|
26
|
-
sapiopycommons/files/file_util.py,sha256=
|
|
27
|
-
sapiopycommons/files/file_validator.py,sha256=
|
|
28
|
-
sapiopycommons/files/file_writer.py,sha256=
|
|
29
|
-
sapiopycommons/flowcyto/flow_cyto.py,sha256=
|
|
22
|
+
sapiopycommons/files/file_bridge.py,sha256=WwCVegk0OGA8eqho8chsOsLlqg1nXctO75zfh-rHF-g,5950
|
|
23
|
+
sapiopycommons/files/file_bridge_handler.py,sha256=bt2IfIsxJ4lcJYo_NHvCQ17ZV6C4fSAEa8Zcgixh7B4,14263
|
|
24
|
+
sapiopycommons/files/file_data_handler.py,sha256=SCsjODMJIPEBSsahzXUeOM7CfSCmYwPPoGAM6aOfelo,36743
|
|
25
|
+
sapiopycommons/files/file_util.py,sha256=wbL3rxcFc-t2mXaPWWkoFWYGopvTcQts9Wf-L5GkhT8,29498
|
|
26
|
+
sapiopycommons/files/file_validator.py,sha256=4OvY98ueJWPJdpndwnKv2nqVvLP9S2W7Il_dM0Y0ojo,28709
|
|
27
|
+
sapiopycommons/files/file_writer.py,sha256=96Xl8TTT46Krxe_J8rmmlEMtel4nzZB961f5Yqtl1-I,17616
|
|
28
|
+
sapiopycommons/flowcyto/flow_cyto.py,sha256=YlkKJR_zEHYRuNW0bnTqlTyZeXs0lOaeSCfG2fnfD7E,3227
|
|
30
29
|
sapiopycommons/flowcyto/flowcyto_data.py,sha256=mYKFuLbtpJ-EsQxLGtu4tNHVlygTxKixgJxJqD68F58,2596
|
|
31
30
|
sapiopycommons/general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
|
-
sapiopycommons/general/accession_service.py,sha256=
|
|
33
|
-
sapiopycommons/general/aliases.py,sha256=
|
|
34
|
-
sapiopycommons/general/audit_log.py,sha256=
|
|
35
|
-
sapiopycommons/general/custom_report_util.py,sha256=
|
|
36
|
-
sapiopycommons/general/
|
|
37
|
-
sapiopycommons/general/
|
|
38
|
-
sapiopycommons/general/popup_util.py,sha256=HKILegU1uCL_6abNlNL0Wn3xgX2JNa_kJeq7e5CZu6Q,31923
|
|
31
|
+
sapiopycommons/general/accession_service.py,sha256=HYgyOsH_UaoRnoury-c2yTW8SeG4OtjLemdpCzoV4R8,13484
|
|
32
|
+
sapiopycommons/general/aliases.py,sha256=tdDBNWSGx6s39eHJ3n2kscc4xxW3ZYaUfDftct6FmJE,12910
|
|
33
|
+
sapiopycommons/general/audit_log.py,sha256=pvXPfLr4Rw2X52wAXJGsX5fsPllswnCqaFZQ181j_GU,8727
|
|
34
|
+
sapiopycommons/general/custom_report_util.py,sha256=6ZIg_Jl02TYUygfc5xqBoI1srPsSxLyxaJ9jwTolGcM,16671
|
|
35
|
+
sapiopycommons/general/exceptions.py,sha256=GY7fe0qOgoy4kQVn_Pn3tdzHsJZyNIpa6VCChg6tzuM,1813
|
|
36
|
+
sapiopycommons/general/popup_util.py,sha256=L-4qpTemSZdlD6_6oEsDYIzLOCiZgDK6wC6DqUwzOYA,31925
|
|
39
37
|
sapiopycommons/general/sapio_links.py,sha256=o9Z-8y2rz6AI0Cy6tq58ElPge9RBnisGc9NyccbaJxs,2610
|
|
40
38
|
sapiopycommons/general/storage_util.py,sha256=ovmK_jN7v09BoX07XxwShpBUC5WYQOM7dbKV_VeLXJU,8892
|
|
41
39
|
sapiopycommons/general/time_util.py,sha256=jU1urPoZRv6evNucR0-288EyT4PrsDpCr-H1-7BKq9A,12363
|
|
42
|
-
sapiopycommons/multimodal/multimodal.py,sha256=
|
|
40
|
+
sapiopycommons/multimodal/multimodal.py,sha256=A1QsC8QTPmgZyPr7KtMbPRedn2Ie4WIErodUvQ9otgU,6724
|
|
43
41
|
sapiopycommons/multimodal/multimodal_data.py,sha256=0BeVPr9HaC0hNTF1v1phTIKGruvNnwerHsD994qJKBg,15099
|
|
44
42
|
sapiopycommons/processtracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
-
sapiopycommons/processtracking/custom_workflow_handler.py,sha256=
|
|
43
|
+
sapiopycommons/processtracking/custom_workflow_handler.py,sha256=B8KxQlwlLGkxnEid2jb9igGyGqrtcsBMhcOb9MfnO9E,23465
|
|
46
44
|
sapiopycommons/processtracking/endpoints.py,sha256=w5bziI2xC7450M95rCF8JpRwkoni1kEDibyAux9B12Q,10848
|
|
47
45
|
sapiopycommons/recordmodel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
-
sapiopycommons/recordmodel/record_handler.py,sha256=
|
|
46
|
+
sapiopycommons/recordmodel/record_handler.py,sha256=5kCO5zsSg0kp3uYpgw1vf0WLHw30pMNC_6Bn3G7iQkI,64796
|
|
49
47
|
sapiopycommons/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
48
|
sapiopycommons/rules/eln_rule_handler.py,sha256=JYzDA_14D2nLnlqwbpIxVOrfKWzbOS27AYf4TQfGr4Q,10469
|
|
51
49
|
sapiopycommons/rules/on_save_rule_handler.py,sha256=Rkqvph20RbNq6m-RF4fbvCP-YfD2CZYBM2iTr3nl0eY,10236
|
|
52
|
-
sapiopycommons/samples/aliquot.py,sha256=mWOJUqaQh0t3HklNuGdmuV7D5zzXs6fpLwtDdM6_XTo,3018
|
|
53
50
|
sapiopycommons/sftpconnect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
51
|
sapiopycommons/sftpconnect/sftp_builder.py,sha256=lFK3FeXk-sFLefW0hqY8WGUQDeYiGaT6yDACzT_zFgQ,3015
|
|
55
52
|
sapiopycommons/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
53
|
sapiopycommons/webhook/webhook_context.py,sha256=D793uLsb1691SalaPnBUk3rOSxn_hYLhdvkaIxjNXss,1909
|
|
57
|
-
sapiopycommons/webhook/webhook_handlers.py,sha256=
|
|
54
|
+
sapiopycommons/webhook/webhook_handlers.py,sha256=MdsVK4bHffkMNmNWl0_qvu-5Lz8-qGu4Ryi7lZO1BZs,18586
|
|
58
55
|
sapiopycommons/webhook/webservice_handlers.py,sha256=Y5dHx_UFWFuSqaoPL6Re-fsKYRuxvCWZ8bj6KSZ3jfM,14285
|
|
59
|
-
sapiopycommons-2025.3.
|
|
60
|
-
sapiopycommons-2025.3.
|
|
61
|
-
sapiopycommons-2025.3.
|
|
62
|
-
sapiopycommons-2025.3.
|
|
56
|
+
sapiopycommons-2025.3.10a455.dist-info/METADATA,sha256=PYodInpHx8Ia7h-pIQalBtai4RLvP1s4xqSSFgnTrWg,3143
|
|
57
|
+
sapiopycommons-2025.3.10a455.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
58
|
+
sapiopycommons-2025.3.10a455.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
|
|
59
|
+
sapiopycommons-2025.3.10a455.dist-info/RECORD,,
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
from abc import ABC
|
|
2
|
-
from copy import copy
|
|
3
|
-
from queue import Queue
|
|
4
|
-
|
|
5
|
-
from sapiopylib.rest.CustomReportService import CustomReportManager
|
|
6
|
-
from sapiopylib.rest.DataMgmtService import DataMgmtServer
|
|
7
|
-
from sapiopylib.rest.pojo.CustomReport import CustomReportCriteria, CustomReport, RawReportTerm, ReportColumn
|
|
8
|
-
from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
|
|
9
|
-
from sapiopylib.rest.utils.autopaging import SapioPyAutoPager, PagerResultCriteriaType, _default_report_page_size, \
|
|
10
|
-
_default_record_page_size
|
|
11
|
-
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
12
|
-
|
|
13
|
-
from sapiopycommons.general.aliases import FieldValue, UserIdentifier, AliasUtil, RecordModel
|
|
14
|
-
from sapiopycommons.general.custom_report_util import CustomReportUtil
|
|
15
|
-
from sapiopycommons.general.exceptions import SapioException
|
|
16
|
-
from sapiopycommons.recordmodel.record_handler import RecordHandler
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# FR-47389: Create auto pagers for running custom/system/quick reports that return dictionaries or records for each row.
|
|
20
|
-
class _DictReportPagerBase(SapioPyAutoPager[CustomReportCriteria, dict[str, FieldValue]], ABC):
|
|
21
|
-
"""
|
|
22
|
-
A base class for automatically paging through a report and returning the results as a list of dictionaries.
|
|
23
|
-
"""
|
|
24
|
-
_columns: list[ReportColumn]
|
|
25
|
-
_report_man: CustomReportManager
|
|
26
|
-
|
|
27
|
-
def __init__(self, user: UserIdentifier, first_page_criteria: CustomReportCriteria):
|
|
28
|
-
self._columns = first_page_criteria.column_list
|
|
29
|
-
super().__init__(AliasUtil.to_sapio_user(user), first_page_criteria)
|
|
30
|
-
self._report_man = DataMgmtServer.get_custom_report_manager(self.user)
|
|
31
|
-
|
|
32
|
-
def get_all_at_once(self) -> list[dict[str, FieldValue]]:
|
|
33
|
-
"""
|
|
34
|
-
Get the results of all pages. Be cautious of client memory usage.
|
|
35
|
-
"""
|
|
36
|
-
if self.has_iterated:
|
|
37
|
-
raise BrokenPipeError("Cannot use this method if the iterator has already been used.")
|
|
38
|
-
return [x for x in self]
|
|
39
|
-
|
|
40
|
-
def default_first_page_criteria(self) -> PagerResultCriteriaType:
|
|
41
|
-
raise ValueError("Cannot generate a default first page criteria for custom reports.")
|
|
42
|
-
|
|
43
|
-
def get_next_page_result(self) -> tuple[CustomReportCriteria | None, Queue[dict[str, FieldValue]]]:
|
|
44
|
-
report: CustomReport = self._report_man.run_custom_report(self.next_page_criteria)
|
|
45
|
-
queue: Queue[dict[str, FieldValue]] = Queue()
|
|
46
|
-
for row in _process_results(report.result_table, self._columns):
|
|
47
|
-
queue.put(row)
|
|
48
|
-
if report.has_next_page:
|
|
49
|
-
next_page_criteria = copy(self.next_page_criteria)
|
|
50
|
-
next_page_criteria.page_number += 1
|
|
51
|
-
return next_page_criteria, queue
|
|
52
|
-
else:
|
|
53
|
-
return None, queue
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class CustomReportDictAutoPager(_DictReportPagerBase):
|
|
57
|
-
"""
|
|
58
|
-
A class that automatically pages through a custom report and returns the results as a list of dictionaries.
|
|
59
|
-
"""
|
|
60
|
-
def __init__(self, user: UserIdentifier, report_criteria: CustomReportCriteria,
|
|
61
|
-
page_number: int = 0, page_size: int = _default_report_page_size):
|
|
62
|
-
"""
|
|
63
|
-
:param user: The current webhook context or a user object to send requests from.
|
|
64
|
-
:param report_criteria: The custom report criteria to run.
|
|
65
|
-
:param page_number: The page number to start on. The first page is page 0.
|
|
66
|
-
:param page_size: The number of results to return per page.
|
|
67
|
-
"""
|
|
68
|
-
first_page_criteria: CustomReportCriteria = copy(report_criteria)
|
|
69
|
-
first_page_criteria.page_number = page_number
|
|
70
|
-
first_page_criteria.page_size = page_size
|
|
71
|
-
super().__init__(user, first_page_criteria)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
class SystemReportDictAutoPager(_DictReportPagerBase):
|
|
75
|
-
"""
|
|
76
|
-
A class that automatically pages through a system report and returns the results as a list of dictionaries.
|
|
77
|
-
|
|
78
|
-
System reports are also known as predefined searches in the system and must be defined in the data designer for
|
|
79
|
-
a specific data type. That is, saved searches created by users cannot be run using this function.
|
|
80
|
-
"""
|
|
81
|
-
def __init__(self, user: UserIdentifier, report_name: str,
|
|
82
|
-
page_number: int = 0, page_size: int = _default_report_page_size):
|
|
83
|
-
"""
|
|
84
|
-
:param user: The current webhook context or a user object to send requests from.
|
|
85
|
-
:param report_name: The name of the system report to run.
|
|
86
|
-
:param page_number: The page number to start on. The first page is page 0.
|
|
87
|
-
:param page_size: The number of results to return per page.
|
|
88
|
-
"""
|
|
89
|
-
first_page_criteria: CustomReportCriteria = CustomReportUtil.get_system_report_criteria(user, report_name)
|
|
90
|
-
first_page_criteria.page_number = page_number
|
|
91
|
-
first_page_criteria.page_size = page_size
|
|
92
|
-
super().__init__(user, first_page_criteria)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
class QuickReportDictAutoPager(_DictReportPagerBase):
|
|
96
|
-
"""
|
|
97
|
-
A class that automatically pages through a quick report and returns the results as a list of dictionaries.
|
|
98
|
-
"""
|
|
99
|
-
def __init__(self, user: UserIdentifier, report_term: RawReportTerm,
|
|
100
|
-
page_number: int = 0, page_size: int = _default_report_page_size):
|
|
101
|
-
"""
|
|
102
|
-
:param user: The current webhook context or a user object to send requests from.
|
|
103
|
-
:param report_term: The raw report term to use for the quick report.
|
|
104
|
-
:param page_number: The page number to start on. The first page is page 0.
|
|
105
|
-
:param page_size: The number of results to return per page.
|
|
106
|
-
"""
|
|
107
|
-
first_page_criteria: CustomReportCriteria = CustomReportUtil.get_quick_report_criteria(user, report_term)
|
|
108
|
-
first_page_criteria.page_number = page_number
|
|
109
|
-
first_page_criteria.page_size = page_size
|
|
110
|
-
super().__init__(user, first_page_criteria)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
class _RecordReportPagerBase(SapioPyAutoPager[CustomReportCriteria, WrappedType], ABC):
|
|
114
|
-
"""
|
|
115
|
-
A base class for automatically paging through a report and returning the results as a list of records.
|
|
116
|
-
"""
|
|
117
|
-
_columns: list[ReportColumn]
|
|
118
|
-
_wrapper: type[WrappedType]
|
|
119
|
-
_data_type: str
|
|
120
|
-
_rec_handler: RecordHandler
|
|
121
|
-
_report_man: CustomReportManager
|
|
122
|
-
|
|
123
|
-
def __init__(self, user: UserIdentifier, first_page_criteria: CustomReportCriteria, wrapper_type: type[WrappedType]):
|
|
124
|
-
self._columns = first_page_criteria.column_list
|
|
125
|
-
self._wrapper = wrapper_type
|
|
126
|
-
self._data_type = wrapper_type.get_wrapper_data_type_name()
|
|
127
|
-
self._rec_handler = RecordHandler(user)
|
|
128
|
-
super().__init__(AliasUtil.to_sapio_user(user), first_page_criteria)
|
|
129
|
-
self._report_man = DataMgmtServer.get_custom_report_manager(self.user)
|
|
130
|
-
|
|
131
|
-
def get_all_at_once(self) -> list[RecordModel]:
|
|
132
|
-
"""
|
|
133
|
-
Get the results of all pages. Be cautious of client memory usage.
|
|
134
|
-
"""
|
|
135
|
-
if self.has_iterated:
|
|
136
|
-
raise BrokenPipeError("Cannot use this method if the iterator has already been used.")
|
|
137
|
-
return [x for x in self]
|
|
138
|
-
|
|
139
|
-
def default_first_page_criteria(self) -> PagerResultCriteriaType:
|
|
140
|
-
raise ValueError("Cannot generate a default first page criteria for custom reports.")
|
|
141
|
-
|
|
142
|
-
def get_next_page_result(self) -> tuple[CustomReportCriteria | None, Queue[WrappedType]]:
|
|
143
|
-
report: CustomReport = self._report_man.run_custom_report(self.next_page_criteria)
|
|
144
|
-
queue: Queue[WrappedType] = Queue()
|
|
145
|
-
id_index: int = -1
|
|
146
|
-
for i, column in enumerate(self._columns):
|
|
147
|
-
if column.data_type_name == self._data_type and column.data_field_name == "RecordId":
|
|
148
|
-
id_index = i
|
|
149
|
-
break
|
|
150
|
-
if id_index == -1:
|
|
151
|
-
raise SapioException(f"This report does not contain a Record ID column for the given record model type "
|
|
152
|
-
f"{self._data_type}.")
|
|
153
|
-
ids: list[int] = [row[id_index] for row in report.result_table]
|
|
154
|
-
for row in self._rec_handler.query_models_by_id(self._wrapper, ids, page_size=report.page_size):
|
|
155
|
-
queue.put(row)
|
|
156
|
-
if report.has_next_page:
|
|
157
|
-
next_page_criteria = copy(self.next_page_criteria)
|
|
158
|
-
next_page_criteria.page_number += 1
|
|
159
|
-
return next_page_criteria, queue
|
|
160
|
-
else:
|
|
161
|
-
return None, queue
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
class CustomReportRecordAutoPager(_RecordReportPagerBase):
|
|
165
|
-
"""
|
|
166
|
-
A class that automatically pages through a custom report and returns the results as a list of records.
|
|
167
|
-
"""
|
|
168
|
-
def __init__(self, user: UserIdentifier, report_criteria: CustomReportCriteria, wrapper_type: type[WrappedType],
|
|
169
|
-
page_number: int = 0, page_size: int = _default_record_page_size):
|
|
170
|
-
"""
|
|
171
|
-
:param user: The current webhook context or a user object to send requests from.
|
|
172
|
-
:param report_criteria: The custom report criteria to run.
|
|
173
|
-
:param wrapper_type: The record model wrapper type to use for the records.
|
|
174
|
-
:param page_number: The page number to start on. The first page is page 0.
|
|
175
|
-
:param page_size: The number of results to return per page.
|
|
176
|
-
"""
|
|
177
|
-
first_page_criteria: CustomReportCriteria = copy(report_criteria)
|
|
178
|
-
_add_record_id_column(first_page_criteria, wrapper_type)
|
|
179
|
-
first_page_criteria.page_number = page_number
|
|
180
|
-
first_page_criteria.page_size = page_size
|
|
181
|
-
super().__init__(user, first_page_criteria, wrapper_type)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
class SystemReportRecordAutoPager(_RecordReportPagerBase):
|
|
185
|
-
"""
|
|
186
|
-
A class that automatically pages through a system report and returns the results as a list of records.
|
|
187
|
-
|
|
188
|
-
System reports are also known as predefined searches in the system and must be defined in the data designer for
|
|
189
|
-
a specific data type. That is, saved searches created by users cannot be run using this function.
|
|
190
|
-
"""
|
|
191
|
-
def __init__(self, user: UserIdentifier, report_name: str, wrapper_type: type[WrappedType],
|
|
192
|
-
page_number: int = 0, page_size: int = _default_record_page_size):
|
|
193
|
-
"""
|
|
194
|
-
:param user: The current webhook context or a user object to send requests from.
|
|
195
|
-
:param report_name: The name of the system report to run.
|
|
196
|
-
:param wrapper_type: The record model wrapper type to use for the records.
|
|
197
|
-
:param page_number: The page number to start on. The first page is page 0.
|
|
198
|
-
:param page_size: The number of results to return per page.
|
|
199
|
-
"""
|
|
200
|
-
first_page_criteria: CustomReportCriteria = CustomReportUtil.get_system_report_criteria(user, report_name)
|
|
201
|
-
_add_record_id_column(first_page_criteria, wrapper_type)
|
|
202
|
-
first_page_criteria.page_number = page_number
|
|
203
|
-
first_page_criteria.page_size = page_size
|
|
204
|
-
super().__init__(user, first_page_criteria, wrapper_type)
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
class QuickReportRecordAutoPager(_RecordReportPagerBase):
|
|
208
|
-
"""
|
|
209
|
-
A class that automatically pages through a quick report and returns the results as a list of records.
|
|
210
|
-
"""
|
|
211
|
-
def __init__(self, user: UserIdentifier, report_term: RawReportTerm, wrapper_type: type[WrappedType],
|
|
212
|
-
page_number: int = 0, page_size: int = _default_record_page_size):
|
|
213
|
-
"""
|
|
214
|
-
:param user: The current webhook context or a user object to send requests from.
|
|
215
|
-
:param report_term: The raw report term to use for the quick report.
|
|
216
|
-
:param wrapper_type: The record model wrapper type to use for the records.
|
|
217
|
-
:param page_number: The page number to start on. The first page is page 0.
|
|
218
|
-
:param page_size: The number of results to return per page.
|
|
219
|
-
"""
|
|
220
|
-
if report_term.data_type_name != wrapper_type.get_wrapper_data_type_name():
|
|
221
|
-
raise SapioException("The data type name of the report term must match the data type name of the wrapper type.")
|
|
222
|
-
first_page_criteria: CustomReportCriteria = CustomReportUtil.get_quick_report_criteria(user, report_term)
|
|
223
|
-
first_page_criteria.page_number = page_number
|
|
224
|
-
first_page_criteria.page_size = page_size
|
|
225
|
-
super().__init__(user, first_page_criteria, wrapper_type)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
def _add_record_id_column(report: CustomReportCriteria, wrapper_type: type[WrappedType]) -> None:
|
|
229
|
-
"""
|
|
230
|
-
Given a custom report criteria, ensure that the report contains a Record ID column for the given record model's
|
|
231
|
-
data type. Add one if it is missing.
|
|
232
|
-
"""
|
|
233
|
-
dt: str = wrapper_type.get_wrapper_data_type_name()
|
|
234
|
-
# Ensure that the root data type is the one we're looking for.
|
|
235
|
-
report.root_data_type = dt
|
|
236
|
-
# Enforce that the given custom report has a record ID column.
|
|
237
|
-
if not any([x.data_type_name == dt and x.data_field_name == "RecordId" for x in report.column_list]):
|
|
238
|
-
report.column_list.append(ReportColumn(dt, "RecordId", FieldType.LONG))
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
def _process_results(rows: list[list[FieldValue]], columns: list[ReportColumn]) -> list[dict[str, FieldValue]]:
|
|
242
|
-
"""
|
|
243
|
-
Given the results of a report as a list of row values and the report's columns, combine these lists to
|
|
244
|
-
result in a singular list of dictionaries for each row in the results.
|
|
245
|
-
"""
|
|
246
|
-
# It may be the case that two columns have the same data field name but differing data type names.
|
|
247
|
-
# If this occurs, then we need to be able to differentiate these columns in the resulting dictionary.
|
|
248
|
-
prepend_dt: set[str] = set()
|
|
249
|
-
encountered_names: list[str] = []
|
|
250
|
-
for column in columns:
|
|
251
|
-
field_name: str = column.data_field_name
|
|
252
|
-
if field_name in encountered_names:
|
|
253
|
-
prepend_dt.add(field_name)
|
|
254
|
-
else:
|
|
255
|
-
encountered_names.append(field_name)
|
|
256
|
-
|
|
257
|
-
ret: list[dict[str, FieldValue]] = []
|
|
258
|
-
for row in rows:
|
|
259
|
-
row_data: dict[str, FieldValue] = {}
|
|
260
|
-
filter_row: bool = False
|
|
261
|
-
for value, column in zip(row, columns):
|
|
262
|
-
header: str = column.data_field_name
|
|
263
|
-
# If two columns share the same data field name, prepend the data type name of the column to the
|
|
264
|
-
# data field name.
|
|
265
|
-
if header in prepend_dt:
|
|
266
|
-
header = column.data_type_name + "." + header
|
|
267
|
-
row_data.update({header: value})
|
|
268
|
-
if filter_row is False:
|
|
269
|
-
ret.append(row_data)
|
|
270
|
-
return ret
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
from typing import Iterable, cast
|
|
2
|
-
|
|
3
|
-
from sapiopylib.rest.User import SapioUser
|
|
4
|
-
from sapiopylib.rest.pojo.CustomReport import CustomReportCriteria, CustomReport
|
|
5
|
-
from sapiopylib.rest.pojo.webhook.WebhookDirective import HomePageDirective, FormDirective, TableDirective, \
|
|
6
|
-
CustomReportDirective, ElnExperimentDirective, ExperimentEntryDirective
|
|
7
|
-
|
|
8
|
-
from sapiopycommons.general.aliases import SapioRecord, AliasUtil, ExperimentIdentifier, ExperimentEntryIdentifier, \
|
|
9
|
-
UserIdentifier
|
|
10
|
-
from sapiopycommons.general.custom_report_util import CustomReportUtil
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
# FR-47392: Create a DirectiveUtil class to simplify the creation of directives.
|
|
14
|
-
class DirectiveUtil:
|
|
15
|
-
"""
|
|
16
|
-
DirectiveUtil is a class for creating webhook directives. The utility functions reduce the provided variables
|
|
17
|
-
down to the exact type that the directives require, removing the need for the caller to handle the conversion.
|
|
18
|
-
"""
|
|
19
|
-
user: SapioUser
|
|
20
|
-
|
|
21
|
-
def __init__(self, context: UserIdentifier):
|
|
22
|
-
"""
|
|
23
|
-
:param context: The current webhook context or a user object to send requests from.
|
|
24
|
-
"""
|
|
25
|
-
self.user = AliasUtil.to_sapio_user(context)
|
|
26
|
-
|
|
27
|
-
@staticmethod
|
|
28
|
-
def homepage() -> HomePageDirective:
|
|
29
|
-
"""
|
|
30
|
-
:return: A directive that sends the user back to their home page.
|
|
31
|
-
"""
|
|
32
|
-
return HomePageDirective()
|
|
33
|
-
|
|
34
|
-
@staticmethod
|
|
35
|
-
def record_form(record: SapioRecord) -> FormDirective:
|
|
36
|
-
"""
|
|
37
|
-
:param record: A record in the system.
|
|
38
|
-
:return: A directive that sends the user to a specific data record form.
|
|
39
|
-
"""
|
|
40
|
-
return FormDirective(AliasUtil.to_data_record(record))
|
|
41
|
-
|
|
42
|
-
@staticmethod
|
|
43
|
-
def record_table(records: Iterable[SapioRecord]) -> TableDirective:
|
|
44
|
-
"""
|
|
45
|
-
:param records: A list of records in the system.
|
|
46
|
-
:return: A directive that sends the user to a table of data records.
|
|
47
|
-
"""
|
|
48
|
-
return TableDirective(AliasUtil.to_data_records(records))
|
|
49
|
-
|
|
50
|
-
@staticmethod
|
|
51
|
-
def record_adaptive(records: Iterable[SapioRecord]) -> TableDirective | FormDirective:
|
|
52
|
-
"""
|
|
53
|
-
:param records: A list of records in the system.
|
|
54
|
-
:return: A directive that sends the user to a table of data records if there are multiple records,
|
|
55
|
-
or a directive that sends the user to a specific data record form if there is only one record.
|
|
56
|
-
"""
|
|
57
|
-
records: list[SapioRecord] = list(records)
|
|
58
|
-
if len(records) == 1:
|
|
59
|
-
return DirectiveUtil.record_form(records[0])
|
|
60
|
-
return DirectiveUtil.record_table(records)
|
|
61
|
-
|
|
62
|
-
def custom_report(self, report: CustomReport | CustomReportCriteria | str) -> CustomReportDirective:
|
|
63
|
-
"""
|
|
64
|
-
:param report: A custom report, the criteria for a custom report, or the name of a system report.
|
|
65
|
-
:return: A directive that sends the user to the results of the provided custom report.
|
|
66
|
-
"""
|
|
67
|
-
if isinstance(report, str):
|
|
68
|
-
report: CustomReport = CustomReportUtil.get_system_report_criteria(self.user, report)
|
|
69
|
-
return CustomReportDirective(cast(CustomReport, report))
|
|
70
|
-
|
|
71
|
-
@staticmethod
|
|
72
|
-
def eln_experiment(experiment: ExperimentIdentifier) -> ElnExperimentDirective:
|
|
73
|
-
"""
|
|
74
|
-
:param experiment: An identifier for an experiment.
|
|
75
|
-
:return: A directive that sends the user to the ELN experiment.
|
|
76
|
-
"""
|
|
77
|
-
return ElnExperimentDirective(AliasUtil.to_notebook_id(experiment))
|
|
78
|
-
|
|
79
|
-
@staticmethod
|
|
80
|
-
def eln_entry(experiment: ExperimentIdentifier, entry: ExperimentEntryIdentifier) -> ExperimentEntryDirective:
|
|
81
|
-
"""
|
|
82
|
-
:param experiment: An identifier for an experiment.
|
|
83
|
-
:param entry: An identifier for an entry in the experiment.
|
|
84
|
-
:return: A directive that sends the user to the provided experiment entry within its ELN experiment.
|
|
85
|
-
"""
|
|
86
|
-
return ExperimentEntryDirective(AliasUtil.to_notebook_id(experiment), AliasUtil.to_entry_id(entry))
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
from sapiopycommons.general.aliases import SapioRecord
|
|
2
|
-
from sapiopylib.rest.User import SapioUser
|
|
3
|
-
from sapiopylib.rest.utils.MultiMap import SetMultimap
|
|
4
|
-
|
|
5
|
-
# FR-47421 Added module
|
|
6
|
-
|
|
7
|
-
def create_aliquot_for_samples(parent_sample_to_num_aliquots_map: dict[SapioRecord, int], user: SapioUser) -> SetMultimap[SapioRecord, int]:
|
|
8
|
-
""""
|
|
9
|
-
Ask server to create aliquot records for provided sample parent records.
|
|
10
|
-
:param parent_sample_to_num_aliquots_map: The dictionary containing (parent sample record) -> (number of aliquots to create) mapping.
|
|
11
|
-
:return: The dictionary containing (parent sample record) -> (list of new aliquot record ids) mapping.
|
|
12
|
-
"""
|
|
13
|
-
# throw error if at least one record id is blank
|
|
14
|
-
has_negative_record_ids = any([record.record_id < 0 for record in parent_sample_to_num_aliquots_map.keys()])
|
|
15
|
-
if has_negative_record_ids:
|
|
16
|
-
raise ValueError("At least one record requested for aliquot has a negative record ID. "
|
|
17
|
-
"You should have stored record model changes first.")
|
|
18
|
-
has_blank_record_ids = any([record.record_id is None for record in parent_sample_to_num_aliquots_map.keys()])
|
|
19
|
-
if has_blank_record_ids:
|
|
20
|
-
raise ValueError("At least one record requested for aliquot does not currently have a record ID.")
|
|
21
|
-
record_id_to_sapio_record_map = {record.record_id: record for record in parent_sample_to_num_aliquots_map.keys()}
|
|
22
|
-
parent_record_id_to_num_aliquots_map = {record.record_id: num_aliquots for record, num_aliquots in parent_sample_to_num_aliquots_map.items()}
|
|
23
|
-
aliquot_result: SetMultimap[int, int] = create_aliquot_for_samples_record_ids(parent_record_id_to_num_aliquots_map, user)
|
|
24
|
-
ret: SetMultimap[SapioRecord, int] = SetMultimap()
|
|
25
|
-
for parent_record_id in aliquot_result.keys():
|
|
26
|
-
parent_record = record_id_to_sapio_record_map[parent_record_id]
|
|
27
|
-
for aliquot_record_id in aliquot_result.get(parent_record_id):
|
|
28
|
-
ret.put(parent_record, aliquot_record_id)
|
|
29
|
-
return ret
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def create_aliquot_for_samples_record_ids(parent_record_id_to_num_aliquots_map: dict[int, int], user: SapioUser) -> SetMultimap[int, int]:
|
|
33
|
-
"""
|
|
34
|
-
Ask the server to create aliquot records for the provided sample record IDs.
|
|
35
|
-
:param sample_record_id_list: The dictionary containing (parent sample record id) -> (number of aliquots to create) mapping.
|
|
36
|
-
:return: The dictionary containing (parent sample record id) -> (list of new aliquot record ids) mapping.
|
|
37
|
-
"""
|
|
38
|
-
if not parent_record_id_to_num_aliquots_map:
|
|
39
|
-
return SetMultimap()
|
|
40
|
-
endpoint_path = 'sample/aliquot'
|
|
41
|
-
response = user.plugin_put(endpoint_path, payload=parent_record_id_to_num_aliquots_map)
|
|
42
|
-
user.raise_for_status(response)
|
|
43
|
-
response_map: dict[int, list[int]] = response.json()
|
|
44
|
-
ret: SetMultimap[int, int] = SetMultimap()
|
|
45
|
-
for parent_record_id, aliquot_record_ids in response_map.items():
|
|
46
|
-
for aliquot_record_id in aliquot_record_ids:
|
|
47
|
-
ret.put(int(parent_record_id), int(aliquot_record_id))
|
|
48
|
-
return ret
|
|
File without changes
|
{sapiopycommons-2025.3.6a454.dist-info → sapiopycommons-2025.3.10a455.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|