sapiopycommons 2025.7.1a576__py3-none-any.whl → 2025.7.2a579__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 +665 -332
- sapiopycommons/callbacks/field_builder.py +2 -0
- sapiopycommons/chem/IndigoMolecules.py +31 -1
- sapiopycommons/chem/Molecules.py +3 -3
- sapiopycommons/chem/ps_commons.py +381 -0
- sapiopycommons/customreport/auto_pagers.py +26 -1
- sapiopycommons/customreport/term_builder.py +1 -1
- sapiopycommons/datatype/pseudo_data_types.py +349 -326
- sapiopycommons/eln/experiment_cache.py +188 -0
- sapiopycommons/eln/experiment_handler.py +408 -767
- sapiopycommons/eln/experiment_report_util.py +11 -6
- sapiopycommons/eln/experiment_step_factory.py +476 -0
- sapiopycommons/eln/plate_designer.py +7 -2
- sapiopycommons/eln/step_creation.py +236 -0
- sapiopycommons/files/file_util.py +7 -5
- sapiopycommons/general/accession_service.py +2 -2
- sapiopycommons/general/aliases.py +3 -1
- sapiopycommons/general/audit_log.py +7 -0
- sapiopycommons/general/custom_report_util.py +12 -0
- sapiopycommons/general/data_structure_util.py +115 -0
- sapiopycommons/processtracking/custom_workflow_handler.py +11 -1
- sapiopycommons/processtracking/endpoints.py +27 -0
- sapiopycommons/recordmodel/record_handler.py +657 -317
- sapiopycommons/rules/eln_rule_handler.py +8 -1
- sapiopycommons/rules/on_save_rule_handler.py +8 -1
- sapiopycommons/webhook/webhook_handlers.py +3 -0
- sapiopycommons/webhook/webservice_handlers.py +2 -2
- {sapiopycommons-2025.7.1a576.dist-info → sapiopycommons-2025.7.2a579.dist-info}/METADATA +2 -2
- sapiopycommons-2025.7.2a579.dist-info/RECORD +69 -0
- sapiopycommons/ai/__init__.py +0 -0
- sapiopycommons/ai/api/fielddefinitions/proto/fields_pb2.py +0 -43
- sapiopycommons/ai/api/fielddefinitions/proto/fields_pb2.pyi +0 -31
- sapiopycommons/ai/api/fielddefinitions/proto/fields_pb2_grpc.py +0 -24
- sapiopycommons/ai/api/fielddefinitions/proto/velox_field_def_pb2.py +0 -123
- sapiopycommons/ai/api/fielddefinitions/proto/velox_field_def_pb2.pyi +0 -598
- sapiopycommons/ai/api/fielddefinitions/proto/velox_field_def_pb2_grpc.py +0 -24
- sapiopycommons/ai/api/plan/proto/step_output_pb2.py +0 -45
- sapiopycommons/ai/api/plan/proto/step_output_pb2.pyi +0 -42
- sapiopycommons/ai/api/plan/proto/step_output_pb2_grpc.py +0 -24
- sapiopycommons/ai/api/plan/proto/step_pb2.py +0 -43
- sapiopycommons/ai/api/plan/proto/step_pb2.pyi +0 -43
- sapiopycommons/ai/api/plan/proto/step_pb2_grpc.py +0 -24
- sapiopycommons/ai/api/plan/script/proto/script_pb2.py +0 -53
- sapiopycommons/ai/api/plan/script/proto/script_pb2.pyi +0 -99
- sapiopycommons/ai/api/plan/script/proto/script_pb2_grpc.py +0 -153
- sapiopycommons/ai/api/plan/tool/proto/entry_pb2.py +0 -57
- sapiopycommons/ai/api/plan/tool/proto/entry_pb2.pyi +0 -96
- sapiopycommons/ai/api/plan/tool/proto/entry_pb2_grpc.py +0 -24
- sapiopycommons/ai/api/plan/tool/proto/tool_pb2.py +0 -67
- sapiopycommons/ai/api/plan/tool/proto/tool_pb2.pyi +0 -220
- sapiopycommons/ai/api/plan/tool/proto/tool_pb2_grpc.py +0 -154
- sapiopycommons/ai/api/session/proto/sapio_conn_info_pb2.py +0 -39
- sapiopycommons/ai/api/session/proto/sapio_conn_info_pb2.pyi +0 -32
- sapiopycommons/ai/api/session/proto/sapio_conn_info_pb2_grpc.py +0 -24
- sapiopycommons/ai/protobuf_utils.py +0 -508
- sapiopycommons/ai/test_client.py +0 -216
- sapiopycommons/ai/tool_service_base.py +0 -798
- sapiopycommons-2025.7.1a576.dist-info/RECORD +0 -92
- {sapiopycommons-2025.7.1a576.dist-info → sapiopycommons-2025.7.2a579.dist-info}/WHEEL +0 -0
- {sapiopycommons-2025.7.1a576.dist-info → sapiopycommons-2025.7.2a579.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,7 +2,7 @@ from sapiopylib.rest.ELNService import ElnManager
|
|
|
2
2
|
from sapiopylib.rest.User import SapioUser
|
|
3
3
|
from sapiopylib.rest.pojo.CustomReport import CustomReportCriteria, AbstractReportTerm, RawReportTerm
|
|
4
4
|
from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
|
|
5
|
-
from sapiopylib.rest.pojo.eln.ElnExperiment import ElnExperiment, ElnExperimentQueryCriteria
|
|
5
|
+
from sapiopylib.rest.pojo.eln.ElnExperiment import ElnExperiment, ElnExperimentQueryCriteria, ElnTemplate
|
|
6
6
|
from sapiopylib.rest.pojo.eln.SapioELNEnums import ElnExperimentStatus, ElnBaseDataType
|
|
7
7
|
from sapiopylib.rest.utils.recordmodel.PyRecordModel import PyRecordModel
|
|
8
8
|
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
@@ -250,17 +250,22 @@ class ExperimentReportUtil:
|
|
|
250
250
|
return {exp: list(records) for exp, records in exp_to_records.items()}
|
|
251
251
|
|
|
252
252
|
@staticmethod
|
|
253
|
-
def get_experiment_options(context: UserIdentifier, experiments: list[ExperimentIdentifier]) \
|
|
253
|
+
def get_experiment_options(context: UserIdentifier, experiments: list[ExperimentIdentifier | ElnTemplate]) \
|
|
254
254
|
-> dict[int, dict[str, str]]:
|
|
255
255
|
"""
|
|
256
|
-
Run a custom report to retrieve the experiment options for all the provided experiments
|
|
257
|
-
version of the get_notebook_experiment_options function of ElnManager.
|
|
256
|
+
Run a custom report to retrieve the experiment options for all the provided experiments or experiment templates.
|
|
257
|
+
Effectively a batched version of the get_notebook_experiment_options function of ElnManager.
|
|
258
258
|
|
|
259
259
|
:param context: The current webhook context or a user object to send requests from.
|
|
260
|
-
:param experiments: The experiment identifiers to retrieve the experiment options for.
|
|
260
|
+
:param experiments: The experiment/template identifiers to retrieve the experiment options for.
|
|
261
261
|
:return: A dictionary mapping the notebook experiment ID to the options for that experiment.
|
|
262
262
|
"""
|
|
263
|
-
exp_ids: list[int] =
|
|
263
|
+
exp_ids: list[int] = []
|
|
264
|
+
for exp in experiments:
|
|
265
|
+
if isinstance(exp, ElnTemplate):
|
|
266
|
+
exp_ids.append(exp.template_id)
|
|
267
|
+
else:
|
|
268
|
+
exp_ids.append(AliasUtil.to_notebook_id(exp))
|
|
264
269
|
|
|
265
270
|
report_builder = CustomReportBuilder(NotebookExperimentOptionPseudoDef.DATA_TYPE_NAME)
|
|
266
271
|
tb = report_builder.get_term_builder()
|
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
from typing import cast
|
|
5
|
+
|
|
6
|
+
from sapiopycommons.datatype.data_fields import SystemFields
|
|
7
|
+
from sapiopycommons.eln.step_creation import StepCreation, AttachmentStepCreation, GlobalDtFormStepCreation, \
|
|
8
|
+
ELnDtFormStepCreation, PluginStepCreation, TextStepCreation, TempDataStepCreation, ELnDtTableStepCreation, \
|
|
9
|
+
GlobalDtTableStepCreation, DashboardStepCreation
|
|
10
|
+
from sapiopycommons.eln.experiment_cache import ExperimentCacheManager
|
|
11
|
+
from sapiopycommons.eln.experiment_handler import ExperimentHandler, Step
|
|
12
|
+
from sapiopycommons.eln.experiment_tags import PLATE_DESIGNER_PLUGIN
|
|
13
|
+
from sapiopycommons.general.aliases import AliasUtil, SapioRecord, DataTypeIdentifier, FieldMap, \
|
|
14
|
+
ExperimentEntryIdentifier
|
|
15
|
+
from sapiopycommons.general.exceptions import SapioException
|
|
16
|
+
from sapiopylib.rest.DataRecordManagerService import DataRecordManager
|
|
17
|
+
from sapiopylib.rest.ELNService import ElnManager
|
|
18
|
+
from sapiopylib.rest.User import SapioUser
|
|
19
|
+
from sapiopylib.rest.pojo.DataRecord import DataRecord
|
|
20
|
+
from sapiopylib.rest.pojo.TableColumn import TableColumn
|
|
21
|
+
from sapiopylib.rest.pojo.eln.ElnEntryPosition import ElnEntryPosition
|
|
22
|
+
from sapiopylib.rest.pojo.eln.ExperimentEntry import EntryAttachment, EntryRecordAttachment
|
|
23
|
+
from sapiopylib.rest.pojo.eln.ExperimentEntryCriteria import ExperimentEntryCriteriaUtil, AbstractElnEntryCriteria, \
|
|
24
|
+
ElnAttachmentEntryCriteria, \
|
|
25
|
+
ElnFormEntryCriteria, ElnPluginEntryCriteria, ElnTextEntryCriteria, ElnTempDataEntryCriteria, ElnTableEntryCriteria, \
|
|
26
|
+
ElnDashboardEntryCriteria
|
|
27
|
+
from sapiopylib.rest.pojo.eln.SapioELNEnums import ElnBaseDataType
|
|
28
|
+
from sapiopylib.rest.pojo.eln.eln_headings import ElnExperimentTab
|
|
29
|
+
from sapiopylib.rest.pojo.eln.field_set import ElnFieldSetInfo
|
|
30
|
+
from sapiopylib.rest.utils.Protocols import ElnEntryStep
|
|
31
|
+
from sapiopylib.rest.utils.plates.MultiLayerPlating import MultiLayerPlateConfig, MultiLayerPlateLayer, \
|
|
32
|
+
MultiLayerDataTypeConfig, MultiLayerReplicateConfig, MultiLayerDilutionConfig
|
|
33
|
+
from sapiopylib.rest.utils.plates.MultiLayerPlatingUtils import MultiLayerPlatingManager
|
|
34
|
+
from sapiopylib.rest.utils.plates.PlatingUtils import PlatingOrder
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# CR-47564: Moved the entry creation functions to their own class.
|
|
38
|
+
class ExperimentStepFactory:
|
|
39
|
+
user: SapioUser
|
|
40
|
+
_exp_handler: ExperimentHandler
|
|
41
|
+
|
|
42
|
+
_exp_id: int
|
|
43
|
+
|
|
44
|
+
_eln_man: ElnManager
|
|
45
|
+
_exp_cache: ExperimentCacheManager
|
|
46
|
+
|
|
47
|
+
def __init__(self, exp_handler: ExperimentHandler):
|
|
48
|
+
self.user = exp_handler.user
|
|
49
|
+
self._exp_handler = exp_handler
|
|
50
|
+
self._exp_id = exp_handler.protocol.get_id()
|
|
51
|
+
self._eln_man = ElnManager(self.user)
|
|
52
|
+
self._exp_cache = ExperimentCacheManager(self.user)
|
|
53
|
+
|
|
54
|
+
# FR-47468: Add functions for creating new entries in the experiment.
|
|
55
|
+
def create_attachment_step(self, entry_name: str, data_type: DataTypeIdentifier,
|
|
56
|
+
attachments: Iterable[EntryAttachment] | Iterable[SapioRecord] | None = None,
|
|
57
|
+
criteria: AttachmentStepCreation = AttachmentStepCreation(),
|
|
58
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
59
|
+
"""
|
|
60
|
+
Create a new attachment entry in the experiment.
|
|
61
|
+
|
|
62
|
+
:param entry_name: The name of the entry.
|
|
63
|
+
:param data_type: The data type of the entry.
|
|
64
|
+
:param attachments: The list of attachments to initially populate the entry with.
|
|
65
|
+
:param criteria: Additional criteria for creating the entry.
|
|
66
|
+
:param position: Information about where to place the entry in the experiment.
|
|
67
|
+
:return: The newly created attachment entry.
|
|
68
|
+
"""
|
|
69
|
+
entry_criteria = cast(ElnAttachmentEntryCriteria,
|
|
70
|
+
self._create_step_criteria(entry_name, data_type, criteria, position))
|
|
71
|
+
|
|
72
|
+
if attachments:
|
|
73
|
+
entry_attachments: list[EntryAttachment] = []
|
|
74
|
+
for entry in attachments:
|
|
75
|
+
if isinstance(entry, EntryAttachment):
|
|
76
|
+
entry_attachments.append(entry)
|
|
77
|
+
elif isinstance(entry, SapioRecord):
|
|
78
|
+
entry: SapioRecord
|
|
79
|
+
file_name: str = entry.get_field_value("FilePath")
|
|
80
|
+
if not file_name:
|
|
81
|
+
file_name = entry.get_field_value(SystemFields.DATA_RECORD_NAME__FIELD.field_name)
|
|
82
|
+
rec_id: int = AliasUtil.to_record_id(entry)
|
|
83
|
+
entry_attachments.append(EntryRecordAttachment(file_name, rec_id))
|
|
84
|
+
else:
|
|
85
|
+
raise SapioException("Attachments must be of type EntryAttachment or SapioRecord.")
|
|
86
|
+
entry_criteria.entry_attachment_list = entry_attachments
|
|
87
|
+
|
|
88
|
+
step = self._exp_handler.add_entry_to_caches(self._eln_man.add_experiment_entry(self._exp_id, entry_criteria))
|
|
89
|
+
return step
|
|
90
|
+
|
|
91
|
+
def create_dashboard_step(self, entry_name, data_type: DataTypeIdentifier,
|
|
92
|
+
criteria: DashboardStepCreation = DashboardStepCreation(),
|
|
93
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
94
|
+
entry_criteria = cast(ElnDashboardEntryCriteria,
|
|
95
|
+
self._create_step_criteria(entry_name, data_type, criteria, position))
|
|
96
|
+
|
|
97
|
+
if criteria:
|
|
98
|
+
if entry_criteria.source_entry_id:
|
|
99
|
+
entry_criteria.source_entry_id = self.__to_entry_ids([criteria.source_entry])[0]
|
|
100
|
+
if criteria.dashboard_guids:
|
|
101
|
+
entry_criteria.dashboard_guid_list = list(criteria.dashboard_guids)
|
|
102
|
+
|
|
103
|
+
step = self._exp_handler.add_entry_to_caches(self._eln_man.add_experiment_entry(self._exp_id, entry_criteria))
|
|
104
|
+
return step
|
|
105
|
+
|
|
106
|
+
def create_form_step(self, entry_name: str,
|
|
107
|
+
data_type: DataTypeIdentifier,
|
|
108
|
+
record: SapioRecord | None = None,
|
|
109
|
+
criteria: GlobalDtFormStepCreation = GlobalDtFormStepCreation(),
|
|
110
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
111
|
+
"""
|
|
112
|
+
Create a new form entry in the experiment.
|
|
113
|
+
|
|
114
|
+
:param entry_name: The name of the entry.
|
|
115
|
+
:param data_type: The data type of the entry.
|
|
116
|
+
:param record: The record to initially populate the entry with.
|
|
117
|
+
:param criteria: Additional criteria for creating the entry.
|
|
118
|
+
:param position: Information about where to place the entry in the experiment.
|
|
119
|
+
:return: The newly created form entry.
|
|
120
|
+
"""
|
|
121
|
+
if record:
|
|
122
|
+
rdt: str = AliasUtil.to_data_type_name(record)
|
|
123
|
+
sdt: str = AliasUtil.to_data_type_name(data_type)
|
|
124
|
+
if rdt != sdt:
|
|
125
|
+
raise SapioException(f"Cannot set {rdt} records for entry {entry_name} of type "
|
|
126
|
+
f"{sdt}.")
|
|
127
|
+
|
|
128
|
+
entry_criteria = cast(ElnFormEntryCriteria,
|
|
129
|
+
self._create_step_criteria(entry_name, data_type, criteria, position))
|
|
130
|
+
if record:
|
|
131
|
+
entry_criteria.record_id = AliasUtil.to_record_id(record)
|
|
132
|
+
if criteria:
|
|
133
|
+
entry_criteria.data_type_layout_name = criteria.layout_name
|
|
134
|
+
if entry_criteria.form_name_list:
|
|
135
|
+
entry_criteria.form_name_list = list(criteria.form_names)
|
|
136
|
+
if criteria.extension_types:
|
|
137
|
+
entry_criteria.extension_type_list = AliasUtil.to_data_type_names(criteria.extension_types)
|
|
138
|
+
if criteria.field_names:
|
|
139
|
+
entry_criteria.data_field_name_list = AliasUtil.to_data_field_names(criteria.field_names)
|
|
140
|
+
|
|
141
|
+
step = self._exp_handler.add_entry_to_caches(self._eln_man.add_experiment_entry(self._exp_id, entry_criteria))
|
|
142
|
+
return step
|
|
143
|
+
|
|
144
|
+
def create_experiment_detail_form_step(self, entry_name: str,
|
|
145
|
+
field_map: FieldMap | None = None,
|
|
146
|
+
criteria: ELnDtFormStepCreation = ELnDtFormStepCreation(),
|
|
147
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
148
|
+
"""
|
|
149
|
+
Create a new ELN experiment details form entry in the experiment.
|
|
150
|
+
|
|
151
|
+
:param entry_name: The name of the entry.
|
|
152
|
+
:param field_map: A field map that will be used to populate the entry. The data field names in
|
|
153
|
+
the map must match the field names of the provided field definitions.
|
|
154
|
+
:param criteria: Additional criteria for creating the entry.
|
|
155
|
+
:param position: Information about where to place the entry in the experiment.
|
|
156
|
+
:return: The newly created form entry.
|
|
157
|
+
"""
|
|
158
|
+
dt = ElnBaseDataType.EXPERIMENT_DETAIL
|
|
159
|
+
return self._create_eln_dt_form_step(entry_name, dt, field_map, criteria, position)
|
|
160
|
+
|
|
161
|
+
def create_sample_detail_form_step(self, entry_name: str,
|
|
162
|
+
field_map: FieldMap | None = None,
|
|
163
|
+
criteria: ELnDtFormStepCreation = ELnDtFormStepCreation(),
|
|
164
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
165
|
+
"""
|
|
166
|
+
Create a new ELN sample details form entry in the experiment.
|
|
167
|
+
|
|
168
|
+
:param entry_name: The name of the entry.
|
|
169
|
+
:param field_map: A field map that will be used to populate the entry. The data field names in
|
|
170
|
+
the map must match the field names of the provided field definitions.
|
|
171
|
+
:param criteria: Additional criteria for creating the entry.
|
|
172
|
+
:param position: Information about where to place the entry in the experiment.
|
|
173
|
+
:return: The newly created form entry.
|
|
174
|
+
"""
|
|
175
|
+
dt = ElnBaseDataType.SAMPLE_DETAIL
|
|
176
|
+
return self._create_eln_dt_form_step(entry_name, dt, field_map, criteria, position)
|
|
177
|
+
|
|
178
|
+
def _create_eln_dt_form_step(self, entry_name: str,
|
|
179
|
+
dt: ElnBaseDataType,
|
|
180
|
+
field_map: FieldMap | None = None,
|
|
181
|
+
criteria: ELnDtFormStepCreation = ELnDtFormStepCreation(),
|
|
182
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
183
|
+
entry_criteria = cast(ElnFormEntryCriteria,
|
|
184
|
+
self._create_step_criteria(entry_name, dt.data_type_name, criteria, position))
|
|
185
|
+
|
|
186
|
+
if field_map:
|
|
187
|
+
entry_criteria.field_map = field_map
|
|
188
|
+
if criteria:
|
|
189
|
+
entry_criteria.is_field_addable = criteria.is_field_addable
|
|
190
|
+
entry_criteria.is_existing_field_removable = criteria.is_existing_field_removable
|
|
191
|
+
if criteria.field_sets:
|
|
192
|
+
field_sets: set[int] = set()
|
|
193
|
+
for field_set in criteria.field_sets:
|
|
194
|
+
if isinstance(field_set, int):
|
|
195
|
+
field_sets.add(field_set)
|
|
196
|
+
elif isinstance(field_set, ElnFieldSetInfo):
|
|
197
|
+
field_sets.add(field_set.field_set_id)
|
|
198
|
+
elif isinstance(field_set, str):
|
|
199
|
+
field_sets.add(self._exp_cache.get_field_set(field_set).field_set_id)
|
|
200
|
+
entry_criteria.field_set_id_list = list(field_sets)
|
|
201
|
+
if criteria.field_definitions:
|
|
202
|
+
entry_criteria.field_definition_list = list(criteria.field_definitions)
|
|
203
|
+
if criteria.predefined_field_names:
|
|
204
|
+
if entry_criteria.field_definition_list is None:
|
|
205
|
+
entry_criteria.field_definition_list = []
|
|
206
|
+
for field_name in criteria.predefined_field_names:
|
|
207
|
+
entry_criteria.field_definition_list.append(self._exp_cache.get_predefined_field(field_name, dt))
|
|
208
|
+
|
|
209
|
+
step = self._exp_handler.add_entry_to_caches(self._eln_man.add_experiment_entry(self._exp_id, entry_criteria))
|
|
210
|
+
return step
|
|
211
|
+
|
|
212
|
+
def create_plugin_step(self, entry_name: str,
|
|
213
|
+
data_type: DataTypeIdentifier,
|
|
214
|
+
criteria: PluginStepCreation | None,
|
|
215
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
216
|
+
"""
|
|
217
|
+
Create a new plugin entry in the experiment.
|
|
218
|
+
|
|
219
|
+
:param entry_name: The name of the entry.
|
|
220
|
+
:param data_type: The data type of the entry.
|
|
221
|
+
:param criteria: Additional criteria for creating the entry, such as plugin name and whether it provides
|
|
222
|
+
:param position: Information about where to place the entry in the experiment.
|
|
223
|
+
:return: The newly created plugin entry.
|
|
224
|
+
"""
|
|
225
|
+
entry_criteria = cast(ElnPluginEntryCriteria,
|
|
226
|
+
self._create_step_criteria(entry_name, data_type, criteria, position))
|
|
227
|
+
|
|
228
|
+
if criteria:
|
|
229
|
+
entry_criteria.csp_plugin_name = criteria.plugin_name
|
|
230
|
+
entry_criteria.using_template_data = criteria.using_template_data
|
|
231
|
+
entry_criteria.provides_template_data = criteria.provides_template_data
|
|
232
|
+
|
|
233
|
+
step = self._exp_handler.add_entry_to_caches(self._eln_man.add_experiment_entry(self._exp_id, entry_criteria))
|
|
234
|
+
return step
|
|
235
|
+
|
|
236
|
+
def create_table_step(self, entry_name: str, data_type: DataTypeIdentifier,
|
|
237
|
+
records: list[SapioRecord] | None = None,
|
|
238
|
+
criteria: GlobalDtTableStepCreation = GlobalDtTableStepCreation(),
|
|
239
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
240
|
+
"""
|
|
241
|
+
Create a new table entry in the experiment.
|
|
242
|
+
|
|
243
|
+
:param entry_name: The name of the entry.
|
|
244
|
+
:param data_type: The data type of the entry.
|
|
245
|
+
:param criteria: Additional criteria for creating the entry.
|
|
246
|
+
:param position: Information about where to place the entry in the experiment.
|
|
247
|
+
:param records: The list of records to initially populate the entry with.
|
|
248
|
+
:return: The newly created table entry.
|
|
249
|
+
"""
|
|
250
|
+
entry_criteria = cast(ElnTableEntryCriteria,
|
|
251
|
+
self._create_step_criteria(entry_name, data_type, criteria, position))
|
|
252
|
+
if criteria:
|
|
253
|
+
entry_criteria.data_type_layout_name = criteria.layout_name
|
|
254
|
+
entry_criteria.show_key_fields = criteria.show_key_fields
|
|
255
|
+
if criteria.extension_types:
|
|
256
|
+
entry_criteria.extension_type_list = list(criteria.extension_types)
|
|
257
|
+
if criteria.table_columns:
|
|
258
|
+
entry_criteria.table_column_list = list(criteria.table_columns)
|
|
259
|
+
if criteria.field_names:
|
|
260
|
+
if entry_criteria.table_column_list is None:
|
|
261
|
+
entry_criteria.table_column_list = []
|
|
262
|
+
for field in AliasUtil.to_data_field_names(criteria.field_names):
|
|
263
|
+
entry_criteria.table_column_list.append(TableColumn(data_type, field))
|
|
264
|
+
|
|
265
|
+
step = self._exp_handler.add_entry_to_caches(self._eln_man.add_experiment_entry(self._exp_id, entry_criteria))
|
|
266
|
+
if records:
|
|
267
|
+
self._exp_handler.set_step_records(step, records)
|
|
268
|
+
return step
|
|
269
|
+
|
|
270
|
+
def create_experiment_detail_table_step(self, entry_name: str,
|
|
271
|
+
field_maps: list[FieldMap] | None = None,
|
|
272
|
+
criteria: ELnDtTableStepCreation = ELnDtTableStepCreation(),
|
|
273
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
274
|
+
"""
|
|
275
|
+
Create a new ELN experiment details table entry in the experiment.
|
|
276
|
+
|
|
277
|
+
:param entry_name: The name of the entry.
|
|
278
|
+
:param field_maps: A field maps list that will be used to populate the entry. The data field names in
|
|
279
|
+
the maps must match the field names of the provided field definitions.
|
|
280
|
+
:param criteria: Additional criteria for creating the entry.
|
|
281
|
+
:param position: Information about where to place the entry in the experiment.
|
|
282
|
+
:return: The newly created table entry.
|
|
283
|
+
"""
|
|
284
|
+
dt = ElnBaseDataType.EXPERIMENT_DETAIL
|
|
285
|
+
return self._create_eln_dt_table_step(entry_name, dt, field_maps, criteria, position)
|
|
286
|
+
|
|
287
|
+
def create_sample_detail_table_step(self, entry_name: str,
|
|
288
|
+
field_maps: list[FieldMap] | None = None,
|
|
289
|
+
criteria: ELnDtTableStepCreation = ELnDtTableStepCreation(),
|
|
290
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
291
|
+
"""
|
|
292
|
+
Create a new ELN sample details table entry in the experiment.
|
|
293
|
+
|
|
294
|
+
:param entry_name: The name of the entry.
|
|
295
|
+
:param field_maps: A field maps list that will be used to populate the entry. The data field names in
|
|
296
|
+
the maps must match the field names of the provided field definitions.
|
|
297
|
+
:param criteria: Additional criteria for creating the entry.
|
|
298
|
+
:param position: Information about where to place the entry in the experiment.
|
|
299
|
+
:return: The newly created table entry.
|
|
300
|
+
"""
|
|
301
|
+
dt = ElnBaseDataType.SAMPLE_DETAIL
|
|
302
|
+
return self._create_eln_dt_table_step(entry_name, dt, field_maps, criteria, position)
|
|
303
|
+
|
|
304
|
+
def _create_eln_dt_table_step(self, entry_name: str,
|
|
305
|
+
dt: ElnBaseDataType,
|
|
306
|
+
field_maps: list[FieldMap] | None = None,
|
|
307
|
+
criteria: ELnDtTableStepCreation = ELnDtTableStepCreation(),
|
|
308
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
309
|
+
entry_criteria = cast(ElnTableEntryCriteria,
|
|
310
|
+
self._create_step_criteria(entry_name, dt.data_type_name, criteria, position))
|
|
311
|
+
|
|
312
|
+
if field_maps:
|
|
313
|
+
entry_criteria.field_map_list = field_maps
|
|
314
|
+
if criteria:
|
|
315
|
+
entry_criteria.is_field_addable = criteria.is_field_addable
|
|
316
|
+
entry_criteria.is_existing_field_removable = criteria.is_existing_field_removable
|
|
317
|
+
if criteria.field_sets:
|
|
318
|
+
field_sets: set[int] = set()
|
|
319
|
+
for field_set in criteria.field_sets:
|
|
320
|
+
if isinstance(field_set, int):
|
|
321
|
+
field_sets.add(field_set)
|
|
322
|
+
elif isinstance(field_set, ElnFieldSetInfo):
|
|
323
|
+
field_sets.add(field_set.field_set_id)
|
|
324
|
+
elif isinstance(field_set, str):
|
|
325
|
+
field_sets.add(self._exp_cache.get_field_set(field_set).field_set_id)
|
|
326
|
+
entry_criteria.field_set_id_list = list(field_sets)
|
|
327
|
+
if criteria.field_definitions:
|
|
328
|
+
entry_criteria.field_definition_list = list(criteria.field_definitions)
|
|
329
|
+
if criteria.predefined_field_names:
|
|
330
|
+
if entry_criteria.field_definition_list is None:
|
|
331
|
+
entry_criteria.field_definition_list = []
|
|
332
|
+
for field_name in criteria.predefined_field_names:
|
|
333
|
+
entry_criteria.field_definition_list.append(self._exp_cache.get_predefined_field(field_name, dt))
|
|
334
|
+
if criteria.table_columns:
|
|
335
|
+
entry_criteria.table_column_list = list(criteria.table_columns)
|
|
336
|
+
|
|
337
|
+
step = self._exp_handler.add_entry_to_caches(self._eln_man.add_experiment_entry(self._exp_id, entry_criteria))
|
|
338
|
+
return step
|
|
339
|
+
|
|
340
|
+
def create_temp_data_step(self, entry_name: str,
|
|
341
|
+
data_type: DataTypeIdentifier,
|
|
342
|
+
criteria: TempDataStepCreation = TempDataStepCreation(),
|
|
343
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
344
|
+
"""
|
|
345
|
+
Create a new temp data entry in the experiment.
|
|
346
|
+
|
|
347
|
+
:param entry_name: The name of the entry.
|
|
348
|
+
:param data_type: The data type of the entry.
|
|
349
|
+
:param criteria: Additional criteria for creating the entry.
|
|
350
|
+
:param position: Information about where to place the entry in the experiment.
|
|
351
|
+
:return: The newly created temp data entry.
|
|
352
|
+
"""
|
|
353
|
+
entry_criteria = cast(ElnTempDataEntryCriteria,
|
|
354
|
+
self._create_step_criteria(entry_name, data_type, criteria, position))
|
|
355
|
+
|
|
356
|
+
if criteria:
|
|
357
|
+
entry_criteria.temp_data_plugin_path = criteria.plugin_path
|
|
358
|
+
|
|
359
|
+
step = self._exp_handler.add_entry_to_caches(self._eln_man.add_experiment_entry(self._exp_id, entry_criteria))
|
|
360
|
+
return step
|
|
361
|
+
|
|
362
|
+
def create_text_step(self, entry_name: str,
|
|
363
|
+
text: str | None = None,
|
|
364
|
+
criteria: TextStepCreation = TextStepCreation(),
|
|
365
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
366
|
+
"""
|
|
367
|
+
Create a new text entry in the experiment.
|
|
368
|
+
|
|
369
|
+
:param entry_name: The name of the entry.
|
|
370
|
+
:param text: The text to populate the entry with.
|
|
371
|
+
:param criteria: Additional criteria for creating the entry.
|
|
372
|
+
:param position: Information about where to place the entry in the experiment.
|
|
373
|
+
:return: The newly created text entry.
|
|
374
|
+
"""
|
|
375
|
+
dt = ElnBaseDataType.TEXT_ENTRY_DETAIL.data_type_name
|
|
376
|
+
entry_criteria = cast(ElnTextEntryCriteria,
|
|
377
|
+
self._create_step_criteria(entry_name, dt, criteria, position))
|
|
378
|
+
|
|
379
|
+
step = self._exp_handler.add_entry_to_caches(self._eln_man.add_experiment_entry(self._exp_id, entry_criteria))
|
|
380
|
+
|
|
381
|
+
if text:
|
|
382
|
+
text_record: DataRecord = step.get_records()[0]
|
|
383
|
+
text_record.set_field_value(ElnBaseDataType.get_text_entry_data_field_name(), text)
|
|
384
|
+
DataRecordManager(self.user).commit_data_records([text_record])
|
|
385
|
+
|
|
386
|
+
return step
|
|
387
|
+
|
|
388
|
+
def create_plate_designer_step(self, entry_name: str,
|
|
389
|
+
source_entry: Step | None = None,
|
|
390
|
+
criteria: PluginStepCreation = PluginStepCreation(),
|
|
391
|
+
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
392
|
+
"""
|
|
393
|
+
Create a new 3D plate designer entry in the experiment.
|
|
394
|
+
|
|
395
|
+
:param entry_name: The name of the entry.
|
|
396
|
+
:param source_entry: The entry that the plate designer will source its samples from.
|
|
397
|
+
:param criteria: Additional criteria for creating the entry.
|
|
398
|
+
:param position: Information about where to place the entry in the experiment.
|
|
399
|
+
:return: The newly created plate designer entry.
|
|
400
|
+
"""
|
|
401
|
+
criteria.plugin_name = PLATE_DESIGNER_PLUGIN
|
|
402
|
+
if criteria.entry_height is None:
|
|
403
|
+
criteria.entry_height = 600
|
|
404
|
+
if source_entry is not None:
|
|
405
|
+
criteria.related_entry_set = [source_entry]
|
|
406
|
+
default_layer = MultiLayerPlateLayer(
|
|
407
|
+
MultiLayerDataTypeConfig("Sample"),
|
|
408
|
+
PlatingOrder.FillBy.BY_COLUMN,
|
|
409
|
+
MultiLayerReplicateConfig(),
|
|
410
|
+
MultiLayerDilutionConfig()
|
|
411
|
+
)
|
|
412
|
+
initial_step_options: dict[str, str] = {
|
|
413
|
+
"MultiLayerPlating_Plate_RecordIdList": "",
|
|
414
|
+
"MultiLayerPlating_Entry_Prefs": MultiLayerPlatingManager.get_entry_prefs_json([default_layer]),
|
|
415
|
+
"MultiLayerPlating_Entry_PrePlating_Prefs": MultiLayerPlatingManager.get_plate_configs_json(MultiLayerPlateConfig())
|
|
416
|
+
}
|
|
417
|
+
if criteria.entry_options is None:
|
|
418
|
+
criteria.entry_options = initial_step_options
|
|
419
|
+
else:
|
|
420
|
+
criteria.entry_options.update(initial_step_options)
|
|
421
|
+
return self.create_plugin_step(entry_name, "Sample", criteria, position)
|
|
422
|
+
|
|
423
|
+
def _create_step_criteria(self, name: str, dt: DataTypeIdentifier,
|
|
424
|
+
criteria: StepCreation, position: ElnEntryPosition | None) \
|
|
425
|
+
-> AbstractElnEntryCriteria:
|
|
426
|
+
"""
|
|
427
|
+
Create the criteria for a new entry in the experiment of the given type.
|
|
428
|
+
"""
|
|
429
|
+
if position is not None:
|
|
430
|
+
order: int = position.order
|
|
431
|
+
tab_id: int = position.tab_id
|
|
432
|
+
column_order: int = position.column_order
|
|
433
|
+
column_span: int = position.column_span
|
|
434
|
+
else:
|
|
435
|
+
last_tab: ElnExperimentTab = self._exp_handler.get_last_tab()
|
|
436
|
+
order: int = self._exp_handler.get_next_entry_order_in_tab(last_tab)
|
|
437
|
+
tab_id: int = last_tab.tab_id
|
|
438
|
+
column_order: int = 0
|
|
439
|
+
column_span: int = last_tab.max_number_of_columns
|
|
440
|
+
|
|
441
|
+
dt: str = AliasUtil.to_data_type_name(dt)
|
|
442
|
+
et = criteria.entry_type
|
|
443
|
+
entry_criteria = ExperimentEntryCriteriaUtil.get_entry_creation_criteria(et, name, dt, order)
|
|
444
|
+
entry_criteria.notebook_experiment_tab_id = tab_id
|
|
445
|
+
entry_criteria.column_order = column_order
|
|
446
|
+
entry_criteria.column_span = column_span
|
|
447
|
+
if criteria:
|
|
448
|
+
entry_criteria.is_shown_in_template = criteria.is_shown_in_template
|
|
449
|
+
entry_criteria.is_removable = criteria.is_removable
|
|
450
|
+
entry_criteria.is_renamable = criteria.is_renamable
|
|
451
|
+
entry_criteria.is_static_view = criteria.is_static_view
|
|
452
|
+
if criteria.related_entry_set:
|
|
453
|
+
entry_criteria.related_entry_set = self.__to_entry_ids(criteria.related_entry_set)
|
|
454
|
+
if criteria.dependency_set:
|
|
455
|
+
entry_criteria.dependency_set = self.__to_entry_ids(criteria.dependency_set)
|
|
456
|
+
entry_criteria.requires_grabber_plugin = criteria.requires_grabber_plugin
|
|
457
|
+
entry_criteria.entry_singleton_id = criteria.entry_singleton_id
|
|
458
|
+
entry_criteria.is_hidden = criteria.is_hidden
|
|
459
|
+
entry_criteria.entry_height = criteria.entry_height
|
|
460
|
+
entry_criteria.description = criteria.description
|
|
461
|
+
entry_criteria.is_initialization_required = criteria.is_initialization_required
|
|
462
|
+
entry_criteria.collapse_entry = criteria.collapse_entry
|
|
463
|
+
entry_criteria.entry_status = criteria.entry_status
|
|
464
|
+
entry_criteria.template_item_fulfilled_timestamp = criteria.template_item_fulfilled_timestamp
|
|
465
|
+
entry_criteria.entry_options = criteria.entry_options
|
|
466
|
+
|
|
467
|
+
return entry_criteria
|
|
468
|
+
|
|
469
|
+
def __to_entry_ids(self, entries: Iterable[ExperimentEntryIdentifier | str]) -> list[int] | None:
|
|
470
|
+
entry_ids: set[int] = set()
|
|
471
|
+
for entry in entries:
|
|
472
|
+
if isinstance(entry, str):
|
|
473
|
+
entry_ids.add(self._exp_handler.get_step(entry).get_id())
|
|
474
|
+
else:
|
|
475
|
+
entry_ids.add(AliasUtil.to_entry_id(entry))
|
|
476
|
+
return list(entry_ids)
|
|
@@ -167,7 +167,7 @@ class PlateDesignerEntry(ElnEntryStep):
|
|
|
167
167
|
self._designer_elements_by_plate = {}
|
|
168
168
|
self._designer_elements_by_plate.clear()
|
|
169
169
|
for element in self._designer_elements:
|
|
170
|
-
plate_id: int = element.
|
|
170
|
+
plate_id: int = element.get_field_value(WellElement.PLATE_RECORD_ID__FIELD.field_name)
|
|
171
171
|
self._designer_elements_by_plate.setdefault(plate_id, []).append(element)
|
|
172
172
|
return self._designer_elements
|
|
173
173
|
|
|
@@ -194,7 +194,8 @@ class PlateDesignerEntry(ElnEntryStep):
|
|
|
194
194
|
return self._designer_elements_by_plate[plate]
|
|
195
195
|
|
|
196
196
|
def create_well_element(self, sample: RecordModel, plate: RecordModel, location: PlateLocation | None = None,
|
|
197
|
-
wrapper_type: type[WrappedType] | None = None)
|
|
197
|
+
layer: int = 1, wrapper_type: type[WrappedType] | None = None) \
|
|
198
|
+
-> WrappedType | PyRecordModel:
|
|
198
199
|
"""
|
|
199
200
|
Create a new plate designer well element for the input sample and plate. A record model manager store and commit
|
|
200
201
|
must be called to save this new well element to the server.
|
|
@@ -203,6 +204,7 @@ class PlateDesignerEntry(ElnEntryStep):
|
|
|
203
204
|
:param plate: The plate that the element is for. Must exist in the system (i.e. have a >0 record ID).
|
|
204
205
|
:param location: The location of the well element. If not provided, the row and column position fields of the
|
|
205
206
|
sample will be used.
|
|
207
|
+
:param layer: The layer that the well element is on.
|
|
206
208
|
:param wrapper_type: The record model wrapper to use for the plate designer well element. If not provided, the
|
|
207
209
|
returned record will be a PyRecordModel instead of a WrappedRecordModel.
|
|
208
210
|
:return: The newly created PlateDesignerWellElementModel.
|
|
@@ -216,6 +218,8 @@ class PlateDesignerEntry(ElnEntryStep):
|
|
|
216
218
|
raise SapioException("Sample record must be of type Sample.")
|
|
217
219
|
if AliasUtil.to_data_type_name(plate) != "Plate":
|
|
218
220
|
raise SapioException("Plate record must be of type Plate.")
|
|
221
|
+
if layer < 1:
|
|
222
|
+
raise SapioException("Layer must be greater than 0.")
|
|
219
223
|
|
|
220
224
|
dt: type[WrappedType] | str = wrapper_type if wrapper_type else WellElement.DATA_TYPE_NAME
|
|
221
225
|
plate_id: int = AliasUtil.to_record_id(plate)
|
|
@@ -225,6 +229,7 @@ class PlateDesignerEntry(ElnEntryStep):
|
|
|
225
229
|
WellElement.ROW_POSITION__FIELD: location.row_pos if location else sample.get_field_value("RowPosition"),
|
|
226
230
|
WellElement.COL_POSITION__FIELD: str(location.col_pos) if location else sample.get_field_value("ColPosition"),
|
|
227
231
|
WellElement.SOURCE_DATA_TYPE_NAME__FIELD: "Sample",
|
|
232
|
+
WellElement.LAYER__FIELD: layer,
|
|
228
233
|
}
|
|
229
234
|
element = self._rec_handler.add_models_with_data(dt, [fields])[0]
|
|
230
235
|
|