sapiopycommons 2025.3.21a458__tar.gz → 2025.3.26a460__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.3.21a458 → sapiopycommons-2025.3.26a460}/PKG-INFO +1 -1
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/pyproject.toml +1 -1
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/callbacks/callback_util.py +25 -17
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/customreport/auto_pagers.py +28 -18
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/datatype/attachment_util.py +4 -2
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/datatype/data_fields.py +22 -0
- sapiopycommons-2025.3.26a460/src/sapiopycommons/eln/experiment_handler.py +2153 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/eln/experiment_report_util.py +8 -3
- sapiopycommons-2025.3.26a460/src/sapiopycommons/eln/experiment_tags.py +7 -0
- sapiopycommons-2025.3.26a460/src/sapiopycommons/eln/plate_designer.py +252 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/processtracking/custom_workflow_handler.py +42 -27
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/recordmodel/record_handler.py +187 -130
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/rules/eln_rule_handler.py +33 -29
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/rules/on_save_rule_handler.py +33 -29
- sapiopycommons-2025.3.21a458/src/sapiopycommons/eln/experiment_handler.py +0 -1225
- sapiopycommons-2025.3.21a458/src/sapiopycommons/eln/plate_designer.py +0 -152
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/.gitignore +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/LICENSE +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/README.md +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/__init__.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/callbacks/__init__.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/callbacks/field_builder.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/chem/IndigoMolecules.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/chem/Molecules.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/chem/__init__.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/customreport/__init__.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/customreport/column_builder.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/customreport/custom_report_builder.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/customreport/term_builder.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/datatype/__init__.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/datatype/pseudo_data_types.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/eln/__init__.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/files/__init__.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/files/complex_data_loader.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/files/file_bridge.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/files/file_bridge_handler.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/files/file_data_handler.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/files/file_util.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/files/file_validator.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/files/file_writer.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/flowcyto/flow_cyto.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/flowcyto/flowcyto_data.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/general/__init__.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/general/accession_service.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/general/aliases.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/general/audit_log.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/general/custom_report_util.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/general/directive_util.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/general/exceptions.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/general/popup_util.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/general/sapio_links.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/general/storage_util.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/general/time_util.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/multimodal/multimodal.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/multimodal/multimodal_data.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/processtracking/__init__.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/processtracking/endpoints.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/recordmodel/__init__.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/rules/__init__.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/samples/aliquot.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/sftpconnect/__init__.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/sftpconnect/sftp_builder.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/webhook/__init__.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/webhook/webhook_context.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/webhook/webhook_handlers.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/src/sapiopycommons/webhook/webservice_handlers.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/AF-A0A009IHW8-F1-model_v4.cif +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/_do_not_add_init_py_here +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/accession_test.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/aliquot_test.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/bio_reg_test.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/chem_test.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/chem_test_curation_queue.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/curation_queue_test.sdf +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/data_type_models.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/flowcyto/101_DEN084Y5_15_E01_008_clean.fcs +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/flowcyto/101_DEN084Y5_15_E03_009_clean.fcs +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/flowcyto/101_DEN084Y5_15_E05_010_clean.fcs +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/flowcyto/8_color_ICS.wsp +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/flowcyto/COVID19_W_001_O.fcs +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/flowcyto_test.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/kappa.chains.fasta +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/mafft_test.py +0 -0
- {sapiopycommons-2025.3.21a458 → sapiopycommons-2025.3.26a460}/tests/test.gb +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: sapiopycommons
|
|
3
|
-
Version: 2025.3.
|
|
3
|
+
Version: 2025.3.26a460
|
|
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>
|
|
@@ -28,6 +28,7 @@ from sapiopylib.rest.pojo.webhook.WebhookEnums import FormAccessLevel, ScanToSel
|
|
|
28
28
|
from sapiopylib.rest.utils.DataTypeCacheManager import DataTypeCacheManager
|
|
29
29
|
from sapiopylib.rest.utils.FormBuilder import FormBuilder
|
|
30
30
|
from sapiopylib.rest.utils.recorddatasinks import InMemoryRecordDataSink
|
|
31
|
+
from sapiopylib.rest.utils.recordmodel.PyRecordModel import PyRecordModel
|
|
31
32
|
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
32
33
|
|
|
33
34
|
from sapiopycommons.callbacks.field_builder import FieldBuilder, AnyFieldInfo
|
|
@@ -182,7 +183,7 @@ class CallbackUtil:
|
|
|
182
183
|
def ok_dialog(self, title: str, msg: str) -> None:
|
|
183
184
|
"""
|
|
184
185
|
Create an option dialog where the only option is "OK". Doesn't allow the user to cancel the
|
|
185
|
-
dialog using the X
|
|
186
|
+
dialog using the X in the top right corner. Returns nothing.
|
|
186
187
|
|
|
187
188
|
:param title: The title of the dialog.
|
|
188
189
|
:param msg: The message to display in the dialog. This can be formatted using HTML elements.
|
|
@@ -407,15 +408,17 @@ class CallbackUtil:
|
|
|
407
408
|
default_modifier=default_modifier, field_modifiers=field_modifiers)
|
|
408
409
|
record.set_field_values(results)
|
|
409
410
|
|
|
411
|
+
# CR-47491: Support providing a data type name string to receive PyRecordModels instead of requiring a WrapperType.
|
|
410
412
|
def create_record_form_dialog(self,
|
|
411
413
|
title: str,
|
|
412
414
|
msg: str,
|
|
413
415
|
fields: list[FieldIdentifier | FieldFilterCriteria] | DataTypeLayoutIdentifier,
|
|
414
|
-
wrapper_type: type[WrappedType],
|
|
416
|
+
wrapper_type: type[WrappedType] | str,
|
|
415
417
|
column_positions: dict[str, tuple[int, int]] | None = None,
|
|
416
418
|
*,
|
|
417
419
|
default_modifier: FieldModifier | None = None,
|
|
418
|
-
field_modifiers: dict[FieldIdentifier, FieldModifier] | None = None)
|
|
420
|
+
field_modifiers: dict[FieldIdentifier, FieldModifier] | None = None) \
|
|
421
|
+
-> WrappedType | PyRecordModel:
|
|
419
422
|
"""
|
|
420
423
|
Create a form dialog where the user may input data into the fields of the form. The form is constructed from
|
|
421
424
|
a record that is created using the given record model wrapper. After the user submits this dialog, the values
|
|
@@ -431,7 +434,9 @@ class CallbackUtil:
|
|
|
431
434
|
[Extension Data Type Name].[Data Field Name]. This parameter may also be an identifier for a data type
|
|
432
435
|
layout from the data type of the provided records. If None, then the layout assigned to the current user's
|
|
433
436
|
group for this data type will be used. FieldFilterCriteria may also be provided in lieu of field names.
|
|
434
|
-
:param wrapper_type: The record model wrapper of the record to be created and updated.
|
|
437
|
+
:param wrapper_type: The record model wrapper or data type name of the record to be created and updated.
|
|
438
|
+
If a data type name is provided, the returned record will be a PyRecordModel instead of a
|
|
439
|
+
WrappedRecordModel.
|
|
435
440
|
:param column_positions: If a tuple is provided for a field name, alters that field's column position and column
|
|
436
441
|
span. (Field order is still determined by the fields list.) Has no effect if the fields parameter provides
|
|
437
442
|
a data type layout.
|
|
@@ -446,7 +451,7 @@ class CallbackUtil:
|
|
|
446
451
|
None, then the default modifier will be used.
|
|
447
452
|
:return: The record model that was created and updated by the user.
|
|
448
453
|
"""
|
|
449
|
-
record: WrappedType = self.rec_handler.add_model(wrapper_type)
|
|
454
|
+
record: WrappedType | PyRecordModel = self.rec_handler.add_model(wrapper_type)
|
|
450
455
|
self.set_record_form_dialog(title, msg, fields, record, column_positions,
|
|
451
456
|
default_modifier=default_modifier, field_modifiers=field_modifiers)
|
|
452
457
|
return record
|
|
@@ -794,7 +799,7 @@ class CallbackUtil:
|
|
|
794
799
|
title: str,
|
|
795
800
|
msg: str,
|
|
796
801
|
fields: list[FieldValue] | DataTypeLayoutIdentifier,
|
|
797
|
-
wrapper_type: type[WrappedType],
|
|
802
|
+
wrapper_type: type[WrappedType] | str,
|
|
798
803
|
count: int | tuple[int, int],
|
|
799
804
|
*,
|
|
800
805
|
default_modifier: FieldModifier | None = None,
|
|
@@ -803,7 +808,7 @@ class CallbackUtil:
|
|
|
803
808
|
image_data: list[bytes] | None = None,
|
|
804
809
|
require_input: bool = False,
|
|
805
810
|
repeat_message: str | None = "Please provide a value to continue.") \
|
|
806
|
-
-> list[WrappedType]:
|
|
811
|
+
-> list[WrappedType] | list[PyRecordModel]:
|
|
807
812
|
"""
|
|
808
813
|
Create a table dialog where the user may input data into the fields of the table. The table is constructed from
|
|
809
814
|
a list of records that are created using the given record model wrapper. After the user submits this dialog,
|
|
@@ -819,7 +824,8 @@ class CallbackUtil:
|
|
|
819
824
|
[Extension Data Type Name].[Data Field Name]. This parameter may also be an identifier for a data type
|
|
820
825
|
layout from the data type of the provided records. If None, then the layout assigned to the current user's
|
|
821
826
|
group for this data type will be used.
|
|
822
|
-
:param wrapper_type: The record model wrapper of the records to be created and updated.
|
|
827
|
+
:param wrapper_type: The record model wrapper or data type name of the records to be created and updated. If
|
|
828
|
+
a data type name is provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
823
829
|
:param count: The number of records to create. If provided as a tuple of two integers, the user will first be
|
|
824
830
|
prompted to select an integer between the two values in the tuple.
|
|
825
831
|
:param default_modifier: A default field modifier that will be applied to the given fields. This can be used to
|
|
@@ -844,7 +850,7 @@ class CallbackUtil:
|
|
|
844
850
|
count: int = self.__prompt_for_count(count, wrapper_type, require_input, repeat_message)
|
|
845
851
|
if count <= 0:
|
|
846
852
|
return []
|
|
847
|
-
records: list[WrappedType] = self.rec_handler.add_models(wrapper_type, count)
|
|
853
|
+
records: list[WrappedType] | list[PyRecordModel] = self.rec_handler.add_models(wrapper_type, count)
|
|
848
854
|
self.set_record_table_dialog(title, msg, fields, records,
|
|
849
855
|
default_modifier=default_modifier, field_modifiers=field_modifiers,
|
|
850
856
|
group_by=group_by, image_data=image_data)
|
|
@@ -974,7 +980,7 @@ class CallbackUtil:
|
|
|
974
980
|
title: str,
|
|
975
981
|
msg: str,
|
|
976
982
|
fields: list[FieldValue] | DataTypeLayoutIdentifier,
|
|
977
|
-
wrapper_type: type[WrappedType],
|
|
983
|
+
wrapper_type: type[WrappedType] | str,
|
|
978
984
|
count: int | tuple[int, int],
|
|
979
985
|
*,
|
|
980
986
|
default_modifier: FieldModifier | None = None,
|
|
@@ -1003,7 +1009,8 @@ class CallbackUtil:
|
|
|
1003
1009
|
[Extension Data Type Name].[Data Field Name]. This parameter may also be an identifier for a data type
|
|
1004
1010
|
layout from the data type of the provided records. If None, then the layout assigned to the current user's
|
|
1005
1011
|
group for this data type will be used.
|
|
1006
|
-
:param wrapper_type: The record model wrapper of the records to be created and updated.
|
|
1012
|
+
:param wrapper_type: The record model wrapper or data type name of the records to be created and updated. If
|
|
1013
|
+
a data type name is provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
1007
1014
|
:param count: The number of records to create. If provided as a tuple of two integers, the user will first be
|
|
1008
1015
|
prompted to select an integer between the two values in the tuple.
|
|
1009
1016
|
:param default_modifier: A default field modifier that will be applied to the given fields. This can be used to
|
|
@@ -1427,7 +1434,7 @@ class CallbackUtil:
|
|
|
1427
1434
|
|
|
1428
1435
|
# CR-47377: Add allow_creation and default_creation_number to cover new parameters of this request type from 24.12.
|
|
1429
1436
|
def input_selection_dialog(self,
|
|
1430
|
-
wrapper_type: type[WrappedType],
|
|
1437
|
+
wrapper_type: type[WrappedType] | str,
|
|
1431
1438
|
msg: str,
|
|
1432
1439
|
multi_select: bool = True,
|
|
1433
1440
|
only_key_fields: bool = False,
|
|
@@ -1442,13 +1449,14 @@ class CallbackUtil:
|
|
|
1442
1449
|
*,
|
|
1443
1450
|
require_selection: bool = False,
|
|
1444
1451
|
repeat_message: str | None = "Please provide a selection to continue.") \
|
|
1445
|
-
-> list[WrappedType]:
|
|
1452
|
+
-> list[WrappedType] | list[PyRecordModel]:
|
|
1446
1453
|
"""
|
|
1447
1454
|
Display a table of records that exist in the system matching the given data type and filter criteria and have
|
|
1448
1455
|
the user select one or more records from the table.
|
|
1449
1456
|
The title of a selection dialog will always be "Select [plural display name]".
|
|
1450
1457
|
|
|
1451
|
-
:param wrapper_type: The record model wrapper for the records to display in the dialog.
|
|
1458
|
+
:param wrapper_type: The record model wrapper or data type name for the records to display in the dialog. If
|
|
1459
|
+
a data type name is provided, the returned records will be PyRecordModels instead of WrappedRecordModels.
|
|
1452
1460
|
:param msg: The message to show near the top of the dialog, below the title. This can be used to
|
|
1453
1461
|
instruct the user on what is desired from the dialog. This can be formatted using HTML elements.
|
|
1454
1462
|
:param multi_select: Whether the user may select multiple items at once in this dialog.
|
|
@@ -1489,7 +1497,7 @@ class CallbackUtil:
|
|
|
1489
1497
|
:return: A list of the records selected by the user in the dialog, wrapped as record models using the provided
|
|
1490
1498
|
wrapper.
|
|
1491
1499
|
"""
|
|
1492
|
-
data_type: str =
|
|
1500
|
+
data_type: str = AliasUtil.to_data_type_name(wrapper_type)
|
|
1493
1501
|
|
|
1494
1502
|
# Reduce the provided lists of records down to lists of record IDs.
|
|
1495
1503
|
if preselected_records:
|
|
@@ -1795,7 +1803,7 @@ class CallbackUtil:
|
|
|
1795
1803
|
temp_dt.set_field_definition(modifier.modify_field(field_def))
|
|
1796
1804
|
return temp_dt
|
|
1797
1805
|
|
|
1798
|
-
def __prompt_for_count(self, count: tuple[int, int] | int, wrapper_type: type[WrappedType],
|
|
1806
|
+
def __prompt_for_count(self, count: tuple[int, int] | int, wrapper_type: type[WrappedType] | str,
|
|
1799
1807
|
require_input: bool, repeat_message: str) -> int:
|
|
1800
1808
|
"""
|
|
1801
1809
|
Given a count value, if it is a tuple representing an allowable range of values for a number of records to
|
|
@@ -1806,7 +1814,7 @@ class CallbackUtil:
|
|
|
1806
1814
|
if hasattr(wrapper_type, "PLURAL_DISPLAY_NAME"):
|
|
1807
1815
|
plural: str = wrapper_type.PLURAL_DISPLAY_NAME
|
|
1808
1816
|
else:
|
|
1809
|
-
plural: str = self.dt_cache.get_plural_display_name(
|
|
1817
|
+
plural: str = self.dt_cache.get_plural_display_name(AliasUtil.to_data_type_name(wrapper_type))
|
|
1810
1818
|
min_val, max_val = count
|
|
1811
1819
|
msg: str = f"How many {plural} should be created? ({min_val} to {max_val})"
|
|
1812
1820
|
count: int = self.integer_input_dialog(f"Create {plural}", msg, "Count", min_val, min_val, max_val,
|
|
@@ -8,6 +8,7 @@ from sapiopylib.rest.pojo.CustomReport import CustomReportCriteria, CustomReport
|
|
|
8
8
|
from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
|
|
9
9
|
from sapiopylib.rest.utils.autopaging import SapioPyAutoPager, PagerResultCriteriaType, _default_report_page_size, \
|
|
10
10
|
_default_record_page_size
|
|
11
|
+
from sapiopylib.rest.utils.recordmodel.PyRecordModel import PyRecordModel
|
|
11
12
|
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
12
13
|
|
|
13
14
|
from sapiopycommons.general.aliases import FieldValue, UserIdentifier, AliasUtil, RecordModel
|
|
@@ -110,20 +111,22 @@ class QuickReportDictAutoPager(_DictReportPagerBase):
|
|
|
110
111
|
super().__init__(user, first_page_criteria)
|
|
111
112
|
|
|
112
113
|
|
|
113
|
-
|
|
114
|
+
# CR-47491: Support providing a data type name string to receive PyRecordModels instead of requiring a WrapperType.
|
|
115
|
+
class _RecordReportPagerBase(SapioPyAutoPager[CustomReportCriteria, WrappedType | PyRecordModel], ABC):
|
|
114
116
|
"""
|
|
115
117
|
A base class for automatically paging through a report and returning the results as a list of records.
|
|
116
118
|
"""
|
|
117
119
|
_columns: list[ReportColumn]
|
|
118
|
-
|
|
120
|
+
_query_type: type[WrappedType] | str
|
|
119
121
|
_data_type: str
|
|
120
122
|
_rec_handler: RecordHandler
|
|
121
123
|
_report_man: CustomReportManager
|
|
122
124
|
|
|
123
|
-
def __init__(self, user: UserIdentifier, first_page_criteria: CustomReportCriteria,
|
|
125
|
+
def __init__(self, user: UserIdentifier, first_page_criteria: CustomReportCriteria,
|
|
126
|
+
wrapper_type: type[WrappedType] | str):
|
|
124
127
|
self._columns = first_page_criteria.column_list
|
|
125
|
-
self.
|
|
126
|
-
self._data_type =
|
|
128
|
+
self._query_type = wrapper_type
|
|
129
|
+
self._data_type = AliasUtil.to_data_type_name(wrapper_type)
|
|
127
130
|
self._rec_handler = RecordHandler(user)
|
|
128
131
|
super().__init__(AliasUtil.to_sapio_user(user), first_page_criteria)
|
|
129
132
|
self._report_man = DataMgmtServer.get_custom_report_manager(self.user)
|
|
@@ -139,9 +142,9 @@ class _RecordReportPagerBase(SapioPyAutoPager[CustomReportCriteria, WrappedType]
|
|
|
139
142
|
def default_first_page_criteria(self) -> PagerResultCriteriaType:
|
|
140
143
|
raise ValueError("Cannot generate a default first page criteria for custom reports.")
|
|
141
144
|
|
|
142
|
-
def get_next_page_result(self) -> tuple[CustomReportCriteria | None, Queue[WrappedType]]:
|
|
145
|
+
def get_next_page_result(self) -> tuple[CustomReportCriteria | None, Queue[WrappedType] | Queue[PyRecordModel]]:
|
|
143
146
|
report: CustomReport = self._report_man.run_custom_report(self.next_page_criteria)
|
|
144
|
-
queue
|
|
147
|
+
queue = Queue()
|
|
145
148
|
id_index: int = -1
|
|
146
149
|
for i, column in enumerate(self._columns):
|
|
147
150
|
if column.data_type_name == self._data_type and column.data_field_name == "RecordId":
|
|
@@ -151,7 +154,7 @@ class _RecordReportPagerBase(SapioPyAutoPager[CustomReportCriteria, WrappedType]
|
|
|
151
154
|
raise SapioException(f"This report does not contain a Record ID column for the given record model type "
|
|
152
155
|
f"{self._data_type}.")
|
|
153
156
|
ids: list[int] = [row[id_index] for row in report.result_table]
|
|
154
|
-
for row in self._rec_handler.query_models_by_id(self.
|
|
157
|
+
for row in self._rec_handler.query_models_by_id(self._query_type, ids, page_size=report.page_size):
|
|
155
158
|
queue.put(row)
|
|
156
159
|
if report.has_next_page:
|
|
157
160
|
next_page_criteria = copy(self.next_page_criteria)
|
|
@@ -165,12 +168,15 @@ class CustomReportRecordAutoPager(_RecordReportPagerBase):
|
|
|
165
168
|
"""
|
|
166
169
|
A class that automatically pages through a custom report and returns the results as a list of records.
|
|
167
170
|
"""
|
|
168
|
-
def __init__(self, user: UserIdentifier, report_criteria: CustomReportCriteria,
|
|
169
|
-
|
|
171
|
+
def __init__(self, user: UserIdentifier, report_criteria: CustomReportCriteria,
|
|
172
|
+
wrapper_type: type[WrappedType] | str, page_number: int = 0,
|
|
173
|
+
page_size: int = _default_record_page_size):
|
|
170
174
|
"""
|
|
171
175
|
:param user: The current webhook context or a user object to send requests from.
|
|
172
176
|
:param report_criteria: The custom report criteria to run.
|
|
173
|
-
:param wrapper_type: The record model wrapper type
|
|
177
|
+
:param wrapper_type: The record model wrapper type or data type name of the records being searched for.
|
|
178
|
+
If a data type name was used instead of a model wrapper, then the returned records will be PyRecordModels
|
|
179
|
+
instead of WrappedRecordModels.
|
|
174
180
|
:param page_number: The page number to start on. The first page is page 0.
|
|
175
181
|
:param page_size: The number of results to return per page.
|
|
176
182
|
"""
|
|
@@ -188,12 +194,14 @@ class SystemReportRecordAutoPager(_RecordReportPagerBase):
|
|
|
188
194
|
System reports are also known as predefined searches in the system and must be defined in the data designer for
|
|
189
195
|
a specific data type. That is, saved searches created by users cannot be run using this function.
|
|
190
196
|
"""
|
|
191
|
-
def __init__(self, user: UserIdentifier, report_name: str, wrapper_type: type[WrappedType],
|
|
197
|
+
def __init__(self, user: UserIdentifier, report_name: str, wrapper_type: type[WrappedType] | str,
|
|
192
198
|
page_number: int = 0, page_size: int = _default_record_page_size):
|
|
193
199
|
"""
|
|
194
200
|
:param user: The current webhook context or a user object to send requests from.
|
|
195
201
|
:param report_name: The name of the system report to run.
|
|
196
|
-
:param wrapper_type: The record model wrapper type
|
|
202
|
+
:param wrapper_type: The record model wrapper type or data type name of the records being searched for.
|
|
203
|
+
If a data type name was used instead of a model wrapper, then the returned records will be PyRecordModels
|
|
204
|
+
instead of WrappedRecordModels.
|
|
197
205
|
:param page_number: The page number to start on. The first page is page 0.
|
|
198
206
|
:param page_size: The number of results to return per page.
|
|
199
207
|
"""
|
|
@@ -208,16 +216,18 @@ class QuickReportRecordAutoPager(_RecordReportPagerBase):
|
|
|
208
216
|
"""
|
|
209
217
|
A class that automatically pages through a quick report and returns the results as a list of records.
|
|
210
218
|
"""
|
|
211
|
-
def __init__(self, user: UserIdentifier, report_term: RawReportTerm, wrapper_type: type[WrappedType],
|
|
219
|
+
def __init__(self, user: UserIdentifier, report_term: RawReportTerm, wrapper_type: type[WrappedType] | str,
|
|
212
220
|
page_number: int = 0, page_size: int = _default_record_page_size):
|
|
213
221
|
"""
|
|
214
222
|
:param user: The current webhook context or a user object to send requests from.
|
|
215
223
|
:param report_term: The raw report term to use for the quick report.
|
|
216
|
-
:param wrapper_type: The record model wrapper type
|
|
224
|
+
:param wrapper_type: The record model wrapper type or data type name of the records being searched for.
|
|
225
|
+
If a data type name was used instead of a model wrapper, then the returned records will be PyRecordModels
|
|
226
|
+
instead of WrappedRecordModels.
|
|
217
227
|
:param page_number: The page number to start on. The first page is page 0.
|
|
218
228
|
:param page_size: The number of results to return per page.
|
|
219
229
|
"""
|
|
220
|
-
if report_term.data_type_name !=
|
|
230
|
+
if report_term.data_type_name != AliasUtil.to_data_type_name(wrapper_type):
|
|
221
231
|
raise SapioException("The data type name of the report term must match the data type name of the wrapper type.")
|
|
222
232
|
first_page_criteria: CustomReportCriteria = CustomReportUtil.get_quick_report_criteria(user, report_term)
|
|
223
233
|
first_page_criteria.page_number = page_number
|
|
@@ -225,12 +235,12 @@ class QuickReportRecordAutoPager(_RecordReportPagerBase):
|
|
|
225
235
|
super().__init__(user, first_page_criteria, wrapper_type)
|
|
226
236
|
|
|
227
237
|
|
|
228
|
-
def _add_record_id_column(report: CustomReportCriteria, wrapper_type: type[WrappedType]) -> None:
|
|
238
|
+
def _add_record_id_column(report: CustomReportCriteria, wrapper_type: type[WrappedType] | str) -> None:
|
|
229
239
|
"""
|
|
230
240
|
Given a custom report criteria, ensure that the report contains a Record ID column for the given record model's
|
|
231
241
|
data type. Add one if it is missing.
|
|
232
242
|
"""
|
|
233
|
-
dt: str =
|
|
243
|
+
dt: str = AliasUtil.to_data_type_name(wrapper_type)
|
|
234
244
|
# Ensure that the root data type is the one we're looking for.
|
|
235
245
|
report.root_data_type = dt
|
|
236
246
|
# Enforce that the given custom report has a record ID column.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import io
|
|
2
2
|
|
|
3
3
|
from sapiopylib.rest.DataMgmtService import DataMgmtServer
|
|
4
|
+
from sapiopylib.rest.utils.recordmodel.PyRecordModel import PyRecordModel
|
|
4
5
|
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
5
6
|
|
|
6
7
|
from sapiopycommons.general.aliases import AliasUtil, SapioRecord, UserIdentifier
|
|
@@ -49,9 +50,10 @@ class AttachmentUtil:
|
|
|
49
50
|
with io.BytesIO(file_bytes) as stream:
|
|
50
51
|
dr_man.set_attachment_data(attachment, file_name, stream)
|
|
51
52
|
|
|
53
|
+
# CR-47491: Support providing a data type name string to receive PyRecordModels instead of requiring a WrapperType.
|
|
52
54
|
@staticmethod
|
|
53
55
|
def create_attachment(context: UserIdentifier, file_name: str, file_bytes: bytes,
|
|
54
|
-
wrapper_type: type[WrappedType]) -> WrappedType:
|
|
56
|
+
wrapper_type: type[WrappedType] | str) -> WrappedType | PyRecordModel:
|
|
55
57
|
"""
|
|
56
58
|
Create an attachment data type and initialize its attachment bytes at the same time.
|
|
57
59
|
Makes a webservice call to create the attachment record and a second to set its bytes.
|
|
@@ -62,6 +64,6 @@ class AttachmentUtil:
|
|
|
62
64
|
:param wrapper_type: The attachment type to create.
|
|
63
65
|
:return: A record model for the newly created attachment.
|
|
64
66
|
"""
|
|
65
|
-
attachment: WrappedType = RecordHandler(context).create_models(wrapper_type, 1)[0]
|
|
67
|
+
attachment: WrappedType | PyRecordModel = RecordHandler(context).create_models(wrapper_type, 1)[0]
|
|
66
68
|
AttachmentUtil.set_attachment_bytes(context, attachment, file_name, file_bytes)
|
|
67
69
|
return attachment
|
|
@@ -59,3 +59,25 @@ class ProcessWorkflowTrackingFields:
|
|
|
59
59
|
WORKFLOW_START_USER_ID__FIELD = WrapperField("WorkflowStartUserId", FieldType.STRING)
|
|
60
60
|
WORKFLOW_TAT__FIELD = WrapperField("WorkflowTAT", FieldType.DOUBLE)
|
|
61
61
|
WORKFLOW_VERSION__FIELD = WrapperField("WorkflowVersion", FieldType.LONG)
|
|
62
|
+
|
|
63
|
+
class PlateDesignerWellElementFields:
|
|
64
|
+
DATA_TYPE_NAME = 'PlateDesignerWellElement'
|
|
65
|
+
ACTUAL_VOLUME_REMOVED__FIELD = WrapperField("ActualVolumeRemoved", FieldType.DOUBLE)
|
|
66
|
+
ALIQUOT_SAMPLE_RECORD_ID__FIELD = WrapperField("AliquotSampleRecordId", FieldType.LONG)
|
|
67
|
+
COL_POSITION__FIELD = WrapperField("ColPosition", FieldType.SELECTION)
|
|
68
|
+
CONCENTRATION__FIELD = WrapperField("Concentration", FieldType.DOUBLE)
|
|
69
|
+
CONCENTRATION_UNITS__FIELD = WrapperField("ConcentrationUnits", FieldType.STRING)
|
|
70
|
+
CONTROL_TYPE__FIELD = WrapperField("ControlType", FieldType.STRING)
|
|
71
|
+
DILUTION_SCHEME__FIELD = WrapperField("DilutionScheme", FieldType.DOUBLE)
|
|
72
|
+
IS_CONTROL__FIELD = WrapperField("IsControl", FieldType.BOOLEAN)
|
|
73
|
+
LAYER__FIELD = WrapperField("Layer", FieldType.INTEGER)
|
|
74
|
+
PLATE_RECORD_ID__FIELD = WrapperField("PlateRecordId", FieldType.LONG)
|
|
75
|
+
ROW_POSITION__FIELD = WrapperField("RowPosition", FieldType.SELECTION)
|
|
76
|
+
SOURCE_DATA_TYPE_NAME__FIELD = WrapperField("SourceDataTypeName", FieldType.STRING)
|
|
77
|
+
SOURCE_RECORD_ID__FIELD = WrapperField("SourceRecordId", FieldType.LONG)
|
|
78
|
+
SOURCE_SAMPLE_CONCENTRATION__FIELD = WrapperField("SourceSampleConcentration", FieldType.DOUBLE)
|
|
79
|
+
SOURCE_SAMPLE_MASS__FIELD = WrapperField("SourceSampleMass", FieldType.DOUBLE)
|
|
80
|
+
SOURCE_SAMPLE_VOLUME__FIELD = WrapperField("SourceSampleVolume", FieldType.DOUBLE)
|
|
81
|
+
SOURCE_VOLUME_TO_REMOVE__FIELD = WrapperField("SourceVolumeToRemove", FieldType.DOUBLE)
|
|
82
|
+
TARGET_MASS__FIELD = WrapperField("TargetMass", FieldType.DOUBLE)
|
|
83
|
+
VOLUME__FIELD = WrapperField("Volume", FieldType.DOUBLE)
|