sapiopycommons 2024.3.18a156__py3-none-any.whl → 2025.1.17a402__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sapiopycommons might be problematic. Click here for more details.
- sapiopycommons/callbacks/__init__.py +0 -0
- sapiopycommons/callbacks/callback_util.py +2041 -0
- sapiopycommons/callbacks/field_builder.py +545 -0
- sapiopycommons/chem/IndigoMolecules.py +52 -5
- sapiopycommons/chem/Molecules.py +114 -30
- sapiopycommons/customreport/__init__.py +0 -0
- sapiopycommons/customreport/column_builder.py +60 -0
- sapiopycommons/customreport/custom_report_builder.py +137 -0
- sapiopycommons/customreport/term_builder.py +315 -0
- sapiopycommons/datatype/attachment_util.py +17 -15
- sapiopycommons/datatype/data_fields.py +61 -0
- sapiopycommons/datatype/pseudo_data_types.py +440 -0
- sapiopycommons/eln/experiment_handler.py +390 -90
- sapiopycommons/eln/experiment_report_util.py +649 -0
- sapiopycommons/eln/plate_designer.py +152 -0
- sapiopycommons/files/complex_data_loader.py +31 -0
- sapiopycommons/files/file_bridge.py +153 -25
- sapiopycommons/files/file_bridge_handler.py +555 -0
- sapiopycommons/files/file_data_handler.py +633 -0
- sapiopycommons/files/file_util.py +270 -158
- sapiopycommons/files/file_validator.py +569 -0
- sapiopycommons/files/file_writer.py +377 -0
- sapiopycommons/flowcyto/flow_cyto.py +77 -0
- sapiopycommons/flowcyto/flowcyto_data.py +75 -0
- sapiopycommons/general/accession_service.py +375 -0
- sapiopycommons/general/aliases.py +259 -18
- sapiopycommons/general/audit_log.py +185 -0
- sapiopycommons/general/custom_report_util.py +252 -31
- sapiopycommons/general/directive_util.py +86 -0
- sapiopycommons/general/exceptions.py +69 -7
- sapiopycommons/general/popup_util.py +85 -18
- sapiopycommons/general/sapio_links.py +50 -0
- sapiopycommons/general/storage_util.py +148 -0
- sapiopycommons/general/time_util.py +97 -7
- sapiopycommons/multimodal/multimodal.py +146 -0
- sapiopycommons/multimodal/multimodal_data.py +490 -0
- sapiopycommons/processtracking/__init__.py +0 -0
- sapiopycommons/processtracking/custom_workflow_handler.py +406 -0
- sapiopycommons/processtracking/endpoints.py +192 -0
- sapiopycommons/recordmodel/record_handler.py +653 -149
- sapiopycommons/rules/eln_rule_handler.py +89 -8
- sapiopycommons/rules/on_save_rule_handler.py +89 -12
- sapiopycommons/sftpconnect/__init__.py +0 -0
- sapiopycommons/sftpconnect/sftp_builder.py +70 -0
- sapiopycommons/webhook/webhook_context.py +39 -0
- sapiopycommons/webhook/webhook_handlers.py +617 -69
- sapiopycommons/webhook/webservice_handlers.py +317 -0
- {sapiopycommons-2024.3.18a156.dist-info → sapiopycommons-2025.1.17a402.dist-info}/METADATA +5 -4
- sapiopycommons-2025.1.17a402.dist-info/RECORD +60 -0
- {sapiopycommons-2024.3.18a156.dist-info → sapiopycommons-2025.1.17a402.dist-info}/WHEEL +1 -1
- sapiopycommons-2024.3.18a156.dist-info/RECORD +0 -28
- {sapiopycommons-2024.3.18a156.dist-info → sapiopycommons-2025.1.17a402.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
from sapiopylib.rest.pojo.DateRange import DateRange
|
|
2
|
+
from sapiopylib.rest.pojo.Sort import SortDirection
|
|
3
|
+
from sapiopylib.rest.pojo.datatype.FieldDefinition import VeloxStringFieldDefinition, SapioStringFormat, \
|
|
4
|
+
FieldValidator, VeloxAccessionFieldDefinition, VeloxBooleanFieldDefinition, VeloxDateFieldDefinition, \
|
|
5
|
+
VeloxDateRangeFieldDefinition, VeloxDoubleFieldDefinition, VeloxEnumFieldDefinition, VeloxIntegerFieldDefinition, \
|
|
6
|
+
VeloxLongFieldDefinition, VeloxPickListFieldDefinition, VeloxSelectionFieldDefinition, VeloxShortFieldDefinition, \
|
|
7
|
+
SapioDoubleFormat, ListMode
|
|
8
|
+
|
|
9
|
+
from sapiopycommons.general.aliases import FieldIdentifier, DataTypeIdentifier, AliasUtil
|
|
10
|
+
from sapiopycommons.general.exceptions import SapioException
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AnyFieldInfo:
|
|
14
|
+
"""
|
|
15
|
+
Field definition information that can apply to any created field def. This excludes various members of
|
|
16
|
+
AbstractVeloxFieldDefinition such as system_field that wouldn't make sense to edit for a created field def
|
|
17
|
+
used in a temp data type.
|
|
18
|
+
"""
|
|
19
|
+
editable: bool
|
|
20
|
+
required: bool
|
|
21
|
+
visible: bool
|
|
22
|
+
description: str | None
|
|
23
|
+
sort_direction: SortDirection | None
|
|
24
|
+
sort_order: int | None
|
|
25
|
+
default_table_column_width: int | None
|
|
26
|
+
|
|
27
|
+
def __init__(self, editable: bool = True, required: bool = False, visible: bool = True,
|
|
28
|
+
description: str | None = None, sort_direction: SortDirection | None = None,
|
|
29
|
+
sort_order: int | None = None, default_table_column_width: int | None = None):
|
|
30
|
+
"""
|
|
31
|
+
:param editable: Whether this field can be edited by the user.
|
|
32
|
+
:param required: Whether input is required for this field before the user can submit the dialog that it is a
|
|
33
|
+
part of.
|
|
34
|
+
:param visible: Whether this field is visible to the user.
|
|
35
|
+
:param description: The description of this field that will appear when the user hovers the cursor over it.
|
|
36
|
+
:param sort_direction: The default sort direction of this field in tables. The user may still change the column
|
|
37
|
+
sorting.
|
|
38
|
+
:param sort_order: The default sort order of this field in tables. The user may still change the column sorting.
|
|
39
|
+
:param default_table_column_width: The width in pixels that this field's column will appear with in tables by
|
|
40
|
+
default. The user may still change the column width.
|
|
41
|
+
"""
|
|
42
|
+
self.editable = editable
|
|
43
|
+
self.required = required
|
|
44
|
+
self.visible = visible
|
|
45
|
+
self.description = description
|
|
46
|
+
self.sort_direction = sort_direction
|
|
47
|
+
self.sort_order = sort_order
|
|
48
|
+
self.default_table_column_width = default_table_column_width
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class FieldBuilder:
|
|
52
|
+
"""
|
|
53
|
+
A class used for building fields for temporary data types. Currently designed to only create fields which can
|
|
54
|
+
be used in client callbacks that use temp data types. Some fields will not be displayed in temp data types,
|
|
55
|
+
including but not limited to: action fields, child/parent/side link fields.
|
|
56
|
+
"""
|
|
57
|
+
data_type: str
|
|
58
|
+
|
|
59
|
+
def __init__(self, data_type: DataTypeIdentifier = "Default"):
|
|
60
|
+
"""
|
|
61
|
+
:param data_type: The data type name that fields created from this builder will use as their data type.
|
|
62
|
+
"""
|
|
63
|
+
self.data_type = AliasUtil.to_data_type_name(data_type)
|
|
64
|
+
|
|
65
|
+
def accession_field(self, field_name: FieldIdentifier, sequence_key: str, prefix: str | None = None,
|
|
66
|
+
suffix: str | None = None, number_of_digits: int = 8, starting_value: int = 1,
|
|
67
|
+
link_out: dict[str, str] | None = None, abstract_info: AnyFieldInfo | None = None, *,
|
|
68
|
+
data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
|
|
69
|
+
-> VeloxAccessionFieldDefinition:
|
|
70
|
+
"""
|
|
71
|
+
Create an accession field definition. Accession fields are text fields which generate a unique value
|
|
72
|
+
that has not been used before, incrementing from the most recently generated value. This can be used when a
|
|
73
|
+
guaranteed unique ID is necessary.
|
|
74
|
+
|
|
75
|
+
:param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
|
|
76
|
+
the display name.
|
|
77
|
+
:param sequence_key: The key of the accession sequence that this field will use.
|
|
78
|
+
:param prefix: The text that should appear before the numerical value.
|
|
79
|
+
:param suffix: The text that should appear after the numerical value.
|
|
80
|
+
:param number_of_digits: The number of digits in the numerical value.
|
|
81
|
+
:param starting_value: The starting value of the numerical value.
|
|
82
|
+
:param link_out: A dictionary where the keys are the display names of the links and the values are the links to
|
|
83
|
+
navigate the user to if this field is clicked on. If the values contain the string "[[LINK_OUT]]" then that
|
|
84
|
+
macro will be replaced with the value of the string field when it is clicked. The display name is only
|
|
85
|
+
important if there is more than one link in the dictionary, in which case all available link out locations
|
|
86
|
+
will display in a dialog with their display names for the user to select. If a non-empty dictionary is
|
|
87
|
+
provided, this becomes a link-out field.
|
|
88
|
+
If the value is not determined to have the appearance of a URL (e.g. it doesn't start with https://), then
|
|
89
|
+
the system will prepend "https://<app-url>/veloxClient/" to the start of the URL. This allows you to create
|
|
90
|
+
links to other locations in the system without needing to know what the app URL is. For example, if you have
|
|
91
|
+
a link out string field that contains a record ID to a Sample, you could set the link value to
|
|
92
|
+
"#dataType=Sample;recordId=[[LINK_OUT]];view=dataRecord" and the client will, seeing that this is not a
|
|
93
|
+
normal looking URL, route the user to
|
|
94
|
+
https://<app-url>/veloxClient/#dataType=Sample;recordId=[[LINK_OUT]];view=dataRecord, which is the form
|
|
95
|
+
page of the Sample corresponding to the record ID recorded by the field value.
|
|
96
|
+
:param abstract_info: The abstract field info for this field, such as whether it is editable or required.
|
|
97
|
+
:param data_type_name: An optional override for the data type name used for this field. If not provided, then
|
|
98
|
+
the data field name of the FieldBuilder is used.
|
|
99
|
+
:param display_name: An optional override for the display name of this field. If not provided, then the data
|
|
100
|
+
field name doubles as the display name.
|
|
101
|
+
:return: An accession field definition with settings from the input criteria.
|
|
102
|
+
"""
|
|
103
|
+
data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
|
|
104
|
+
field_name: str = AliasUtil.to_data_field_name(field_name)
|
|
105
|
+
if abstract_info is None:
|
|
106
|
+
abstract_info = AnyFieldInfo()
|
|
107
|
+
if not display_name:
|
|
108
|
+
display_name = field_name
|
|
109
|
+
# Accession fields lock editable to false.
|
|
110
|
+
abstract_info.editable = False
|
|
111
|
+
link_out, link_out_url = self._convert_link_out(link_out)
|
|
112
|
+
# The unique parameter has no effect, so just always set it to false.
|
|
113
|
+
return VeloxAccessionFieldDefinition(data_type_name, field_name, display_name, sequence_key, prefix, suffix,
|
|
114
|
+
number_of_digits, False, starting_value, link_out, link_out_url,
|
|
115
|
+
**abstract_info.__dict__)
|
|
116
|
+
|
|
117
|
+
def boolean_field(self, field_name: FieldIdentifier, default_value: bool | None = False,
|
|
118
|
+
abstract_info: AnyFieldInfo | None = None, *, data_type_name: DataTypeIdentifier | None = None,
|
|
119
|
+
display_name: str | None = None) -> VeloxBooleanFieldDefinition:
|
|
120
|
+
"""
|
|
121
|
+
Create a boolean field definition. Boolean fields are fields which may have a value of true or false.
|
|
122
|
+
They appear as a checkbox in the UI. Boolean fields may also have a value of null if the field is not required.
|
|
123
|
+
|
|
124
|
+
:param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
|
|
125
|
+
the display name.
|
|
126
|
+
:param default_value: The default value to display in this field before the user edits it.
|
|
127
|
+
:param abstract_info: The abstract field info for this field, such as whether it is editable or required.
|
|
128
|
+
:param data_type_name: An optional override for the data type name used for this field. If not provided, then
|
|
129
|
+
the data field name of the FieldBuilder is used.
|
|
130
|
+
:param display_name: An optional override for the display name of this field. If not provided, then the data
|
|
131
|
+
field name doubles as the display name.
|
|
132
|
+
:return: A boolean field definition with settings from the input criteria.
|
|
133
|
+
"""
|
|
134
|
+
data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
|
|
135
|
+
field_name: str = AliasUtil.to_data_field_name(field_name)
|
|
136
|
+
if abstract_info is None:
|
|
137
|
+
abstract_info = AnyFieldInfo()
|
|
138
|
+
# Boolean fields assume that they are required if no abstract info is provided.
|
|
139
|
+
abstract_info.required = True
|
|
140
|
+
if not display_name:
|
|
141
|
+
display_name = field_name
|
|
142
|
+
return VeloxBooleanFieldDefinition(data_type_name, field_name, display_name, default_value,
|
|
143
|
+
**abstract_info.__dict__)
|
|
144
|
+
|
|
145
|
+
def date_field(self, field_name: FieldIdentifier, default_value: int | None = None, date_time_format: str = "MMM dd, yyyy",
|
|
146
|
+
static_date: bool = False, abstract_info: AnyFieldInfo | None = None, *,
|
|
147
|
+
data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
|
|
148
|
+
-> VeloxDateFieldDefinition:
|
|
149
|
+
"""
|
|
150
|
+
Create a date field definition. Date fields store date and time information as an integer
|
|
151
|
+
representing the number of milliseconds since the unix epoch. This timestamp is then displayed to users in a
|
|
152
|
+
human-readable format.
|
|
153
|
+
|
|
154
|
+
:param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
|
|
155
|
+
the display name.
|
|
156
|
+
:param default_value: The default value to display in this field before the user edits it.
|
|
157
|
+
:param date_time_format: The format that this date field should appear in. The date format is Java-style.
|
|
158
|
+
See https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/text/SimpleDateFormat.html for more
|
|
159
|
+
details.
|
|
160
|
+
:param static_date: If true, this date displays in UTC regardless of the user's timezone. If false, this date
|
|
161
|
+
displays the time in the user's timezone.
|
|
162
|
+
:param abstract_info: The abstract field info for this field, such as whether it is editable or required.
|
|
163
|
+
:param data_type_name: An optional override for the data type name used for this field. If not provided, then
|
|
164
|
+
the data field name of the FieldBuilder is used.
|
|
165
|
+
:param display_name: An optional override for the display name of this field. If not provided, then the data
|
|
166
|
+
field name doubles as the display name.
|
|
167
|
+
:return: A date field definition with settings from the input criteria.
|
|
168
|
+
"""
|
|
169
|
+
data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
|
|
170
|
+
field_name: str = AliasUtil.to_data_field_name(field_name)
|
|
171
|
+
if abstract_info is None:
|
|
172
|
+
abstract_info = AnyFieldInfo()
|
|
173
|
+
if not display_name:
|
|
174
|
+
display_name = field_name
|
|
175
|
+
return VeloxDateFieldDefinition(data_type_name, field_name, display_name, date_time_format, default_value,
|
|
176
|
+
static_date, **abstract_info.__dict__)
|
|
177
|
+
|
|
178
|
+
def date_range_field(self, field_name: FieldIdentifier, default_value: str | DateRange | None = None,
|
|
179
|
+
date_time_format: str = "MMM dd, yyyy", static_date: bool = False,
|
|
180
|
+
abstract_info: AnyFieldInfo | None = None, *, data_type_name: DataTypeIdentifier | None = None,
|
|
181
|
+
display_name: str | None = None) -> VeloxDateRangeFieldDefinition:
|
|
182
|
+
"""
|
|
183
|
+
Create a date range field definition. Date range fields store two unix epoch timestamps as a string of the
|
|
184
|
+
format "[start timestamp]/[end timestamp]". This string is then displayed to users in a human-readable
|
|
185
|
+
format as two dates.
|
|
186
|
+
|
|
187
|
+
See the DateRange class from sapiopylib for an easy means of converting to/from millisecond timestamps and a
|
|
188
|
+
date range field's string value.
|
|
189
|
+
|
|
190
|
+
:param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
|
|
191
|
+
the display name.
|
|
192
|
+
:param default_value: The default value to display in this field before the user edits it.
|
|
193
|
+
:param date_time_format: The format that this date field should appear in. The date format is Java-style.
|
|
194
|
+
See https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/text/SimpleDateFormat.html for more
|
|
195
|
+
details.
|
|
196
|
+
:param static_date: If true, these dates display in UTC regardless of the user's timezone. If false, they
|
|
197
|
+
display the time in the user's timezone.
|
|
198
|
+
:param abstract_info: The abstract field info for this field, such as whether it is editable or required.
|
|
199
|
+
:param data_type_name: An optional override for the data type name used for this field. If not provided, then
|
|
200
|
+
the data field name of the FieldBuilder is used.
|
|
201
|
+
:param display_name: An optional override for the display name of this field. If not provided, then the data
|
|
202
|
+
field name doubles as the display name.
|
|
203
|
+
:return: A date range field definition with settings from the input criteria.
|
|
204
|
+
"""
|
|
205
|
+
data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
|
|
206
|
+
field_name: str = AliasUtil.to_data_field_name(field_name)
|
|
207
|
+
if abstract_info is None:
|
|
208
|
+
abstract_info = AnyFieldInfo()
|
|
209
|
+
if not display_name:
|
|
210
|
+
display_name = field_name
|
|
211
|
+
if isinstance(default_value, DateRange):
|
|
212
|
+
default_value = str(default_value)
|
|
213
|
+
return VeloxDateRangeFieldDefinition(data_type_name, field_name, display_name, date_time_format, static_date,
|
|
214
|
+
default_value, **abstract_info.__dict__)
|
|
215
|
+
|
|
216
|
+
def double_field(self, field_name: FieldIdentifier, default_value: float | None = None, min_value: float = -10.**120,
|
|
217
|
+
max_value: float = 10.**120, precision: int = 1, double_format: SapioDoubleFormat | None = None,
|
|
218
|
+
abstract_info: AnyFieldInfo | None = None, *, data_type_name: DataTypeIdentifier | None = None,
|
|
219
|
+
display_name: str | None = None) -> VeloxDoubleFieldDefinition:
|
|
220
|
+
"""
|
|
221
|
+
Create a double field definition. Double fields represent decimal numerical values. They can also
|
|
222
|
+
be configured to represent currencies or percentages by changing the format parameter.
|
|
223
|
+
|
|
224
|
+
:param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
|
|
225
|
+
the display name.
|
|
226
|
+
:param default_value: The default value to display in this field before the user edits it.
|
|
227
|
+
:param min_value: The minimum allowed value in this field.
|
|
228
|
+
:param max_value: The maximum allowed value in this field.
|
|
229
|
+
:param precision: The number of digits past the decimal point to display for this field.
|
|
230
|
+
:param double_format: The format that this double field is displayed in. If no value is provided, the field
|
|
231
|
+
display as a normal numerical value.
|
|
232
|
+
:param abstract_info: The abstract field info for this field, such as whether it is editable or required.
|
|
233
|
+
:param data_type_name: An optional override for the data type name used for this field. If not provided, then
|
|
234
|
+
the data field name of the FieldBuilder is used.
|
|
235
|
+
:param display_name: An optional override for the display name of this field. If not provided, then the data
|
|
236
|
+
field name doubles as the display name.
|
|
237
|
+
:return: A double field definition with settings from the input criteria.
|
|
238
|
+
"""
|
|
239
|
+
data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
|
|
240
|
+
field_name: str = AliasUtil.to_data_field_name(field_name)
|
|
241
|
+
if abstract_info is None:
|
|
242
|
+
abstract_info = AnyFieldInfo()
|
|
243
|
+
if not display_name:
|
|
244
|
+
display_name = field_name
|
|
245
|
+
return VeloxDoubleFieldDefinition(data_type_name, field_name, display_name, min_value, max_value, default_value,
|
|
246
|
+
precision, double_format, **abstract_info.__dict__)
|
|
247
|
+
|
|
248
|
+
def enum_field(self, field_name: FieldIdentifier, options: list[str], default_value: int | None = None,
|
|
249
|
+
abstract_info: AnyFieldInfo | None = None, *, data_type_name: DataTypeIdentifier | None = None,
|
|
250
|
+
display_name: str | None = None) -> VeloxEnumFieldDefinition:
|
|
251
|
+
"""
|
|
252
|
+
Create an enum field definition. Enum fields allow for the display of a list of options as a field
|
|
253
|
+
definition without the need of a backing method in the system like pick list or selection lists do. Note that
|
|
254
|
+
when setting the default value or reading the return value of an enum field, the value is an integer
|
|
255
|
+
representing the index of the values list, as opposed to a string for the exact value chosen.
|
|
256
|
+
|
|
257
|
+
Note that this field is mainly here for completeness' sake. You can now use the static_values parameter of a
|
|
258
|
+
selection list to achieve the same thing without needing to worry about the field value being the index of the
|
|
259
|
+
options.
|
|
260
|
+
|
|
261
|
+
:param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
|
|
262
|
+
the display name.
|
|
263
|
+
:param options: The list of strings that the user may select from for this enum field. Note that when a client
|
|
264
|
+
callback returns the value from an enum field, it will be the index of the option in the options list that
|
|
265
|
+
the user chose.
|
|
266
|
+
:param default_value: The default value to display in this field before the user edits it. This is the index of
|
|
267
|
+
the option from the options list that you wish to appear as the default.
|
|
268
|
+
:param abstract_info: The abstract field info for this field, such as whether it is editable or required.
|
|
269
|
+
:param data_type_name: An optional override for the data type name used for this field. If not provided, then
|
|
270
|
+
the data field name of the FieldBuilder is used.
|
|
271
|
+
:param display_name: An optional override for the display name of this field. If not provided, then the data
|
|
272
|
+
field name doubles as the display name.
|
|
273
|
+
:return: An enum field definition with settings from the input criteria.
|
|
274
|
+
"""
|
|
275
|
+
data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
|
|
276
|
+
field_name: str = AliasUtil.to_data_field_name(field_name)
|
|
277
|
+
if abstract_info is None:
|
|
278
|
+
abstract_info = AnyFieldInfo()
|
|
279
|
+
if not display_name:
|
|
280
|
+
display_name = field_name
|
|
281
|
+
return VeloxEnumFieldDefinition(data_type_name, field_name, display_name, default_value, options,
|
|
282
|
+
**abstract_info.__dict__)
|
|
283
|
+
|
|
284
|
+
def int_field(self, field_name: FieldIdentifier, default_value: int | None = None, min_value: int = -2**31,
|
|
285
|
+
max_value: int = 2**31 - 1, unique_value: bool = False, abstract_info: AnyFieldInfo | None = None, *,
|
|
286
|
+
data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
|
|
287
|
+
-> VeloxIntegerFieldDefinition:
|
|
288
|
+
"""
|
|
289
|
+
Create an integer field definition. Integer fields are 32-bit whole numbers.
|
|
290
|
+
|
|
291
|
+
:param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
|
|
292
|
+
the display name.
|
|
293
|
+
:param default_value: The default value to display in this field before the user edits it.
|
|
294
|
+
:param min_value: The minimum allowed value in this field.
|
|
295
|
+
:param max_value: The maximum allowed value in this field.
|
|
296
|
+
:param unique_value: Whether the value in this field must be unique across all temp records in the dialog.
|
|
297
|
+
:param abstract_info: The abstract field info for this field, such as whether it is editable or required.
|
|
298
|
+
:param data_type_name: An optional override for the data type name used for this field. If not provided, then
|
|
299
|
+
the data field name of the FieldBuilder is used.
|
|
300
|
+
:param display_name: An optional override for the display name of this field. If not provided, then the data
|
|
301
|
+
field name doubles as the display name.
|
|
302
|
+
:return: An integer field definition with settings from the input criteria.
|
|
303
|
+
"""
|
|
304
|
+
data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
|
|
305
|
+
field_name: str = AliasUtil.to_data_field_name(field_name)
|
|
306
|
+
if abstract_info is None:
|
|
307
|
+
abstract_info = AnyFieldInfo()
|
|
308
|
+
if not display_name:
|
|
309
|
+
display_name = field_name
|
|
310
|
+
return VeloxIntegerFieldDefinition(data_type_name, field_name, display_name, min_value, max_value,
|
|
311
|
+
default_value, unique_value, **abstract_info.__dict__)
|
|
312
|
+
|
|
313
|
+
def long_field(self, field_name: FieldIdentifier, default_value: int | None = None, min_value: int = -2**63,
|
|
314
|
+
max_value: int = 2**63 - 1, unique_value: bool = False, abstract_info: AnyFieldInfo | None = None, *,
|
|
315
|
+
data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
|
|
316
|
+
-> VeloxLongFieldDefinition:
|
|
317
|
+
"""
|
|
318
|
+
Create a long field definition. Long fields are 64-bit whole numbers.
|
|
319
|
+
|
|
320
|
+
:param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
|
|
321
|
+
the display name.
|
|
322
|
+
:param default_value: The default value to display in this field before the user edits it.
|
|
323
|
+
:param min_value: The minimum allowed value in this field.
|
|
324
|
+
:param max_value: The maximum allowed value in this field.
|
|
325
|
+
:param unique_value: Whether the value in this field must be unique across all temp records in the dialog.
|
|
326
|
+
:param abstract_info: The abstract field info for this field, such as whether it is editable or required.
|
|
327
|
+
:param data_type_name: An optional override for the data type name used for this field. If not provided, then
|
|
328
|
+
the data field name of the FieldBuilder is used.
|
|
329
|
+
:param display_name: An optional override for the display name of this field. If not provided, then the data
|
|
330
|
+
field name doubles as the display name.
|
|
331
|
+
:return: A long field definition with settings from the input criteria.
|
|
332
|
+
"""
|
|
333
|
+
data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
|
|
334
|
+
field_name: str = AliasUtil.to_data_field_name(field_name)
|
|
335
|
+
if abstract_info is None:
|
|
336
|
+
abstract_info = AnyFieldInfo()
|
|
337
|
+
if not display_name:
|
|
338
|
+
display_name = field_name
|
|
339
|
+
return VeloxLongFieldDefinition(data_type_name, field_name, display_name, min_value, max_value, default_value,
|
|
340
|
+
unique_value, **abstract_info.__dict__)
|
|
341
|
+
|
|
342
|
+
def pick_list_field(self, field_name: FieldIdentifier, pick_list_name: str, default_value: str | None = None,
|
|
343
|
+
direct_edit: bool = False, abstract_info: AnyFieldInfo | None = None, *,
|
|
344
|
+
data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
|
|
345
|
+
-> VeloxPickListFieldDefinition:
|
|
346
|
+
"""
|
|
347
|
+
Create a pick list field definition. Pick list fields are string fields that display a drop-down list of options
|
|
348
|
+
when being edited by a user. The list of options is backed by a pick list defined in the list manager sections
|
|
349
|
+
of the app setup.
|
|
350
|
+
|
|
351
|
+
Selection lists can do everything pick lists can do and more, so often it is better to use a selection list.
|
|
352
|
+
|
|
353
|
+
:param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
|
|
354
|
+
the display name.
|
|
355
|
+
:param pick_list_name: The name of the pick list to populate the options of this field.
|
|
356
|
+
:param default_value: The default value to display in this field before the user edits it.
|
|
357
|
+
:param direct_edit: Whether the user may input values not present in the list of options.
|
|
358
|
+
:param abstract_info: The abstract field info for this field, such as whether it is editable or required.
|
|
359
|
+
:param data_type_name: An optional override for the data type name used for this field. If not provided, then
|
|
360
|
+
the data field name of the FieldBuilder is used.
|
|
361
|
+
:param display_name: An optional override for the display name of this field. If not provided, then the data
|
|
362
|
+
field name doubles as the display name.
|
|
363
|
+
:return: A pick list field definition with settings from the input criteria.
|
|
364
|
+
"""
|
|
365
|
+
data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
|
|
366
|
+
field_name: str = AliasUtil.to_data_field_name(field_name)
|
|
367
|
+
if abstract_info is None:
|
|
368
|
+
abstract_info = AnyFieldInfo()
|
|
369
|
+
if not display_name:
|
|
370
|
+
display_name = field_name
|
|
371
|
+
return VeloxPickListFieldDefinition(data_type_name, field_name, display_name, pick_list_name, default_value,
|
|
372
|
+
direct_edit, **abstract_info.__dict__)
|
|
373
|
+
|
|
374
|
+
def selection_list_field(self, field_name: FieldIdentifier, default_value: str | None = None,
|
|
375
|
+
direct_edit: bool = False, multi_select: bool = False, unique_value: bool = False,
|
|
376
|
+
abstract_info: AnyFieldInfo | None = None, *, pick_list_name: str | None = None,
|
|
377
|
+
custom_report_name: str | None = None, plugin_name: str | None = None,
|
|
378
|
+
static_values: list[str] | None = None, user_list: bool = False,
|
|
379
|
+
user_group_list: bool = False, non_api_user_list: bool = False,
|
|
380
|
+
data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
|
|
381
|
+
-> VeloxSelectionFieldDefinition:
|
|
382
|
+
"""
|
|
383
|
+
Create a selection list field definition. Selection list fields are string fields that display a drop-down list
|
|
384
|
+
of options when being edited by a user. The list of options can be populated from a number of locations,
|
|
385
|
+
including pick lists, predefined searches (custom reports), all usernames or groups in the system, and more.
|
|
386
|
+
|
|
387
|
+
Note that the different list types are mutually exclusive with one another. You must only provide the parameter
|
|
388
|
+
necessary for a singular selection list type.
|
|
389
|
+
|
|
390
|
+
:param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
|
|
391
|
+
the display name.
|
|
392
|
+
:param default_value: The default value to display in this field before the user edits it.
|
|
393
|
+
:param direct_edit: Whether the user may input values not present in the list of options.
|
|
394
|
+
:param multi_select: Whether the user may select multiple options from the list of this field.
|
|
395
|
+
:param unique_value: Whether the value in this field must be unique across all temp records in the dialog.
|
|
396
|
+
:param pick_list_name: The name of the pick list to populate the options of this field.
|
|
397
|
+
:param custom_report_name: The name of the custom report (predefined search) to populate the options of this
|
|
398
|
+
field.
|
|
399
|
+
:param plugin_name: The path to the plugin used to populate the options of this field.
|
|
400
|
+
:param static_values: The list of string values used to populate the options of this field.
|
|
401
|
+
:param user_list: Whether this field is populated by a list of all users in the system.
|
|
402
|
+
:param user_group_list: Whether this field is populated by a list of all user groups in the system.
|
|
403
|
+
:param non_api_user_list: Whether this field is populated by a list of all non-API users in the system.
|
|
404
|
+
:param abstract_info: The abstract field info for this field, such as whether it is editable or required.
|
|
405
|
+
:param data_type_name: An optional override for the data type name used for this field. If not provided, then
|
|
406
|
+
the data field name of the FieldBuilder is used.
|
|
407
|
+
:param display_name: An optional override for the display name of this field. If not provided, then the data
|
|
408
|
+
field name doubles as the display name.
|
|
409
|
+
:return: A selection list field definition with settings from the input criteria.
|
|
410
|
+
"""
|
|
411
|
+
data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
|
|
412
|
+
field_name: str = AliasUtil.to_data_field_name(field_name)
|
|
413
|
+
if abstract_info is None:
|
|
414
|
+
abstract_info = AnyFieldInfo()
|
|
415
|
+
if not display_name:
|
|
416
|
+
display_name = field_name
|
|
417
|
+
|
|
418
|
+
list_mode: ListMode | None = None
|
|
419
|
+
if pick_list_name:
|
|
420
|
+
list_mode = ListMode.LIST
|
|
421
|
+
if custom_report_name:
|
|
422
|
+
if list_mode:
|
|
423
|
+
raise SapioException("Unable to set multiple list modes at once for a selection list.")
|
|
424
|
+
list_mode = ListMode.REPORT
|
|
425
|
+
if plugin_name:
|
|
426
|
+
if list_mode:
|
|
427
|
+
raise SapioException("Unable to set multiple list modes at once for a selection list.")
|
|
428
|
+
list_mode = ListMode.PLUGIN
|
|
429
|
+
if user_list:
|
|
430
|
+
if list_mode:
|
|
431
|
+
raise SapioException("Unable to set multiple list modes at once for a selection list.")
|
|
432
|
+
list_mode = ListMode.USER
|
|
433
|
+
if user_group_list:
|
|
434
|
+
if list_mode:
|
|
435
|
+
raise SapioException("Unable to set multiple list modes at once for a selection list.")
|
|
436
|
+
list_mode = ListMode.USER_GROUP
|
|
437
|
+
if non_api_user_list:
|
|
438
|
+
if list_mode:
|
|
439
|
+
raise SapioException("Unable to set multiple list modes at once for a selection list.")
|
|
440
|
+
list_mode = ListMode.NON_API_USER
|
|
441
|
+
if static_values:
|
|
442
|
+
if list_mode:
|
|
443
|
+
raise SapioException("Unable to set multiple list modes at once for a selection list.")
|
|
444
|
+
# Static values don't have a list mode. Evaluate this last so that the multiple list modes check doesn't
|
|
445
|
+
# need to be more complex.
|
|
446
|
+
|
|
447
|
+
if not list_mode and static_values is None:
|
|
448
|
+
raise SapioException("A list mode must be chosen for selection list fields.")
|
|
449
|
+
return VeloxSelectionFieldDefinition(data_type_name, field_name, display_name,
|
|
450
|
+
list_mode, unique_value, multi_select,
|
|
451
|
+
default_value, pick_list_name, custom_report_name,
|
|
452
|
+
plugin_name, direct_edit, static_values,
|
|
453
|
+
**abstract_info.__dict__)
|
|
454
|
+
|
|
455
|
+
def short_field(self, field_name: FieldIdentifier, default_value: int | None = None, min_value: int = -2**15,
|
|
456
|
+
max_value: int = 2**15 - 1, unique_value: bool = False, abstract_info: AnyFieldInfo | None = None,
|
|
457
|
+
*, data_type_name: DataTypeIdentifier | None = None, display_name: str | None = None) \
|
|
458
|
+
-> VeloxShortFieldDefinition:
|
|
459
|
+
"""
|
|
460
|
+
Create a short field definition. Short fields are 16-bit whole numbers.
|
|
461
|
+
|
|
462
|
+
:param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
|
|
463
|
+
the display name.
|
|
464
|
+
:param default_value: The default value to display in this field before the user edits it.
|
|
465
|
+
:param min_value: The minimum allowed value in this field.
|
|
466
|
+
:param max_value: The maximum allowed value in this field.
|
|
467
|
+
:param unique_value: Whether the value in this field must be unique across all temp records in the dialog.
|
|
468
|
+
:param abstract_info: The abstract field info for this field, such as whether it is editable or required.
|
|
469
|
+
:param data_type_name: An optional override for the data type name used for this field. If not provided, then
|
|
470
|
+
the data field name of the FieldBuilder is used.
|
|
471
|
+
:param display_name: An optional override for the display name of this field. If not provided, then the data
|
|
472
|
+
field name doubles as the display name.
|
|
473
|
+
:return: A short field definition with settings from the input criteria.
|
|
474
|
+
"""
|
|
475
|
+
data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
|
|
476
|
+
field_name: str = AliasUtil.to_data_field_name(field_name)
|
|
477
|
+
if abstract_info is None:
|
|
478
|
+
abstract_info = AnyFieldInfo()
|
|
479
|
+
if not display_name:
|
|
480
|
+
display_name = field_name
|
|
481
|
+
return VeloxShortFieldDefinition(data_type_name, field_name, display_name, min_value, max_value, default_value,
|
|
482
|
+
unique_value, **abstract_info.__dict__)
|
|
483
|
+
|
|
484
|
+
def string_field(self, field_name: FieldIdentifier, default_value: str | None = None, max_length: int = 100,
|
|
485
|
+
unique_value: bool = False, html_editor: bool = False,
|
|
486
|
+
string_format: SapioStringFormat | None = None, num_lines: int = 1, auto_size: bool = False,
|
|
487
|
+
link_out: dict[str, str] | None = None, field_validator: FieldValidator | None = None,
|
|
488
|
+
abstract_info: AnyFieldInfo | None = None, *, data_type_name: DataTypeIdentifier | None = None,
|
|
489
|
+
display_name: str | None = None) -> VeloxStringFieldDefinition:
|
|
490
|
+
"""
|
|
491
|
+
Create a string field definition. String fields represent text, and are highly customizable, allowing the
|
|
492
|
+
field to be plain text or rich HTML, take up one line of space or multiple on a form, format as emails or
|
|
493
|
+
phone numbers, or create links to other websites or other locations in the system.
|
|
494
|
+
|
|
495
|
+
:param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
|
|
496
|
+
the display name.
|
|
497
|
+
:param default_value: The default value to display in this field before the user edits it.
|
|
498
|
+
:param max_length: The maximum allowed character length of this field.
|
|
499
|
+
:param unique_value: Whether the value in this field must be unique across all temp records in the dialog.
|
|
500
|
+
:param html_editor: Whether this field allows the user to use an HTML editor.
|
|
501
|
+
:param string_format: The format that this string field is displayed in. If no value is provided, the field
|
|
502
|
+
display as a normal string.
|
|
503
|
+
:param num_lines: The number of lines of space that this field takes up on a form.
|
|
504
|
+
:param auto_size: Whether this field should auto-size itself to fix the text when taking up space on a form.
|
|
505
|
+
:param link_out: A dictionary where the keys are the display names of the links and the values are the links to
|
|
506
|
+
navigate the user to if this field is clicked on. If the values contain the string "[[LINK_OUT]]" then that
|
|
507
|
+
macro will be replaced with the value of the string field when it is clicked. The display name is only
|
|
508
|
+
important if there is more than one link in the dictionary, in which case all available link out locations
|
|
509
|
+
will display in a dialog with their display names for the user to select. If a non-empty dictionary is
|
|
510
|
+
provided, this becomes a link-out field.
|
|
511
|
+
If the value is not determined to have the appearance of a URL (e.g. it doesn't start with https://), then
|
|
512
|
+
the system will prepend "https://<app-url>/veloxClient/" to the start of the URL. This allows you to create
|
|
513
|
+
links to other locations in the system without needing to know what the app URL is. For example, if you have
|
|
514
|
+
a link out string field that contains a record ID to a Sample, you could set the link value to
|
|
515
|
+
"#dataType=Sample;recordId=[[LINK_OUT]];view=dataRecord" and the client will, seeing that this is not a
|
|
516
|
+
normal looking URL, route the user to
|
|
517
|
+
https://<app-url>/veloxClient/#dataType=Sample;recordId=[[LINK_OUT]];view=dataRecord, which is the form
|
|
518
|
+
page of the Sample corresponding to the record ID recorded by the field value.
|
|
519
|
+
:param field_validator: If provided, the user's input for this field must pass the regex of the given validator.
|
|
520
|
+
:param abstract_info: The abstract field info for this field, such as whether it is editable or required.
|
|
521
|
+
:param data_type_name: An optional override for the data type name used for this field. If not provided, then
|
|
522
|
+
the data field name of the FieldBuilder is used.
|
|
523
|
+
:param display_name: An optional override for the display name of this field. If not provided, then the data
|
|
524
|
+
field name doubles as the display name.
|
|
525
|
+
:return: A string field definition with settings from the input criteria.
|
|
526
|
+
"""
|
|
527
|
+
data_type_name: str = AliasUtil.to_data_type_name(data_type_name) if data_type_name else self.data_type
|
|
528
|
+
field_name: str = AliasUtil.to_data_field_name(field_name)
|
|
529
|
+
if abstract_info is None:
|
|
530
|
+
abstract_info = AnyFieldInfo()
|
|
531
|
+
if not display_name:
|
|
532
|
+
display_name = field_name
|
|
533
|
+
link_out, link_out_url = self._convert_link_out(link_out)
|
|
534
|
+
return VeloxStringFieldDefinition(data_type_name, field_name, display_name, default_value, max_length,
|
|
535
|
+
unique_value, html_editor, string_format, num_lines, auto_size, link_out,
|
|
536
|
+
link_out_url, field_validator, **abstract_info.__dict__)
|
|
537
|
+
|
|
538
|
+
@staticmethod
|
|
539
|
+
def _convert_link_out(link_out: dict[str, str] | None) -> tuple[bool, str | None]:
|
|
540
|
+
"""
|
|
541
|
+
Given a dictionary of link-out URLs, convert them to the string format that the field definition expects.
|
|
542
|
+
"""
|
|
543
|
+
if link_out:
|
|
544
|
+
return True, "\t".join([display_name + "\t" + link for display_name, link in link_out.items()])
|
|
545
|
+
return False, None
|
|
@@ -6,16 +6,62 @@ indigo = Indigo()
|
|
|
6
6
|
renderer = IndigoRenderer(indigo)
|
|
7
7
|
indigo.setOption("render-output-format", "svg")
|
|
8
8
|
indigo.setOption("ignore-stereochemistry-errors", True)
|
|
9
|
+
indigo.setOption("render-stereo-style", "ext")
|
|
9
10
|
indigo.setOption("aromaticity-model", "generic")
|
|
10
11
|
indigo.setOption("render-coloring", True)
|
|
11
|
-
|
|
12
|
+
indigo.setOption("molfile-saving-mode", "3000")
|
|
13
|
+
indigo_inchi = IndigoInchi(indigo)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Function to process dative bonds in a molecule
|
|
17
|
+
# Returns True if at least one dative bond (_BOND_COORDINATION) was removed
|
|
18
|
+
def remove_dative_bonds_in_mol(molecule: IndigoObject) -> bool:
|
|
19
|
+
"""
|
|
20
|
+
Remove all dative bonds in a molecule or a query molecule.
|
|
21
|
+
:param molecule: The molecule to remove.
|
|
22
|
+
:return: Whether there are any dative bonds in the molecule that were removed.
|
|
23
|
+
"""
|
|
24
|
+
dative_bond_removed = False # Flag to track if any dative bond was removed
|
|
25
|
+
|
|
26
|
+
bonds_idx_to_remove = []
|
|
27
|
+
for bond in molecule.iterateBonds():
|
|
28
|
+
# Check if the bond is of a dative type (_BOND_COORDINATION = 9)
|
|
29
|
+
if bond.bondOrder() == 9: # _BOND_COORDINATION
|
|
30
|
+
atom1 = bond.source()
|
|
31
|
+
atom2 = bond.destination()
|
|
32
|
+
|
|
33
|
+
# Print bond details for debugging
|
|
34
|
+
print(f"Processing dative bond between atoms {atom1.index()} and {atom2.index()}")
|
|
35
|
+
|
|
36
|
+
# Cache bond information
|
|
37
|
+
# bond.setBondOrder(1)
|
|
38
|
+
bonds_idx_to_remove.append(bond.index())
|
|
39
|
+
dative_bond_removed = True # Set flag to True
|
|
40
|
+
|
|
41
|
+
if not dative_bond_removed:
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
molecule.removeBonds(bonds_idx_to_remove)
|
|
45
|
+
return True # Return whether any dative bond was removed
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def remove_dative_in_reaction(reaction: IndigoObject) -> bool:
|
|
49
|
+
"""
|
|
50
|
+
Remove all dative bonds in a reaction or a query reaction, from all reactants and products.
|
|
51
|
+
:param reaction: The reaction to remove dative bonds.
|
|
52
|
+
:return: Whether there are any dative bonds in the reaction that were removed.
|
|
53
|
+
"""
|
|
54
|
+
reactant_dative_removed: bool = any(remove_dative_bonds_in_mol(reactant) for reactant in reaction.iterateReactants())
|
|
55
|
+
product_dative_removed: bool = any(remove_dative_bonds_in_mol(product) for product in reaction.iterateProducts())
|
|
56
|
+
return reactant_dative_removed or product_dative_removed
|
|
12
57
|
|
|
13
58
|
|
|
14
59
|
def highlight_mol_substructure(query: IndigoObject, sub_match: IndigoObject):
|
|
15
60
|
"""
|
|
16
|
-
Highlight the bonds and atoms for substructure search result
|
|
17
|
-
|
|
18
|
-
:param
|
|
61
|
+
Highlight the bonds and atoms for substructure search result.
|
|
62
|
+
|
|
63
|
+
:param sub_match: The substructure search match obtained from indigo.substructureMatcher(mol).match(query).
|
|
64
|
+
:param query: The query we were running to match the original structure.
|
|
19
65
|
"""
|
|
20
66
|
for qatom in query.iterateAtoms():
|
|
21
67
|
atom = sub_match.mapAtom(qatom)
|
|
@@ -38,8 +84,9 @@ def highlight_mol_substructure(query: IndigoObject, sub_match: IndigoObject):
|
|
|
38
84
|
def highlight_reactions(query_reaction_smarts: IndigoObject, reaction_match: IndigoObject):
|
|
39
85
|
"""
|
|
40
86
|
Highlight the bonds and atoms for substructure search result of reaction that's in the query and survived the mapping.
|
|
87
|
+
|
|
41
88
|
:param query_reaction_smarts: The query we ran substructure search on.
|
|
42
|
-
:param reaction_match: The substructure search match obtained from indigo.substructureMatcher(reaction).match(query)
|
|
89
|
+
:param reaction_match: The substructure search match obtained from indigo.substructureMatcher(reaction).match(query).
|
|
43
90
|
:return:
|
|
44
91
|
"""
|
|
45
92
|
for q_mol in query_reaction_smarts.iterateMolecules():
|