sapiopycommons 2025.4.5a470__py3-none-any.whl → 2025.4.8a473__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/eln/experiment_handler.py +90 -440
- sapiopycommons/eln/experiment_step_factory.py +474 -0
- sapiopycommons/eln/step_creation.py +235 -0
- sapiopycommons/general/aliases.py +3 -0
- {sapiopycommons-2025.4.5a470.dist-info → sapiopycommons-2025.4.8a473.dist-info}/METADATA +1 -1
- {sapiopycommons-2025.4.5a470.dist-info → sapiopycommons-2025.4.8a473.dist-info}/RECORD +9 -7
- /sapiopycommons/{datatype → eln}/experiment_cache.py +0 -0
- {sapiopycommons-2025.4.5a470.dist-info → sapiopycommons-2025.4.8a473.dist-info}/WHEEL +0 -0
- {sapiopycommons-2025.4.5a470.dist-info → sapiopycommons-2025.4.8a473.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,60 +5,43 @@ from collections.abc import Mapping, Iterable
|
|
|
5
5
|
from typing import TypeAlias
|
|
6
6
|
from weakref import WeakValueDictionary
|
|
7
7
|
|
|
8
|
+
from sapiopycommons.eln.experiment_cache import ExperimentCacheManager
|
|
9
|
+
from sapiopycommons.eln.experiment_report_util import ExperimentReportUtil
|
|
10
|
+
from sapiopycommons.general.aliases import AliasUtil, SapioRecord, ExperimentIdentifier, UserIdentifier, \
|
|
11
|
+
DataTypeIdentifier, RecordModel, ExperimentEntryIdentifier, TabIdentifier
|
|
12
|
+
from sapiopycommons.general.exceptions import SapioException
|
|
13
|
+
from sapiopycommons.general.time_util import TimeUtil
|
|
14
|
+
from sapiopycommons.recordmodel.record_handler import RecordHandler
|
|
8
15
|
from sapiopylib.rest.DataMgmtService import DataMgmtServer
|
|
9
|
-
from sapiopylib.rest.DataRecordManagerService import DataRecordManager
|
|
10
16
|
from sapiopylib.rest.ELNService import ElnManager
|
|
11
17
|
from sapiopylib.rest.User import SapioUser
|
|
12
18
|
from sapiopylib.rest.pojo.DataRecord import DataRecord
|
|
13
|
-
from sapiopylib.rest.pojo.TableColumn import TableColumn
|
|
14
|
-
from sapiopylib.rest.pojo.datatype.FieldDefinition import AbstractVeloxFieldDefinition
|
|
15
19
|
from sapiopylib.rest.pojo.eln.ElnEntryPosition import ElnEntryPosition
|
|
16
20
|
from sapiopylib.rest.pojo.eln.ElnExperiment import ElnExperiment, TemplateExperimentQueryPojo, ElnTemplate, \
|
|
17
21
|
InitializeNotebookExperimentPojo, ElnExperimentUpdateCriteria
|
|
18
22
|
from sapiopylib.rest.pojo.eln.ExperimentEntry import ExperimentEntry, ExperimentTableEntry, ExperimentFormEntry, \
|
|
19
23
|
ExperimentAttachmentEntry, ExperimentPluginEntry, ExperimentDashboardEntry, ExperimentTextEntry, \
|
|
20
|
-
ExperimentTempDataEntry
|
|
24
|
+
ExperimentTempDataEntry
|
|
21
25
|
from sapiopylib.rest.pojo.eln.ExperimentEntryCriteria import AbstractElnEntryUpdateCriteria, \
|
|
22
26
|
ElnTableEntryUpdateCriteria, ElnFormEntryUpdateCriteria, ElnAttachmentEntryUpdateCriteria, \
|
|
23
27
|
ElnPluginEntryUpdateCriteria, ElnDashboardEntryUpdateCriteria, ElnTextEntryUpdateCriteria, \
|
|
24
|
-
ElnTempDataEntryUpdateCriteria
|
|
28
|
+
ElnTempDataEntryUpdateCriteria
|
|
25
29
|
from sapiopylib.rest.pojo.eln.SapioELNEnums import ExperimentEntryStatus, ElnExperimentStatus, ElnEntryType, \
|
|
26
30
|
ElnBaseDataType
|
|
27
31
|
from sapiopylib.rest.pojo.eln.eln_headings import ElnExperimentTab, ElnExperimentTabAddCriteria
|
|
28
|
-
from sapiopylib.rest.pojo.eln.field_set import ElnFieldSetInfo
|
|
29
32
|
from sapiopylib.rest.pojo.eln.protocol_template import ProtocolTemplateInfo
|
|
30
33
|
from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
|
|
31
34
|
from sapiopylib.rest.pojo.webhook.WebhookDirective import ElnExperimentDirective
|
|
32
35
|
from sapiopylib.rest.pojo.webhook.WebhookResult import SapioWebhookResult
|
|
33
36
|
from sapiopylib.rest.utils.Protocols import ElnEntryStep, ElnExperimentProtocol
|
|
34
|
-
from sapiopylib.rest.utils.plates.MultiLayerPlating import MultiLayerPlateConfig, MultiLayerPlateLayer, \
|
|
35
|
-
MultiLayerDataTypeConfig, MultiLayerReplicateConfig, MultiLayerDilutionConfig
|
|
36
|
-
from sapiopylib.rest.utils.plates.MultiLayerPlatingUtils import MultiLayerPlatingManager
|
|
37
|
-
from sapiopylib.rest.utils.plates.PlatingUtils import PlatingOrder
|
|
38
37
|
from sapiopylib.rest.utils.recordmodel.PyRecordModel import PyRecordModel
|
|
39
38
|
from sapiopylib.rest.utils.recordmodel.RecordModelManager import RecordModelInstanceManager, RecordModelManager
|
|
40
39
|
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
41
40
|
from sapiopylib.rest.utils.recordmodel.properties import Child
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
from sapiopycommons.datatype.experiment_cache import ExperimentCacheManager
|
|
45
|
-
from sapiopycommons.eln.experiment_report_util import ExperimentReportUtil
|
|
46
|
-
from sapiopycommons.eln.experiment_tags import PLATE_DESIGNER_PLUGIN
|
|
47
|
-
from sapiopycommons.general.aliases import AliasUtil, SapioRecord, ExperimentIdentifier, UserIdentifier, \
|
|
48
|
-
DataTypeIdentifier, RecordModel, FieldMap, FieldIdentifier
|
|
49
|
-
from sapiopycommons.general.exceptions import SapioException
|
|
50
|
-
from sapiopycommons.general.time_util import TimeUtil
|
|
51
|
-
from sapiopycommons.recordmodel.record_handler import RecordHandler
|
|
52
|
-
|
|
53
|
-
Step: TypeAlias = str | ElnEntryStep
|
|
42
|
+
Step: TypeAlias = str | ExperimentEntryIdentifier
|
|
54
43
|
"""An object representing an identifier to an ElnEntryStep. May be either the name of the step or the ElnEntryStep
|
|
55
44
|
itself."""
|
|
56
|
-
TabIdentifier: TypeAlias = int | str | ElnExperimentTab
|
|
57
|
-
"""An object representing an identifier to an ElnExperimentTab. May be either the ID or name of the tab, or the
|
|
58
|
-
ElnExperimentTab itself."""
|
|
59
|
-
ElnDataTypeFields: TypeAlias = AbstractVeloxFieldDefinition | ElnFieldSetInfo | str | int
|
|
60
|
-
"""An object representing an identifier to an ElnDataType field. These can be field definitions for ad hoc fields,
|
|
61
|
-
predefined field set info objects, predefined field names, or integers for predefined field set IDs."""
|
|
62
45
|
|
|
63
46
|
|
|
64
47
|
# FR-46064 - Initial port of PyWebhookUtils to sapiopycommons.
|
|
@@ -99,7 +82,9 @@ class ExperimentHandler:
|
|
|
99
82
|
|
|
100
83
|
_queried_all_steps: bool
|
|
101
84
|
"""Whether this ExperimentHandler has queried the system for all steps in the experiment."""
|
|
102
|
-
_steps:
|
|
85
|
+
_steps: list[ElnEntryStep]
|
|
86
|
+
"""The sorted list of steps for this experiment. All steps are cached the first time any individual step is accessed."""
|
|
87
|
+
_steps_by_name: dict[str, ElnEntryStep]
|
|
103
88
|
"""Steps from this experiment by their name. All steps are cached the first time any individual step is accessed."""
|
|
104
89
|
_steps_by_id: dict[int, ElnEntryStep]
|
|
105
90
|
"""Steps from this experiment by their ID. All steps are cached the first time any individual step is accessed."""
|
|
@@ -113,7 +98,9 @@ class ExperimentHandler:
|
|
|
113
98
|
_queried_all_tabs: bool
|
|
114
99
|
"""Whether this ExperimentHandler has queried the system for all tabs in the experiment."""
|
|
115
100
|
_tabs: list[ElnExperimentTab]
|
|
116
|
-
"""The tabs for this experiment. Only cached when first accessed."""
|
|
101
|
+
"""The sorted tabs for this experiment. Only cached when first accessed."""
|
|
102
|
+
_tabs_by_id: dict[int, ElnExperimentTab]
|
|
103
|
+
"""The tabs for this experiment by their ID. Only cached when first accessed."""
|
|
117
104
|
_tabs_by_name: dict[str, ElnExperimentTab]
|
|
118
105
|
"""The tabs for this experiment by their name. Only cached when first accessed."""
|
|
119
106
|
|
|
@@ -180,12 +167,14 @@ class ExperimentHandler:
|
|
|
180
167
|
|
|
181
168
|
# Create empty caches to fill when necessary.
|
|
182
169
|
self._queried_all_steps = False
|
|
183
|
-
self.
|
|
170
|
+
self._steps_by_name = {}
|
|
184
171
|
self._steps_by_id = {}
|
|
172
|
+
self._steps = []
|
|
185
173
|
self._step_options = {}
|
|
186
174
|
self._step_updates = {}
|
|
187
175
|
|
|
188
176
|
self._tabs = []
|
|
177
|
+
self._tabs_by_id = {}
|
|
189
178
|
self._tabs_by_name = {}
|
|
190
179
|
|
|
191
180
|
self._queried_all_tabs = False
|
|
@@ -201,7 +190,8 @@ class ExperimentHandler:
|
|
|
201
190
|
for entry in self.context.experiment_entry_list:
|
|
202
191
|
cache_steps.append(ElnEntryStep(self._protocol, entry))
|
|
203
192
|
for step in cache_steps:
|
|
204
|
-
self._steps.
|
|
193
|
+
self._steps.append(step)
|
|
194
|
+
self._steps_by_name.update({step.get_name(): step})
|
|
205
195
|
self._steps_by_id.update({step.get_id(): step})
|
|
206
196
|
|
|
207
197
|
@staticmethod
|
|
@@ -266,6 +256,7 @@ class ExperimentHandler:
|
|
|
266
256
|
"""
|
|
267
257
|
self._queried_all_steps = False
|
|
268
258
|
self._steps.clear()
|
|
259
|
+
self._steps_by_name.clear()
|
|
269
260
|
self._steps_by_id.clear()
|
|
270
261
|
self._step_options.clear()
|
|
271
262
|
self._step_updates.clear()
|
|
@@ -284,6 +275,7 @@ class ExperimentHandler:
|
|
|
284
275
|
"""
|
|
285
276
|
self._queried_all_tabs = False
|
|
286
277
|
self._tabs.clear()
|
|
278
|
+
self._tabs_by_id.clear()
|
|
287
279
|
self._tabs_by_name.clear()
|
|
288
280
|
|
|
289
281
|
def add_entry_to_caches(self, entry: ExperimentEntry | ElnEntryStep) -> ElnEntryStep:
|
|
@@ -297,7 +289,8 @@ class ExperimentHandler:
|
|
|
297
289
|
"""
|
|
298
290
|
if isinstance(entry, ExperimentEntry):
|
|
299
291
|
entry = ElnEntryStep(self._protocol, entry)
|
|
300
|
-
self._steps.
|
|
292
|
+
self._steps.append(entry)
|
|
293
|
+
self._steps_by_name.update({entry.get_name(): entry})
|
|
301
294
|
self._steps_by_id.update({entry.get_id(): entry})
|
|
302
295
|
# Skipping the options cache. The get_step_options method will update the cache when necessary.
|
|
303
296
|
return entry
|
|
@@ -326,6 +319,7 @@ class ExperimentHandler:
|
|
|
326
319
|
"""
|
|
327
320
|
self._tabs.append(tab)
|
|
328
321
|
self._tabs.sort(key=lambda t: t.tab_order)
|
|
322
|
+
self._tabs_by_id[tab.tab_id] = tab
|
|
329
323
|
self._tabs_by_name[tab.tab_name] = tab
|
|
330
324
|
|
|
331
325
|
# FR-46495: Split the creation of the experiment in launch_experiment into a create_experiment function.
|
|
@@ -632,14 +626,14 @@ class ExperimentHandler:
|
|
|
632
626
|
"""
|
|
633
627
|
return all([x is not None for x in self.get_steps(step_names, False)])
|
|
634
628
|
|
|
635
|
-
def get_step(self, step_name: str, exception_on_none: bool = True) -> ElnEntryStep | None:
|
|
629
|
+
def get_step(self, step_name: str | int, exception_on_none: bool = True) -> ElnEntryStep | None:
|
|
636
630
|
"""
|
|
637
631
|
Get the step of the given name from the experiment.
|
|
638
632
|
|
|
639
633
|
If no step functions have been called before and a step is being searched for by name, queries for the
|
|
640
634
|
list of steps in the experiment and caches them.
|
|
641
635
|
|
|
642
|
-
:param step_name: The name for the step to return.
|
|
636
|
+
:param step_name: The name or ID for the step to return.
|
|
643
637
|
:param exception_on_none: If false, returns None if the entry can't be found. If true, raises an exception
|
|
644
638
|
when the named entry doesn't exist in the experiment.
|
|
645
639
|
:return: An ElnEntrySteps matching the provided name. If there is no match and no exception is to be thrown,
|
|
@@ -647,31 +641,32 @@ class ExperimentHandler:
|
|
|
647
641
|
"""
|
|
648
642
|
return self.get_steps([step_name], exception_on_none)[0]
|
|
649
643
|
|
|
650
|
-
def get_steps(self, step_names: Iterable[str], exception_on_none: bool = True) -> list[ElnEntryStep | None]:
|
|
644
|
+
def get_steps(self, step_names: Iterable[str | int], exception_on_none: bool = True) -> list[ElnEntryStep | None]:
|
|
651
645
|
"""
|
|
652
646
|
Get a list of steps of the given names from the experiment, sorted in the same order as the names are provided.
|
|
653
647
|
|
|
654
648
|
If no step functions have been called before and a step is being searched for by name, queries for the
|
|
655
649
|
list of steps in the experiment and caches them.
|
|
656
650
|
|
|
657
|
-
:param step_names: A list of names for the entries to return and the order to return them in.
|
|
651
|
+
:param step_names: A list of names or IDs for the entries to return and the order to return them in.
|
|
658
652
|
:param exception_on_none: If false, returns None for entries that can't be found. If true, raises an exception
|
|
659
653
|
when the named entry doesn't exist in the experiment.
|
|
660
654
|
:return: A list of ElnEntrySteps matching the provided names in the order they were provided in. If there is no
|
|
661
655
|
match for a given step and no exception is to be thrown, returns None for that step.
|
|
662
656
|
"""
|
|
663
657
|
ret_list: list[ElnEntryStep | None] = []
|
|
664
|
-
for
|
|
658
|
+
for step_id in step_names:
|
|
665
659
|
# If we haven't queried the system for all steps in the experiment yet, then the reason that a step is
|
|
666
660
|
# missing may be because it wasn't in the webhook context. Therefore, query all steps and then check
|
|
667
661
|
# if the step name is still missing from the experiment before potentially throwing an exception.
|
|
668
|
-
if self._queried_all_steps is False and
|
|
669
|
-
self.
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
662
|
+
if self._queried_all_steps is False and step_id not in self._steps_by_name and step_id not in self._steps_by_id:
|
|
663
|
+
self._query_all_steps()
|
|
664
|
+
if isinstance(step_id, str):
|
|
665
|
+
step: ElnEntryStep = self._steps_by_name.get(step_id)
|
|
666
|
+
else:
|
|
667
|
+
step: ElnEntryStep = self._steps_by_id.get(step_id)
|
|
673
668
|
if step is None and exception_on_none is True:
|
|
674
|
-
raise SapioException(f"ElnEntryStep of name \"{
|
|
669
|
+
raise SapioException(f"ElnEntryStep of name \"{step_id}\" not found in experiment with ID {self._exp_id}.")
|
|
675
670
|
ret_list.append(step)
|
|
676
671
|
return ret_list
|
|
677
672
|
|
|
@@ -686,14 +681,34 @@ class ExperimentHandler:
|
|
|
686
681
|
:return: Every entry in the experiment in order of appearance that match the provided data type, if any.
|
|
687
682
|
"""
|
|
688
683
|
if self._queried_all_steps is False:
|
|
689
|
-
self.
|
|
690
|
-
|
|
691
|
-
|
|
684
|
+
self._query_all_steps()
|
|
685
|
+
else:
|
|
686
|
+
# Re-sort the steps in case any new steps were added before the last time that this was called.
|
|
687
|
+
def sort_steps(step: ElnEntryStep) -> tuple:
|
|
688
|
+
entry = step.eln_entry
|
|
689
|
+
tab_order: int = self.get_tab(entry.notebook_experiment_tab_id).tab_order
|
|
690
|
+
entry_order: int = entry.order
|
|
691
|
+
column_order: int = entry.column_order
|
|
692
|
+
return tab_order, entry_order, column_order
|
|
693
|
+
|
|
694
|
+
self._steps.sort(key=sort_steps)
|
|
695
|
+
all_steps: list[ElnEntryStep] = self._steps
|
|
692
696
|
if data_type is None:
|
|
693
697
|
return all_steps
|
|
694
698
|
data_type: str = AliasUtil.to_data_type_name(data_type)
|
|
695
699
|
return [x for x in all_steps if data_type in x.get_data_type_names()]
|
|
696
700
|
|
|
701
|
+
def _query_all_steps(self) -> None:
|
|
702
|
+
"""
|
|
703
|
+
Query the system for every step in the experiment and cache them.
|
|
704
|
+
"""
|
|
705
|
+
self._queried_all_steps = True
|
|
706
|
+
self._protocol.invalidate()
|
|
707
|
+
self._steps = self._protocol.get_sorted_step_list()
|
|
708
|
+
for step in self._steps:
|
|
709
|
+
self._steps_by_name[step.get_name()] = step
|
|
710
|
+
self._steps_by_id[step.get_id()] = step
|
|
711
|
+
|
|
697
712
|
def get_step_by_option(self, key: str, value: str | None = None) -> ElnEntryStep:
|
|
698
713
|
"""
|
|
699
714
|
Retrieve the step in this experiment that contains an entry option with the provided key and value.
|
|
@@ -936,7 +951,7 @@ class ExperimentHandler:
|
|
|
936
951
|
"""
|
|
937
952
|
return self.add_sample_details(step, [sample], wrapper_type)[0]
|
|
938
953
|
|
|
939
|
-
def add_sample_details(self, step: Step, samples:
|
|
954
|
+
def add_sample_details(self, step: Step, samples: Iterable[RecordModel],
|
|
940
955
|
wrapper_type: type[WrappedType] | None = None) \
|
|
941
956
|
-> list[WrappedType] | list[PyRecordModel]:
|
|
942
957
|
"""
|
|
@@ -991,7 +1006,7 @@ class ExperimentHandler:
|
|
|
991
1006
|
"""
|
|
992
1007
|
self.remove_eln_rows(step, [record])
|
|
993
1008
|
|
|
994
|
-
def remove_eln_rows(self, step: Step, records:
|
|
1009
|
+
def remove_eln_rows(self, step: Step, records: Iterable[SapioRecord]) -> None:
|
|
995
1010
|
"""
|
|
996
1011
|
Remove rows from an ELNExperimentDetail or ELNSampleDetail table entry. ELN data type table entries display all
|
|
997
1012
|
records in the system that match the entry's data type. This means that removing rows from an ELN data type
|
|
@@ -1370,10 +1385,10 @@ class ExperimentHandler:
|
|
|
1370
1385
|
entry: ExperimentEntry = step.eln_entry
|
|
1371
1386
|
if update.entry_name is not None:
|
|
1372
1387
|
# PR-46477 - Ensure that the previous name of the updated entry already existed in the cache.
|
|
1373
|
-
if entry.entry_name in self.
|
|
1374
|
-
self.
|
|
1388
|
+
if entry.entry_name in self._steps_by_name:
|
|
1389
|
+
self._steps_by_name.pop(entry.entry_name)
|
|
1375
1390
|
entry.entry_name = update.entry_name
|
|
1376
|
-
self.
|
|
1391
|
+
self._steps_by_name.update({update.entry_name: step})
|
|
1377
1392
|
if update.related_entry_set is not None:
|
|
1378
1393
|
entry.related_entry_id_set = update.related_entry_set
|
|
1379
1394
|
if update.dependency_set is not None:
|
|
@@ -1679,6 +1694,7 @@ class ExperimentHandler:
|
|
|
1679
1694
|
if not self._queried_all_tabs:
|
|
1680
1695
|
self._tabs = self._eln_man.get_tabs_for_experiment(self._exp_id)
|
|
1681
1696
|
self._tabs.sort(key=lambda t: t.tab_order)
|
|
1697
|
+
self._tabs_by_id = {tab.tab_id: tab for tab in self._tabs}
|
|
1682
1698
|
self._tabs_by_name = {tab.tab_name: tab for tab in self._tabs}
|
|
1683
1699
|
return self._tabs
|
|
1684
1700
|
|
|
@@ -1712,21 +1728,29 @@ class ExperimentHandler:
|
|
|
1712
1728
|
self.add_tab_to_cache(tab)
|
|
1713
1729
|
return tab
|
|
1714
1730
|
|
|
1715
|
-
def get_tab(self, tab_name: str) -> ElnExperimentTab:
|
|
1731
|
+
def get_tab(self, tab_name: str | int, exception_on_none: bool = True) -> ElnExperimentTab:
|
|
1716
1732
|
"""
|
|
1717
1733
|
Return the tab with the input name.
|
|
1718
1734
|
|
|
1719
1735
|
If no tab functions have been called before and a tab is being searched for by name, queries for the
|
|
1720
1736
|
list of tabs in the experiment and caches them.
|
|
1721
1737
|
|
|
1722
|
-
:param tab_name: The name of the tab to get.
|
|
1723
|
-
:
|
|
1738
|
+
:param tab_name: The name or ID of the tab to get.
|
|
1739
|
+
:param exception_on_none: If True, raises an exception if no tab with the given name exists.
|
|
1740
|
+
:return: The tab with the input name, or None if no such tab exists.
|
|
1724
1741
|
"""
|
|
1725
|
-
if tab_name not in self._tabs_by_name:
|
|
1742
|
+
if tab_name not in self._tabs_by_name and tab_name not in self._tabs_by_id:
|
|
1726
1743
|
self.get_all_tabs()
|
|
1727
|
-
|
|
1744
|
+
if isinstance(tab_name, str):
|
|
1745
|
+
tab = self._tabs_by_name.get(tab_name)
|
|
1746
|
+
else:
|
|
1747
|
+
tab = self._tabs_by_id.get(tab_name)
|
|
1748
|
+
if tab is None and exception_on_none:
|
|
1749
|
+
raise SapioException(f"No tab with the name\\ID \"{tab_name}\" exists in this experiment.")
|
|
1750
|
+
return tab
|
|
1728
1751
|
|
|
1729
|
-
def get_steps_in_tab(self, tab: TabIdentifier, data_type: DataTypeIdentifier | None = None)
|
|
1752
|
+
def get_steps_in_tab(self, tab: TabIdentifier | str, data_type: DataTypeIdentifier | None = None) \
|
|
1753
|
+
-> list[ElnEntryStep]:
|
|
1730
1754
|
"""
|
|
1731
1755
|
Get all the steps in the input tab sorted in order of appearance.
|
|
1732
1756
|
|
|
@@ -1746,10 +1770,9 @@ class ExperimentHandler:
|
|
|
1746
1770
|
for step in self.get_all_steps(data_type):
|
|
1747
1771
|
if step.eln_entry.notebook_experiment_tab_id == tab.tab_id:
|
|
1748
1772
|
steps.append(step)
|
|
1749
|
-
steps.sort(key=lambda s: (s.eln_entry.order, s.eln_entry.column_order))
|
|
1750
1773
|
return steps
|
|
1751
1774
|
|
|
1752
|
-
def get_next_entry_order_in_tab(self, tab: TabIdentifier) -> int:
|
|
1775
|
+
def get_next_entry_order_in_tab(self, tab: TabIdentifier | str) -> int:
|
|
1753
1776
|
"""
|
|
1754
1777
|
Get the next available order for a new entry in the input tab.
|
|
1755
1778
|
|
|
@@ -1824,385 +1847,6 @@ class ExperimentHandler:
|
|
|
1824
1847
|
new_entries: list[ExperimentEntry] = self._eln_man.add_protocol_template(self._exp_id, protocol, position)
|
|
1825
1848
|
return self.add_entries_to_caches(new_entries)
|
|
1826
1849
|
|
|
1827
|
-
# FR-47468: Add functions for creating new entries in the experiment.
|
|
1828
|
-
def create_attachment_step(self, entry_name: str, data_type: DataTypeIdentifier,
|
|
1829
|
-
*,
|
|
1830
|
-
position: ElnEntryPosition | None = None,
|
|
1831
|
-
attachments: list[EntryAttachment] | list[SapioRecord] | None = None) -> ElnEntryStep:
|
|
1832
|
-
"""
|
|
1833
|
-
Create a new attachment entry in the experiment.
|
|
1834
|
-
|
|
1835
|
-
:param entry_name: The name of the entry.
|
|
1836
|
-
:param data_type: The data type of the entry.
|
|
1837
|
-
:param position: Information about where to place the entry in the experiment.
|
|
1838
|
-
:param attachments: The list of attachments to initially populate the entry with.
|
|
1839
|
-
:return: The newly created attachment entry.
|
|
1840
|
-
"""
|
|
1841
|
-
step = self._create_step(ElnEntryType.Attachment, entry_name, data_type, position)
|
|
1842
|
-
if attachments:
|
|
1843
|
-
entry_attachments: list[EntryAttachment] = []
|
|
1844
|
-
for entry in attachments:
|
|
1845
|
-
if isinstance(entry, EntryAttachment):
|
|
1846
|
-
entry_attachments.append(entry)
|
|
1847
|
-
elif isinstance(entry, SapioRecord):
|
|
1848
|
-
entry: SapioRecord
|
|
1849
|
-
file_name: str = entry.get_field_value("FilePath")
|
|
1850
|
-
if not file_name:
|
|
1851
|
-
file_name = entry.get_field_value(SystemFields.DATA_RECORD_NAME__FIELD.field_name)
|
|
1852
|
-
rec_id: int = AliasUtil.to_record_id(entry)
|
|
1853
|
-
entry_attachments.append(EntryRecordAttachment(file_name, rec_id))
|
|
1854
|
-
else:
|
|
1855
|
-
raise SapioException("Attachments must be of type EntryAttachment or SapioRecord.")
|
|
1856
|
-
update = ElnAttachmentEntryUpdateCriteria()
|
|
1857
|
-
update.entry_attachment_list = attachments
|
|
1858
|
-
self.force_step_update(step, update)
|
|
1859
|
-
return step
|
|
1860
|
-
|
|
1861
|
-
def create_form_step(self, entry_name: str,
|
|
1862
|
-
data_type: DataTypeIdentifier,
|
|
1863
|
-
fields: list[FieldIdentifier] | None = None,
|
|
1864
|
-
layout_name: str | None = None,
|
|
1865
|
-
form_names: list[str] | None = None,
|
|
1866
|
-
*,
|
|
1867
|
-
position: ElnEntryPosition | None = None,
|
|
1868
|
-
record: SapioRecord | None = None) -> ElnEntryStep:
|
|
1869
|
-
"""
|
|
1870
|
-
Create a new form entry in the experiment.
|
|
1871
|
-
|
|
1872
|
-
:param entry_name: The name of the entry.
|
|
1873
|
-
:param data_type: The data type of the entry.
|
|
1874
|
-
:param fields: The list of data field names for the given data type that should appear on the
|
|
1875
|
-
form. Fields will appear in the order they are provided. If not provided and a layout name is not provided,
|
|
1876
|
-
the entry will be created with the data type's default layout.
|
|
1877
|
-
:param layout_name: The name of the layout to use for the entry. If not provided and field names are not
|
|
1878
|
-
provided, the entry will be created with the data type's default layout.
|
|
1879
|
-
:param form_names: The list of layout component form names to use from the specified layout name. If not
|
|
1880
|
-
provided, then all available forms from the specified layout will be used.
|
|
1881
|
-
:param position: Information about where to place the entry in the experiment.
|
|
1882
|
-
:param record: The record to initially populate the entry with.
|
|
1883
|
-
:return: The newly created form entry.
|
|
1884
|
-
"""
|
|
1885
|
-
if record:
|
|
1886
|
-
rdt: str = AliasUtil.to_data_type_name(record)
|
|
1887
|
-
sdt: str = AliasUtil.to_data_type_name(data_type)
|
|
1888
|
-
if rdt != sdt:
|
|
1889
|
-
raise SapioException(f"Cannot set {rdt} records for entry {entry_name} of type "
|
|
1890
|
-
f"{sdt}.")
|
|
1891
|
-
|
|
1892
|
-
step = self._create_step(ElnEntryType.Form, entry_name, data_type, position)
|
|
1893
|
-
if fields or layout_name or form_names or record:
|
|
1894
|
-
update = ElnFormEntryUpdateCriteria()
|
|
1895
|
-
if fields:
|
|
1896
|
-
update.data_field_name_list = AliasUtil.to_data_field_names(fields)
|
|
1897
|
-
update.data_type_layout_name = layout_name
|
|
1898
|
-
update.form_name_list = form_names
|
|
1899
|
-
if record:
|
|
1900
|
-
update.record_id = AliasUtil.to_record_id(record)
|
|
1901
|
-
self.force_step_update(step, update)
|
|
1902
|
-
return step
|
|
1903
|
-
|
|
1904
|
-
def create_experiment_detail_form_step(self, entry_name: str,
|
|
1905
|
-
fields: list[ElnDataTypeFields] | None = None,
|
|
1906
|
-
field_map: FieldMap | None = None,
|
|
1907
|
-
*,
|
|
1908
|
-
position: ElnEntryPosition | None = None,
|
|
1909
|
-
is_field_addable: bool | None = None,
|
|
1910
|
-
is_existing_field_removable: bool | None = None) -> ElnEntryStep:
|
|
1911
|
-
"""
|
|
1912
|
-
Create a new ELN experiment details form entry in the experiment.
|
|
1913
|
-
|
|
1914
|
-
:param entry_name: The name of the entry.
|
|
1915
|
-
:param fields: A list of objects representing the data fields that should appear on the entry. Fields
|
|
1916
|
-
will appear in the order they are provided.
|
|
1917
|
-
:param field_map: A field map that will be used to populate the entry. The data field names in
|
|
1918
|
-
the map must match the field names of the provided field definitions.
|
|
1919
|
-
:param position: Information about where to place the entry in the experiment.
|
|
1920
|
-
:param is_field_addable: Whether users are able to add additional fields to this entry in the UI.
|
|
1921
|
-
:param is_existing_field_removable: Whether users are able to remove existing fields from this entry in the UI.
|
|
1922
|
-
:return: The newly created form entry.
|
|
1923
|
-
"""
|
|
1924
|
-
return self._create_eln_dt_form_step(entry_name, ElnBaseDataType.EXPERIMENT_DETAIL, fields, field_map,
|
|
1925
|
-
position=position,
|
|
1926
|
-
is_field_addable=is_field_addable,
|
|
1927
|
-
is_existing_field_removable=is_existing_field_removable)
|
|
1928
|
-
|
|
1929
|
-
def create_sample_detail_form_step(self, entry_name: str,
|
|
1930
|
-
fields: list[ElnDataTypeFields] | None = None,
|
|
1931
|
-
field_map: FieldMap | None = None,
|
|
1932
|
-
*,
|
|
1933
|
-
position: ElnEntryPosition | None = None,
|
|
1934
|
-
is_field_addable: bool | None = None,
|
|
1935
|
-
is_existing_field_removable: bool | None = None) -> ElnEntryStep:
|
|
1936
|
-
"""
|
|
1937
|
-
Create a new ELN sample details form entry in the experiment.
|
|
1938
|
-
|
|
1939
|
-
:param entry_name: The name of the entry.
|
|
1940
|
-
:param fields: A list of objects representing the data fields that should appear on the entry. Fields
|
|
1941
|
-
will appear in the order they are provided.
|
|
1942
|
-
:param field_map: A field map that will be used to populate the entry. The data field names in
|
|
1943
|
-
the map must match the field names of the provided field definitions.
|
|
1944
|
-
:param position: Information about where to place the entry in the experiment.
|
|
1945
|
-
:param is_field_addable: Whether users are able to add additional fields to this entry in the UI.
|
|
1946
|
-
:param is_existing_field_removable: Whether users are able to remove existing fields from this entry in the UI.
|
|
1947
|
-
:return: The newly created form entry.
|
|
1948
|
-
"""
|
|
1949
|
-
return self._create_eln_dt_form_step(entry_name, ElnBaseDataType.SAMPLE_DETAIL, fields, field_map,
|
|
1950
|
-
position=position,
|
|
1951
|
-
is_field_addable=is_field_addable,
|
|
1952
|
-
is_existing_field_removable=is_existing_field_removable)
|
|
1953
|
-
|
|
1954
|
-
def _create_eln_dt_form_step(self, entry_name: str,
|
|
1955
|
-
dt: ElnBaseDataType,
|
|
1956
|
-
fields: list[ElnDataTypeFields] | None = None,
|
|
1957
|
-
field_map: FieldMap | None = None,
|
|
1958
|
-
*,
|
|
1959
|
-
position: ElnEntryPosition | None = None,
|
|
1960
|
-
is_field_addable: bool | None = None,
|
|
1961
|
-
is_existing_field_removable: bool | None = None) -> ElnEntryStep:
|
|
1962
|
-
fields: list[AbstractVeloxFieldDefinition] | None = self._to_field_defs(fields, dt)
|
|
1963
|
-
step = self._create_step(ElnEntryType.Form, entry_name, dt.data_type_name,
|
|
1964
|
-
position,
|
|
1965
|
-
field_definition_list=fields,
|
|
1966
|
-
field_map_list=[field_map] if field_map else None)
|
|
1967
|
-
if is_field_addable is not None or is_existing_field_removable is not None:
|
|
1968
|
-
update = ElnFormEntryUpdateCriteria()
|
|
1969
|
-
update.is_field_addable = is_field_addable
|
|
1970
|
-
update.is_existing_field_removable = is_existing_field_removable
|
|
1971
|
-
self.force_step_update(step, update)
|
|
1972
|
-
return step
|
|
1973
|
-
|
|
1974
|
-
def create_plugin_step(self, entry_name: str, data_type: DataTypeIdentifier, plugin_path: str, *,
|
|
1975
|
-
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
1976
|
-
"""
|
|
1977
|
-
Create a new plugin entry in the experiment.
|
|
1978
|
-
|
|
1979
|
-
:param entry_name: The name of the entry.
|
|
1980
|
-
:param data_type: The data type of the entry.
|
|
1981
|
-
:param plugin_path: The plugin path to the CSP that determines this entry's functionality.
|
|
1982
|
-
:param position: Information about where to place the entry in the experiment.
|
|
1983
|
-
:return: The newly created plugin entry.
|
|
1984
|
-
"""
|
|
1985
|
-
return self._create_step(ElnEntryType.Plugin, entry_name, data_type, position,
|
|
1986
|
-
csp_plugin_name=plugin_path)
|
|
1987
|
-
|
|
1988
|
-
def create_table_step(self, entry_name: str, data_type: DataTypeIdentifier,
|
|
1989
|
-
fields: list[FieldIdentifier] | list[TableColumn] | None = None,
|
|
1990
|
-
layout_name: str | None = None,
|
|
1991
|
-
show_key_fields: bool | None = None,
|
|
1992
|
-
*,
|
|
1993
|
-
position: ElnEntryPosition | None = None,
|
|
1994
|
-
records: list[SapioRecord] | None = None) -> ElnEntryStep:
|
|
1995
|
-
"""
|
|
1996
|
-
Create a new table entry in the experiment.
|
|
1997
|
-
|
|
1998
|
-
:param entry_name: The name of the entry.
|
|
1999
|
-
:param data_type: The data type of the entry.
|
|
2000
|
-
:param fields: The list of data field names for the given data type that should appear on the
|
|
2001
|
-
table. You may also provide a list of TableColumns instead of a list of field names for when you want to
|
|
2002
|
-
set the sort order and direction of the columns in the table. Fields will appear in the order they are
|
|
2003
|
-
provided. If not provided and a layout name is not provided, the entry will be created with the data type's
|
|
2004
|
-
default layout.
|
|
2005
|
-
:param layout_name: The name of the layout to use for the entry. If not provided and field names are not
|
|
2006
|
-
provided, the entry will be created with the data type's default layout.
|
|
2007
|
-
:param show_key_fields: Whether the table should only show key fields of the data type.
|
|
2008
|
-
:param position: Information about where to place the entry in the experiment.
|
|
2009
|
-
:param records: The list of records to initially populate the entry with.
|
|
2010
|
-
:return: The newly created table entry.
|
|
2011
|
-
"""
|
|
2012
|
-
step = self._create_step(ElnEntryType.Table, entry_name, data_type, position)
|
|
2013
|
-
if fields or layout_name:
|
|
2014
|
-
update = ElnTableEntryUpdateCriteria()
|
|
2015
|
-
if fields:
|
|
2016
|
-
dt: str = AliasUtil.to_data_type_name(data_type)
|
|
2017
|
-
columns: list[TableColumn] = []
|
|
2018
|
-
for field in fields:
|
|
2019
|
-
if isinstance(field, TableColumn):
|
|
2020
|
-
columns.append(field)
|
|
2021
|
-
else:
|
|
2022
|
-
columns.append(TableColumn(dt, AliasUtil.to_data_field_name(field)))
|
|
2023
|
-
update.table_column_list = columns
|
|
2024
|
-
update.data_type_layout_name = layout_name
|
|
2025
|
-
update.show_key_fields = show_key_fields
|
|
2026
|
-
self.force_step_update(step, update)
|
|
2027
|
-
if records:
|
|
2028
|
-
self.set_step_records(step, records)
|
|
2029
|
-
return step
|
|
2030
|
-
|
|
2031
|
-
def create_experiment_detail_table_step(self, entry_name: str,
|
|
2032
|
-
fields: list[ElnDataTypeFields] | None = None,
|
|
2033
|
-
field_maps: list[FieldMap] | None = None, *,
|
|
2034
|
-
position: ElnEntryPosition | None = None,
|
|
2035
|
-
is_field_addable: bool | None = None,
|
|
2036
|
-
is_existing_field_removable: bool | None = None) -> ElnEntryStep:
|
|
2037
|
-
"""
|
|
2038
|
-
Create a new ELN experiment details table entry in the experiment.
|
|
2039
|
-
|
|
2040
|
-
:param entry_name: The name of the entry.
|
|
2041
|
-
:param fields: A list of objects representing the data fields that should appear on the entry. Fields
|
|
2042
|
-
will appear in the order they are provided.
|
|
2043
|
-
:param field_maps: A field maps list that will be used to populate the entry. The data field names in
|
|
2044
|
-
the maps must match the field names of the provided field definitions.
|
|
2045
|
-
:param position: Information about where to place the entry in the experiment.
|
|
2046
|
-
:param is_field_addable: Whether users are able to add additional fields to this entry in the UI.
|
|
2047
|
-
:param is_existing_field_removable: Whether users are able to remove existing fields from this entry in the UI.
|
|
2048
|
-
:return: The newly created table entry.
|
|
2049
|
-
"""
|
|
2050
|
-
return self._create_eln_dt_table_step(entry_name, ElnBaseDataType.EXPERIMENT_DETAIL, fields, field_maps,
|
|
2051
|
-
position=position,
|
|
2052
|
-
is_field_addable=is_field_addable,
|
|
2053
|
-
is_existing_field_removable=is_existing_field_removable)
|
|
2054
|
-
|
|
2055
|
-
def create_sample_detail_table_step(self, entry_name: str,
|
|
2056
|
-
fields: list[ElnDataTypeFields] | None = None,
|
|
2057
|
-
field_maps: list[FieldMap] | None = None, *,
|
|
2058
|
-
position: ElnEntryPosition | None = None,
|
|
2059
|
-
is_field_addable: bool | None = None,
|
|
2060
|
-
is_existing_field_removable: bool | None = None) -> ElnEntryStep:
|
|
2061
|
-
"""
|
|
2062
|
-
Create a new ELN sample details table entry in the experiment.
|
|
2063
|
-
|
|
2064
|
-
:param entry_name: The name of the entry.
|
|
2065
|
-
:param fields: A list of objects representing the data fields that should appear on the entry. Fields
|
|
2066
|
-
will appear in the order they are provided.
|
|
2067
|
-
:param field_maps: A field maps list that will be used to populate the entry. The data field names in
|
|
2068
|
-
the maps must match the field names of the provided field definitions.
|
|
2069
|
-
:param position: Information about where to place the entry in the experiment.
|
|
2070
|
-
:param is_field_addable: Whether users are able to add additional fields to this entry in the UI.
|
|
2071
|
-
:param is_existing_field_removable: Whether users are able to remove existing fields from this entry in the UI.
|
|
2072
|
-
:return: The newly created table entry.
|
|
2073
|
-
"""
|
|
2074
|
-
return self._create_eln_dt_table_step(entry_name, ElnBaseDataType.SAMPLE_DETAIL, fields, field_maps,
|
|
2075
|
-
position=position,
|
|
2076
|
-
is_field_addable=is_field_addable,
|
|
2077
|
-
is_existing_field_removable=is_existing_field_removable)
|
|
2078
|
-
|
|
2079
|
-
def _create_eln_dt_table_step(self, entry_name: str,
|
|
2080
|
-
dt: ElnBaseDataType,
|
|
2081
|
-
fields: list[ElnDataTypeFields] | None = None,
|
|
2082
|
-
field_maps: list[FieldMap] | None = None, *,
|
|
2083
|
-
position: ElnEntryPosition | None = None,
|
|
2084
|
-
is_field_addable: bool | None = None,
|
|
2085
|
-
is_existing_field_removable: bool | None = None) -> ElnEntryStep:
|
|
2086
|
-
fields: list[AbstractVeloxFieldDefinition] | None = self._to_field_defs(fields, dt)
|
|
2087
|
-
step = self._create_step(ElnEntryType.Table, entry_name, dt.data_type_name,
|
|
2088
|
-
position,
|
|
2089
|
-
field_definition_list=fields,
|
|
2090
|
-
field_map_list=field_maps)
|
|
2091
|
-
if is_field_addable is not None or is_existing_field_removable is not None:
|
|
2092
|
-
update = ElnTableEntryUpdateCriteria()
|
|
2093
|
-
update.is_field_addable = is_field_addable
|
|
2094
|
-
update.is_existing_field_removable = is_existing_field_removable
|
|
2095
|
-
self.force_step_update(step, update)
|
|
2096
|
-
return step
|
|
2097
|
-
|
|
2098
|
-
def create_temp_data_step(self, entry_name: str, data_type: DataTypeIdentifier, plugin_path: str, *,
|
|
2099
|
-
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
2100
|
-
"""
|
|
2101
|
-
Create a new temp data entry in the experiment.
|
|
2102
|
-
|
|
2103
|
-
:param entry_name: The name of the entry.
|
|
2104
|
-
:param data_type: The data type of the entry.
|
|
2105
|
-
:param plugin_path: The plugin path to the plugin that populates the entry.
|
|
2106
|
-
:param position: Information about where to place the entry in the experiment.
|
|
2107
|
-
:return: The newly created temp data entry.
|
|
2108
|
-
"""
|
|
2109
|
-
return self._create_step(ElnEntryType.TempData, entry_name, data_type, position,
|
|
2110
|
-
temp_data_plugin_path=plugin_path)
|
|
2111
|
-
|
|
2112
|
-
def create_text_step(self, entry_name: str, text: str | None = None, *,
|
|
2113
|
-
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
2114
|
-
"""
|
|
2115
|
-
Create a new text entry in the experiment.
|
|
2116
|
-
|
|
2117
|
-
:param entry_name: The name of the entry.
|
|
2118
|
-
:param text: The text to populate the entry with.
|
|
2119
|
-
:param position: Information about where to place the entry in the experiment.
|
|
2120
|
-
:return: The newly created text entry.
|
|
2121
|
-
"""
|
|
2122
|
-
step: ElnEntryStep = self._create_step(ElnEntryType.Text, entry_name,
|
|
2123
|
-
ElnBaseDataType.TEXT_ENTRY_DETAIL.data_type_name, position)
|
|
2124
|
-
if text:
|
|
2125
|
-
text_record: DataRecord = self.get_step_records(entry_name)[0]
|
|
2126
|
-
text_record.set_field_value(ElnBaseDataType.get_text_entry_data_field_name(), text)
|
|
2127
|
-
DataRecordManager(self.user).commit_data_records([text_record])
|
|
2128
|
-
return step
|
|
2129
|
-
|
|
2130
|
-
def create_plate_designer_step(self, entry_name: str, source_entry: Step, *,
|
|
2131
|
-
position: ElnEntryPosition | None = None) -> ElnEntryStep:
|
|
2132
|
-
"""
|
|
2133
|
-
Create a new 3D plate designer entry in the experiment.
|
|
2134
|
-
|
|
2135
|
-
:param entry_name: The name of the entry.
|
|
2136
|
-
:param source_entry: The entry that the plate designer will source its samples from.
|
|
2137
|
-
:param position: Information about where to place the entry in the experiment.
|
|
2138
|
-
:return: The newly created plate designer entry.
|
|
2139
|
-
"""
|
|
2140
|
-
step = self.create_plugin_step(entry_name, "Sample", PLATE_DESIGNER_PLUGIN, position=position)
|
|
2141
|
-
default_layer = MultiLayerPlateLayer(
|
|
2142
|
-
MultiLayerDataTypeConfig("Sample"),
|
|
2143
|
-
PlatingOrder.FillBy.BY_COLUMN,
|
|
2144
|
-
MultiLayerReplicateConfig(),
|
|
2145
|
-
MultiLayerDilutionConfig()
|
|
2146
|
-
)
|
|
2147
|
-
initial_step_options: dict[str, str] = {
|
|
2148
|
-
"MultiLayerPlating_Plate_RecordIdList": "",
|
|
2149
|
-
"MultiLayerPlating_Entry_Prefs": MultiLayerPlatingManager.get_entry_prefs_json([default_layer]),
|
|
2150
|
-
"MultiLayerPlating_Entry_PrePlating_Prefs": MultiLayerPlatingManager.get_plate_configs_json(MultiLayerPlateConfig())
|
|
2151
|
-
}
|
|
2152
|
-
source_entry = self.__to_eln_step(source_entry)
|
|
2153
|
-
self.force_step_update_params(step, entry_height=600, entry_options_map=initial_step_options,
|
|
2154
|
-
related_entry_set=[source_entry.get_id()])
|
|
2155
|
-
return step
|
|
2156
|
-
|
|
2157
|
-
# TODO: Update these functions to use entry type-specific creation criteria once Sapiopylib supports that.
|
|
2158
|
-
# This should take in an entry creation criteria object that handles all the abstract attributes that
|
|
2159
|
-
# every entry type shares.
|
|
2160
|
-
def _create_step(self, entry_type: ElnEntryType, entry_name: str, data_type: DataTypeIdentifier,
|
|
2161
|
-
position: ElnEntryPosition | None = None, **kwargs) \
|
|
2162
|
-
-> ElnEntryStep:
|
|
2163
|
-
"""
|
|
2164
|
-
Create a new entry in the experiment of the given type.
|
|
2165
|
-
"""
|
|
2166
|
-
if position is not None:
|
|
2167
|
-
order: int = position.order
|
|
2168
|
-
tab_id: int = position.tab_id
|
|
2169
|
-
column_order: int = position.column_order
|
|
2170
|
-
column_span: int = position.column_span
|
|
2171
|
-
else:
|
|
2172
|
-
last_tab: ElnExperimentTab = self.get_last_tab()
|
|
2173
|
-
order: int = self.get_next_entry_order_in_tab(last_tab)
|
|
2174
|
-
tab_id: int = last_tab.tab_id
|
|
2175
|
-
column_order: int = 0
|
|
2176
|
-
column_span: int = last_tab.max_number_of_columns
|
|
2177
|
-
|
|
2178
|
-
data_type: str = AliasUtil.to_data_type_name(data_type)
|
|
2179
|
-
crit = ElnEntryCriteria(entry_type, entry_name, data_type, order,
|
|
2180
|
-
notebook_experiment_tab_id=tab_id,
|
|
2181
|
-
column_order=column_order,
|
|
2182
|
-
column_span=column_span,
|
|
2183
|
-
**kwargs)
|
|
2184
|
-
entry: ExperimentEntry = self._eln_man.add_experiment_entry(self._exp_id, crit)
|
|
2185
|
-
|
|
2186
|
-
self.add_entry_to_caches(entry)
|
|
2187
|
-
return ElnEntryStep(self._protocol, entry)
|
|
2188
|
-
|
|
2189
|
-
def _to_field_defs(self, fields: list[ElnDataTypeFields], dt: ElnBaseDataType) \
|
|
2190
|
-
-> list[AbstractVeloxFieldDefinition] | None:
|
|
2191
|
-
"""
|
|
2192
|
-
Convert a list of ElnDataTypeField aliases to field definitions.
|
|
2193
|
-
"""
|
|
2194
|
-
if not fields:
|
|
2195
|
-
return None
|
|
2196
|
-
field_defs: list[AbstractVeloxFieldDefinition] = []
|
|
2197
|
-
for field in fields:
|
|
2198
|
-
if isinstance(field, AbstractVeloxFieldDefinition):
|
|
2199
|
-
field_defs.append(field)
|
|
2200
|
-
elif isinstance(field, (int, ElnFieldSetInfo)):
|
|
2201
|
-
field_defs.extend(self._exp_cache.get_field_set_fields(field))
|
|
2202
|
-
elif isinstance(field, str):
|
|
2203
|
-
field_defs.append(self._exp_cache.get_predefined_field(field, dt))
|
|
2204
|
-
return field_defs
|
|
2205
|
-
|
|
2206
1850
|
def __to_eln_step(self, step: Step) -> ElnEntryStep:
|
|
2207
1851
|
"""
|
|
2208
1852
|
Convert a variable that could be either a string or an ElnEntryStep to just an ElnEntryStep.
|
|
@@ -2211,9 +1855,15 @@ class ExperimentHandler:
|
|
|
2211
1855
|
|
|
2212
1856
|
:return: The input step as an ElnEntryStep.
|
|
2213
1857
|
"""
|
|
2214
|
-
|
|
1858
|
+
if isinstance(step, str):
|
|
1859
|
+
return self.get_step(step)
|
|
1860
|
+
if isinstance(step, int):
|
|
1861
|
+
return self._steps_by_id.get(step)
|
|
1862
|
+
if isinstance(step, ExperimentEntry):
|
|
1863
|
+
return self.add_entry_to_caches(step)
|
|
1864
|
+
return step
|
|
2215
1865
|
|
|
2216
|
-
def __to_eln_tab(self, tab: TabIdentifier) -> ElnExperimentTab:
|
|
1866
|
+
def __to_eln_tab(self, tab: TabIdentifier | str) -> ElnExperimentTab:
|
|
2217
1867
|
"""
|
|
2218
1868
|
Convert a variable that could be either a string or an ElnExperimentTab to just an ElnExperimentTab.
|
|
2219
1869
|
This will query and cache the tabs for the experiment if the input tab is a name and the tabs have not been
|