sapiopycommons 2025.2.25a448__tar.gz → 2025.2.25a450__tar.gz
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-2025.2.25a448 → sapiopycommons-2025.2.25a450}/PKG-INFO +1 -1
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/pyproject.toml +1 -1
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/ai/tool_of_tools.py +120 -66
- sapiopycommons-2025.2.25a448/src/sapiopycommons/ai/biopython_helper.py +0 -639
- sapiopycommons-2025.2.25a448/src/sapiopycommons/ai/rdkit_helper.py +0 -82
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/.gitignore +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/LICENSE +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/README.md +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/ai/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/callbacks/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/callbacks/callback_util.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/callbacks/field_builder.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/chem/IndigoMolecules.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/chem/Molecules.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/chem/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/customreport/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/customreport/auto_pagers.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/customreport/column_builder.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/customreport/custom_report_builder.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/customreport/term_builder.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/datatype/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/datatype/attachment_util.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/datatype/data_fields.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/datatype/pseudo_data_types.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/eln/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/eln/experiment_handler.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/eln/experiment_report_util.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/eln/plate_designer.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/files/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/files/complex_data_loader.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/files/file_bridge.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/files/file_bridge_handler.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/files/file_data_handler.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/files/file_util.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/files/file_validator.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/files/file_writer.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/flowcyto/flow_cyto.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/flowcyto/flowcyto_data.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/general/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/general/accession_service.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/general/aliases.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/general/audit_log.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/general/custom_report_util.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/general/directive_util.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/general/exceptions.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/general/popup_util.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/general/sapio_links.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/general/storage_util.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/general/time_util.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/multimodal/multimodal.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/multimodal/multimodal_data.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/processtracking/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/processtracking/custom_workflow_handler.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/processtracking/endpoints.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/recordmodel/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/recordmodel/record_handler.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/rules/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/rules/eln_rule_handler.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/rules/on_save_rule_handler.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/samples/aliquot.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/sftpconnect/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/sftpconnect/sftp_builder.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/webhook/__init__.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/webhook/webhook_context.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/webhook/webhook_handlers.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/webhook/webservice_handlers.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/AF-A0A009IHW8-F1-model_v4.cif +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/_do_not_add_init_py_here +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/accession_test.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/aliquot_test.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/bio_reg_test.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/chem_test.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/chem_test_curation_queue.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/curation_queue_test.sdf +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/data_type_models.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/flowcyto/101_DEN084Y5_15_E01_008_clean.fcs +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/flowcyto/101_DEN084Y5_15_E03_009_clean.fcs +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/flowcyto/101_DEN084Y5_15_E05_010_clean.fcs +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/flowcyto/8_color_ICS.wsp +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/flowcyto/COVID19_W_001_O.fcs +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/flowcyto_test.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/kappa.chains.fasta +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/mafft_test.py +0 -0
- {sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/tests/test.gb +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: sapiopycommons
|
|
3
|
-
Version: 2025.2.
|
|
3
|
+
Version: 2025.2.25a450
|
|
4
4
|
Summary: Official Sapio Python API Utilities Package
|
|
5
5
|
Project-URL: Homepage, https://github.com/sapiosciences
|
|
6
6
|
Author-email: Jonathan Steck <jsteck@sapiosciences.com>, Yechen Qiao <yqiao@sapiosciences.com>
|
{sapiopycommons-2025.2.25a448 → sapiopycommons-2025.2.25a450}/src/sapiopycommons/ai/tool_of_tools.py
RENAMED
|
@@ -2,19 +2,20 @@ import base64
|
|
|
2
2
|
import io
|
|
3
3
|
import math
|
|
4
4
|
import re
|
|
5
|
-
from typing import Final, Mapping, Any
|
|
5
|
+
from typing import Final, Mapping, Any
|
|
6
6
|
|
|
7
7
|
import requests
|
|
8
8
|
from pandas import DataFrame
|
|
9
9
|
from requests import Response
|
|
10
|
+
from sapiopylib.rest.DataMgmtService import DataMgmtServer
|
|
10
11
|
from sapiopylib.rest.DataRecordManagerService import DataRecordManager
|
|
11
12
|
from sapiopylib.rest.DataTypeService import DataTypeManager
|
|
12
13
|
from sapiopylib.rest.ELNService import ElnManager
|
|
13
14
|
from sapiopylib.rest.User import SapioUser
|
|
14
15
|
from sapiopylib.rest.pojo.DataRecord import DataRecord
|
|
15
16
|
from sapiopylib.rest.pojo.Sort import SortDirection
|
|
16
|
-
from sapiopylib.rest.pojo.chartdata.DashboardDefinition import GaugeChartDefinition
|
|
17
|
-
from sapiopylib.rest.pojo.chartdata.DashboardEnums import ChartGroupingType, ChartOperationType
|
|
17
|
+
from sapiopylib.rest.pojo.chartdata.DashboardDefinition import GaugeChartDefinition, DashboardDefinition
|
|
18
|
+
from sapiopylib.rest.pojo.chartdata.DashboardEnums import ChartGroupingType, ChartOperationType, DashboardScope
|
|
18
19
|
from sapiopylib.rest.pojo.chartdata.DashboardSeries import GaugeChartSeries
|
|
19
20
|
from sapiopylib.rest.pojo.datatype.DataType import DataTypeDefinition
|
|
20
21
|
from sapiopylib.rest.pojo.datatype.DataTypeLayout import DataTypeLayout, TableLayout
|
|
@@ -24,7 +25,7 @@ from sapiopylib.rest.pojo.eln.ElnEntryPosition import ElnEntryPosition
|
|
|
24
25
|
from sapiopylib.rest.pojo.eln.ElnExperiment import ElnExperiment
|
|
25
26
|
from sapiopylib.rest.pojo.eln.ExperimentEntry import ExperimentEntry
|
|
26
27
|
from sapiopylib.rest.pojo.eln.ExperimentEntryCriteria import ElnEntryCriteria, ElnFormEntryUpdateCriteria, \
|
|
27
|
-
ElnDashboardEntryUpdateCriteria
|
|
28
|
+
ElnDashboardEntryUpdateCriteria
|
|
28
29
|
from sapiopylib.rest.pojo.eln.SapioELNEnums import ElnEntryType, ElnBaseDataType
|
|
29
30
|
from sapiopylib.rest.pojo.eln.eln_headings import ElnExperimentTabAddCriteria, ElnExperimentTab
|
|
30
31
|
from sapiopylib.rest.pojo.eln.field_set import ElnFieldSetInfo
|
|
@@ -325,57 +326,48 @@ class AiHelper:
|
|
|
325
326
|
if not json_list:
|
|
326
327
|
return None
|
|
327
328
|
|
|
328
|
-
def
|
|
329
|
+
def update_string_field(f: AbstractVeloxFieldDefinition, v: Any) -> None:
|
|
329
330
|
"""
|
|
330
|
-
|
|
331
|
+
Update the max length of the string field and whether it is a link-out field depending on the length and
|
|
332
|
+
form of the given value.
|
|
331
333
|
|
|
332
|
-
:param
|
|
333
|
-
:param v: A
|
|
334
|
-
:param n: The unique name of the field.
|
|
334
|
+
:param f: The definition of the string field.
|
|
335
|
+
:param v: A field value that will be present for this field.
|
|
335
336
|
"""
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
Update the max length of a string field.
|
|
345
|
-
|
|
346
|
-
:param k: The JSON key that the field value is being pulled from.
|
|
347
|
-
:param v: The field value.
|
|
348
|
-
:param lengths: The dictionary of field lengths.
|
|
349
|
-
"""
|
|
350
|
-
lengths[k] = max(lengths.get(k, 100), len(str(v)) if v is not None else 0)
|
|
337
|
+
if not isinstance(f, VeloxStringFieldDefinition) or v is None:
|
|
338
|
+
return
|
|
339
|
+
sv = str(v)
|
|
340
|
+
f.max_length = max(f.max_length, len(sv))
|
|
341
|
+
if not f.link_out and sv.startswith("http://") or sv.startswith("https://"):
|
|
342
|
+
link_out, link_out_url = FieldBuilder._convert_link_out({"Link": "[[LINK_OUT]]"})
|
|
343
|
+
f.link_out = link_out
|
|
344
|
+
f.link_out_url = link_out_url
|
|
351
345
|
|
|
352
346
|
# Determine which fields in the JSON can be used to create field definitions.
|
|
353
347
|
fb = FieldBuilder()
|
|
354
348
|
json_key_to_field_def: dict[str, AbstractVeloxFieldDefinition] = {}
|
|
355
|
-
json_key_to_field_name: dict[str, str] = {}
|
|
356
|
-
json_key_to_string_length: dict[str, int] = {}
|
|
357
349
|
numeric_string_fields: set[str] = set()
|
|
358
350
|
for values in json_list:
|
|
359
351
|
for key, value in values.items():
|
|
360
|
-
#
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
field_name =
|
|
352
|
+
# Skip null values, since we can't know what type they're meant to represent.
|
|
353
|
+
if value is None:
|
|
354
|
+
continue
|
|
355
|
+
|
|
356
|
+
# The field name is the JSON key name, but with spaces and dashes replaced by underscores and with a
|
|
357
|
+
# leading underscore added if the field name starts with a number.
|
|
358
|
+
field_name: str = key.strip()
|
|
359
|
+
if " " in field_name:
|
|
360
|
+
field_name = field_name.replace(" ", "_")
|
|
361
|
+
if "-" in field_name:
|
|
362
|
+
field_name = field_name.replace("-", "_")
|
|
363
|
+
if field_name[0].isnumeric():
|
|
364
|
+
field_name = "_" + field_name
|
|
373
365
|
|
|
374
366
|
# If this is the first time this key is being encountered, create a field for it.
|
|
375
367
|
if key not in json_key_to_field_def:
|
|
376
368
|
if isinstance(value, str):
|
|
377
|
-
json_key_to_field_def[key] =
|
|
378
|
-
|
|
369
|
+
json_key_to_field_def[key] = fb.string_field(field_name, display_name=key)
|
|
370
|
+
update_string_field(json_key_to_field_def[key], value)
|
|
379
371
|
elif isinstance(value, bool):
|
|
380
372
|
json_key_to_field_def[key] = fb.boolean_field(field_name, display_name=key)
|
|
381
373
|
elif isinstance(value, (int, float)):
|
|
@@ -388,25 +380,21 @@ class AiHelper:
|
|
|
388
380
|
# Strings can be anything, so we don't need to check the value type.
|
|
389
381
|
if field_type == FieldType.STRING:
|
|
390
382
|
# We still need to make sure the lengths are fine.
|
|
391
|
-
|
|
383
|
+
update_string_field(json_key_to_field_def[key], value)
|
|
392
384
|
continue
|
|
393
385
|
# Boolean values can only be booleans.
|
|
394
386
|
if field_type == FieldType.BOOLEAN and isinstance(value, bool):
|
|
395
387
|
continue
|
|
396
388
|
# Integers and floats both fit in DOUBLE fields, but floats can't be NaN or infinity.
|
|
397
|
-
if field_type == FieldType.DOUBLE
|
|
398
|
-
|
|
389
|
+
if field_type == FieldType.DOUBLE:
|
|
390
|
+
# Booleans count as ints for isinstance, so make sure that true integers continue but bools don't.
|
|
391
|
+
if isinstance(value, int) and not isinstance(value, bool):
|
|
399
392
|
continue
|
|
400
393
|
if isinstance(value, float) and not math.isnan(value) and not math.isinf(value):
|
|
401
394
|
continue
|
|
402
395
|
numeric_string_fields.add(key)
|
|
403
|
-
json_key_to_field_def[key] =
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
# Update the max length of each string field.
|
|
407
|
-
for key, value in json_key_to_string_length.items():
|
|
408
|
-
field = cast(VeloxStringFieldDefinition, json_key_to_field_def[key])
|
|
409
|
-
field.max_length = value
|
|
396
|
+
json_key_to_field_def[key] = fb.string_field(field_name, display_name=key)
|
|
397
|
+
update_string_field(json_key_to_field_def[key], value)
|
|
410
398
|
|
|
411
399
|
# Sort the JSON list if requested.
|
|
412
400
|
if sort_field and sort_direction != SortDirection.NONE:
|
|
@@ -428,7 +416,7 @@ class AiHelper:
|
|
|
428
416
|
field_map: dict[str, Any] = {}
|
|
429
417
|
for key, field in json_key_to_field_def.items():
|
|
430
418
|
val: Any = json_dict.get(key)
|
|
431
|
-
if key in numeric_string_fields and val is not None and
|
|
419
|
+
if key in numeric_string_fields and val is not None and isinstance(val, (int, float)):
|
|
432
420
|
val: str = f"{val:.3f}"
|
|
433
421
|
field_map[field.data_field_name] = val
|
|
434
422
|
field_maps.append(field_map)
|
|
@@ -738,26 +726,21 @@ class ToolOfToolsHelper:
|
|
|
738
726
|
# tool started and format the description so that the text isn't too small to read.
|
|
739
727
|
# TODO: Get the UTC offset in seconds from the header once that's being sent.
|
|
740
728
|
now: str = TimeUtil.now_in_format("%Y-%m-%d %H:%M:%S UTC", "UTC")
|
|
741
|
-
|
|
729
|
+
description: str = f"<p>{HtmlFormatter.timestamp(now)}<br>{HtmlFormatter.body(self.description)}</p>"
|
|
730
|
+
text_entry: ElnEntryStep = _ELNStepFactory.create_text_entry(self.helper.protocol, description,
|
|
731
|
+
column_order=0, column_span=2)
|
|
742
732
|
self.description_entry = text_entry
|
|
743
733
|
self.description_record = text_entry.get_records()[0]
|
|
744
734
|
|
|
745
|
-
# Shrink the text entry by one column.
|
|
746
|
-
text_update_crit = ElnTextEntryUpdateCriteria()
|
|
747
|
-
text_update_crit.column_order = 0
|
|
748
|
-
text_update_crit.column_span = 2
|
|
749
|
-
self.eln_man.update_experiment_entry(self.exp_id, self.description_entry.get_id(), text_update_crit)
|
|
750
|
-
|
|
751
735
|
# Create a gauge entry to display the progress.
|
|
752
|
-
gauge_entry: ElnEntryStep =
|
|
753
|
-
|
|
736
|
+
gauge_entry: ElnEntryStep = _ELNStepFactory._create_gauge_chart(self.helper.protocol, progress_entry,
|
|
737
|
+
f"{self.name} Progress", "Progress", "StatusMsg",
|
|
738
|
+
column_order=2, column_span=2)
|
|
754
739
|
self.progress_gauge_entry = gauge_entry
|
|
755
740
|
|
|
756
741
|
# Make sure the gauge entry isn't too big and stick it to the right of the text entry.
|
|
757
742
|
dash_update_crit = ElnDashboardEntryUpdateCriteria()
|
|
758
743
|
dash_update_crit.entry_height = 250
|
|
759
|
-
dash_update_crit.column_order = 2
|
|
760
|
-
dash_update_crit.column_span = 2
|
|
761
744
|
self.eln_man.update_experiment_entry(self.exp_id, self.progress_gauge_entry.get_id(), dash_update_crit)
|
|
762
745
|
|
|
763
746
|
# Create a results entry if this tool produces result records.
|
|
@@ -852,10 +835,37 @@ class ToolOfToolsHelper:
|
|
|
852
835
|
|
|
853
836
|
return self.helper.create_attachment_entry_from_file(self.tab, entry_name, file_path)
|
|
854
837
|
|
|
838
|
+
|
|
839
|
+
class _ELNStepFactory:
|
|
840
|
+
"""
|
|
841
|
+
Factory that provides simple functions to create a new ELN step under an ELN protocol.
|
|
842
|
+
"""
|
|
843
|
+
@staticmethod
|
|
844
|
+
def create_text_entry(protocol: ElnExperimentProtocol, text_data: str,
|
|
845
|
+
position: ElnEntryPosition | None = None, **kwargs) -> ElnEntryStep:
|
|
846
|
+
"""
|
|
847
|
+
Create a text entry at the end of the protocol, with a initial text specified in the text entry.
|
|
848
|
+
:param protocol: The protocol to create a new step for.
|
|
849
|
+
:param text_data: Must be non-blank. This is what will be displayed. Some HTML format tags can be inserted.
|
|
850
|
+
:param position: The position of the new step. If not specified, the new step will be added at the end.
|
|
851
|
+
:return: The new text entry step.
|
|
852
|
+
"""
|
|
853
|
+
eln_manager, new_entry = _ELNStepFactory._get_entry_creation_criteria(ElnBaseDataType.TEXT_ENTRY_DETAIL.data_type_name,
|
|
854
|
+
protocol, 'Text Entry', ElnEntryType.Text,
|
|
855
|
+
position, **kwargs)
|
|
856
|
+
record = eln_manager.get_data_records_for_entry(protocol.eln_experiment.notebook_experiment_id,
|
|
857
|
+
new_entry.entry_id).result_list[0]
|
|
858
|
+
record.set_field_value(ElnBaseDataType.get_text_entry_data_field_name(), text_data)
|
|
859
|
+
DataMgmtServer.get_data_record_manager(protocol.user).commit_data_records([record])
|
|
860
|
+
ret = ElnEntryStep(protocol, new_entry)
|
|
861
|
+
protocol.invalidate()
|
|
862
|
+
return ret
|
|
863
|
+
|
|
855
864
|
# TODO: Remove this once pylib's gauge chart definition is up to date.
|
|
856
865
|
@staticmethod
|
|
857
866
|
def _create_gauge_chart(protocol: ElnExperimentProtocol, data_source_step: ElnEntryStep, step_name: str,
|
|
858
|
-
field_name: str, status_field: str, group_by_field_name: str = "DataRecordName"
|
|
867
|
+
field_name: str, status_field: str, group_by_field_name: str = "DataRecordName",
|
|
868
|
+
**kwargs) \
|
|
859
869
|
-> ElnEntryStep:
|
|
860
870
|
"""
|
|
861
871
|
Create a gauge chart step in the experiment protocol.
|
|
@@ -874,11 +884,55 @@ class ToolOfToolsHelper:
|
|
|
874
884
|
chart.grouping_type = ChartGroupingType.GROUP_BY_FIELD
|
|
875
885
|
chart.grouping_type_data_type_name = data_type_name
|
|
876
886
|
chart.grouping_type_data_field_name = group_by_field_name
|
|
877
|
-
dashboard, step =
|
|
878
|
-
|
|
887
|
+
dashboard, step = _ELNStepFactory._create_dashboard_step_from_chart(chart, data_source_step, protocol, step_name,
|
|
888
|
+
None, **kwargs)
|
|
879
889
|
protocol.invalidate()
|
|
880
890
|
return step
|
|
881
891
|
|
|
892
|
+
@staticmethod
|
|
893
|
+
def _create_dashboard_step_from_chart(chart: GaugeChartDefinition, data_source_step: ElnEntryStep,
|
|
894
|
+
protocol: ElnExperimentProtocol, step_name: str,
|
|
895
|
+
position: ElnEntryPosition | None = None, **kwargs) -> \
|
|
896
|
+
tuple[DashboardDefinition, ElnEntryStep]:
|
|
897
|
+
dashboard: DashboardDefinition = DashboardDefinition()
|
|
898
|
+
dashboard.chart_definition_list = [chart]
|
|
899
|
+
dashboard.dashboard_scope = DashboardScope.PRIVATE_ELN
|
|
900
|
+
dashboard = DataMgmtServer.get_dashboard_manager(protocol.user).store_dashboard_definition(dashboard)
|
|
901
|
+
eln_manager, new_entry = _ELNStepFactory._get_entry_creation_criteria("", protocol, step_name,
|
|
902
|
+
ElnEntryType.Dashboard, position,
|
|
903
|
+
**kwargs)
|
|
904
|
+
# noinspection PyTypeChecker
|
|
905
|
+
update_criteria = ElnDashboardEntryUpdateCriteria()
|
|
906
|
+
update_criteria.dashboard_guid = dashboard.dashboard_guid
|
|
907
|
+
update_criteria.data_source_entry_id = data_source_step.get_id()
|
|
908
|
+
update_criteria.entry_height = 500
|
|
909
|
+
eln_manager.update_experiment_entry(protocol.eln_experiment.notebook_experiment_id, new_entry.entry_id,
|
|
910
|
+
update_criteria)
|
|
911
|
+
step = ElnEntryStep(protocol, new_entry)
|
|
912
|
+
return dashboard, step
|
|
913
|
+
|
|
914
|
+
@staticmethod
|
|
915
|
+
def _get_entry_creation_criteria(data_type_name: str | None, protocol: ElnExperimentProtocol,
|
|
916
|
+
step_name: str, entry_type: ElnEntryType, position: ElnEntryPosition | None = None,
|
|
917
|
+
**kwargs):
|
|
918
|
+
tab_id: int | None = None
|
|
919
|
+
order: int | None = None
|
|
920
|
+
if position:
|
|
921
|
+
tab_id = position.tab_id
|
|
922
|
+
order = position.order
|
|
923
|
+
# noinspection PyTypeChecker
|
|
924
|
+
last_step: ElnEntryStep = protocol.get_sorted_step_list()[-1]
|
|
925
|
+
if tab_id is None:
|
|
926
|
+
tab_id = last_step.eln_entry.notebook_experiment_tab_id
|
|
927
|
+
if order is None:
|
|
928
|
+
order = last_step.eln_entry.order + 1
|
|
929
|
+
eln_manager = DataMgmtServer.get_eln_manager(protocol.user)
|
|
930
|
+
entry_criteria = ElnEntryCriteria(entry_type, step_name, data_type_name=data_type_name,
|
|
931
|
+
order=order, notebook_experiment_tab_id=tab_id, **kwargs)
|
|
932
|
+
new_entry: ExperimentEntry = eln_manager.add_experiment_entry(protocol.eln_experiment.notebook_experiment_id,
|
|
933
|
+
entry_criteria)
|
|
934
|
+
return eln_manager, new_entry
|
|
935
|
+
|
|
882
936
|
|
|
883
937
|
# TODO: Using this to set the new status field setting.
|
|
884
938
|
class _GaugeChartDefinition(GaugeChartDefinition):
|