sapiopycommons 2024.3.18a156__py3-none-any.whl → 2025.1.17a402__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/__init__.py +0 -0
- sapiopycommons/callbacks/callback_util.py +2041 -0
- sapiopycommons/callbacks/field_builder.py +545 -0
- sapiopycommons/chem/IndigoMolecules.py +52 -5
- sapiopycommons/chem/Molecules.py +114 -30
- sapiopycommons/customreport/__init__.py +0 -0
- sapiopycommons/customreport/column_builder.py +60 -0
- sapiopycommons/customreport/custom_report_builder.py +137 -0
- sapiopycommons/customreport/term_builder.py +315 -0
- sapiopycommons/datatype/attachment_util.py +17 -15
- sapiopycommons/datatype/data_fields.py +61 -0
- sapiopycommons/datatype/pseudo_data_types.py +440 -0
- sapiopycommons/eln/experiment_handler.py +390 -90
- sapiopycommons/eln/experiment_report_util.py +649 -0
- sapiopycommons/eln/plate_designer.py +152 -0
- sapiopycommons/files/complex_data_loader.py +31 -0
- sapiopycommons/files/file_bridge.py +153 -25
- sapiopycommons/files/file_bridge_handler.py +555 -0
- sapiopycommons/files/file_data_handler.py +633 -0
- sapiopycommons/files/file_util.py +270 -158
- sapiopycommons/files/file_validator.py +569 -0
- sapiopycommons/files/file_writer.py +377 -0
- sapiopycommons/flowcyto/flow_cyto.py +77 -0
- sapiopycommons/flowcyto/flowcyto_data.py +75 -0
- sapiopycommons/general/accession_service.py +375 -0
- sapiopycommons/general/aliases.py +259 -18
- sapiopycommons/general/audit_log.py +185 -0
- sapiopycommons/general/custom_report_util.py +252 -31
- sapiopycommons/general/directive_util.py +86 -0
- sapiopycommons/general/exceptions.py +69 -7
- sapiopycommons/general/popup_util.py +85 -18
- sapiopycommons/general/sapio_links.py +50 -0
- sapiopycommons/general/storage_util.py +148 -0
- sapiopycommons/general/time_util.py +97 -7
- sapiopycommons/multimodal/multimodal.py +146 -0
- sapiopycommons/multimodal/multimodal_data.py +490 -0
- sapiopycommons/processtracking/__init__.py +0 -0
- sapiopycommons/processtracking/custom_workflow_handler.py +406 -0
- sapiopycommons/processtracking/endpoints.py +192 -0
- sapiopycommons/recordmodel/record_handler.py +653 -149
- sapiopycommons/rules/eln_rule_handler.py +89 -8
- sapiopycommons/rules/on_save_rule_handler.py +89 -12
- sapiopycommons/sftpconnect/__init__.py +0 -0
- sapiopycommons/sftpconnect/sftp_builder.py +70 -0
- sapiopycommons/webhook/webhook_context.py +39 -0
- sapiopycommons/webhook/webhook_handlers.py +617 -69
- sapiopycommons/webhook/webservice_handlers.py +317 -0
- {sapiopycommons-2024.3.18a156.dist-info → sapiopycommons-2025.1.17a402.dist-info}/METADATA +5 -4
- sapiopycommons-2025.1.17a402.dist-info/RECORD +60 -0
- {sapiopycommons-2024.3.18a156.dist-info → sapiopycommons-2025.1.17a402.dist-info}/WHEEL +1 -1
- sapiopycommons-2024.3.18a156.dist-info/RECORD +0 -28
- {sapiopycommons-2024.3.18a156.dist-info → sapiopycommons-2025.1.17a402.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
from sapiopylib.rest.ELNService import ElnManager
|
|
2
|
+
from sapiopylib.rest.User import SapioUser
|
|
3
|
+
from sapiopylib.rest.pojo.CustomReport import CustomReportCriteria, AbstractReportTerm, RawReportTerm
|
|
4
|
+
from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
|
|
5
|
+
from sapiopylib.rest.pojo.eln.ElnExperiment import ElnExperiment, ElnExperimentQueryCriteria
|
|
6
|
+
from sapiopylib.rest.pojo.eln.SapioELNEnums import ElnExperimentStatus, ElnBaseDataType
|
|
7
|
+
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
8
|
+
|
|
9
|
+
from sapiopycommons.customreport.custom_report_builder import CustomReportBuilder
|
|
10
|
+
from sapiopycommons.customreport.term_builder import TermBuilder
|
|
11
|
+
from sapiopycommons.datatype.pseudo_data_types import EnbEntryOptionsPseudoDef, NotebookExperimentOptionPseudoDef, \
|
|
12
|
+
NotebookExperimentPseudoDef, ExperimentEntryRecordPseudoDef, EnbEntryPseudoDef
|
|
13
|
+
from sapiopycommons.general.aliases import SapioRecord, UserIdentifier, AliasUtil, FieldValue, \
|
|
14
|
+
ExperimentEntryIdentifier, ExperimentIdentifier
|
|
15
|
+
from sapiopycommons.general.custom_report_util import CustomReportUtil
|
|
16
|
+
from sapiopycommons.general.exceptions import SapioException
|
|
17
|
+
from sapiopycommons.recordmodel.record_handler import RecordHandler
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# FR-47234: Create an ExperimentReportCriteria class for finer experiment searching
|
|
21
|
+
# and create/update ExperimentReportUtil functions to use it.
|
|
22
|
+
class ExperimentReportCriteria:
|
|
23
|
+
"""
|
|
24
|
+
Experiment report criteria is used to restrict the results of searches for experiments.
|
|
25
|
+
"""
|
|
26
|
+
notebook_ids: list[int] | None
|
|
27
|
+
not_notebook_ids: list[int] | None
|
|
28
|
+
names: list[str] | None
|
|
29
|
+
not_names: list[str] | None
|
|
30
|
+
created_by: list[str] | None
|
|
31
|
+
not_created_by: list[str] | None
|
|
32
|
+
last_modified_by: list[str] | None
|
|
33
|
+
not_last_modified_by: list[str] | None
|
|
34
|
+
owners: list[str] | None
|
|
35
|
+
not_owners: list[str] | None
|
|
36
|
+
statuses: list[str] | None
|
|
37
|
+
not_statuses: list[str] | None
|
|
38
|
+
templates: str | None
|
|
39
|
+
not_templates: str | None
|
|
40
|
+
is_from_template: bool | None
|
|
41
|
+
is_from_protocol_template: bool | None
|
|
42
|
+
is_modifiable: bool | None
|
|
43
|
+
is_active: bool | None
|
|
44
|
+
created_before: int | None
|
|
45
|
+
created_after: int | None
|
|
46
|
+
due_before: int | None
|
|
47
|
+
due_after: int | None
|
|
48
|
+
last_modified_before: int | None
|
|
49
|
+
last_modified_after: int | None
|
|
50
|
+
|
|
51
|
+
def __init__(self, *,
|
|
52
|
+
notebook_ids: list[ExperimentIdentifier] | None = None,
|
|
53
|
+
not_notebook_ids: list[ExperimentIdentifier] | None = None,
|
|
54
|
+
names: list[str] | None = None,
|
|
55
|
+
not_names: list[str] | None = None,
|
|
56
|
+
created_by: list[str] | None = None,
|
|
57
|
+
not_created_by: list[str] | None = None,
|
|
58
|
+
last_modified_by: list[str] | None = None,
|
|
59
|
+
not_last_modified_by: list[str] | None = None,
|
|
60
|
+
owners: list[str] | None = None,
|
|
61
|
+
not_owners: list[str] | None = None,
|
|
62
|
+
statuses: list[ElnExperimentStatus | str] | None = None,
|
|
63
|
+
not_statuses: list[ElnExperimentStatus | str] | None = None,
|
|
64
|
+
templates: list[str] | None = None,
|
|
65
|
+
not_templates: list[str] | None = None,
|
|
66
|
+
is_from_template: bool | None = None,
|
|
67
|
+
is_from_protocol_template: bool | None = None,
|
|
68
|
+
is_modifiable: bool | None = None,
|
|
69
|
+
is_active: bool | None = None,
|
|
70
|
+
created_after: int | None = None,
|
|
71
|
+
created_before: int | None = None,
|
|
72
|
+
due_after: int | None = None,
|
|
73
|
+
due_before: int | None = None,
|
|
74
|
+
last_modified_after: int | None = None,
|
|
75
|
+
last_modified_before: int | None = None):
|
|
76
|
+
"""
|
|
77
|
+
Restrict searches using the following criteria.
|
|
78
|
+
|
|
79
|
+
:param notebook_ids: The allowed notebook ID(s) of the experiment.
|
|
80
|
+
:param not_notebook_ids: The disallowed notebook ID(s) of the experiment.
|
|
81
|
+
:param names: The allowed name(s) of the experiment.
|
|
82
|
+
:param not_names: The disallowed name(s) of the experiment.
|
|
83
|
+
:param created_by: The allowed username(s) of the user who created the experiment.
|
|
84
|
+
:param not_created_by: The disallowed username(s) of the user who created the experiment.
|
|
85
|
+
:param last_modified_by: The allowed username(s) of the user who last modified the experiment.
|
|
86
|
+
:param not_last_modified_by: The disallowed username(s) of the user who last modified the experiment.
|
|
87
|
+
:param owners: The allowed username(s) of the user who owns the experiment.
|
|
88
|
+
:param not_owners: The disallowed username(s) of the user who owns the experiment.
|
|
89
|
+
:param statuses: The allowed status(es) of the experiment.
|
|
90
|
+
:param not_statuses: The disallowed status(es) of the experiment.
|
|
91
|
+
:param templates: The allowed template name(s) that the experiment was created from.
|
|
92
|
+
:param not_templates: The disallowed template name(s) that the experiment was created from.
|
|
93
|
+
:param is_from_template: Whether the experiment was created from a template.
|
|
94
|
+
:param is_from_protocol_template: Whether the experiment was created from a protocol template.
|
|
95
|
+
:param is_modifiable: Whether the experiment is modifiable.
|
|
96
|
+
:param is_active: Whether the experiment is from an active template.
|
|
97
|
+
:param created_after: A timestamp after which the experiment was created.
|
|
98
|
+
:param created_before: A timestamp before which the experiment was created.
|
|
99
|
+
:param due_after: A timestamp after which the experiment's approval is due.
|
|
100
|
+
:param due_before: A timestamp before which the experiment's approval is due.
|
|
101
|
+
:param last_modified_after: A timestamp after which the experiment last modified.
|
|
102
|
+
:param last_modified_before: A timestamp before which the experiment last modified.
|
|
103
|
+
"""
|
|
104
|
+
self.notebook_ids = notebook_ids
|
|
105
|
+
self.not_notebook_ids = not_notebook_ids
|
|
106
|
+
self.names = names
|
|
107
|
+
self.not_names = not_names
|
|
108
|
+
self.created_by = created_by
|
|
109
|
+
self.not_created_by = not_created_by
|
|
110
|
+
self.last_modified_by = last_modified_by
|
|
111
|
+
self.not_last_modified_by = not_last_modified_by
|
|
112
|
+
self.owners = owners
|
|
113
|
+
self.not_owners = not_owners
|
|
114
|
+
self.statuses = statuses
|
|
115
|
+
self.not_statuses = not_statuses
|
|
116
|
+
self.templates = templates
|
|
117
|
+
self.not_templates = not_templates
|
|
118
|
+
|
|
119
|
+
self.is_from_template = is_from_template
|
|
120
|
+
self.is_from_protocol_template = is_from_protocol_template
|
|
121
|
+
self.is_modifiable = is_modifiable
|
|
122
|
+
self.is_active = is_active
|
|
123
|
+
|
|
124
|
+
self.created_before = created_before
|
|
125
|
+
self.created_after = created_after
|
|
126
|
+
self.due_before = due_before
|
|
127
|
+
self.due_after = due_after
|
|
128
|
+
self.last_modified_before = last_modified_before
|
|
129
|
+
self.last_modified_after = last_modified_after
|
|
130
|
+
|
|
131
|
+
if self.notebook_ids is not None:
|
|
132
|
+
self.notebook_ids = AliasUtil.to_notebook_ids(self.notebook_ids)
|
|
133
|
+
if self.not_notebook_ids is not None:
|
|
134
|
+
self.not_notebook_ids = AliasUtil.to_notebook_ids(self.not_notebook_ids)
|
|
135
|
+
if self.statuses is not None:
|
|
136
|
+
self.statuses = [x.description if isinstance(x, ElnExperimentStatus) else x for x in self.statuses]
|
|
137
|
+
if self.not_statuses is not None:
|
|
138
|
+
self.not_statuses = [x.description if isinstance(x, ElnExperimentStatus) else x for x in self.not_statuses]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# FR-46908 - Provide a utility class that holds experiment related custom reports e.g. getting all the experiments
|
|
142
|
+
# that given records were used in or getting all records of a datatype used in given experiments.
|
|
143
|
+
class ExperimentReportUtil:
|
|
144
|
+
@staticmethod
|
|
145
|
+
def map_records_to_experiment_ids(context: UserIdentifier, records: list[SapioRecord]) \
|
|
146
|
+
-> dict[SapioRecord, list[int]]:
|
|
147
|
+
"""
|
|
148
|
+
Return a dictionary mapping each record to a list of ids of experiments that they were used in.
|
|
149
|
+
If a record wasn't used in any experiments then it will be mapped to an empty list.
|
|
150
|
+
|
|
151
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
152
|
+
:param records: A list of records of the same data type.
|
|
153
|
+
:return: A dictionary mapping each record to a list of ids of each experiment it was used in.
|
|
154
|
+
"""
|
|
155
|
+
if not records:
|
|
156
|
+
return {}
|
|
157
|
+
|
|
158
|
+
user: SapioUser = AliasUtil.to_sapio_user(context)
|
|
159
|
+
data_type_name: str = AliasUtil.to_singular_data_type_name(records)
|
|
160
|
+
|
|
161
|
+
record_ids: list[int] = AliasUtil.to_record_ids(records)
|
|
162
|
+
rows = ExperimentReportUtil.__get_record_experiment_relation_rows(user, data_type_name, record_ids=record_ids)
|
|
163
|
+
|
|
164
|
+
id_to_record: dict[int, SapioRecord] = RecordHandler.map_by_id(records)
|
|
165
|
+
record_to_exps: dict[SapioRecord, set[int]] = {record: set() for record in records}
|
|
166
|
+
for row in rows:
|
|
167
|
+
record_id: int = row["RecordId"]
|
|
168
|
+
exp_id: int = row[NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME.field_name]
|
|
169
|
+
record = id_to_record[record_id]
|
|
170
|
+
record_to_exps[record].add(exp_id)
|
|
171
|
+
|
|
172
|
+
return {record: list(exps) for record, exps in record_to_exps.items()}
|
|
173
|
+
|
|
174
|
+
@staticmethod
|
|
175
|
+
def map_eln_records_to_experiment_ids(context: UserIdentifier, records: list[SapioRecord]) \
|
|
176
|
+
-> dict[SapioRecord, int]:
|
|
177
|
+
"""
|
|
178
|
+
Return a dictionary mapping each ELN record to the ID of the experiments that each were used in.
|
|
179
|
+
|
|
180
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
181
|
+
:param records: A list of ELN data type records. They are permitted to be of mixed data type names.
|
|
182
|
+
:return: A dictionary mapping each record to the ID of the experiment it was used in.
|
|
183
|
+
"""
|
|
184
|
+
if not records:
|
|
185
|
+
return {}
|
|
186
|
+
|
|
187
|
+
data_types: set[str] = AliasUtil.to_data_type_names(records, True, False)
|
|
188
|
+
for data_type in data_types:
|
|
189
|
+
if not ElnBaseDataType.is_eln_type(data_type):
|
|
190
|
+
raise SapioException(f"Data type {data_type} is not an ELN data type.")
|
|
191
|
+
|
|
192
|
+
dt_to_record: dict[str, list[SapioRecord]] = {}
|
|
193
|
+
for record in records:
|
|
194
|
+
dt_to_record.setdefault(AliasUtil.to_data_type_name(record, False), []).append(record)
|
|
195
|
+
|
|
196
|
+
report_builder = CustomReportBuilder(EnbEntryPseudoDef.DATA_TYPE_NAME)
|
|
197
|
+
tb = report_builder.get_term_builder()
|
|
198
|
+
report_builder.add_column(EnbEntryPseudoDef.DATA_TYPE_NAME__FIELD_NAME)
|
|
199
|
+
report_builder.add_column(EnbEntryPseudoDef.EXPERIMENT_ID__FIELD_NAME)
|
|
200
|
+
report_builder.set_root_term(tb.is_term(EnbEntryPseudoDef.DATA_TYPE_NAME__FIELD_NAME, data_types))
|
|
201
|
+
criteria = report_builder.build_report_criteria()
|
|
202
|
+
|
|
203
|
+
ret_val: dict[SapioRecord, int] = {}
|
|
204
|
+
rows: list[dict[str, FieldValue]] = CustomReportUtil.run_custom_report(context, criteria)
|
|
205
|
+
for row in rows:
|
|
206
|
+
dt: str = row[EnbEntryPseudoDef.DATA_TYPE_NAME__FIELD_NAME.field_name]
|
|
207
|
+
exp_id: int = row[EnbEntryPseudoDef.EXPERIMENT_ID__FIELD_NAME.field_name]
|
|
208
|
+
for record in dt_to_record[dt]:
|
|
209
|
+
ret_val[record] = exp_id
|
|
210
|
+
return ret_val
|
|
211
|
+
|
|
212
|
+
@staticmethod
|
|
213
|
+
def map_experiments_to_records_of_type(context: UserIdentifier, exp_ids: list[ExperimentIdentifier],
|
|
214
|
+
wrapper_type: type[WrappedType]) -> dict[int, list[WrappedType]]:
|
|
215
|
+
"""
|
|
216
|
+
Return a dictionary mapping each experiment id to a list of records of the given type that were used in each
|
|
217
|
+
experiment. If an experiment didn't use any records of the given type then it will be mapped to an empty list.
|
|
218
|
+
|
|
219
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
220
|
+
:param exp_ids: A list of experiment identifiers.
|
|
221
|
+
:param wrapper_type: The record model wrapper to use, corresponds to which data type we will query for.
|
|
222
|
+
:return: A dictionary mapping each experiment id to a list of records of the given type that were used in that
|
|
223
|
+
experiment.
|
|
224
|
+
"""
|
|
225
|
+
if not exp_ids:
|
|
226
|
+
return {}
|
|
227
|
+
|
|
228
|
+
user = AliasUtil.to_sapio_user(context)
|
|
229
|
+
record_handler = RecordHandler(user)
|
|
230
|
+
data_type_name: str = wrapper_type.get_wrapper_data_type_name()
|
|
231
|
+
|
|
232
|
+
exp_ids: list[int] = AliasUtil.to_notebook_ids(exp_ids)
|
|
233
|
+
rows = ExperimentReportUtil.__get_record_experiment_relation_rows(user, data_type_name, exp_ids=exp_ids)
|
|
234
|
+
record_ids: set[int] = {row["RecordId"] for row in rows}
|
|
235
|
+
records = record_handler.query_models_by_id(wrapper_type, record_ids)
|
|
236
|
+
|
|
237
|
+
id_to_record: dict[int, WrappedType] = RecordHandler.map_by_id(records)
|
|
238
|
+
exp_to_records: dict[int, set[SapioRecord]] = {exp: set() for exp in exp_ids}
|
|
239
|
+
for row in rows:
|
|
240
|
+
record_id: int = row["RecordId"]
|
|
241
|
+
exp_id: int = row[NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME.field_name]
|
|
242
|
+
record = id_to_record[record_id]
|
|
243
|
+
exp_to_records[exp_id].add(record)
|
|
244
|
+
|
|
245
|
+
return {exp: list(records) for exp, records in exp_to_records.items()}
|
|
246
|
+
|
|
247
|
+
@staticmethod
|
|
248
|
+
def get_experiment_options(context: UserIdentifier, experiments: list[ExperimentIdentifier]) \
|
|
249
|
+
-> dict[int, dict[str, str]]:
|
|
250
|
+
"""
|
|
251
|
+
Run a custom report to retrieve the experiment options for all the provided experiments. Effectively a batched
|
|
252
|
+
version of the get_notebook_experiment_options function of ElnManager.
|
|
253
|
+
|
|
254
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
255
|
+
:param experiments: The experiment identifiers to retrieve the experiment options for.
|
|
256
|
+
:return: A dictionary mapping the notebook experiment ID to the options for that experiment.
|
|
257
|
+
"""
|
|
258
|
+
exp_ids: list[int] = AliasUtil.to_notebook_ids(experiments)
|
|
259
|
+
|
|
260
|
+
report_builder = CustomReportBuilder(NotebookExperimentOptionPseudoDef.DATA_TYPE_NAME)
|
|
261
|
+
tb = report_builder.get_term_builder()
|
|
262
|
+
root = tb.is_term(NotebookExperimentOptionPseudoDef.EXPERIMENT_ID__FIELD_NAME, exp_ids)
|
|
263
|
+
report_builder.set_root_term(root)
|
|
264
|
+
report_builder.add_column(NotebookExperimentOptionPseudoDef.EXPERIMENT_ID__FIELD_NAME)
|
|
265
|
+
report_builder.add_column(NotebookExperimentOptionPseudoDef.OPTION_KEY__FIELD_NAME)
|
|
266
|
+
report_builder.add_column(NotebookExperimentOptionPseudoDef.OPTION_VALUE__FIELD_NAME)
|
|
267
|
+
report = report_builder.build_report_criteria()
|
|
268
|
+
|
|
269
|
+
# Ensure that each experiment appears in the dictionary, even if it has no experiment options.
|
|
270
|
+
options: dict[int, dict[str, str]] = {x: {} for x in exp_ids}
|
|
271
|
+
results: list[dict[str, FieldValue]] = CustomReportUtil.run_custom_report(context, report)
|
|
272
|
+
for row in results:
|
|
273
|
+
exp_id: int = row[NotebookExperimentOptionPseudoDef.EXPERIMENT_ID__FIELD_NAME.field_name]
|
|
274
|
+
key: str = row[NotebookExperimentOptionPseudoDef.OPTION_KEY__FIELD_NAME.field_name]
|
|
275
|
+
value: str = row[NotebookExperimentOptionPseudoDef.OPTION_VALUE__FIELD_NAME.field_name]
|
|
276
|
+
options[exp_id][key] = value
|
|
277
|
+
return options
|
|
278
|
+
|
|
279
|
+
@staticmethod
|
|
280
|
+
def get_experiment_entry_options(context: UserIdentifier, entries: list[ExperimentEntryIdentifier]) \
|
|
281
|
+
-> dict[int, dict[str, str]]:
|
|
282
|
+
"""
|
|
283
|
+
Run a custom report to retrieve the entry options for all the provided entries. Effectively a batched
|
|
284
|
+
version of the get_experiment_entry_options function of ElnManager.
|
|
285
|
+
|
|
286
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
287
|
+
:param entries: The experiment entry identifiers to retrieve the entry options for.
|
|
288
|
+
:return: A dictionary mapping the entry ID to the options for that entry.
|
|
289
|
+
"""
|
|
290
|
+
entries: list[int] = AliasUtil.to_entry_ids(entries)
|
|
291
|
+
report_builder = CustomReportBuilder(EnbEntryOptionsPseudoDef.DATA_TYPE_NAME)
|
|
292
|
+
tb = report_builder.get_term_builder()
|
|
293
|
+
root = tb.is_term(EnbEntryOptionsPseudoDef.ENTRY_ID__FIELD_NAME, entries)
|
|
294
|
+
report_builder.set_root_term(root)
|
|
295
|
+
report_builder.add_column(EnbEntryOptionsPseudoDef.ENTRY_ID__FIELD_NAME)
|
|
296
|
+
report_builder.add_column(EnbEntryOptionsPseudoDef.ENTRY_OPTION_KEY__FIELD_NAME)
|
|
297
|
+
report_builder.add_column(EnbEntryOptionsPseudoDef.ENTRY_OPTION_VALUE__FIELD_NAME)
|
|
298
|
+
report = report_builder.build_report_criteria()
|
|
299
|
+
|
|
300
|
+
# Ensure that each entry appears in the dictionary, even if it has no entry options.
|
|
301
|
+
options: dict[int, dict[str, str]] = {x: {} for x in entries}
|
|
302
|
+
results: list[dict[str, FieldValue]] = CustomReportUtil.run_custom_report(context, report)
|
|
303
|
+
for row in results:
|
|
304
|
+
entry_id: int = row[EnbEntryOptionsPseudoDef.ENTRY_ID__FIELD_NAME.field_name]
|
|
305
|
+
key: str = row[EnbEntryOptionsPseudoDef.ENTRY_OPTION_KEY__FIELD_NAME.field_name]
|
|
306
|
+
value: str = row[EnbEntryOptionsPseudoDef.ENTRY_OPTION_VALUE__FIELD_NAME.field_name]
|
|
307
|
+
options[entry_id][key] = value
|
|
308
|
+
return options
|
|
309
|
+
|
|
310
|
+
@staticmethod
|
|
311
|
+
def get_template_names_for_experiments(context: UserIdentifier, experiments: list[ExperimentIdentifier]) \
|
|
312
|
+
-> dict[int, str]:
|
|
313
|
+
"""
|
|
314
|
+
Run a custom report to retrieve the template names for all the provided experiments.
|
|
315
|
+
|
|
316
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
317
|
+
:param experiments: The experiment identifiers to retrieve the template names for.
|
|
318
|
+
:return: A dictionary mapping the notebook experiment ID to the template name that the experiment was created from.
|
|
319
|
+
"""
|
|
320
|
+
exp_ids: list[int] = AliasUtil.to_notebook_ids(experiments)
|
|
321
|
+
|
|
322
|
+
report_builder = CustomReportBuilder(NotebookExperimentPseudoDef.DATA_TYPE_NAME)
|
|
323
|
+
tb = report_builder.get_term_builder()
|
|
324
|
+
root = tb.is_term(NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME, exp_ids)
|
|
325
|
+
report_builder.add_join(tb.compare_is_term("ELNExperiment",
|
|
326
|
+
"RecordId",
|
|
327
|
+
NotebookExperimentPseudoDef.DATA_TYPE_NAME,
|
|
328
|
+
NotebookExperimentPseudoDef.EXPERIMENT_RECORD_ID__FIELD_NAME))
|
|
329
|
+
report_builder.set_root_term(root)
|
|
330
|
+
report_builder.add_column(NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME)
|
|
331
|
+
report_builder.add_column("TemplateExperimentName", FieldType.STRING, data_type="ELNExperiment")
|
|
332
|
+
report = report_builder.build_report_criteria()
|
|
333
|
+
|
|
334
|
+
ret_val: dict[int, str] = {}
|
|
335
|
+
results: list[dict[str, FieldValue]] = CustomReportUtil.run_custom_report(context, report)
|
|
336
|
+
for row in results:
|
|
337
|
+
exp_id: int = row[NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME.field_name]
|
|
338
|
+
name: str = row["TemplateExperimentName"]
|
|
339
|
+
ret_val[exp_id] = name
|
|
340
|
+
return ret_val
|
|
341
|
+
|
|
342
|
+
@staticmethod
|
|
343
|
+
def get_experiments_for_criteria(context: UserIdentifier, criteria: ExperimentReportCriteria) -> list[ElnExperiment]:
|
|
344
|
+
"""
|
|
345
|
+
Run a custom report that retrieves every experiment in the system for the given search criteria.
|
|
346
|
+
|
|
347
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
348
|
+
:param criteria: The search criteria to query for experiments with.
|
|
349
|
+
:return: A list of every experiment in the system that matches the search criteria.
|
|
350
|
+
"""
|
|
351
|
+
report: CustomReportCriteria = ExperimentReportUtil.build_experiment_id_report(criteria)
|
|
352
|
+
return ExperimentReportUtil.get_experiments_for_report(context, report)
|
|
353
|
+
|
|
354
|
+
@staticmethod
|
|
355
|
+
def get_experiments_by_name(context: UserIdentifier, name: str,
|
|
356
|
+
criteria: ExperimentReportCriteria = ExperimentReportCriteria()) -> list[ElnExperiment]:
|
|
357
|
+
"""
|
|
358
|
+
Run a custom report that retrieves every experiment in the system with a given name.
|
|
359
|
+
|
|
360
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
361
|
+
:param name: The name of the experiment to query for.
|
|
362
|
+
:param criteria: Additional search criteria to filter the results.
|
|
363
|
+
:return: A list of every experiment in the system with a name that matches the input
|
|
364
|
+
that matches the given criteria.
|
|
365
|
+
"""
|
|
366
|
+
return ExperimentReportUtil.get_experiments_by_names(context, [name], criteria)[name]
|
|
367
|
+
|
|
368
|
+
@staticmethod
|
|
369
|
+
def get_experiments_by_names(context: UserIdentifier, names: list[str],
|
|
370
|
+
criteria: ExperimentReportCriteria = ExperimentReportCriteria()) -> dict[str, list[ElnExperiment]]:
|
|
371
|
+
"""
|
|
372
|
+
Run a custom report that retrieves every experiment in the system with a name from a list of names.
|
|
373
|
+
|
|
374
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
375
|
+
:param names: The names of the experiment to query for.
|
|
376
|
+
:param criteria: Additional search criteria to filter the results.
|
|
377
|
+
:return: A dictionary mapping the experiment name to a list of every experiment in the system with that name
|
|
378
|
+
that matches the given criteria.
|
|
379
|
+
"""
|
|
380
|
+
criteria.names = names
|
|
381
|
+
experiments: list[ElnExperiment] = ExperimentReportUtil.get_experiments_for_criteria(context, criteria)
|
|
382
|
+
|
|
383
|
+
# Ensure that each name appears in the dictionary, even if it has no experiments.
|
|
384
|
+
ret_val: dict[str, list[ElnExperiment]] = {x: [] for x in names}
|
|
385
|
+
for experiment in experiments:
|
|
386
|
+
ret_val.get(experiment.notebook_experiment_name).append(experiment)
|
|
387
|
+
return ret_val
|
|
388
|
+
|
|
389
|
+
@staticmethod
|
|
390
|
+
def get_experiments_by_owner(context: UserIdentifier, owner: str,
|
|
391
|
+
criteria: ExperimentReportCriteria = ExperimentReportCriteria()) -> list[ElnExperiment]:
|
|
392
|
+
"""
|
|
393
|
+
Run a custom report that retrieves every experiment in the system with a given owner.
|
|
394
|
+
|
|
395
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
396
|
+
:param owner: The username of the owner of the experiments to query for.
|
|
397
|
+
:param criteria: Additional search criteria to filter the results.
|
|
398
|
+
:return: A list of every experiment in the system with the owner that matches the input
|
|
399
|
+
that matches the given criteria.
|
|
400
|
+
"""
|
|
401
|
+
return ExperimentReportUtil.get_experiments_by_owners(context, [owner], criteria)[owner]
|
|
402
|
+
|
|
403
|
+
@staticmethod
|
|
404
|
+
def get_experiments_by_owners(context: UserIdentifier, owners: list[str],
|
|
405
|
+
criteria: ExperimentReportCriteria = ExperimentReportCriteria) \
|
|
406
|
+
-> dict[str, list[ElnExperiment]]:
|
|
407
|
+
"""
|
|
408
|
+
Run a custom report that retrieves every experiment in the system with a given list of owners.
|
|
409
|
+
|
|
410
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
411
|
+
:param owners: The usernames of the owner of the experiments to query for.
|
|
412
|
+
:param criteria: Additional search criteria to filter the results.
|
|
413
|
+
:return: A dictionary mapping the owner username to a list of every experiment in the system from that owner
|
|
414
|
+
that matches the given criteria.
|
|
415
|
+
"""
|
|
416
|
+
criteria.owners = owners
|
|
417
|
+
experiments: list[ElnExperiment] = ExperimentReportUtil.get_experiments_for_criteria(context, criteria)
|
|
418
|
+
|
|
419
|
+
# Ensure that each name appears in the dictionary, even if it has no experiments.
|
|
420
|
+
ret_val: dict[str, list[ElnExperiment]] = {x: [] for x in owners}
|
|
421
|
+
for experiment in experiments:
|
|
422
|
+
ret_val.get(experiment.owner).append(experiment)
|
|
423
|
+
return ret_val
|
|
424
|
+
|
|
425
|
+
@staticmethod
|
|
426
|
+
def get_experiments_by_creator(context: UserIdentifier, created_by: str,
|
|
427
|
+
criteria: ExperimentReportCriteria = ExperimentReportCriteria()) \
|
|
428
|
+
-> list[ElnExperiment]:
|
|
429
|
+
"""
|
|
430
|
+
Run a custom report that retrieves every experiment in the system with a given creator.
|
|
431
|
+
|
|
432
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
433
|
+
:param created_by: The username of the creator of the experiments to query for.
|
|
434
|
+
:param criteria: Additional search criteria to filter the results.
|
|
435
|
+
:return: A list of every experiment in the system with the creator that matches the input
|
|
436
|
+
that matches the given criteria.
|
|
437
|
+
"""
|
|
438
|
+
return ExperimentReportUtil.get_experiments_by_creators(context, [created_by], criteria=criteria)[created_by]
|
|
439
|
+
|
|
440
|
+
@staticmethod
|
|
441
|
+
def get_experiments_by_creators(context: UserIdentifier, created_by: list[str],
|
|
442
|
+
criteria: ExperimentReportCriteria = ExperimentReportCriteria()) \
|
|
443
|
+
-> dict[str, list[ElnExperiment]]:
|
|
444
|
+
"""
|
|
445
|
+
Run a custom report that retrieves every experiment in the system with a given list of creators.
|
|
446
|
+
|
|
447
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
448
|
+
:param created_by: The usernames of the creator of the experiments to query for.
|
|
449
|
+
:param criteria: Additional search criteria to filter the results.
|
|
450
|
+
:return: A dictionary mapping the owner username to a list of every experiment in the system from that creator
|
|
451
|
+
that matches the given criteria.
|
|
452
|
+
"""
|
|
453
|
+
criteria.created_by = created_by
|
|
454
|
+
experiments: list[ElnExperiment] = ExperimentReportUtil.get_experiments_for_criteria(context, criteria)
|
|
455
|
+
|
|
456
|
+
# Ensure that each name appears in the dictionary, even if it has no experiments.
|
|
457
|
+
ret_val: dict[str, list[ElnExperiment]] = {x: [] for x in created_by}
|
|
458
|
+
for experiment in experiments:
|
|
459
|
+
ret_val.get(experiment.created_by).append(experiment)
|
|
460
|
+
return ret_val
|
|
461
|
+
|
|
462
|
+
@staticmethod
|
|
463
|
+
def build_experiment_id_report(criteria: ExperimentReportCriteria = ExperimentReportCriteria()) \
|
|
464
|
+
-> CustomReportCriteria:
|
|
465
|
+
"""
|
|
466
|
+
Construct a custom report using the provided ExperimentReportCriteria.
|
|
467
|
+
|
|
468
|
+
:param criteria: The criteria to construct a custom report from.
|
|
469
|
+
:return: A custom report that can be used to search for experiment IDs that match the given criteria.
|
|
470
|
+
"""
|
|
471
|
+
dt: str = NotebookExperimentPseudoDef.DATA_TYPE_NAME
|
|
472
|
+
report_builder = CustomReportBuilder(dt)
|
|
473
|
+
tb = report_builder.get_term_builder()
|
|
474
|
+
report_builder.add_column(NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME)
|
|
475
|
+
|
|
476
|
+
root: AbstractReportTerm | None = None
|
|
477
|
+
if criteria.notebook_ids is not None:
|
|
478
|
+
root = tb.is_term(NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME, criteria.notebook_ids)
|
|
479
|
+
if criteria.not_notebook_ids is not None:
|
|
480
|
+
term = tb.not_term(NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME, criteria.not_notebook_ids)
|
|
481
|
+
if root is not None:
|
|
482
|
+
root = tb.and_terms(root, term)
|
|
483
|
+
if root is None:
|
|
484
|
+
root = ExperimentReportUtil.all_notebook_experiments_term()
|
|
485
|
+
|
|
486
|
+
if criteria.names is not None:
|
|
487
|
+
term = tb.is_term(NotebookExperimentPseudoDef.EXPERIMENT_NAME__FIELD_NAME, criteria.names)
|
|
488
|
+
root = tb.and_terms(root, term)
|
|
489
|
+
if criteria.not_names is not None:
|
|
490
|
+
term = tb.not_term(NotebookExperimentPseudoDef.EXPERIMENT_NAME__FIELD_NAME, criteria.not_names)
|
|
491
|
+
root = tb.and_terms(root, term)
|
|
492
|
+
|
|
493
|
+
if criteria.created_by is not None:
|
|
494
|
+
term = tb.is_term(NotebookExperimentPseudoDef.CREATED_BY__FIELD_NAME, criteria.created_by)
|
|
495
|
+
root = tb.and_terms(root, term)
|
|
496
|
+
if criteria.not_created_by is not None:
|
|
497
|
+
term = tb.not_term(NotebookExperimentPseudoDef.CREATED_BY__FIELD_NAME, criteria.not_created_by)
|
|
498
|
+
root = tb.and_terms(root, term)
|
|
499
|
+
|
|
500
|
+
if criteria.last_modified_by is not None:
|
|
501
|
+
term = tb.is_term(NotebookExperimentPseudoDef.LAST_MODIFIED_BY__FIELD_NAME, criteria.last_modified_by)
|
|
502
|
+
root = tb.and_terms(root, term)
|
|
503
|
+
if criteria.not_last_modified_by is not None:
|
|
504
|
+
term = tb.not_term(NotebookExperimentPseudoDef.LAST_MODIFIED_BY__FIELD_NAME, criteria.not_last_modified_by)
|
|
505
|
+
root = tb.and_terms(root, term)
|
|
506
|
+
|
|
507
|
+
if criteria.owners is not None:
|
|
508
|
+
term = tb.is_term(NotebookExperimentPseudoDef.EXPERIMENT_OWNER__FIELD_NAME, criteria.owners)
|
|
509
|
+
root = tb.and_terms(root, term)
|
|
510
|
+
if criteria.not_owners is not None:
|
|
511
|
+
term = tb.not_term(NotebookExperimentPseudoDef.EXPERIMENT_OWNER__FIELD_NAME, criteria.not_owners)
|
|
512
|
+
root = tb.and_terms(root, term)
|
|
513
|
+
|
|
514
|
+
if criteria.statuses is not None:
|
|
515
|
+
term = tb.is_term(NotebookExperimentPseudoDef.STATUS__FIELD_NAME, criteria.statuses)
|
|
516
|
+
root = tb.and_terms(root, term)
|
|
517
|
+
if criteria.not_statuses is not None:
|
|
518
|
+
term = tb.not_term(NotebookExperimentPseudoDef.STATUS__FIELD_NAME, criteria.not_statuses)
|
|
519
|
+
root = tb.and_terms(root, term)
|
|
520
|
+
|
|
521
|
+
# For the template name term, we need to join on the experiment record.
|
|
522
|
+
if criteria.templates is not None or criteria.not_templates is not None:
|
|
523
|
+
join = tb.compare_is_term("ELNExperiment",
|
|
524
|
+
"RecordId",
|
|
525
|
+
NotebookExperimentPseudoDef.DATA_TYPE_NAME,
|
|
526
|
+
NotebookExperimentPseudoDef.EXPERIMENT_RECORD_ID__FIELD_NAME)
|
|
527
|
+
report_builder.add_join(join)
|
|
528
|
+
if criteria.templates is not None:
|
|
529
|
+
term = tb.is_term("TemplateExperimentName", criteria.templates, data_type="ELNExperiment")
|
|
530
|
+
root = tb.and_terms(root, term)
|
|
531
|
+
if criteria.not_templates is not None:
|
|
532
|
+
term = tb.not_term("TemplateExperimentName", criteria.not_templates, data_type="ELNExperiment")
|
|
533
|
+
root = tb.and_terms(root, term)
|
|
534
|
+
|
|
535
|
+
if criteria.is_from_template is not None:
|
|
536
|
+
term = tb.is_term(NotebookExperimentPseudoDef.IS_TEMPLATE__FIELD_NAME, criteria.is_from_template)
|
|
537
|
+
root = tb.and_terms(root, term)
|
|
538
|
+
if criteria.is_from_protocol_template is not None:
|
|
539
|
+
term = tb.is_term(NotebookExperimentPseudoDef.IS_PROTOCOL_TEMPLATE__FIELD_NAME, criteria.is_from_protocol_template)
|
|
540
|
+
root = tb.and_terms(root, term)
|
|
541
|
+
if criteria.is_modifiable is not None:
|
|
542
|
+
term = tb.is_term(NotebookExperimentPseudoDef.IS_MODIFIABLE__FIELD_NAME, criteria.is_modifiable)
|
|
543
|
+
root = tb.and_terms(root, term)
|
|
544
|
+
if criteria.is_active is not None:
|
|
545
|
+
term = tb.is_term(NotebookExperimentPseudoDef.IS_ACTIVE__FIELD_NAME, criteria.is_active)
|
|
546
|
+
root = tb.and_terms(root, term)
|
|
547
|
+
|
|
548
|
+
if criteria.created_after is not None:
|
|
549
|
+
term = tb.gte_term(NotebookExperimentPseudoDef.DATE_CREATED__FIELD_NAME, criteria.created_after)
|
|
550
|
+
root = tb.and_terms(root, term)
|
|
551
|
+
if criteria.created_before is not None:
|
|
552
|
+
term = tb.lte_term(NotebookExperimentPseudoDef.DATE_CREATED__FIELD_NAME, criteria.created_before)
|
|
553
|
+
root = tb.and_terms(root, term)
|
|
554
|
+
|
|
555
|
+
if criteria.last_modified_after is not None:
|
|
556
|
+
term = tb.gte_term(NotebookExperimentPseudoDef.LAST_MODIFIED_DATE__FIELD_NAME, criteria.last_modified_after)
|
|
557
|
+
root = tb.and_terms(root, term)
|
|
558
|
+
if criteria.last_modified_before is not None:
|
|
559
|
+
term = tb.lte_term(NotebookExperimentPseudoDef.LAST_MODIFIED_DATE__FIELD_NAME, criteria.last_modified_before)
|
|
560
|
+
root = tb.and_terms(root, term)
|
|
561
|
+
|
|
562
|
+
if criteria.due_after is not None:
|
|
563
|
+
term = tb.gte_term(NotebookExperimentPseudoDef.APPROVAL_DUE_DATE__FIELD_NAME, criteria.due_after)
|
|
564
|
+
root = tb.and_terms(root, term)
|
|
565
|
+
if criteria.due_before is not None:
|
|
566
|
+
term = tb.lte_term(NotebookExperimentPseudoDef.APPROVAL_DUE_DATE__FIELD_NAME, criteria.due_before)
|
|
567
|
+
root = tb.and_terms(root, term)
|
|
568
|
+
|
|
569
|
+
report_builder.set_root_term(root)
|
|
570
|
+
return report_builder.build_report_criteria()
|
|
571
|
+
|
|
572
|
+
@staticmethod
|
|
573
|
+
def get_experiments_for_report(context: UserIdentifier, report: CustomReportCriteria) -> list[ElnExperiment]:
|
|
574
|
+
"""
|
|
575
|
+
Retrieve the ELN experiment objects for experiments whose notebook IDs appear in a custom report.
|
|
576
|
+
|
|
577
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
578
|
+
:param report: A custom report that searches for ELN experiments, containing an "Experiment ID" column to
|
|
579
|
+
query for the experiments of.
|
|
580
|
+
:return: The ELN experiments that match the provided report.
|
|
581
|
+
"""
|
|
582
|
+
user = AliasUtil.to_sapio_user(context)
|
|
583
|
+
exp_ids: list[int] = []
|
|
584
|
+
for row in CustomReportUtil.run_custom_report(user, report):
|
|
585
|
+
exp_ids.append(row[NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME.field_name])
|
|
586
|
+
if not exp_ids:
|
|
587
|
+
return []
|
|
588
|
+
|
|
589
|
+
criteria = ElnExperimentQueryCriteria(notebook_experiment_id_white_list=exp_ids)
|
|
590
|
+
return ElnManager(user).get_eln_experiment_by_criteria(criteria)
|
|
591
|
+
|
|
592
|
+
@staticmethod
|
|
593
|
+
def all_notebook_experiments_term() -> RawReportTerm:
|
|
594
|
+
"""
|
|
595
|
+
:return: A report term searching for all notebook experiments.
|
|
596
|
+
"""
|
|
597
|
+
tb = TermBuilder(NotebookExperimentPseudoDef.DATA_TYPE_NAME)
|
|
598
|
+
return tb.gte_term(NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME, 0)
|
|
599
|
+
|
|
600
|
+
@staticmethod
|
|
601
|
+
def __get_record_experiment_relation_rows(user: SapioUser, data_type_name: str, record_ids: list[int] | None = None,
|
|
602
|
+
exp_ids: list[int] | None = None) -> list[dict[str, FieldValue]]:
|
|
603
|
+
"""
|
|
604
|
+
Return a list of dicts mapping \"RECORDID\" to the record id and \"EXPERIMENTID\" to the experiment id.
|
|
605
|
+
At least one of record_ids and exp_ids should be provided.
|
|
606
|
+
"""
|
|
607
|
+
assert (record_ids or exp_ids)
|
|
608
|
+
|
|
609
|
+
report_builder = CustomReportBuilder(data_type_name)
|
|
610
|
+
tb = report_builder.get_term_builder()
|
|
611
|
+
if record_ids:
|
|
612
|
+
records_term = tb.is_term("RecordId", record_ids)
|
|
613
|
+
else:
|
|
614
|
+
# Get all records of the given type
|
|
615
|
+
records_term = tb.all_records_term()
|
|
616
|
+
|
|
617
|
+
if exp_ids:
|
|
618
|
+
exp_term = tb.is_term(NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME, exp_ids,
|
|
619
|
+
data_type=NotebookExperimentPseudoDef.DATA_TYPE_NAME)
|
|
620
|
+
else:
|
|
621
|
+
# Get all experiments
|
|
622
|
+
exp_term = ExperimentReportUtil.all_notebook_experiments_term()
|
|
623
|
+
|
|
624
|
+
root_term = tb.and_terms(records_term, exp_term)
|
|
625
|
+
|
|
626
|
+
# Join records on the experiment entry records that correspond to them.
|
|
627
|
+
records_entry_join = tb.compare_is_term(data_type_name,
|
|
628
|
+
"RecordId",
|
|
629
|
+
ExperimentEntryRecordPseudoDef.DATA_TYPE_NAME,
|
|
630
|
+
ExperimentEntryRecordPseudoDef.RECORD_ID__FIELD_NAME)
|
|
631
|
+
# Join entry records on the experiment entries they are in.
|
|
632
|
+
experiment_entry_enb_entry_join = tb.compare_is_term(EnbEntryPseudoDef.DATA_TYPE_NAME,
|
|
633
|
+
EnbEntryPseudoDef.ENTRY_ID__FIELD_NAME,
|
|
634
|
+
ExperimentEntryRecordPseudoDef.DATA_TYPE_NAME,
|
|
635
|
+
ExperimentEntryRecordPseudoDef.ENTRY_ID__FIELD_NAME)
|
|
636
|
+
# Join entries on the experiments they are in.
|
|
637
|
+
enb_entry_experiment_join = tb.compare_is_term(NotebookExperimentPseudoDef.DATA_TYPE_NAME,
|
|
638
|
+
NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME,
|
|
639
|
+
EnbEntryPseudoDef.DATA_TYPE_NAME,
|
|
640
|
+
EnbEntryPseudoDef.EXPERIMENT_ID__FIELD_NAME)
|
|
641
|
+
|
|
642
|
+
report_builder.set_root_term(root_term)
|
|
643
|
+
report_builder.add_column("RecordId", FieldType.LONG)
|
|
644
|
+
report_builder.add_column(NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME,
|
|
645
|
+
data_type=NotebookExperimentPseudoDef.DATA_TYPE_NAME)
|
|
646
|
+
report_builder.add_join(records_entry_join, ExperimentEntryRecordPseudoDef.DATA_TYPE_NAME)
|
|
647
|
+
report_builder.add_join(experiment_entry_enb_entry_join, EnbEntryPseudoDef.DATA_TYPE_NAME)
|
|
648
|
+
report_builder.add_join(enb_entry_experiment_join, NotebookExperimentPseudoDef.DATA_TYPE_NAME)
|
|
649
|
+
return CustomReportUtil.run_custom_report(user, report_builder.build_report_criteria())
|