sapiopycommons 2024.10.14a341__py3-none-any.whl → 2024.10.25a345__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/chem/Molecules.py +1 -1
- sapiopycommons/customreport/column_builder.py +2 -2
- sapiopycommons/datatype/pseudo_data_types.py +440 -0
- sapiopycommons/eln/experiment_handler.py +44 -20
- sapiopycommons/eln/experiment_report_util.py +151 -24
- sapiopycommons/general/aliases.py +50 -3
- sapiopycommons/general/audit_log.py +40 -47
- sapiopycommons/general/time_util.py +6 -40
- sapiopycommons/sftpconnect/__init__.py +0 -0
- sapiopycommons/sftpconnect/sftp_builder.py +69 -0
- {sapiopycommons-2024.10.14a341.dist-info → sapiopycommons-2024.10.25a345.dist-info}/METADATA +1 -1
- {sapiopycommons-2024.10.14a341.dist-info → sapiopycommons-2024.10.25a345.dist-info}/RECORD +14 -11
- {sapiopycommons-2024.10.14a341.dist-info → sapiopycommons-2024.10.25a345.dist-info}/WHEEL +0 -0
- {sapiopycommons-2024.10.14a341.dist-info → sapiopycommons-2024.10.25a345.dist-info}/licenses/LICENSE +0 -0
sapiopycommons/chem/Molecules.py
CHANGED
|
@@ -231,7 +231,7 @@ def mol_to_sapio_substance(mol: Mol, include_stereoisomers=False,
|
|
|
231
231
|
indigo_mol.aromatize()
|
|
232
232
|
if enhanced_stereo:
|
|
233
233
|
# Remove enhanced stereo layer when generating InChI as the stereo hash is generated separately for reg.
|
|
234
|
-
mol_copy: Mol = Chem.
|
|
234
|
+
mol_copy: Mol = Chem.Mol(mol)
|
|
235
235
|
Chem.CanonicalizeEnhancedStereo(mol_copy)
|
|
236
236
|
molecule["inchi"] = Chem.MolToInchi(mol_copy)
|
|
237
237
|
molecule["inchiKey"] = Chem.MolToInchiKey(mol_copy)
|
|
@@ -52,9 +52,9 @@ class ColumnBuilder:
|
|
|
52
52
|
:param field: The field name to return the type of.
|
|
53
53
|
:return: The field type of the given field name.
|
|
54
54
|
"""
|
|
55
|
-
# Check if the field name is a system field. If it
|
|
55
|
+
# Check if the field name is a system field. If it is, use the field type defined in this file.
|
|
56
56
|
field_name: str = AliasUtil.to_data_field_name(field)
|
|
57
57
|
if field_name in SYSTEM_FIELDS:
|
|
58
58
|
return SYSTEM_FIELDS.get(field_name)
|
|
59
59
|
# Otherwise, check if the field type can be found from the wrapper.
|
|
60
|
-
return AliasUtil.to_field_type(
|
|
60
|
+
return AliasUtil.to_field_type(field, data_type)
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
|
|
2
|
+
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrapperField
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ActiveTaskPseudoDef:
|
|
6
|
+
DATA_TYPE_NAME: str = "ActiveTask"
|
|
7
|
+
ACTIVE_TASK_ID__FIELD_NAME = WrapperField("ActiveTaskId", FieldType.LONG)
|
|
8
|
+
ACTIVE_WORKFLOW_ID__FIELD_NAME = WrapperField("ActiveWorkflowId", FieldType.LONG)
|
|
9
|
+
DATE_EDITED__FIELD_NAME = WrapperField("DateEdited", FieldType.DATE)
|
|
10
|
+
EDITED_BY__FIELD_NAME = WrapperField("EditedBy", FieldType.STRING)
|
|
11
|
+
STATUS__FIELD_NAME = WrapperField("Status", FieldType.ENUM)
|
|
12
|
+
TASK_USAGE_ID__FIELD_NAME = WrapperField("TaskUsageId", FieldType.LONG)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ActiveWorkflowPseudoDef:
|
|
16
|
+
DATA_TYPE_NAME: str = "ActiveWorkflow"
|
|
17
|
+
ACTIVE_WORKFLOW_ID__FIELD_NAME = WrapperField("ActiveWorkflowId", FieldType.LONG)
|
|
18
|
+
CREATED_BY__FIELD_NAME = WrapperField("CreatedBy", FieldType.STRING)
|
|
19
|
+
DATE_CREATED__FIELD_NAME = WrapperField("DateCreated", FieldType.DATE)
|
|
20
|
+
DATE_EDITED__FIELD_NAME = WrapperField("DateEdited", FieldType.STRING)
|
|
21
|
+
EDITED_BY__FIELD_NAME = WrapperField("EditedBy", FieldType.STRING)
|
|
22
|
+
ESTIMATED_ATTACHMENTS__FIELD_NAME = WrapperField("EstimatedAttachments", FieldType.LONG)
|
|
23
|
+
NAME__FIELD_NAME = WrapperField("Name", FieldType.STRING)
|
|
24
|
+
RELATED_RECORD_ID__FIELD_NAME = WrapperField("RelatedRecordId", FieldType.LONG)
|
|
25
|
+
STATUS__FIELD_NAME = WrapperField("Status", FieldType.ENUM)
|
|
26
|
+
WORKFLOW_ID__FIELD_NAME = WrapperField("WorkflowId", FieldType.LONG)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AuditLogPseudoDef:
|
|
30
|
+
DATA_TYPE_NAME: str = "AuditLog"
|
|
31
|
+
DATA_FIELD_NAME__FIELD_NAME = WrapperField("DataFieldName", FieldType.STRING)
|
|
32
|
+
DATA_TYPE_NAME__FIELD_NAME = WrapperField("DataTypeName", FieldType.STRING)
|
|
33
|
+
DESCRIPTION__FIELD_NAME = WrapperField("Description", FieldType.STRING)
|
|
34
|
+
EVENT_TYPE__FIELD_NAME = WrapperField("EventType", FieldType.ENUM)
|
|
35
|
+
FULL_NAME__FIELD_NAME = WrapperField("FullName", FieldType.STRING)
|
|
36
|
+
NEW_VALUE__FIELD_NAME = WrapperField("NewValue", FieldType.STRING)
|
|
37
|
+
ORIGINAL_VALUE__FIELD_NAME = WrapperField("OriginalValue", FieldType.STRING)
|
|
38
|
+
RECORD_ID__FIELD_NAME = WrapperField("RecordId", FieldType.LONG)
|
|
39
|
+
RECORD_NAME__FIELD_NAME = WrapperField("RecordName", FieldType.STRING)
|
|
40
|
+
TIME_STAMP__FIELD_NAME = WrapperField("TimeStamp", FieldType.DATE)
|
|
41
|
+
USER_COMMENT__FIELD_NAME = WrapperField("UserComment", FieldType.STRING)
|
|
42
|
+
USER_NAME__FIELD_NAME = WrapperField("UserName", FieldType.STRING)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class DataFieldDefinitionPseudoDef:
|
|
46
|
+
DATA_TYPE_NAME: str = "DataFieldDefinition"
|
|
47
|
+
APPROVE_EDIT__FIELD_NAME = WrapperField("ApproveEdit", FieldType.BOOLEAN)
|
|
48
|
+
AUTO_CLEAR_FIELD_LIST__FIELD_NAME = WrapperField("AutoClearFieldList", FieldType.STRING)
|
|
49
|
+
AUTO_SORT__FIELD_NAME = WrapperField("AutoSort", FieldType.BOOLEAN)
|
|
50
|
+
COLOR_MAPPING_ID__FIELD_NAME = WrapperField("ColorMappingId", FieldType.LONG)
|
|
51
|
+
DATA_FIELD_NAME__FIELD_NAME = WrapperField("DataFieldName", FieldType.STRING)
|
|
52
|
+
DATA_FIELD_TAG__FIELD_NAME = WrapperField("DataFieldTag", FieldType.STRING)
|
|
53
|
+
DATA_FIELD_TYPE__FIELD_NAME = WrapperField("DataFieldType", FieldType.STRING)
|
|
54
|
+
DATA_TYPE_NAME__FIELD_NAME = WrapperField("DataTypeName", FieldType.STRING)
|
|
55
|
+
DECIMAL_DIGITS__FIELD_NAME = WrapperField("DecimalDigits", FieldType.INTEGER)
|
|
56
|
+
DEFAULT_VALUE__FIELD_NAME = WrapperField("DefaultValue", FieldType.STRING)
|
|
57
|
+
DEPENDENT_FIELDS__FIELD_NAME = WrapperField("Dependent_Fields", FieldType.STRING)
|
|
58
|
+
DESCRIPTION__FIELD_NAME = WrapperField("Description", FieldType.STRING)
|
|
59
|
+
DIRECT_EDIT__FIELD_NAME = WrapperField("DirectEdit", FieldType.BOOLEAN)
|
|
60
|
+
DISPLAY_NAME__FIELD_NAME = WrapperField("DisplayName", FieldType.STRING)
|
|
61
|
+
EDITABLE__FIELD_NAME = WrapperField("Editable", FieldType.BOOLEAN)
|
|
62
|
+
ENUM_VALUES__FIELD_NAME = WrapperField("EnumValues", FieldType.STRING)
|
|
63
|
+
FORM_COL__FIELD_NAME = WrapperField("FormCol", FieldType.SHORT)
|
|
64
|
+
FORM_COL_SPAN__FIELD_NAME = WrapperField("FormColSpan", FieldType.SHORT)
|
|
65
|
+
FORM_NAME__FIELD_NAME = WrapperField("FormName", FieldType.STRING)
|
|
66
|
+
HTML_EDITOR__FIELD_NAME = WrapperField("HtmlEditor", FieldType.BOOLEAN)
|
|
67
|
+
IDENTIFIER__FIELD_NAME = WrapperField("Identifier", FieldType.BOOLEAN)
|
|
68
|
+
INDEX_FOR_SEARCH__FIELD_NAME = WrapperField("IndexForSearch", FieldType.BOOLEAN)
|
|
69
|
+
LINK_OUT__FIELD_NAME = WrapperField("LinkOut", FieldType.BOOLEAN)
|
|
70
|
+
LINK_OUT_URL__FIELD_NAME = WrapperField("LinkOutUrl", FieldType.BOOLEAN)
|
|
71
|
+
MAX_LENGTH__FIELD_NAME = WrapperField("MaxLength", FieldType.INTEGER)
|
|
72
|
+
MAXIMUM_VALUE__FIELD_NAME = WrapperField("MaximumValue", FieldType.DOUBLE)
|
|
73
|
+
MINIMUM_VALUE__FIELD_NAME = WrapperField("MinimumValue", FieldType.DOUBLE)
|
|
74
|
+
MULT_SELECT__FIELD_NAME = WrapperField("MultSelect", FieldType.BOOLEAN)
|
|
75
|
+
NUM_LINES__FIELD_NAME = WrapperField("NumLines", FieldType.INTEGER)
|
|
76
|
+
REQUIRED__FIELD_NAME = WrapperField("Required", FieldType.BOOLEAN)
|
|
77
|
+
SORT_KEY__FIELD_NAME = WrapperField("SortKey", FieldType.BOOLEAN)
|
|
78
|
+
STATIC_DATE__FIELD_NAME = WrapperField("StaticDate", FieldType.BOOLEAN)
|
|
79
|
+
UNIQUE_VALUE__FIELD_NAME = WrapperField("UniqueValue", FieldType.BOOLEAN)
|
|
80
|
+
VISIBLE__FIELD_NAME = WrapperField("Visible", FieldType.BOOLEAN)
|
|
81
|
+
WORKFLOW_ONLY_EDITING__FIELD_NAME = WrapperField("WorkflowOnlyEditing", FieldType.BOOLEAN)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class DataTypeDefinitionPseudoDef:
|
|
85
|
+
DATA_TYPE_NAME: str = "DataTypeDefinition"
|
|
86
|
+
ADDABLE__FIELD_NAME = WrapperField("Addable", FieldType.BOOLEAN)
|
|
87
|
+
ATTACHMENT__FIELD_NAME = WrapperField("Attachment", FieldType.BOOLEAN)
|
|
88
|
+
ATTACHMENT_TYPE__FIELD_NAME = WrapperField("AttachmentType", FieldType.STRING)
|
|
89
|
+
DATA_TYPE_TAG__FIELD_NAME = WrapperField("DATA_TYPE_TAG", FieldType.STRING)
|
|
90
|
+
DATA_TYPE_ID__FIELD_NAME = WrapperField("DataTypeId", FieldType.LONG)
|
|
91
|
+
DATA_TYPE_NAME__FIELD_NAME = WrapperField("DataTypeName", FieldType.STRING)
|
|
92
|
+
DELETABLE__FIELD_NAME = WrapperField("Deletable", FieldType.BOOLEAN)
|
|
93
|
+
DESCRIPTION__FIELD_NAME = WrapperField("Description", FieldType.STRING)
|
|
94
|
+
DISPLAY_NAME__FIELD_NAME = WrapperField("DisplayName", FieldType.STRING)
|
|
95
|
+
EXTENSION_TYPE__FIELD_NAME = WrapperField("ExtensionType", FieldType.BOOLEAN)
|
|
96
|
+
GROUP_ADDABLE__FIELD_NAME = WrapperField("GroupAddable", FieldType.BOOLEAN)
|
|
97
|
+
HIDE_DATA_RECORDS__FIELD_NAME = WrapperField("HideDataRecords", FieldType.BOOLEAN)
|
|
98
|
+
HIGH_VOLUME__FIELD_NAME = WrapperField("HighVolume", FieldType.BOOLEAN)
|
|
99
|
+
IS_HIDE_IN_MOBILE__FIELD_NAME = WrapperField("IS_HIDE_IN_MOBILE", FieldType.BOOLEAN)
|
|
100
|
+
IS_HVDT_ON_SAVE_ENABLED__FIELD_NAME = WrapperField("IS_HVDT_ON_SAVE_ENABLED", FieldType.BOOLEAN)
|
|
101
|
+
IS_PUBLIC_ATTACHMENT__FIELD_NAME = WrapperField("IS_PUBLIC_ATTACHMENT", FieldType.BOOLEAN)
|
|
102
|
+
ICON_COLOR__FIELD_NAME = WrapperField("IconColor", FieldType.STRING)
|
|
103
|
+
ICON_NAME__FIELD_NAME = WrapperField("IconName", FieldType.STRING)
|
|
104
|
+
IMPORTABLE__FIELD_NAME = WrapperField("Importable", FieldType.BOOLEAN)
|
|
105
|
+
IS_ACTIVE__FIELD_NAME = WrapperField("IsActive", FieldType.BOOLEAN)
|
|
106
|
+
IS_HIDDEN__FIELD_NAME = WrapperField("IsHidden", FieldType.BOOLEAN)
|
|
107
|
+
MAX_TABLE_ROW_COUNT__FIELD_NAME = WrapperField("MaxTableRowCount", FieldType.LONG)
|
|
108
|
+
PLURAL_DISPLAY_NAME__FIELD_NAME = WrapperField("PluralDisplayName", FieldType.STRING)
|
|
109
|
+
RECORD_ASSIGNABLE__FIELD_NAME = WrapperField("RecordAssignable", FieldType.BOOLEAN)
|
|
110
|
+
RECORD_IMAGE_ASSIGNABLE__FIELD_NAME = WrapperField("RecordImageAssignable", FieldType.BOOLEAN)
|
|
111
|
+
RECORD_IMAGE_MANUALLY_ADDABLE__FIELD_NAME = WrapperField("RecordImageManuallyAddable", FieldType.BOOLEAN)
|
|
112
|
+
REMOVABLE__FIELD_NAME = WrapperField("Removable", FieldType.BOOLEAN)
|
|
113
|
+
RESTRICTED__FIELD_NAME = WrapperField("Restricted", FieldType.BOOLEAN)
|
|
114
|
+
SHOW_ON_HOME_SCREEN__FIELD_NAME = WrapperField("ShowOnHomeScreen", FieldType.BOOLEAN)
|
|
115
|
+
SHOW_SUB_TABLES__FIELD_NAME = WrapperField("ShowSubTables", FieldType.BOOLEAN)
|
|
116
|
+
SHOW_TABS__FIELD_NAME = WrapperField("ShowTabs", FieldType.BOOLEAN)
|
|
117
|
+
SINGLE_PARENT__FIELD_NAME = WrapperField("SingleParent", FieldType.BOOLEAN)
|
|
118
|
+
UNDER_CONTAINER__FIELD_NAME = WrapperField("UnderContainer", FieldType.BOOLEAN)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class EnbDataTypeDefinitionPseudoDef:
|
|
122
|
+
DATA_TYPE_NAME: str = "EnbDataTypeDefinition"
|
|
123
|
+
DATA_TYPE_ID__FIELD_NAME = WrapperField("DataTypeId", FieldType.LONG)
|
|
124
|
+
DATA_TYPE_NAME__FIELD_NAME = WrapperField("DataTypeName", FieldType.STRING)
|
|
125
|
+
DESCRIPTION__FIELD_NAME = WrapperField("Description", FieldType.STRING)
|
|
126
|
+
DISPLAY_NAME__FIELD_NAME = WrapperField("DisplayName", FieldType.STRING)
|
|
127
|
+
ENB_DATA_TYPE_NAME__FIELD_NAME = WrapperField("EnbDataTypeName", FieldType.STRING)
|
|
128
|
+
ICON_COLOR__FIELD_NAME = WrapperField("IconColor", FieldType.STRING)
|
|
129
|
+
ICON_NAME__FIELD_NAME = WrapperField("IconName", FieldType.STRING)
|
|
130
|
+
NOTEBOOK_EXPERIMENT_ID__FIELD_NAME = WrapperField("Notebook_Experiment_ID", FieldType.LONG)
|
|
131
|
+
PLURAL_DISPLAY_NAME__FIELD_NAME = WrapperField("PluralDisplayName", FieldType.STRING)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class EnbEntryPseudoDef:
|
|
135
|
+
DATA_TYPE_NAME: str = "EnbEntry"
|
|
136
|
+
APPROVAL_DUE_DATE__FIELD_NAME = WrapperField("ApprovalDueDate", FieldType.DATE)
|
|
137
|
+
COLUMN_ORDER__FIELD_NAME = WrapperField("ColumnOrder", FieldType.INTEGER)
|
|
138
|
+
COLUMN_SPAN__FIELD_NAME = WrapperField("ColumnSpan", FieldType.INTEGER)
|
|
139
|
+
CREATED_BY__FIELD_NAME = WrapperField("CreatedBy", FieldType.STRING)
|
|
140
|
+
DATA_TYPE_NAME__FIELD_NAME = WrapperField("DataTypeName", FieldType.STRING)
|
|
141
|
+
DATE_CREATED__FIELD_NAME = WrapperField("DateCreated", FieldType.DATE)
|
|
142
|
+
DEPENDENT_ENTRY_ID_LIST__FIELD_NAME = WrapperField("DependentEntryIdList", FieldType.STRING)
|
|
143
|
+
ENTRY_DESCRIPTION__FIELD_NAME = WrapperField("EntryDescription", FieldType.STRING)
|
|
144
|
+
ENTRY_HEIGHT__FIELD_NAME = WrapperField("EntryHeight", FieldType.INTEGER)
|
|
145
|
+
ENTRY_ID__FIELD_NAME = WrapperField("EntryId", FieldType.LONG)
|
|
146
|
+
ENTRY_NAME__FIELD_NAME = WrapperField("EntryName", FieldType.STRING)
|
|
147
|
+
ENTRY_ORDER__FIELD_NAME = WrapperField("EntryOrder", FieldType.INTEGER)
|
|
148
|
+
ENTRY_REQUIRES_E_SIGN__FIELD_NAME = WrapperField("EntryRequiresESign", FieldType.BOOLEAN)
|
|
149
|
+
ENTRY_SINGLETON_ID__FIELD_NAME = WrapperField("EntrySingletonId", FieldType.STRING)
|
|
150
|
+
ENTRY_STATUS__FIELD_NAME = WrapperField("EntryStatus", FieldType.STRING)
|
|
151
|
+
ENTRY_TYPE__FIELD_NAME = WrapperField("EntryType", FieldType.STRING)
|
|
152
|
+
EXPERIMENT_ID__FIELD_NAME = WrapperField("ExperimentId", FieldType.LONG)
|
|
153
|
+
HAS_COMMENTS__FIELD_NAME = WrapperField("HasComments", FieldType.BOOLEAN)
|
|
154
|
+
IS_CREATED_FROM_TEMPLATE__FIELD_NAME = WrapperField("IsCreatedFromTemplate", FieldType.BOOLEAN)
|
|
155
|
+
IS_REQUIRED_ENTRY__FIELD_NAME = WrapperField("IsRequiredEntry", FieldType.BOOLEAN)
|
|
156
|
+
IS_SHOWN_IN_TEMPLATE__FIELD_NAME = WrapperField("IsShownInTemplate", FieldType.BOOLEAN)
|
|
157
|
+
LAST_MODIFIED_BY__FIELD_NAME = WrapperField("LastModifiedBy", FieldType.STRING)
|
|
158
|
+
LAST_MODIFIED_DATE__FIELD_NAME = WrapperField("LastModifiedDate", FieldType.DATE)
|
|
159
|
+
RELATED_ENTRY_ID_LIST__FIELD_NAME = WrapperField("RelatedEntryIdList", FieldType.STRING)
|
|
160
|
+
REQUIRES_GRABBER_PLUGIN__FIELD_NAME = WrapperField("RequriesGrabberPlugin", FieldType.BOOLEAN)
|
|
161
|
+
SOURCE_ENTRY_ID__FIELD_NAME = WrapperField("SourceEntryId", FieldType.LONG)
|
|
162
|
+
SUBMITTED_BY__FIELD_NAME = WrapperField("SubmittedBy", FieldType.STRING)
|
|
163
|
+
SUBMITTED_DATE__FIELD_NAME = WrapperField("SubmittedDate", FieldType.DATE)
|
|
164
|
+
TAB_ID__FIELD_NAME = WrapperField("TabId", FieldType.LONG)
|
|
165
|
+
TEMPLATE_ITEM_FULFILLED_TIME_STAMP__FIELD_NAME = WrapperField("TemplateItemFulfilledTimeStamp", FieldType.LONG)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class EnbEntryOptionsPseudoDef:
|
|
169
|
+
DATA_TYPE_NAME: str = "EnbEntryOptions"
|
|
170
|
+
ENTRY_ID__FIELD_NAME = WrapperField("EntryId", FieldType.LONG)
|
|
171
|
+
ENTRY_OPTION_VALUE__FIELD_NAME = WrapperField("EntryOptionValue", FieldType.STRING)
|
|
172
|
+
ENTRY_OPTION_KEY__FIELD_NAME = WrapperField("EntryOptionkey", FieldType.STRING)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class ExperimentEntryRecordPseudoDef:
|
|
176
|
+
DATA_TYPE_NAME: str = "ExperimentEntryRecord"
|
|
177
|
+
ENTRY_ID__FIELD_NAME = WrapperField("EntryId", FieldType.LONG)
|
|
178
|
+
RECORD_ID__FIELD_NAME = WrapperField("RecordId", FieldType.LONG)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class LimsUserPseudoDef:
|
|
182
|
+
DATA_TYPE_NAME: str = "LimsUser"
|
|
183
|
+
API_USER__FIELD_NAME = WrapperField("ApiUser", FieldType.BOOLEAN)
|
|
184
|
+
EMAIL_ADDRESS__FIELD_NAME = WrapperField("EmailAddress", FieldType.STRING)
|
|
185
|
+
FIRST_NAME__FIELD_NAME = WrapperField("FirstName", FieldType.STRING)
|
|
186
|
+
LAST_NAME__FIELD_NAME = WrapperField("LastName", FieldType.STRING)
|
|
187
|
+
MIDDLE_NAME__FIELD_NAME = WrapperField("MiddleName", FieldType.STRING)
|
|
188
|
+
PWD_EXPIRE_DATE__FIELD_NAME = WrapperField("PwdExpireDate", FieldType.DATE)
|
|
189
|
+
PWD_EXPIRE_INTERVAL__FIELD_NAME = WrapperField("PwdExpireInterval", FieldType.INTEGER)
|
|
190
|
+
USER_NAME__FIELD_NAME = WrapperField("UserName", FieldType.STRING)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class NotebookExperimentPseudoDef:
|
|
194
|
+
DATA_TYPE_NAME: str = "NotebookExperiment"
|
|
195
|
+
ACCESS_LEVEL__FIELD_NAME = WrapperField("AccessLevel", FieldType.STRING)
|
|
196
|
+
APPROVAL_DUE_DATE__FIELD_NAME = WrapperField("ApprovalDueDate", FieldType.DATE)
|
|
197
|
+
CREATED_BY__FIELD_NAME = WrapperField("CreatedBy", FieldType.STRING)
|
|
198
|
+
DATE_CREATED__FIELD_NAME = WrapperField("DateCreated", FieldType.DATE)
|
|
199
|
+
DESCRIPTION__FIELD_NAME = WrapperField("Description", FieldType.STRING)
|
|
200
|
+
EXPERIMENT_ID__FIELD_NAME = WrapperField("ExperimentId", FieldType.LONG)
|
|
201
|
+
EXPERIMENT_NAME__FIELD_NAME = WrapperField("ExperimentName", FieldType.STRING)
|
|
202
|
+
EXPERIMENT_OWNER__FIELD_NAME = WrapperField("ExperimentOwner", FieldType.STRING)
|
|
203
|
+
EXPERIMENT_RECORD_ID__FIELD_NAME = WrapperField("ExperimentRecordId", FieldType.LONG)
|
|
204
|
+
EXPERIMENT_TYPE_NAME__FIELD_NAME = WrapperField("ExperimentTypeName", FieldType.STRING)
|
|
205
|
+
IS_ACTIVE__FIELD_NAME = WrapperField("IsActive", FieldType.BOOLEAN)
|
|
206
|
+
IS_MODIFIABLE__FIELD_NAME = WrapperField("IsModifiable", FieldType.BOOLEAN)
|
|
207
|
+
IS_TEMPLATE__FIELD_NAME = WrapperField("IsTemplate", FieldType.BOOLEAN)
|
|
208
|
+
IS_PROTOCOL_TEMPLATE__FIELD_NAME = WrapperField("Is_Protocol_Template", FieldType.BOOLEAN)
|
|
209
|
+
LAST_MODIFIED_BY__FIELD_NAME = WrapperField("LastModifiedBy", FieldType.STRING)
|
|
210
|
+
LAST_MODIFIED_DATE__FIELD_NAME = WrapperField("LastModifiedDate", FieldType.DATE)
|
|
211
|
+
SOURCE_TEMPLATE_ID__FIELD_NAME = WrapperField("SourceTemplateId", FieldType.LONG)
|
|
212
|
+
STATUS__FIELD_NAME = WrapperField("Status", FieldType.STRING)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class NotebookExperimentOptionPseudoDef:
|
|
216
|
+
DATA_TYPE_NAME: str = "NotebookExperimentOption"
|
|
217
|
+
EXPERIMENT_ID__FIELD_NAME = WrapperField("ExperimentId", FieldType.LONG)
|
|
218
|
+
OPTION_KEY__FIELD_NAME = WrapperField("OptionKey", FieldType.STRING)
|
|
219
|
+
OPTION_VALUE__FIELD_NAME = WrapperField("OptionValue", FieldType.STRING)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class SystemLogPseudoDef:
|
|
223
|
+
DATA_TYPE_NAME: str = "SystemLog"
|
|
224
|
+
DATA_FIELD_NAME__FIELD_NAME = WrapperField("DataFieldName", FieldType.STRING)
|
|
225
|
+
DATA_TYPE_NAME__FIELD_NAME = WrapperField("DataTypeName", FieldType.STRING)
|
|
226
|
+
DESCRIPTION__FIELD_NAME = WrapperField("Description", FieldType.STRING)
|
|
227
|
+
EVENT_ID__FIELD_NAME = WrapperField("EventId", FieldType.LONG)
|
|
228
|
+
EVENT_TYPE__FIELD_NAME = WrapperField("EventType", FieldType.STRING)
|
|
229
|
+
FULL_NAME__FIELD_NAME = WrapperField("FullName", FieldType.STRING)
|
|
230
|
+
NEW_VALUE__FIELD_NAME = WrapperField("NewValue", FieldType.STRING)
|
|
231
|
+
ORIGINAL_VALUE__FIELD_NAME = WrapperField("OriginalValue", FieldType.STRING)
|
|
232
|
+
RECORD_ID__FIELD_NAME = WrapperField("RecordId", FieldType.LONG)
|
|
233
|
+
RECORD_NAME__FIELD_NAME = WrapperField("RecordName", FieldType.STRING)
|
|
234
|
+
TIMESTAMP__FIELD_NAME = WrapperField("Timestamp", FieldType.DATE)
|
|
235
|
+
USER_COMMENT__FIELD_NAME = WrapperField("UserComment", FieldType.STRING)
|
|
236
|
+
USERNAME__FIELD_NAME = WrapperField("Username", FieldType.STRING)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class SystemObjectChangeLogPseudoDef:
|
|
240
|
+
DATA_TYPE_NAME: str = "System_Object_Change_Log"
|
|
241
|
+
ALT_ID__FIELD_NAME = WrapperField("Alt_Id", FieldType.STRING)
|
|
242
|
+
ATTRIBUTE_NAME__FIELD_NAME = WrapperField("Attribute_Name", FieldType.STRING)
|
|
243
|
+
CHANGE_TYPE__FIELD_NAME = WrapperField("Change_Type", FieldType.STRING)
|
|
244
|
+
DATA_FIELD_NAME__FIELD_NAME = WrapperField("Data_Field_Name", FieldType.STRING)
|
|
245
|
+
DATA_TYPE_NAME__FIELD_NAME = WrapperField("Data_Type_Name", FieldType.STRING)
|
|
246
|
+
EVENT_ID__FIELD_NAME = WrapperField("Event_Id", FieldType.STRING)
|
|
247
|
+
NEW_VALUE__FIELD_NAME = WrapperField("New_Value", FieldType.STRING)
|
|
248
|
+
OBJECT_ID__FIELD_NAME = WrapperField("Object_Id", FieldType.STRING)
|
|
249
|
+
OBJECT_TYPE__FIELD_NAME = WrapperField("Object_Type", FieldType.STRING)
|
|
250
|
+
OLD_VALUE__FIELD_NAME = WrapperField("Old_Value", FieldType.STRING)
|
|
251
|
+
TIMESTAMP__FIELD_NAME = WrapperField("Timestamp", FieldType.DATE)
|
|
252
|
+
USERNAME__FIELD_NAME = WrapperField("Username", FieldType.STRING)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class TaskPseudoDef:
|
|
256
|
+
DATA_TYPE_NAME: str = "Task"
|
|
257
|
+
ATTACHMENT_REQUIRED__FIELD_NAME = WrapperField("AttachmentRequired", FieldType.BOOLEAN)
|
|
258
|
+
CREATED_BY__FIELD_NAME = WrapperField("CreatedBy", FieldType.STRING)
|
|
259
|
+
CUSTOM_ACTION__FIELD_NAME = WrapperField("CustomAction", FieldType.STRING)
|
|
260
|
+
DATA_TYPE_NAME_LIST__FIELD_NAME = WrapperField("DataTypeNameList", FieldType.STRING)
|
|
261
|
+
DATE_CREATED__FIELD_NAME = WrapperField("DateCreated", FieldType.DATE)
|
|
262
|
+
DATE_EDITED__FIELD_NAME = WrapperField("DateEdited", FieldType.DATE)
|
|
263
|
+
DISPLAY_TYPE__FIELD_NAME = WrapperField("DisplayType", FieldType.ENUM)
|
|
264
|
+
EDITED_BY__FIELD_NAME = WrapperField("EditedBy", FieldType.STRING)
|
|
265
|
+
INPUT_DATA_TYPE_NAME__FIELD_NAME = WrapperField("InputDataTypeName", FieldType.STRING)
|
|
266
|
+
IS_TEMPLATE__FIELD_NAME = WrapperField("IsTemplate", FieldType.BOOLEAN)
|
|
267
|
+
LONG_DESC__FIELD_NAME = WrapperField("LongDesc", FieldType.STRING)
|
|
268
|
+
MENU_TASK_ID__FIELD_NAME = WrapperField("MenuTaskId", FieldType.ENUM)
|
|
269
|
+
NAME__FIELD_NAME = WrapperField("Name", FieldType.STRING)
|
|
270
|
+
SHORT_DESC__FIELD_NAME = WrapperField("ShortDesc", FieldType.STRING)
|
|
271
|
+
TASK_ID__FIELD_NAME = WrapperField("TaskId", FieldType.LONG)
|
|
272
|
+
TASK_VERSION__FIELD_NAME = WrapperField("TaskVersion", FieldType.LONG)
|
|
273
|
+
TEMPLATE_TASK_ID__FIELD_NAME = WrapperField("TemplateTaskId", FieldType.LONG)
|
|
274
|
+
TYPE__FIELD_NAME = WrapperField("Type", FieldType.ENUM)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class TaskAttachmentPseudoDef:
|
|
278
|
+
DATA_TYPE_NAME: str = "TaskAttachment"
|
|
279
|
+
ACTIVE_TASK_ID__FIELD_NAME = WrapperField("ActiveTaskId", FieldType.LONG)
|
|
280
|
+
RECORD_ID__FIELD_NAME = WrapperField("RecordId", FieldType.LONG)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class TaskOptionPseudoDef:
|
|
284
|
+
DATA_TYPE_NAME: str = "TaskOption"
|
|
285
|
+
OPTION_KEY__FIELD_NAME = WrapperField("OptionKey", FieldType.STRING)
|
|
286
|
+
OPTION_VALUE__FIELD_NAME = WrapperField("OptionValue", FieldType.STRING)
|
|
287
|
+
TASK_ID__FIELD_NAME = WrapperField("TaskId", FieldType.LONG)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class TaskUsagePseudoDef:
|
|
291
|
+
DATA_TYPE_NAME: str = "TaskUsage"
|
|
292
|
+
FORCE_ATTACH__FIELD_NAME = WrapperField("ForceAttach", FieldType.BOOLEAN)
|
|
293
|
+
IS_TEMPLATE__FIELD_NAME = WrapperField("IsTemplate", FieldType.BOOLEAN)
|
|
294
|
+
TASK_ID__FIELD_NAME = WrapperField("TaskId", FieldType.LONG)
|
|
295
|
+
TASK_ORDER__FIELD_NAME = WrapperField("TaskOrder", FieldType.INTEGER)
|
|
296
|
+
TASK_USAGE_ID__FIELD_NAME = WrapperField("TaskUsageId", FieldType.LONG)
|
|
297
|
+
WORKFLOW_ID__FIELD_NAME = WrapperField("WorkflowId", FieldType.LONG)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class VeloxWebhookPseudoDef:
|
|
301
|
+
DATA_TYPE_NAME: str = "VELOXWEBHOOK"
|
|
302
|
+
CUSTOM_PLUGIN_POINT__FIELD_NAME = WrapperField("CUSTOM_PLUGIN_POINT", FieldType.STRING)
|
|
303
|
+
DATA_TYPE_NAME_SET__FIELD_NAME = WrapperField("DATA_TYPE_NAME_SET", FieldType.STRING)
|
|
304
|
+
DESCRIPTION__FIELD_NAME = WrapperField("DESCRIPTION", FieldType.STRING)
|
|
305
|
+
ENB_ENTRY_TYPE__FIELD_NAME = WrapperField("ENB_ENTRY_TYPE", FieldType.STRING)
|
|
306
|
+
EXPERIMENT_ENTRY_NAME__FIELD_NAME = WrapperField("EXPERIMENT_ENTRY_NAME", FieldType.STRING)
|
|
307
|
+
GUID__FIELD_NAME = WrapperField("GUID", FieldType.STRING)
|
|
308
|
+
ICON_COLOR__FIELD_NAME = WrapperField("ICON_COLOR", FieldType.STRING)
|
|
309
|
+
ICON_GUID__FIELD_NAME = WrapperField("ICON_GUID", FieldType.STRING)
|
|
310
|
+
IS_RETRY_ON_FAILURE__FIELD_NAME = WrapperField("IS_RETRY_ON_FAILURE", FieldType.BOOLEAN)
|
|
311
|
+
IS_TRANSACTIONAL__FIELD_NAME = WrapperField("IS_TRANSACTIONAL", FieldType.BOOLEAN)
|
|
312
|
+
LINE_1_TEXT__FIELD_NAME = WrapperField("LINE_1_TEXT", FieldType.STRING)
|
|
313
|
+
LINE_2_TEXT__FIELD_NAME = WrapperField("LINE_2_TEXT", FieldType.STRING)
|
|
314
|
+
PLUGIN_ORDER__FIELD_NAME = WrapperField("PLUGIN_ORDER", FieldType.INTEGER)
|
|
315
|
+
PLUGIN_POINT__FIELD_NAME = WrapperField("PLUGIN_POINT", FieldType.STRING)
|
|
316
|
+
SECTION_NAME_PATH__FIELD_NAME = WrapperField("SECTION_NAME_PATH", FieldType.STRING)
|
|
317
|
+
TEMPLATE_NAME__FIELD_NAME = WrapperField("TEMPLATE_NAME", FieldType.STRING)
|
|
318
|
+
WEBHOOK_URL__FIELD_NAME = WrapperField("WEBHOOK_URL", FieldType.STRING)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
class VeloxWebhookExecutionPseudoDef:
|
|
322
|
+
DATA_TYPE_NAME: str = "VELOXWEBHOOK_EXECUTION"
|
|
323
|
+
EXECUTION_TIMESTAMP__FIELD_NAME = WrapperField("EXECUTION_TIMESTAMP", FieldType.DATE)
|
|
324
|
+
EXECUTION_USERNAME__FIELD_NAME = WrapperField("EXECUTION_USERNAME", FieldType.STRING)
|
|
325
|
+
GUID__FIELD_NAME = WrapperField("GUID", FieldType.STRING)
|
|
326
|
+
LAST_ATTEMPT_NUMBER__FIELD_NAME = WrapperField("LAST_ATTEMPT_NUMBER", FieldType.INTEGER)
|
|
327
|
+
LAST_ATTEMPT_RESULT__FIELD_NAME = WrapperField("LAST_ATTEMPT_RESULT", FieldType.STRING)
|
|
328
|
+
REQUEST_BODY__FIELD_NAME = WrapperField("REQUEST_BODY", FieldType.STRING)
|
|
329
|
+
WEBHOOK_GUID__FIELD_NAME = WrapperField("WEBHOOK_GUID", FieldType.STRING)
|
|
330
|
+
WEBHOOK_URL__FIELD_NAME = WrapperField("WEBHOOK_URL", FieldType.STRING)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
class VeloxWebhookExecutionAttemptPseudoDef:
|
|
334
|
+
DATA_TYPE_NAME: str = "VELOXWEBHOOK_EXECUTION_ATTEMPT"
|
|
335
|
+
ATTEMPT_DURATION__FIELD_NAME = WrapperField("ATTEMPT_DURATION", FieldType.INTEGER)
|
|
336
|
+
ATTEMPT_NUMBER__FIELD_NAME = WrapperField("ATTEMPT_NUMBER", FieldType.INTEGER)
|
|
337
|
+
ATTEMPT_RESULT__FIELD_NAME = WrapperField("ATTEMPT_RESULT", FieldType.STRING)
|
|
338
|
+
ATTEMPT_TIMESTAMP__FIELD_NAME = WrapperField("ATTEMPT_TIMESTAMP", FieldType.DATE)
|
|
339
|
+
EXECUTION_GUID__FIELD_NAME = WrapperField("EXECUTION_GUID", FieldType.STRING)
|
|
340
|
+
GUID__FIELD_NAME = WrapperField("GUID", FieldType.STRING)
|
|
341
|
+
RESPONSE_BODY__FIELD_NAME = WrapperField("RESPONSE_BODY", FieldType.STRING)
|
|
342
|
+
RESPONSE_CODE__FIELD_NAME = WrapperField("RESPONSE_CODE", FieldType.INTEGER)
|
|
343
|
+
WEBHOOK_GUID__FIELD_NAME = WrapperField("WEBHOOK_GUID", FieldType.STRING)
|
|
344
|
+
WEBHOOK_URL__FIELD_NAME = WrapperField("WEBHOOK_URL", FieldType.STRING)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class VeloxWebhookExecutionLogPseudoDef:
|
|
348
|
+
DATA_TYPE_NAME: str = "VELOXWEBHOOK_EXECUTION_LOG"
|
|
349
|
+
ATTEMPT_GUID__FIELD_NAME = WrapperField("ATTEMPT_GUID", FieldType.STRING)
|
|
350
|
+
LOG_LEVEL__FIELD_NAME = WrapperField("LOG_LEVEL", FieldType.STRING)
|
|
351
|
+
LOG_LINE_NUM__FIELD_NAME = WrapperField("LOG_LINE_NUM", FieldType.INTEGER)
|
|
352
|
+
LOG_MESSAGE__FIELD_NAME = WrapperField("LOG_MESSAGE", FieldType.STRING)
|
|
353
|
+
LOG_TIMESTAMP__FIELD_NAME = WrapperField("LOG_TIMESTAMP", FieldType.DATE)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class VeloxRuleCostPseudoDef:
|
|
357
|
+
DATA_TYPE_NAME: str = "VELOX_RULE_COST"
|
|
358
|
+
ACTION_COST__FIELD_NAME = WrapperField("ACTION_COST", FieldType.LONG)
|
|
359
|
+
ACTION_COUNT__FIELD_NAME = WrapperField("ACTION_COUNT", FieldType.LONG)
|
|
360
|
+
ANCESTOR_DESCENDANT_COUNT__FIELD_NAME = WrapperField("ANCESTOR_DESCENDANT_COUNT", FieldType.LONG)
|
|
361
|
+
PARENT_CHILD_COUNT__FIELD_NAME = WrapperField("PARENT_CHILD_COUNT", FieldType.LONG)
|
|
362
|
+
PROCESSING_TIME__FIELD_NAME = WrapperField("PROCESSING_TIME", FieldType.LONG)
|
|
363
|
+
RULE_GUID__FIELD_NAME = WrapperField("RULE_GUID", FieldType.STRING)
|
|
364
|
+
SOURCE_RECORD_COUNT__FIELD_NAME = WrapperField("SOURCE_RECORD_COUNT", FieldType.LONG)
|
|
365
|
+
TIMESTAMP__FIELD_NAME = WrapperField("TIMESTAMP", FieldType.DATE)
|
|
366
|
+
TOTAL_COST__FIELD_NAME = WrapperField("TOTAL_COST", FieldType.LONG)
|
|
367
|
+
TRANSACTION_GUID__FIELD_NAME = WrapperField("TRANSACTION_GUID", FieldType.STRING)
|
|
368
|
+
USERNAME__FIELD_NAME = WrapperField("USERNAME", FieldType.STRING)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
class VeloxConversationPseudoDef:
|
|
372
|
+
DATA_TYPE_NAME: str = "VeloxConversation"
|
|
373
|
+
CONVERSATION_DESCRIPTION__FIELD_NAME = WrapperField("ConversationDescription", FieldType.STRING)
|
|
374
|
+
CONVERSATION_GUID__FIELD_NAME = WrapperField("ConversationGuid", FieldType.STRING)
|
|
375
|
+
CONVERSATION_NAME__FIELD_NAME = WrapperField("ConversationName", FieldType.STRING)
|
|
376
|
+
CREATED_BY__FIELD_NAME = WrapperField("CreatedBy", FieldType.STRING)
|
|
377
|
+
DATE_CREATED__FIELD_NAME = WrapperField("DateCreated", FieldType.DATE)
|
|
378
|
+
SERVER_PLUGIN_PATH__FIELD_NAME = WrapperField("Server_Plugin_Path", FieldType.STRING)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class VeloxConversationMessagePseudoDef:
|
|
382
|
+
DATA_TYPE_NAME: str = "VeloxConversationMessage"
|
|
383
|
+
CONVERSATION_GUID__FIELD_NAME = WrapperField("ConversationGuid", FieldType.STRING)
|
|
384
|
+
MESSAGE__FIELD_NAME = WrapperField("Message", FieldType.STRING)
|
|
385
|
+
MESSAGE_GUID__FIELD_NAME = WrapperField("MessageGuid", FieldType.STRING)
|
|
386
|
+
MESSAGE_SENDER__FIELD_NAME = WrapperField("MessageSender", FieldType.STRING)
|
|
387
|
+
MESSAGE_TIMESTAMP__FIELD_NAME = WrapperField("MessageTimestamp", FieldType.DATE)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
class VeloxScriptPseudoDef:
|
|
391
|
+
DATA_TYPE_NAME: str = "VeloxScript"
|
|
392
|
+
CODE__FIELD_NAME = WrapperField("Code", FieldType.STRING)
|
|
393
|
+
CREATED_BY__FIELD_NAME = WrapperField("CreatedBy", FieldType.STRING)
|
|
394
|
+
DATE_CREATED__FIELD_NAME = WrapperField("DateCreated", FieldType.LONG)
|
|
395
|
+
LAST_MODIFIED_BY__FIELD_NAME = WrapperField("LastModifiedBy", FieldType.STRING)
|
|
396
|
+
LAST_MODIFIED_DATE__FIELD_NAME = WrapperField("LastModifiedDate", FieldType.LONG)
|
|
397
|
+
PATH__FIELD_NAME = WrapperField("Path", FieldType.STRING)
|
|
398
|
+
PLUGIN_DESCRIPTION__FIELD_NAME = WrapperField("PluginDescription", FieldType.STRING)
|
|
399
|
+
PLUGIN_LINE1_TEXT__FIELD_NAME = WrapperField("PluginLine1Text", FieldType.STRING)
|
|
400
|
+
PLUGIN_LINE2_TEXT__FIELD_NAME = WrapperField("PluginLine2Text", FieldType.STRING)
|
|
401
|
+
PLUGIN_ORDER__FIELD_NAME = WrapperField("PluginOrder", FieldType.INTEGER)
|
|
402
|
+
PLUGIN_POINT__FIELD_NAME = WrapperField("PluginPoint", FieldType.STRING)
|
|
403
|
+
PROJECT_GUID__FIELD_NAME = WrapperField("ProjectGuid", FieldType.STRING)
|
|
404
|
+
SCRIPT_GUID__FIELD_NAME = WrapperField("ScriptGuid", FieldType.STRING)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
class VeloxScriptProjectPseudoDef:
|
|
408
|
+
DATA_TYPE_NAME: str = "VeloxScriptProject"
|
|
409
|
+
CLASS_PATH__FIELD_NAME = WrapperField("ClassPath", FieldType.STRING)
|
|
410
|
+
CREATED_BY__FIELD_NAME = WrapperField("CreatedBy", FieldType.STRING)
|
|
411
|
+
DATE_CREATED__FIELD_NAME = WrapperField("DateCreated", FieldType.LONG)
|
|
412
|
+
DEPLOYMENT_OUT_OF_DATE__FIELD_NAME = WrapperField("DeploymentOutOfDate", FieldType.BOOLEAN)
|
|
413
|
+
DESCRIPTION__FIELD_NAME = WrapperField("Description", FieldType.STRING)
|
|
414
|
+
PROJECT_GUID__FIELD_NAME = WrapperField("ProjectGuid", FieldType.STRING)
|
|
415
|
+
PROJECT_NAME__FIELD_NAME = WrapperField("ProjectName", FieldType.STRING)
|
|
416
|
+
SCRIPT_LANGUAGE__FIELD_NAME = WrapperField("ScriptLanguage", FieldType.STRING)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
class WorkflowPseudoDef:
|
|
420
|
+
DATA_TYPE_NAME: str = "Workflow"
|
|
421
|
+
ALL_ACCESS__FIELD_NAME = WrapperField("AllAccess", FieldType.BOOLEAN)
|
|
422
|
+
CATEGORY__FIELD_NAME = WrapperField("Category", FieldType.STRING)
|
|
423
|
+
CREATED_BY__FIELD_NAME = WrapperField("CreatedBy", FieldType.STRING)
|
|
424
|
+
DATE_CREATED__FIELD_NAME = WrapperField("DateCreated", FieldType.DATE)
|
|
425
|
+
DATE_EDITED__FIELD_NAME = WrapperField("DateEdited", FieldType.DATE)
|
|
426
|
+
DIRECT_LAUNCH__FIELD_NAME = WrapperField("DirectLaunch", FieldType.BOOLEAN)
|
|
427
|
+
EDITED_BY__FIELD_NAME = WrapperField("EditedBy", FieldType.STRING)
|
|
428
|
+
IS_TEMPLATE__FIELD_NAME = WrapperField("IsTemplate", FieldType.BOOLEAN)
|
|
429
|
+
LONG_DESC__FIELD_NAME = WrapperField("LongDesc", FieldType.STRING)
|
|
430
|
+
NAME__FIELD_NAME = WrapperField("Name", FieldType.STRING)
|
|
431
|
+
SHORT_DESC__FIELD_NAME = WrapperField("ShortDesc", FieldType.STRING)
|
|
432
|
+
WORKFLOW_ID__FIELD_NAME = WrapperField("WorkflowId", FieldType.LONG)
|
|
433
|
+
WORKFLOW_VERSION__FIELD_NAME = WrapperField("WorkflowVersion", FieldType.LONG)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
class WorkflowOptionPseudoDef:
|
|
437
|
+
DATA_TYPE_NAME: str = "WorkflowOption"
|
|
438
|
+
OPTION_KEY__FIELD_NAME = WrapperField("OptionKey", FieldType.STRING)
|
|
439
|
+
OPTION_VALUE__FIELD_NAME = WrapperField("OptionValue", FieldType.STRING)
|
|
440
|
+
WORKFLOW_ID__FIELD_NAME = WrapperField("WorkflowId", FieldType.LONG)
|
|
@@ -23,6 +23,7 @@ from sapiopylib.rest.utils.recordmodel.RecordModelManager import RecordModelInst
|
|
|
23
23
|
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
24
24
|
from sapiopylib.rest.utils.recordmodel.properties import Child
|
|
25
25
|
|
|
26
|
+
from sapiopycommons.eln.experiment_report_util import ExperimentReportUtil
|
|
26
27
|
from sapiopycommons.general.aliases import AliasUtil, SapioRecord, ExperimentIdentifier, UserIdentifier, \
|
|
27
28
|
DataTypeIdentifier, RecordModel
|
|
28
29
|
from sapiopycommons.general.exceptions import SapioException
|
|
@@ -67,9 +68,9 @@ class ExperimentHandler:
|
|
|
67
68
|
"""Whether this ExperimentHandler has queried the system for all steps in the experiment."""
|
|
68
69
|
__steps: dict[str, ElnEntryStep]
|
|
69
70
|
"""Steps from this experiment. All steps are cached the first time any individual step is accessed."""
|
|
70
|
-
__step_options: dict[
|
|
71
|
-
"""Entry options for each step in this experiment.
|
|
72
|
-
The cache is updated whenever the entry options for a step are changed by this handler."""
|
|
71
|
+
__step_options: dict[int, dict[str, str]]
|
|
72
|
+
"""Entry options for each step in this experiment. All entry options are cached the first time any individual step's
|
|
73
|
+
options are queried. The cache is updated whenever the entry options for a step are changed by this handler."""
|
|
73
74
|
|
|
74
75
|
# Constants
|
|
75
76
|
__ENTRY_COMPLETE_STATUSES = [ExperimentEntryStatus.Completed, ExperimentEntryStatus.CompletedApproved]
|
|
@@ -549,6 +550,39 @@ class ExperimentHandler:
|
|
|
549
550
|
data_type: str = AliasUtil.to_data_type_name(data_type)
|
|
550
551
|
return [x for x in all_steps if data_type in x.get_data_type_names()]
|
|
551
552
|
|
|
553
|
+
def get_step_by_option(self, key: str, value: str | None = None) -> ElnEntryStep:
|
|
554
|
+
"""
|
|
555
|
+
Retrieve the step in this experiment that contains an entry option with the provided key and value.
|
|
556
|
+
Throws an exception if no entries or multiple entries in the experiment match.
|
|
557
|
+
|
|
558
|
+
:param key: The key of the entry option to match on.
|
|
559
|
+
:param value: The value of the entry option to match on. If not provided, then only matches on key.
|
|
560
|
+
:return: The entry in this experiment that matches the provided entry option key and value.
|
|
561
|
+
"""
|
|
562
|
+
steps: list[ElnEntryStep] = self.get_steps_by_option(key, value)
|
|
563
|
+
count: int = len(steps)
|
|
564
|
+
if count != 1:
|
|
565
|
+
option = key + ("::" + value if value is not None else "")
|
|
566
|
+
raise SapioException(f"{('No' if count == 0 else 'Multiple')} entries in this experiment match the "
|
|
567
|
+
f"provided option: {option}")
|
|
568
|
+
return steps[0]
|
|
569
|
+
|
|
570
|
+
def get_steps_by_option(self, key: str, value: str | None = None) -> list[ElnEntryStep]:
|
|
571
|
+
"""
|
|
572
|
+
Retrieve every step in this experiment that contains an entry option with the provided key and value.
|
|
573
|
+
|
|
574
|
+
:param key: The key of the entry option to match on.
|
|
575
|
+
:param value: The value of the entry option to match on. If not provided, then only matches on key.
|
|
576
|
+
:return: The entries in this experiment that match the provided entry option key and value.
|
|
577
|
+
"""
|
|
578
|
+
ret_list: list[ElnEntryStep] = []
|
|
579
|
+
for step in self.get_all_steps():
|
|
580
|
+
options: dict[str, str] = self.get_step_options(step)
|
|
581
|
+
if key in options:
|
|
582
|
+
if value is None or options[key] == value:
|
|
583
|
+
ret_list.append(step)
|
|
584
|
+
return ret_list
|
|
585
|
+
|
|
552
586
|
def get_step_records(self, step: Step) -> list[DataRecord]:
|
|
553
587
|
"""
|
|
554
588
|
Query for the data records for the given step. The returned records are not cached by the ExperimentHandler.
|
|
@@ -966,7 +1000,7 @@ class ExperimentHandler:
|
|
|
966
1000
|
if clear_template_item_fulfilled_timestamp is True:
|
|
967
1001
|
entry.template_item_fulfilled_timestamp = None
|
|
968
1002
|
if entry_options_map is not None:
|
|
969
|
-
self.__step_options.update({step: entry_options_map})
|
|
1003
|
+
self.__step_options.update({step.get_id(): entry_options_map})
|
|
970
1004
|
|
|
971
1005
|
def get_step_option(self, step: Step, option: str) -> str:
|
|
972
1006
|
"""
|
|
@@ -996,8 +1030,8 @@ class ExperimentHandler:
|
|
|
996
1030
|
list of steps in the experiment and caches them.
|
|
997
1031
|
|
|
998
1032
|
Getting the step options requires a webservice query, which is made the first time any step option
|
|
999
|
-
method is called for
|
|
1000
|
-
method
|
|
1033
|
+
method is called for any step in this experiment. The step options are cached so that subsequent calls of this
|
|
1034
|
+
method don't make a webservice call.
|
|
1001
1035
|
|
|
1002
1036
|
:param step:
|
|
1003
1037
|
The step to get the options of.
|
|
@@ -1005,7 +1039,10 @@ class ExperimentHandler:
|
|
|
1005
1039
|
If given a name, throws an exception if no step of the given name exists in the experiment.
|
|
1006
1040
|
:return: The map of options for the input step.
|
|
1007
1041
|
"""
|
|
1008
|
-
|
|
1042
|
+
step = self.__to_eln_step(step)
|
|
1043
|
+
if step not in self.__step_options:
|
|
1044
|
+
self.__step_options.update(ExperimentReportUtil.get_experiment_entry_options(self.user, self.get_all_steps()))
|
|
1045
|
+
return self.__step_options[step.get_id()]
|
|
1009
1046
|
|
|
1010
1047
|
def add_step_options(self, step: Step, mapping: Mapping[str, str]):
|
|
1011
1048
|
"""
|
|
@@ -1175,16 +1212,3 @@ class ExperimentHandler:
|
|
|
1175
1212
|
return self.__exp_options
|
|
1176
1213
|
self.__exp_options = self.__eln_man.get_notebook_experiment_options(self.__exp_id)
|
|
1177
1214
|
return self.__exp_options
|
|
1178
|
-
|
|
1179
|
-
def __get_step_options(self, step: Step) -> dict[str, str]:
|
|
1180
|
-
"""
|
|
1181
|
-
Cache the options for the input step if they haven't been cached yet.
|
|
1182
|
-
|
|
1183
|
-
:return: The entry options for the input step.
|
|
1184
|
-
"""
|
|
1185
|
-
step = self.__to_eln_step(step)
|
|
1186
|
-
if step in self.__step_options:
|
|
1187
|
-
return self.__step_options.get(step)
|
|
1188
|
-
options: dict[str, str] = step.get_options()
|
|
1189
|
-
self.__step_options.update({step: options})
|
|
1190
|
-
return options
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
from sapiopylib.rest.ELNService import ElnManager
|
|
1
2
|
from sapiopylib.rest.User import SapioUser
|
|
2
3
|
from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
|
|
4
|
+
from sapiopylib.rest.pojo.eln.ElnExperiment import ElnExperiment, ElnExperimentQueryCriteria
|
|
3
5
|
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedType
|
|
4
6
|
|
|
5
7
|
from sapiopycommons.customreport.custom_report_builder import CustomReportBuilder
|
|
6
8
|
from sapiopycommons.customreport.term_builder import TermBuilder
|
|
7
|
-
from sapiopycommons.
|
|
9
|
+
from sapiopycommons.datatype.pseudo_data_types import EnbEntryOptionsPseudoDef, NotebookExperimentOptionPseudoDef, \
|
|
10
|
+
NotebookExperimentPseudoDef, ExperimentEntryRecordPseudoDef, EnbEntryPseudoDef
|
|
11
|
+
from sapiopycommons.general.aliases import SapioRecord, UserIdentifier, AliasUtil, FieldValue, \
|
|
12
|
+
ExperimentEntryIdentifier, ExperimentIdentifier
|
|
8
13
|
from sapiopycommons.general.custom_report_util import CustomReportUtil
|
|
9
14
|
from sapiopycommons.recordmodel.record_handler import RecordHandler
|
|
10
15
|
|
|
11
|
-
_NOTEBOOK_ID = "EXPERIMENTID"
|
|
12
|
-
_RECORD_ID = "RECORDID"
|
|
13
|
-
|
|
14
16
|
|
|
15
17
|
# FR-46908 - Provide a utility class that holds experiment related custom reports e.g. getting all the experiments
|
|
16
18
|
# that given records were used in or getting all records of a datatype used in given experiments.
|
|
@@ -23,8 +25,8 @@ class ExperimentReportUtil:
|
|
|
23
25
|
If a record wasn't used in any experiments then it will be mapped to an empty list.
|
|
24
26
|
|
|
25
27
|
:param context: The current webhook context or a user object to send requests from.
|
|
26
|
-
:param records:
|
|
27
|
-
:return:
|
|
28
|
+
:param records: A list of records of the same data type.
|
|
29
|
+
:return: A dictionary mapping each record to a list of ids of each experiment it was used in.
|
|
28
30
|
"""
|
|
29
31
|
if not records:
|
|
30
32
|
return {}
|
|
@@ -38,24 +40,25 @@ class ExperimentReportUtil:
|
|
|
38
40
|
id_to_record: dict[int, SapioRecord] = RecordHandler.map_by_id(records)
|
|
39
41
|
record_to_exps: dict[SapioRecord, set[int]] = {record: set() for record in records}
|
|
40
42
|
for row in rows:
|
|
41
|
-
record_id: int = row[
|
|
42
|
-
exp_id: int = row[
|
|
43
|
+
record_id: int = row["RecordId"]
|
|
44
|
+
exp_id: int = row[NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME.field_name]
|
|
43
45
|
record = id_to_record[record_id]
|
|
44
46
|
record_to_exps[record].add(exp_id)
|
|
45
47
|
|
|
46
48
|
return {record: list(exps) for record, exps in record_to_exps.items()}
|
|
47
49
|
|
|
48
50
|
@staticmethod
|
|
49
|
-
def map_experiments_to_records_of_type(context: UserIdentifier, exp_ids: list[
|
|
51
|
+
def map_experiments_to_records_of_type(context: UserIdentifier, exp_ids: list[ExperimentIdentifier],
|
|
50
52
|
wrapper_type: type[WrappedType]) -> dict[int, list[WrappedType]]:
|
|
51
53
|
"""
|
|
52
|
-
Return a dictionary mapping each experiment id to a list of records of the given type that were used in each
|
|
53
|
-
If an experiment didn't use any records of the given type then it will be mapped to an empty list.
|
|
54
|
+
Return a dictionary mapping each experiment id to a list of records of the given type that were used in each
|
|
55
|
+
experiment. If an experiment didn't use any records of the given type then it will be mapped to an empty list.
|
|
54
56
|
|
|
55
57
|
:param context: The current webhook context or a user object to send requests from.
|
|
56
|
-
:param exp_ids:
|
|
58
|
+
:param exp_ids: A list of experiment identifiers.
|
|
57
59
|
:param wrapper_type: The record model wrapper to use, corresponds to which data type we will query for.
|
|
58
|
-
:return:
|
|
60
|
+
:return: A dictionary mapping each experiment id to a list of records of the given type that were used in that
|
|
61
|
+
experiment.
|
|
59
62
|
"""
|
|
60
63
|
if not exp_ids:
|
|
61
64
|
return {}
|
|
@@ -64,20 +67,130 @@ class ExperimentReportUtil:
|
|
|
64
67
|
record_handler = RecordHandler(user)
|
|
65
68
|
data_type_name: str = wrapper_type.get_wrapper_data_type_name()
|
|
66
69
|
|
|
70
|
+
exp_ids: list[int] = AliasUtil.to_notebook_ids(exp_ids)
|
|
67
71
|
rows = ExperimentReportUtil.__get_record_experiment_relation_rows(user, data_type_name, exp_ids=exp_ids)
|
|
68
|
-
record_ids: set[int] = {row[
|
|
72
|
+
record_ids: set[int] = {row["RecordId"] for row in rows}
|
|
69
73
|
records = record_handler.query_models_by_id(wrapper_type, record_ids)
|
|
70
74
|
|
|
71
75
|
id_to_record: dict[int, WrappedType] = RecordHandler.map_by_id(records)
|
|
72
76
|
exp_to_records: dict[int, set[SapioRecord]] = {exp: set() for exp in exp_ids}
|
|
73
77
|
for row in rows:
|
|
74
|
-
record_id: int = row[
|
|
75
|
-
exp_id: int = row[
|
|
78
|
+
record_id: int = row["RecordId"]
|
|
79
|
+
exp_id: int = row[NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME.field_name]
|
|
76
80
|
record = id_to_record[record_id]
|
|
77
81
|
exp_to_records[exp_id].add(record)
|
|
78
82
|
|
|
79
83
|
return {exp: list(records) for exp, records in exp_to_records.items()}
|
|
80
84
|
|
|
85
|
+
@staticmethod
|
|
86
|
+
def get_experiment_options(context: UserIdentifier, experiments: list[ExperimentIdentifier]) \
|
|
87
|
+
-> dict[int, dict[str, str]]:
|
|
88
|
+
"""
|
|
89
|
+
Run a custom report to retrieve the experiment options for all the provided experiments. Effectively a batched
|
|
90
|
+
version of the get_notebook_experiment_options function of ElnManager.
|
|
91
|
+
|
|
92
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
93
|
+
:param experiments: The experiment identifiers to retrieve the experiment options for.
|
|
94
|
+
:return: A dictionary mapping the notebook experiment ID to the options for that experiment.
|
|
95
|
+
"""
|
|
96
|
+
exp_ids: list[int] = AliasUtil.to_notebook_ids(experiments)
|
|
97
|
+
|
|
98
|
+
report_builder = CustomReportBuilder(NotebookExperimentOptionPseudoDef.DATA_TYPE_NAME)
|
|
99
|
+
root = TermBuilder.is_term(NotebookExperimentOptionPseudoDef.DATA_TYPE_NAME,
|
|
100
|
+
NotebookExperimentOptionPseudoDef.EXPERIMENT_ID__FIELD_NAME,
|
|
101
|
+
exp_ids)
|
|
102
|
+
report_builder.set_root_term(root)
|
|
103
|
+
report_builder.add_column(NotebookExperimentOptionPseudoDef.EXPERIMENT_ID__FIELD_NAME)
|
|
104
|
+
report_builder.add_column(NotebookExperimentOptionPseudoDef.OPTION_KEY__FIELD_NAME)
|
|
105
|
+
report_builder.add_column(NotebookExperimentOptionPseudoDef.OPTION_VALUE__FIELD_NAME)
|
|
106
|
+
report = report_builder.build_report_criteria()
|
|
107
|
+
|
|
108
|
+
# Ensure that each experiment appears in the dictionary, even if it has no experiment options.
|
|
109
|
+
options: dict[int, dict[str, str]] = {x: {} for x in exp_ids}
|
|
110
|
+
results: list[dict[str, FieldValue]] = CustomReportUtil.run_custom_report(context, report)
|
|
111
|
+
for row in results:
|
|
112
|
+
exp_id: int = row[NotebookExperimentOptionPseudoDef.EXPERIMENT_ID__FIELD_NAME.field_name]
|
|
113
|
+
key: str = row[NotebookExperimentOptionPseudoDef.OPTION_KEY__FIELD_NAME.field_name]
|
|
114
|
+
value: str = row[NotebookExperimentOptionPseudoDef.OPTION_VALUE__FIELD_NAME.field_name]
|
|
115
|
+
options[exp_id][key] = value
|
|
116
|
+
return options
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
def get_experiment_entry_options(context: UserIdentifier, entries: list[ExperimentEntryIdentifier]) \
|
|
120
|
+
-> dict[int, dict[str, str]]:
|
|
121
|
+
"""
|
|
122
|
+
Run a custom report to retrieve the entry options for all the provided entries. Effectively a batched
|
|
123
|
+
version of the get_experiment_entry_options function of ElnManager.
|
|
124
|
+
|
|
125
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
126
|
+
:param entries: The experiment entry identifiers to retrieve the entry options for.
|
|
127
|
+
:return: A dictionary mapping the entry ID to the options for that entry.
|
|
128
|
+
"""
|
|
129
|
+
entries: list[int] = AliasUtil.to_entry_ids(entries)
|
|
130
|
+
report_builder = CustomReportBuilder(EnbEntryOptionsPseudoDef.DATA_TYPE_NAME)
|
|
131
|
+
root = TermBuilder.is_term(EnbEntryOptionsPseudoDef.DATA_TYPE_NAME,
|
|
132
|
+
EnbEntryOptionsPseudoDef.ENTRY_ID__FIELD_NAME,
|
|
133
|
+
entries)
|
|
134
|
+
report_builder.set_root_term(root)
|
|
135
|
+
report_builder.add_column(EnbEntryOptionsPseudoDef.ENTRY_ID__FIELD_NAME)
|
|
136
|
+
report_builder.add_column(EnbEntryOptionsPseudoDef.ENTRY_OPTION_KEY__FIELD_NAME)
|
|
137
|
+
report_builder.add_column(EnbEntryOptionsPseudoDef.ENTRY_OPTION_VALUE__FIELD_NAME)
|
|
138
|
+
report = report_builder.build_report_criteria()
|
|
139
|
+
|
|
140
|
+
# Ensure that each entry appears in the dictionary, even if it has no entry options.
|
|
141
|
+
options: dict[int, dict[str, str]] = {x: {} for x in entries}
|
|
142
|
+
results: list[dict[str, FieldValue]] = CustomReportUtil.run_custom_report(context, report)
|
|
143
|
+
for row in results:
|
|
144
|
+
entry_id: int = row[EnbEntryOptionsPseudoDef.ENTRY_ID__FIELD_NAME.field_name]
|
|
145
|
+
key: str = row[EnbEntryOptionsPseudoDef.ENTRY_OPTION_KEY__FIELD_NAME.field_name]
|
|
146
|
+
value: str = row[EnbEntryOptionsPseudoDef.ENTRY_OPTION_VALUE__FIELD_NAME.field_name]
|
|
147
|
+
options[entry_id][key] = value
|
|
148
|
+
return options
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def get_experiments_by_name(context: UserIdentifier, name: str) -> list[ElnExperiment]:
|
|
152
|
+
"""
|
|
153
|
+
Run a custom report that retrieves every experiment in the system with a given name.
|
|
154
|
+
|
|
155
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
156
|
+
:param name: The name of the experiment to query for.
|
|
157
|
+
:return: A list of every experiment in the system with a name that matches the input.
|
|
158
|
+
"""
|
|
159
|
+
return ExperimentReportUtil.get_experiments_by_names(context, [name])[name]
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def get_experiments_by_names(context: UserIdentifier, names: list[str]) -> dict[str, list[ElnExperiment]]:
|
|
163
|
+
"""
|
|
164
|
+
Run a custom report that retrieves every experiment in the system with a name from a list of names.
|
|
165
|
+
|
|
166
|
+
:param context: The current webhook context or a user object to send requests from.
|
|
167
|
+
:param names: The names of the experiment to query for.
|
|
168
|
+
:return: A dictionary mapping the experiment name to a list of every experiment in the system with that name.
|
|
169
|
+
"""
|
|
170
|
+
user = AliasUtil.to_sapio_user(context)
|
|
171
|
+
|
|
172
|
+
report_builder = CustomReportBuilder(NotebookExperimentPseudoDef.DATA_TYPE_NAME)
|
|
173
|
+
report_builder.add_column(NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME)
|
|
174
|
+
root = TermBuilder.is_term(NotebookExperimentPseudoDef.DATA_TYPE_NAME,
|
|
175
|
+
NotebookExperimentPseudoDef.EXPERIMENT_NAME__FIELD_NAME,
|
|
176
|
+
names)
|
|
177
|
+
report_builder.set_root_term(root)
|
|
178
|
+
|
|
179
|
+
# Ensure that each entry appears in the dictionary, even if it has no experiments.
|
|
180
|
+
ret_val: dict[str, list[ElnExperiment]] = {x: [] for x in names}
|
|
181
|
+
|
|
182
|
+
exp_ids: list[int] = []
|
|
183
|
+
for row in CustomReportUtil.run_custom_report(user, report_builder.build_report_criteria()):
|
|
184
|
+
exp_ids.append(row[NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME.field_name])
|
|
185
|
+
if not exp_ids:
|
|
186
|
+
return ret_val
|
|
187
|
+
|
|
188
|
+
criteria = ElnExperimentQueryCriteria(notebook_experiment_id_white_list=exp_ids)
|
|
189
|
+
experiments: list[ElnExperiment] = ElnManager(user).get_eln_experiment_by_criteria(criteria)
|
|
190
|
+
for experiment in experiments:
|
|
191
|
+
ret_val.get(experiment.notebook_experiment_name).append(experiment)
|
|
192
|
+
return ret_val
|
|
193
|
+
|
|
81
194
|
@staticmethod
|
|
82
195
|
def __get_record_experiment_relation_rows(user: SapioUser, data_type_name: str, record_ids: list[int] | None = None,
|
|
83
196
|
exp_ids: list[int] | None = None) -> list[dict[str, int]]:
|
|
@@ -88,30 +201,44 @@ class ExperimentReportUtil:
|
|
|
88
201
|
assert (record_ids or exp_ids)
|
|
89
202
|
|
|
90
203
|
if record_ids:
|
|
91
|
-
records_term = TermBuilder.is_term(data_type_name, "
|
|
204
|
+
records_term = TermBuilder.is_term(data_type_name, "RecordId", record_ids)
|
|
92
205
|
else:
|
|
93
206
|
# Get all records of the given type
|
|
94
207
|
records_term = TermBuilder.all_records_term(data_type_name)
|
|
95
208
|
|
|
96
209
|
if exp_ids:
|
|
97
|
-
exp_term = TermBuilder.is_term(
|
|
210
|
+
exp_term = TermBuilder.is_term(NotebookExperimentPseudoDef.DATA_TYPE_NAME,
|
|
211
|
+
NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME,
|
|
212
|
+
exp_ids)
|
|
98
213
|
else:
|
|
99
214
|
# Get all experiments
|
|
100
|
-
exp_term = TermBuilder.gte_term(
|
|
215
|
+
exp_term = TermBuilder.gte_term(NotebookExperimentPseudoDef.DATA_TYPE_NAME,
|
|
216
|
+
NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME,
|
|
217
|
+
0)
|
|
101
218
|
|
|
102
219
|
root_term = TermBuilder.and_terms(records_term, exp_term)
|
|
103
220
|
|
|
104
221
|
# Join records on the experiment entry records that correspond to them.
|
|
105
|
-
records_entry_join = TermBuilder.compare_is_term(
|
|
222
|
+
records_entry_join = TermBuilder.compare_is_term(ExperimentEntryRecordPseudoDef.DATA_TYPE_NAME,
|
|
223
|
+
ExperimentEntryRecordPseudoDef.RECORD_ID__FIELD_NAME,
|
|
224
|
+
data_type_name,
|
|
225
|
+
"RecordId")
|
|
106
226
|
# Join entry records on the experiment entries they are in.
|
|
107
|
-
experiment_entry_enb_entry_join = TermBuilder.compare_is_term(
|
|
227
|
+
experiment_entry_enb_entry_join = TermBuilder.compare_is_term(EnbEntryPseudoDef.DATA_TYPE_NAME,
|
|
228
|
+
EnbEntryPseudoDef.ENTRY_ID__FIELD_NAME,
|
|
229
|
+
ExperimentEntryRecordPseudoDef.DATA_TYPE_NAME,
|
|
230
|
+
ExperimentEntryRecordPseudoDef.ENTRY_ID__FIELD_NAME)
|
|
108
231
|
# Join entries on the experiments they are in.
|
|
109
|
-
enb_entry_experiment_join = TermBuilder.compare_is_term(
|
|
232
|
+
enb_entry_experiment_join = TermBuilder.compare_is_term(NotebookExperimentPseudoDef.DATA_TYPE_NAME,
|
|
233
|
+
NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME,
|
|
234
|
+
EnbEntryPseudoDef.DATA_TYPE_NAME,
|
|
235
|
+
EnbEntryPseudoDef.EXPERIMENT_ID__FIELD_NAME)
|
|
110
236
|
|
|
111
237
|
report_builder = CustomReportBuilder(data_type_name)
|
|
112
238
|
report_builder.set_root_term(root_term)
|
|
113
|
-
report_builder.add_column("
|
|
114
|
-
report_builder.add_column(
|
|
239
|
+
report_builder.add_column("RecordId", FieldType.LONG)
|
|
240
|
+
report_builder.add_column(NotebookExperimentPseudoDef.EXPERIMENT_ID__FIELD_NAME,
|
|
241
|
+
data_type=NotebookExperimentPseudoDef.DATA_TYPE_NAME)
|
|
115
242
|
report_builder.add_join(records_entry_join)
|
|
116
243
|
report_builder.add_join(experiment_entry_enb_entry_join)
|
|
117
244
|
report_builder.add_join(enb_entry_experiment_join)
|
|
@@ -5,8 +5,9 @@ from sapiopylib.rest.User import SapioUser
|
|
|
5
5
|
from sapiopylib.rest.pojo.DataRecord import DataRecord
|
|
6
6
|
from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
|
|
7
7
|
from sapiopylib.rest.pojo.eln.ElnExperiment import ElnExperiment
|
|
8
|
+
from sapiopylib.rest.pojo.eln.ExperimentEntry import ExperimentEntry
|
|
8
9
|
from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
|
|
9
|
-
from sapiopylib.rest.utils.Protocols import ElnExperimentProtocol
|
|
10
|
+
from sapiopylib.rest.utils.Protocols import ElnExperimentProtocol, ElnEntryStep
|
|
10
11
|
from sapiopylib.rest.utils.recordmodel.PyRecordModel import PyRecordModel
|
|
11
12
|
from sapiopylib.rest.utils.recordmodel.RecordModelWrapper import WrappedRecordModel, WrappedType, WrapperField
|
|
12
13
|
|
|
@@ -31,8 +32,10 @@ dictionaries.."""
|
|
|
31
32
|
HasFieldWrappers = type[WrappedType] | WrappedRecordModel
|
|
32
33
|
"""An identifier for classes that have wrapper fields."""
|
|
33
34
|
ExperimentIdentifier = ElnExperimentProtocol | ElnExperiment | int
|
|
34
|
-
"""An ExperimentIdentifier is either an experiment protocol, experiment, or an integer for
|
|
35
|
+
"""An ExperimentIdentifier is either an experiment protocol, experiment, or an integer for the experiment's notebook
|
|
35
36
|
ID."""
|
|
37
|
+
ExperimentEntryIdentifier = ElnEntryStep | ExperimentEntry | int
|
|
38
|
+
"""An ExperimentEntryIdentifier is either an ELN entry step, experiment entry, or an integer for the entry's ID."""
|
|
36
39
|
FieldMap = dict[str, FieldValue]
|
|
37
40
|
"""A field map is simply a dict of data field names to values. The purpose of aliasing this is to help distinguish
|
|
38
41
|
any random dict in a webhook from one which is explicitly used for record fields."""
|
|
@@ -223,7 +226,51 @@ class AliasUtil:
|
|
|
223
226
|
if isinstance(experiment, ElnExperiment):
|
|
224
227
|
return experiment.notebook_experiment_id
|
|
225
228
|
return experiment.get_id()
|
|
226
|
-
|
|
229
|
+
|
|
230
|
+
@staticmethod
|
|
231
|
+
def to_notebook_ids(experiments: list[ExperimentIdentifier]) -> list[int]:
|
|
232
|
+
"""
|
|
233
|
+
Convert a list of objects that identify ELN experiments to their notebook IDs.
|
|
234
|
+
|
|
235
|
+
:return: The list of notebook IDs for the experiment identifiers.
|
|
236
|
+
"""
|
|
237
|
+
notebook_ids: list[int] = []
|
|
238
|
+
for experiment in experiments:
|
|
239
|
+
notebook_ids.append(AliasUtil.to_notebook_id(experiment))
|
|
240
|
+
return notebook_ids
|
|
241
|
+
|
|
242
|
+
@staticmethod
|
|
243
|
+
def to_entry_id(entry: ExperimentEntryIdentifier) -> int:
|
|
244
|
+
"""
|
|
245
|
+
Convert an object that identifies an experiment entry to its entry ID.
|
|
246
|
+
|
|
247
|
+
:return: The entry ID for the entry identifier.
|
|
248
|
+
"""
|
|
249
|
+
if isinstance(entry, int):
|
|
250
|
+
return entry
|
|
251
|
+
elif isinstance(entry, ExperimentEntry):
|
|
252
|
+
return entry.entry_id
|
|
253
|
+
elif isinstance(entry, ElnEntryStep):
|
|
254
|
+
return entry.get_id()
|
|
255
|
+
raise SapioException(f"Unrecognized entry identifier of type {type(entry)}")
|
|
256
|
+
|
|
257
|
+
@staticmethod
|
|
258
|
+
def to_entry_ids(entries: list[ExperimentEntryIdentifier]) -> list[int]:
|
|
259
|
+
"""
|
|
260
|
+
Convert a list of objects that identify experiment entries to their entry IDs.
|
|
261
|
+
|
|
262
|
+
:return: The list of entry IDs for the entry identifiers.
|
|
263
|
+
"""
|
|
264
|
+
entry_ids: list[int] = []
|
|
265
|
+
for entry in entries:
|
|
266
|
+
entry_ids.append(AliasUtil.to_entry_id(entry))
|
|
267
|
+
return entry_ids
|
|
268
|
+
|
|
227
269
|
@staticmethod
|
|
228
270
|
def to_sapio_user(context: UserIdentifier) -> SapioUser:
|
|
271
|
+
"""
|
|
272
|
+
Convert an object that could be either a SapioUser or SapioWebhookContext to just a SapioUser.
|
|
273
|
+
|
|
274
|
+
:return: A SapioUser object.
|
|
275
|
+
"""
|
|
229
276
|
return context if isinstance(context, SapioUser) else context.user
|
|
@@ -2,24 +2,13 @@ from enum import Enum
|
|
|
2
2
|
|
|
3
3
|
from sapiopylib.rest.User import SapioUser
|
|
4
4
|
from sapiopylib.rest.pojo.CustomReport import ReportColumn, CustomReportCriteria
|
|
5
|
-
from sapiopylib.rest.pojo.datatype.FieldDefinition import FieldType
|
|
6
5
|
|
|
6
|
+
from sapiopycommons.customreport.column_builder import ColumnBuilder
|
|
7
7
|
from sapiopycommons.customreport.term_builder import TermBuilder
|
|
8
|
+
from sapiopycommons.datatype.pseudo_data_types import AuditLogPseudoDef
|
|
8
9
|
from sapiopycommons.general.aliases import RecordIdentifier, AliasUtil, UserIdentifier, FieldIdentifier, FieldValue
|
|
9
10
|
from sapiopycommons.general.custom_report_util import CustomReportUtil
|
|
10
11
|
|
|
11
|
-
EVENTTYPE_COLUMN = "EVENTTYPE"
|
|
12
|
-
TIMESTAMP_COLUMN = "TIMESTAMP"
|
|
13
|
-
DATATYPENAME_COLUMN = "DATATYPENAME"
|
|
14
|
-
RECORDID_COLUMN = "RECORDID"
|
|
15
|
-
DESCRIPTION_COLUMN = "DESCRIPTION"
|
|
16
|
-
USERNAME_COLUMN = "USERNAME"
|
|
17
|
-
USERCOMMENT_COLUMN = "USERCOMMENT"
|
|
18
|
-
RECORDNAME_COLUMN = "RECORDNAME"
|
|
19
|
-
DATAFIELDNAME_COLUMN = "DATAFIELDNAME"
|
|
20
|
-
ORIGINALVALUE_COLUMN = "ORIGINALVALUE"
|
|
21
|
-
NEWVALUE_COLUMN = "NEWVALUE"
|
|
22
|
-
|
|
23
12
|
|
|
24
13
|
class EventType(Enum):
|
|
25
14
|
"""An enum to represent the possible event type values with the event type column in the audit log table."""
|
|
@@ -39,7 +28,6 @@ class EventType(Enum):
|
|
|
39
28
|
|
|
40
29
|
|
|
41
30
|
class AuditLogEntry:
|
|
42
|
-
|
|
43
31
|
__event_type: EventType
|
|
44
32
|
__date: int
|
|
45
33
|
__data_type_name: str
|
|
@@ -97,40 +85,41 @@ class AuditLogEntry:
|
|
|
97
85
|
return self.__new_value
|
|
98
86
|
|
|
99
87
|
def __init__(self, report_row: dict[str, FieldValue]):
|
|
100
|
-
self.__event_type = EventType((report_row[
|
|
101
|
-
self.__date = report_row[
|
|
102
|
-
self.__data_type_name = report_row[
|
|
103
|
-
self.__record_id = report_row[
|
|
104
|
-
self.__description = report_row[
|
|
105
|
-
self.__users_login_name = report_row[
|
|
106
|
-
self.__comment = report_row[
|
|
107
|
-
self.__data_record_name = report_row[
|
|
108
|
-
self.__data_field_name = report_row[
|
|
109
|
-
self.__original_value = report_row[
|
|
110
|
-
self.__new_value = report_row[
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
class
|
|
114
|
-
AUDIT_LOG_PSEUDO_DATATYPE: str = "AUDITLOG"
|
|
115
|
-
EVENT_TYPE: ReportColumn = ReportColumn(AUDIT_LOG_PSEUDO_DATATYPE, EVENTTYPE_COLUMN, FieldType.ENUM)
|
|
116
|
-
DATE: ReportColumn = ReportColumn(AUDIT_LOG_PSEUDO_DATATYPE, TIMESTAMP_COLUMN, FieldType.DATE)
|
|
117
|
-
DATA_TYPE_NAME: ReportColumn = ReportColumn(AUDIT_LOG_PSEUDO_DATATYPE, DATATYPENAME_COLUMN, FieldType.STRING)
|
|
118
|
-
RECORD_ID: ReportColumn = ReportColumn(AUDIT_LOG_PSEUDO_DATATYPE, RECORDID_COLUMN, FieldType.LONG)
|
|
119
|
-
DESCRIPTION: ReportColumn = ReportColumn(AUDIT_LOG_PSEUDO_DATATYPE, DESCRIPTION_COLUMN, FieldType.STRING)
|
|
120
|
-
USERS_LOGIN_NAME: ReportColumn = ReportColumn(AUDIT_LOG_PSEUDO_DATATYPE, USERNAME_COLUMN, FieldType.STRING)
|
|
121
|
-
COMMENT: ReportColumn = ReportColumn(AUDIT_LOG_PSEUDO_DATATYPE, USERCOMMENT_COLUMN, FieldType.STRING)
|
|
122
|
-
DATA_RECORD_NAME: ReportColumn = ReportColumn(AUDIT_LOG_PSEUDO_DATATYPE, RECORDNAME_COLUMN, FieldType.STRING)
|
|
123
|
-
DATA_FIELD_NAME: ReportColumn = ReportColumn(AUDIT_LOG_PSEUDO_DATATYPE, DATAFIELDNAME_COLUMN, FieldType.STRING)
|
|
124
|
-
ORIGINAL_VALUE: ReportColumn = ReportColumn(AUDIT_LOG_PSEUDO_DATATYPE, ORIGINALVALUE_COLUMN, FieldType.STRING)
|
|
125
|
-
NEW_VALUE: ReportColumn = ReportColumn(AUDIT_LOG_PSEUDO_DATATYPE, NEWVALUE_COLUMN, FieldType.STRING)
|
|
126
|
-
|
|
127
|
-
AUDIT_LOG_COLUMNS = [EVENT_TYPE, DATE, DATA_TYPE_NAME, RECORD_ID, DESCRIPTION, USERS_LOGIN_NAME, COMMENT,
|
|
128
|
-
DATA_RECORD_NAME, DATA_FIELD_NAME, ORIGINAL_VALUE, NEW_VALUE]
|
|
88
|
+
self.__event_type = EventType((report_row[AuditLogPseudoDef.EVENT_TYPE__FIELD_NAME.field_name]))
|
|
89
|
+
self.__date = report_row[AuditLogPseudoDef.TIME_STAMP__FIELD_NAME.field_name]
|
|
90
|
+
self.__data_type_name = report_row[AuditLogPseudoDef.DATA_TYPE_NAME__FIELD_NAME.field_name]
|
|
91
|
+
self.__record_id = report_row[AuditLogPseudoDef.RECORD_ID__FIELD_NAME.field_name]
|
|
92
|
+
self.__description = report_row[AuditLogPseudoDef.DESCRIPTION__FIELD_NAME.field_name]
|
|
93
|
+
self.__users_login_name = report_row[AuditLogPseudoDef.USER_NAME__FIELD_NAME.field_name]
|
|
94
|
+
self.__comment = report_row[AuditLogPseudoDef.USER_COMMENT__FIELD_NAME.field_name]
|
|
95
|
+
self.__data_record_name = report_row[AuditLogPseudoDef.RECORD_NAME__FIELD_NAME]
|
|
96
|
+
self.__data_field_name = report_row[AuditLogPseudoDef.DATA_FIELD_NAME__FIELD_NAME.field_name]
|
|
97
|
+
self.__original_value = report_row[AuditLogPseudoDef.ORIGINAL_VALUE__FIELD_NAME.field_name]
|
|
98
|
+
self.__new_value = report_row[AuditLogPseudoDef.NEW_VALUE__FIELD_NAME.field_name]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class AuditLogUtil:
|
|
129
102
|
user: SapioUser
|
|
130
103
|
|
|
131
104
|
def __init__(self, context: UserIdentifier):
|
|
132
105
|
self.user = AliasUtil.to_sapio_user(context)
|
|
133
106
|
|
|
107
|
+
@staticmethod
|
|
108
|
+
def report_columns() -> list[ReportColumn]:
|
|
109
|
+
return [
|
|
110
|
+
ColumnBuilder.build_column(AuditLogPseudoDef.DATA_TYPE_NAME, AuditLogPseudoDef.EVENT_TYPE__FIELD_NAME),
|
|
111
|
+
ColumnBuilder.build_column(AuditLogPseudoDef.DATA_TYPE_NAME, AuditLogPseudoDef.TIME_STAMP__FIELD_NAME),
|
|
112
|
+
ColumnBuilder.build_column(AuditLogPseudoDef.DATA_TYPE_NAME, AuditLogPseudoDef.DATA_TYPE_NAME__FIELD_NAME),
|
|
113
|
+
ColumnBuilder.build_column(AuditLogPseudoDef.DATA_TYPE_NAME, AuditLogPseudoDef.RECORD_ID__FIELD_NAME),
|
|
114
|
+
ColumnBuilder.build_column(AuditLogPseudoDef.DATA_TYPE_NAME, AuditLogPseudoDef.DESCRIPTION__FIELD_NAME),
|
|
115
|
+
ColumnBuilder.build_column(AuditLogPseudoDef.DATA_TYPE_NAME, AuditLogPseudoDef.USER_NAME__FIELD_NAME),
|
|
116
|
+
ColumnBuilder.build_column(AuditLogPseudoDef.DATA_TYPE_NAME, AuditLogPseudoDef.USER_COMMENT__FIELD_NAME),
|
|
117
|
+
ColumnBuilder.build_column(AuditLogPseudoDef.DATA_TYPE_NAME, AuditLogPseudoDef.RECORD_NAME__FIELD_NAME),
|
|
118
|
+
ColumnBuilder.build_column(AuditLogPseudoDef.DATA_TYPE_NAME, AuditLogPseudoDef.DATA_FIELD_NAME__FIELD_NAME),
|
|
119
|
+
ColumnBuilder.build_column(AuditLogPseudoDef.DATA_TYPE_NAME, AuditLogPseudoDef.ORIGINAL_VALUE__FIELD_NAME),
|
|
120
|
+
ColumnBuilder.build_column(AuditLogPseudoDef.DATA_TYPE_NAME, AuditLogPseudoDef.NEW_VALUE__FIELD_NAME)
|
|
121
|
+
]
|
|
122
|
+
|
|
134
123
|
@staticmethod
|
|
135
124
|
def create_data_record_audit_log_report(records: list[RecordIdentifier],
|
|
136
125
|
fields: list[FieldIdentifier] | None = None) -> CustomReportCriteria:
|
|
@@ -148,15 +137,19 @@ class AuditLog:
|
|
|
148
137
|
# Build the raw report term querying for any entry with a matching record ID value to the record ID's
|
|
149
138
|
# passed in.
|
|
150
139
|
record_ids = AliasUtil.to_record_ids(records)
|
|
151
|
-
root_term = TermBuilder.is_term(
|
|
140
|
+
root_term = TermBuilder.is_term(AuditLogPseudoDef.DATA_TYPE_NAME,
|
|
141
|
+
AuditLogPseudoDef.RECORD_ID__FIELD_NAME,
|
|
142
|
+
record_ids)
|
|
152
143
|
|
|
153
144
|
# If the user passed in any specific fields, then we should limit the query to those fields.
|
|
154
145
|
if fields:
|
|
155
146
|
fields: list[str] = AliasUtil.to_data_field_names(fields)
|
|
156
|
-
field_term = TermBuilder.is_term(
|
|
147
|
+
field_term = TermBuilder.is_term(AuditLogPseudoDef.DATA_TYPE_NAME,
|
|
148
|
+
AuditLogPseudoDef.DATA_FIELD_NAME__FIELD_NAME,
|
|
149
|
+
fields)
|
|
157
150
|
root_term = TermBuilder.and_terms(root_term, field_term)
|
|
158
151
|
|
|
159
|
-
return CustomReportCriteria(
|
|
152
|
+
return CustomReportCriteria(AuditLogUtil.report_columns(), root_term)
|
|
160
153
|
|
|
161
154
|
def run_data_record_audit_log_report(self, records: list[RecordIdentifier],
|
|
162
155
|
fields: list[FieldIdentifier] | None = None) \
|
|
@@ -172,7 +165,7 @@ class AuditLog:
|
|
|
172
165
|
"""
|
|
173
166
|
fields: list[str] = AliasUtil.to_data_field_names(fields)
|
|
174
167
|
# First, we must build our report criteria for running the Custom Report.
|
|
175
|
-
criteria =
|
|
168
|
+
criteria = AuditLogUtil.create_data_record_audit_log_report(records, fields)
|
|
176
169
|
|
|
177
170
|
# Then we must run the custom report using that criteria.
|
|
178
171
|
raw_report_data: list[dict[str, FieldValue]] = CustomReportUtil.run_custom_report(self.user, criteria)
|
|
@@ -5,8 +5,6 @@ from datetime import datetime
|
|
|
5
5
|
|
|
6
6
|
import pytz
|
|
7
7
|
|
|
8
|
-
from sapiopycommons.general.exceptions import SapioException
|
|
9
|
-
|
|
10
8
|
__timezone = None
|
|
11
9
|
"""The default timezone. Use TimeUtil.set_default_timezone in a global context before making use of TimeUtil."""
|
|
12
10
|
|
|
@@ -96,9 +94,10 @@ class TimeUtil:
|
|
|
96
94
|
return TimeUtil.current_time(timezone).strftime(time_format)
|
|
97
95
|
|
|
98
96
|
@staticmethod
|
|
99
|
-
def millis_to_format(millis: int, time_format: str, timezone: str | int = None) -> str:
|
|
97
|
+
def millis_to_format(millis: int, time_format: str, timezone: str | int = None) -> str | None:
|
|
100
98
|
"""
|
|
101
|
-
Convert the input time in milliseconds to the provided format.
|
|
99
|
+
Convert the input time in milliseconds to the provided format. If None is passed to the millis parameter,
|
|
100
|
+
None will be returned
|
|
102
101
|
|
|
103
102
|
:param millis: The time in milliseconds to convert from.
|
|
104
103
|
:param time_format: The format to display the input time in. Documentation for how the time formatting works
|
|
@@ -107,6 +106,9 @@ class TimeUtil:
|
|
|
107
106
|
timezone variable set by the TimeUtil. A list of valid timezones can be found at
|
|
108
107
|
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. May also accept a UTC offset in seconds.
|
|
109
108
|
"""
|
|
109
|
+
if millis is None:
|
|
110
|
+
return None
|
|
111
|
+
|
|
110
112
|
tz = TimeUtil.__to_tz(timezone)
|
|
111
113
|
return datetime.fromtimestamp(millis / 1000, tz).strftime(time_format)
|
|
112
114
|
|
|
@@ -141,39 +143,3 @@ class TimeUtil:
|
|
|
141
143
|
return True
|
|
142
144
|
except Exception:
|
|
143
145
|
return False
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
class DateRange:
|
|
147
|
-
start: int | None
|
|
148
|
-
end: int | None
|
|
149
|
-
|
|
150
|
-
@staticmethod
|
|
151
|
-
def from_string(value: str | None) -> DateRange:
|
|
152
|
-
"""
|
|
153
|
-
Construct a DateRange object from a string. The field value of date range fields is a string of the form
|
|
154
|
-
<start timestamp>/<end timestamp>.
|
|
155
|
-
|
|
156
|
-
:param value: A date range field value.
|
|
157
|
-
:return: A DateRange object matching the input field value.
|
|
158
|
-
"""
|
|
159
|
-
if not value:
|
|
160
|
-
return DateRange(None, None)
|
|
161
|
-
values: list[str] = value.split("/")
|
|
162
|
-
return DateRange(int(values[0]), int(values[1]))
|
|
163
|
-
|
|
164
|
-
def __init__(self, start: int | None, end: int | None):
|
|
165
|
-
"""
|
|
166
|
-
:param start: The timestamp for the start of the date range.
|
|
167
|
-
:param end: The timestamp for the end of the date rate.
|
|
168
|
-
"""
|
|
169
|
-
if (start and end is None) or (end and start is None):
|
|
170
|
-
raise SapioException("Both start and end values must be present in a date range.")
|
|
171
|
-
if start and end and end < start:
|
|
172
|
-
raise SapioException(f"End timestamp {end} is earlier than the start timestamp {start}.")
|
|
173
|
-
self.start = start
|
|
174
|
-
self.end = end
|
|
175
|
-
|
|
176
|
-
def __str__(self) -> str | None:
|
|
177
|
-
if not self.start and not self.end:
|
|
178
|
-
return None
|
|
179
|
-
return f"{self.start}/{self.end}"
|
|
File without changes
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import io
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
import paramiko
|
|
5
|
+
from paramiko import pkey
|
|
6
|
+
from paramiko.sftp_client import SFTPClient
|
|
7
|
+
from sapiopycommons.general.exceptions import SapioException
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SFTPAuthMethod(Enum):
|
|
11
|
+
"""
|
|
12
|
+
An enum being used to specify connection type to the target server.
|
|
13
|
+
"""
|
|
14
|
+
PASSWORD = 0
|
|
15
|
+
"""Connection is being done via Password."""
|
|
16
|
+
FILEPATH = 1
|
|
17
|
+
"""Connection is being done using a private key file in the codebase."""
|
|
18
|
+
KEY_STRING = 2
|
|
19
|
+
"""Connection is being done using a private key in string form."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SFTPBuilder:
|
|
23
|
+
"""
|
|
24
|
+
A class for making SFTP connections.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def open_sftp(username: str, host: str, port: int, authentication: str,
|
|
29
|
+
connection_type: SFTPAuthMethod = SFTPAuthMethod.PASSWORD) -> SFTPClient:
|
|
30
|
+
"""
|
|
31
|
+
Builds a SFTP client from user input.
|
|
32
|
+
|
|
33
|
+
:param username: The username of the individual trying to connect to the target server.
|
|
34
|
+
:param host: The hostname/IP address of the target server.
|
|
35
|
+
:param port: The port number used to connect to the target server.
|
|
36
|
+
:param authentication: The string used to connect to the target server. This could hold a filepath, a password
|
|
37
|
+
or a private key in string form depending on the connection_type parameter.
|
|
38
|
+
|
|
39
|
+
If authentication is a private key string, they are generally formated like this:
|
|
40
|
+
-----BEGIN OPENSSH PRIVATE KEY-----\n
|
|
41
|
+
asdfh;hjadfh;jghajdg54646+5fasdfadlajklgajd'gj'ajg654564\n
|
|
42
|
+
asdkjfhj;kghj;ahj;wh41234hjadjkhhdsgadshjkdghjshdlsds468\n
|
|
43
|
+
....
|
|
44
|
+
|
|
45
|
+
:param connection_type: This enum is used to specify how the connection to the target server is being made.
|
|
46
|
+
The options are:
|
|
47
|
+
(0) PASSWORD: This means that the authentication parameter contains a password that will be used to connect to the server
|
|
48
|
+
(1) FILEPATH: This means that the authentication parameter contains a filepath leading to a private key file stored in the codebase
|
|
49
|
+
(2) KEY_STRING: This means that the authentication parameter contains the private key in string form
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
client = paramiko.SSHClient()
|
|
54
|
+
client.set_missing_host_key_policy(paramiko.MissingHostKeyPolicy)
|
|
55
|
+
|
|
56
|
+
if connection_type == SFTPAuthMethod.FILEPATH:
|
|
57
|
+
client.connect(host, username=username, port=port, key_filename=authentication)
|
|
58
|
+
return client.open_sftp()
|
|
59
|
+
|
|
60
|
+
if connection_type == SFTPAuthMethod.KEY_STRING:
|
|
61
|
+
private_key: pkey = paramiko.RSAKey.from_private_key(io.StringIO(authentication))
|
|
62
|
+
client.connect(host, username=username, port=port, pkey=private_key)
|
|
63
|
+
return client.open_sftp()
|
|
64
|
+
|
|
65
|
+
if connection_type == SFTPAuthMethod.PASSWORD:
|
|
66
|
+
client.connect(host, username=username, password=authentication, port=port)
|
|
67
|
+
return client.open_sftp()
|
|
68
|
+
|
|
69
|
+
raise SapioException("The SFTPAuthMethod enumerator was not properly specified.")
|
{sapiopycommons-2024.10.14a341.dist-info → sapiopycommons-2024.10.25a345.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: sapiopycommons
|
|
3
|
-
Version: 2024.10.
|
|
3
|
+
Version: 2024.10.25a345
|
|
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>
|
|
@@ -2,17 +2,18 @@ sapiopycommons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
2
2
|
sapiopycommons/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
sapiopycommons/callbacks/callback_util.py,sha256=nb6cXK8yFq96gtG0Z2NiK-qdNaRh88bavUH-ZoBjh18,67953
|
|
4
4
|
sapiopycommons/chem/IndigoMolecules.py,sha256=3f-aig3AJkKJhRmhlQ0cI-5G8oeaQk_3foJTDZCvoko,2040
|
|
5
|
-
sapiopycommons/chem/Molecules.py,sha256=
|
|
5
|
+
sapiopycommons/chem/Molecules.py,sha256=SQKnqdZnhYj_6HGtEZmE_1DormonRR1-nBAQ__z4gms,11485
|
|
6
6
|
sapiopycommons/chem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
sapiopycommons/customreport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
sapiopycommons/customreport/column_builder.py,sha256=
|
|
8
|
+
sapiopycommons/customreport/column_builder.py,sha256=0RO53e9rKPZ07C--KcepN6_tpRw_FxF3O9vdG0ilKG8,3014
|
|
9
9
|
sapiopycommons/customreport/custom_report_builder.py,sha256=o2O89OrWPm0OYS8Ux6EKZTg6hcUzfz3ZxAgnzJg1wEw,6601
|
|
10
10
|
sapiopycommons/customreport/term_builder.py,sha256=oVsr7iFPnug2TrZUCcAMhyps-b62kDodPcBxyQeneUY,16763
|
|
11
11
|
sapiopycommons/datatype/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
12
|
sapiopycommons/datatype/attachment_util.py,sha256=_l2swuP8noIGAl4bwzBUEhr6YlN_OVZl3-gi1XqFHYA,3364
|
|
13
|
+
sapiopycommons/datatype/pseudo_data_types.py,sha256=Fe75Rnq5evyeJM1nC0sLkLGKAC74g2-GEeTdMeId80o,27649
|
|
13
14
|
sapiopycommons/eln/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
sapiopycommons/eln/experiment_handler.py,sha256=
|
|
15
|
-
sapiopycommons/eln/experiment_report_util.py,sha256=
|
|
15
|
+
sapiopycommons/eln/experiment_handler.py,sha256=jtJC-ZU8fDud5UD8IVLJZVOqgm8QMkyA1kZZujf3fcg,68454
|
|
16
|
+
sapiopycommons/eln/experiment_report_util.py,sha256=nt-zs6ag1oBAN3NSHcdLBtY1EWxsiI10NfW15xTnn4g,14522
|
|
16
17
|
sapiopycommons/eln/plate_designer.py,sha256=FYJfhhNq8hdfuXgDYOYHy6g0m2zNwQXZWF_MTPzElDg,7184
|
|
17
18
|
sapiopycommons/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
19
|
sapiopycommons/files/complex_data_loader.py,sha256=T39veNhvYl6j_uZjIIJ8Mk5Aa7otR5RB-g8XlAdkksA,1421
|
|
@@ -26,14 +27,14 @@ sapiopycommons/flowcyto/flow_cyto.py,sha256=YlkKJR_zEHYRuNW0bnTqlTyZeXs0lOaeSCfG
|
|
|
26
27
|
sapiopycommons/flowcyto/flowcyto_data.py,sha256=mYKFuLbtpJ-EsQxLGtu4tNHVlygTxKixgJxJqD68F58,2596
|
|
27
28
|
sapiopycommons/general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
29
|
sapiopycommons/general/accession_service.py,sha256=HYgyOsH_UaoRnoury-c2yTW8SeG4OtjLemdpCzoV4R8,13484
|
|
29
|
-
sapiopycommons/general/aliases.py,sha256=
|
|
30
|
-
sapiopycommons/general/audit_log.py,sha256=
|
|
30
|
+
sapiopycommons/general/aliases.py,sha256=eyW5VSK8q5xCjWv9fwd3gKEmK_7pboVLa4DPQpJXpe4,12264
|
|
31
|
+
sapiopycommons/general/audit_log.py,sha256=KQI0AGgN9WLwKqnHE4Tm0xeBCfpVBf8rIQ2HFmnyFGI,8956
|
|
31
32
|
sapiopycommons/general/custom_report_util.py,sha256=BGu9Ki0wn3m4Nk-LKM6inDSfe8ULUSG9d-HJJNOTtGc,15653
|
|
32
33
|
sapiopycommons/general/exceptions.py,sha256=GY7fe0qOgoy4kQVn_Pn3tdzHsJZyNIpa6VCChg6tzuM,1813
|
|
33
34
|
sapiopycommons/general/popup_util.py,sha256=L-4qpTemSZdlD6_6oEsDYIzLOCiZgDK6wC6DqUwzOYA,31925
|
|
34
35
|
sapiopycommons/general/sapio_links.py,sha256=o9Z-8y2rz6AI0Cy6tq58ElPge9RBnisGc9NyccbaJxs,2610
|
|
35
36
|
sapiopycommons/general/storage_util.py,sha256=ovmK_jN7v09BoX07XxwShpBUC5WYQOM7dbKV_VeLXJU,8892
|
|
36
|
-
sapiopycommons/general/time_util.py,sha256=
|
|
37
|
+
sapiopycommons/general/time_util.py,sha256=jUAWmQLNcLHZa4UYB4ht_I3d6uoi63VxYdo7T80Ydw0,7458
|
|
37
38
|
sapiopycommons/multimodal/multimodal.py,sha256=A1QsC8QTPmgZyPr7KtMbPRedn2Ie4WIErodUvQ9otgU,6724
|
|
38
39
|
sapiopycommons/multimodal/multimodal_data.py,sha256=t-0uY4cVgm88uXaSOL4ZeB6zmdHufowXuLFlMk61wFg,15087
|
|
39
40
|
sapiopycommons/processtracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -43,10 +44,12 @@ sapiopycommons/recordmodel/record_handler.py,sha256=Uxjrq6f_cWFbqi7KRLySdOvmQGtb
|
|
|
43
44
|
sapiopycommons/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
45
|
sapiopycommons/rules/eln_rule_handler.py,sha256=JYzDA_14D2nLnlqwbpIxVOrfKWzbOS27AYf4TQfGr4Q,10469
|
|
45
46
|
sapiopycommons/rules/on_save_rule_handler.py,sha256=Rkqvph20RbNq6m-RF4fbvCP-YfD2CZYBM2iTr3nl0eY,10236
|
|
47
|
+
sapiopycommons/sftpconnect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
+
sapiopycommons/sftpconnect/sftp_builder.py,sha256=eKYMiyBc10DNTfbeidQUcfZgFTwhu5ZU-nNJMCK_eos,3014
|
|
46
49
|
sapiopycommons/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
47
50
|
sapiopycommons/webhook/webhook_handlers.py,sha256=JTquLBln49L1pJ9txJ4oc4Hpzy9kYtMKs0m4SLaFx78,18363
|
|
48
51
|
sapiopycommons/webhook/webservice_handlers.py,sha256=1J56zFI0pWl5MHoNTznvcZumITXgAHJMluj8-2BqYEw,3315
|
|
49
|
-
sapiopycommons-2024.10.
|
|
50
|
-
sapiopycommons-2024.10.
|
|
51
|
-
sapiopycommons-2024.10.
|
|
52
|
-
sapiopycommons-2024.10.
|
|
52
|
+
sapiopycommons-2024.10.25a345.dist-info/METADATA,sha256=SSy4mFChmb1yl1geoOfc81CC9PW0uxqulO8eHqyRDZA,3177
|
|
53
|
+
sapiopycommons-2024.10.25a345.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
54
|
+
sapiopycommons-2024.10.25a345.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
|
|
55
|
+
sapiopycommons-2024.10.25a345.dist-info/RECORD,,
|
|
File without changes
|
{sapiopycommons-2024.10.14a341.dist-info → sapiopycommons-2024.10.25a345.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|