sapiopycommons 2024.10.29a346__py3-none-any.whl → 2024.10.30a348__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.

@@ -0,0 +1,537 @@
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.exceptions import SapioException
10
+
11
+
12
+ class AnyFieldInfo:
13
+ """
14
+ Field definition information that can apply to any created field def. This excludes various members of
15
+ AbstractVeloxFieldDefinition such as system_field that wouldn't make sense to edit for a created field def
16
+ used in a temp data type.
17
+ """
18
+ editable: bool
19
+ required: bool
20
+ visible: bool
21
+ description: str | None
22
+ sort_direction: SortDirection | None
23
+ sort_order: int | None
24
+ default_table_column_width: int | None
25
+
26
+ def __init__(self, editable: bool = True, required: bool = False, visible: bool = True,
27
+ description: str | None = None, sort_direction: SortDirection | None = None,
28
+ sort_order: int | None = None, default_table_column_width: int | None = None):
29
+ """
30
+ :param editable: Whether this field can be edited by the user.
31
+ :param required: Whether input is required for this field before the user can submit the dialog that it is a
32
+ part of.
33
+ :param visible: Whether this field is visible to the user.
34
+ :param description: The description of this field that will appear when the user hovers the cursor over it.
35
+ :param sort_direction: The default sort direction of this field in tables. The user may still change the column
36
+ sorting.
37
+ :param sort_order: The default sort order of this field in tables. The user may still change the column sorting.
38
+ :param default_table_column_width: The width in pixels that this field's column will appear with in tables by
39
+ default. The user may still change the column width.
40
+ """
41
+ self.editable = editable
42
+ self.required = required
43
+ self.visible = visible
44
+ self.description = description
45
+ self.sort_direction = sort_direction
46
+ self.sort_order = sort_order
47
+ self.default_table_column_width = default_table_column_width
48
+
49
+
50
+ class FieldBuilder:
51
+ """
52
+ A class used for building fields for temporary data types. Currently designed to only create fields which can
53
+ be used in client callbacks that use temp data types. Some fields will not be displayed in temp data types,
54
+ including but not limited to: action fields, child/parent/side link fields.
55
+ """
56
+ data_type: str
57
+
58
+ def __init__(self, data_type: str = "Default"):
59
+ """
60
+ :param data_type: The data type name that fields created from this builder will use as their data type.
61
+ """
62
+ self.data_type = data_type
63
+
64
+ def accession_field(self, field_name: str, sequence_key: str, prefix: str | None = None, suffix: str | None = None,
65
+ number_of_digits: int = 8, starting_value: int = 1, link_out: dict[str, str] | None = None,
66
+ abstract_info: AnyFieldInfo | None = None, *, data_type_name: str | None = None,
67
+ display_name: str | None = None) -> VeloxAccessionFieldDefinition:
68
+ """
69
+ Create an accession field definition. Accession fields are text fields which generate a unique value
70
+ that has not been used before, incrementing from the most recently generated value. This can be used when a
71
+ guaranteed unique ID is necessary.
72
+
73
+ :param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
74
+ the display name.
75
+ :param sequence_key: The key of the accession sequence that this field will use.
76
+ :param prefix: The text that should appear before the numerical value.
77
+ :param suffix: The text that should appear after the numerical value.
78
+ :param number_of_digits: The number of digits in the numerical value.
79
+ :param starting_value: The starting value of the numerical value.
80
+ :param link_out: A dictionary where the keys are the display names of the links and the values are the links to
81
+ navigate the user to if this field is clicked on. If the values contain the string "[[LINK_OUT]]" then that
82
+ macro will be replaced with the value of the string field when it is clicked. The display name is only
83
+ important if there is more than one link in the dictionary, in which case all available link out locations
84
+ will display in a dialog with their display names for the user to select. If a non-empty dictionary is
85
+ provided, this becomes a link-out field.
86
+ If the value is not determined to have the appearance of a URL (e.g. it doesn't start with https://), then
87
+ the system will prepend "https://<app-url>/veloxClient/" to the start of the URL. This allows you to create
88
+ links to other locations in the system without needing to know what the app URL is. For example, if you have
89
+ a link out string field that contains a record ID to a Sample, you could set the link value to
90
+ "#dataType=Sample;recordId=[[LINK_OUT]];view=dataRecord" and the client will, seeing that this is not a
91
+ normal looking URL, route the user to
92
+ https://<app-url>/veloxClient/#dataType=Sample;recordId=[[LINK_OUT]];view=dataRecord, which is the form
93
+ page of the Sample corresponding to the record ID recorded by the field value.
94
+ :param abstract_info: The abstract field info for this field, such as whether it is editable or required.
95
+ :param data_type_name: An optional override for the data type name used for this field. If not provided, then
96
+ the data field name of the FieldBuilder is used.
97
+ :param display_name: An optional override for the display name of this field. If not provided, then the data
98
+ field name doubles as the display name.
99
+ :return: An accession field definition with settings from the input criteria.
100
+ """
101
+ if abstract_info is None:
102
+ abstract_info = AnyFieldInfo()
103
+ if not data_type_name:
104
+ data_type_name = self.data_type
105
+ if not display_name:
106
+ display_name = field_name
107
+ # Accession fields lock editable to false.
108
+ abstract_info.editable = False
109
+ link_out, link_out_url = self._convert_link_out(link_out)
110
+ # The unique parameter has no effect, so just always set it to false.
111
+ return VeloxAccessionFieldDefinition(data_type_name, field_name, display_name, sequence_key, prefix, suffix,
112
+ number_of_digits, False, starting_value, link_out, link_out_url,
113
+ kwargs=abstract_info.__dict__)
114
+
115
+ def boolean_field(self, field_name: str, default_value: bool | None = False, abstract_info: AnyFieldInfo | None = None,
116
+ *, data_type_name: str | None = None, display_name: str | None = None) -> VeloxBooleanFieldDefinition:
117
+ """
118
+ Create a boolean field definition. Boolean fields are fields which may have a value of true or false.
119
+ They appear as a checkbox in the UI. Boolean fields may also have a value of null if the field is not required.
120
+
121
+ :param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
122
+ the display name.
123
+ :param default_value: The default value to display in this field before the user edits it.
124
+ :param abstract_info: The abstract field info for this field, such as whether it is editable or required.
125
+ :param data_type_name: An optional override for the data type name used for this field. If not provided, then
126
+ the data field name of the FieldBuilder is used.
127
+ :param display_name: An optional override for the display name of this field. If not provided, then the data
128
+ field name doubles as the display name.
129
+ :return: A boolean field definition with settings from the input criteria.
130
+ """
131
+ if abstract_info is None:
132
+ abstract_info = AnyFieldInfo()
133
+ # Boolean fields assume that they are required if no abstract info is provided.
134
+ abstract_info.required = True
135
+ if not data_type_name:
136
+ data_type_name = self.data_type
137
+ if not display_name:
138
+ display_name = field_name
139
+ return VeloxBooleanFieldDefinition(data_type_name, field_name, display_name, default_value,
140
+ kwargs=abstract_info.__dict__)
141
+
142
+ def date_field(self, field_name: str, default_value: int | None = None, date_time_format: str = "MMM dd, yyyy",
143
+ static_date: bool = False, abstract_info: AnyFieldInfo | None = None, *,
144
+ data_type_name: str | None = None, display_name: str | None = None) -> VeloxDateFieldDefinition:
145
+ """
146
+ Create a date field definition. Date fields store date and time information as an integer
147
+ representing the number of milliseconds since the unix epoch. This timestamp is then displayed to users in a
148
+ human-readable format.
149
+
150
+ :param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
151
+ the display name.
152
+ :param default_value: The default value to display in this field before the user edits it.
153
+ :param date_time_format: The format that this date field should appear in. The date format is Java-style.
154
+ See https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/text/SimpleDateFormat.html for more
155
+ details.
156
+ :param static_date: If true, this date displays in UTC regardless of the user's timezone. If false, this date
157
+ displays the time in the user's timezone.
158
+ :param abstract_info: The abstract field info for this field, such as whether it is editable or required.
159
+ :param data_type_name: An optional override for the data type name used for this field. If not provided, then
160
+ the data field name of the FieldBuilder is used.
161
+ :param display_name: An optional override for the display name of this field. If not provided, then the data
162
+ field name doubles as the display name.
163
+ :return: A date field definition with settings from the input criteria.
164
+ """
165
+ if abstract_info is None:
166
+ abstract_info = AnyFieldInfo()
167
+ if not data_type_name:
168
+ data_type_name = self.data_type
169
+ if not display_name:
170
+ display_name = field_name
171
+ return VeloxDateFieldDefinition(data_type_name, field_name, display_name, date_time_format, default_value,
172
+ static_date, kwargs=abstract_info.__dict__)
173
+
174
+ def date_range_field(self, field_name: str, default_value: str | DateRange | None = None,
175
+ date_time_format: str = "MMM dd, yyyy", static_date: bool = False,
176
+ abstract_info: AnyFieldInfo | None = None, *, data_type_name: str | None = None,
177
+ display_name: str | None = None) -> VeloxDateRangeFieldDefinition:
178
+ """
179
+ Create a date range field definition. Date range fields store two unix epoch timestamps as a string of the
180
+ format "[start timestamp]/[end timestamp]". This string is then displayed to users in a human-readable
181
+ format as two dates.
182
+
183
+ See the DateRange class from sapiopylib for an easy means of converting to/from millisecond timestamps and a
184
+ date range field's string value.
185
+
186
+ :param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
187
+ the display name.
188
+ :param default_value: The default value to display in this field before the user edits it.
189
+ :param date_time_format: The format that this date field should appear in. The date format is Java-style.
190
+ See https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/text/SimpleDateFormat.html for more
191
+ details.
192
+ :param static_date: If true, these dates display in UTC regardless of the user's timezone. If false, they
193
+ display the time in the user's timezone.
194
+ :param abstract_info: The abstract field info for this field, such as whether it is editable or required.
195
+ :param data_type_name: An optional override for the data type name used for this field. If not provided, then
196
+ the data field name of the FieldBuilder is used.
197
+ :param display_name: An optional override for the display name of this field. If not provided, then the data
198
+ field name doubles as the display name.
199
+ :return: A date range field definition with settings from the input criteria.
200
+ """
201
+ if abstract_info is None:
202
+ abstract_info = AnyFieldInfo()
203
+ if not data_type_name:
204
+ data_type_name = self.data_type
205
+ if not display_name:
206
+ display_name = field_name
207
+ if isinstance(default_value, DateRange):
208
+ default_value = str(default_value)
209
+ return VeloxDateRangeFieldDefinition(data_type_name, field_name, display_name, date_time_format, static_date,
210
+ default_value, kwargs=abstract_info.__dict__)
211
+
212
+ def double_field(self, field_name: str, default_value: float | None = None, min_value: float = -10.**120,
213
+ max_value: float = 10.**120, precision: int = 1, double_format: SapioDoubleFormat | None = None,
214
+ abstract_info: AnyFieldInfo | None = None, *, data_type_name: str | None = None,
215
+ display_name: str | None = None) -> VeloxDoubleFieldDefinition:
216
+ """
217
+ Create a double field definition. Double fields represent decimal numerical values. They can also
218
+ be configured to represent currencies or percentages by changing the format parameter.
219
+
220
+ :param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
221
+ the display name.
222
+ :param default_value: The default value to display in this field before the user edits it.
223
+ :param min_value: The minimum allowed value in this field.
224
+ :param max_value: The maximum allowed value in this field.
225
+ :param precision: The number of digits past the decimal point to display for this field.
226
+ :param double_format: The format that this double field is displayed in. If no value is provided, the field
227
+ display as a normal numerical value.
228
+ :param abstract_info: The abstract field info for this field, such as whether it is editable or required.
229
+ :param data_type_name: An optional override for the data type name used for this field. If not provided, then
230
+ the data field name of the FieldBuilder is used.
231
+ :param display_name: An optional override for the display name of this field. If not provided, then the data
232
+ field name doubles as the display name.
233
+ :return: A double field definition with settings from the input criteria.
234
+ """
235
+ if abstract_info is None:
236
+ abstract_info = AnyFieldInfo()
237
+ if not data_type_name:
238
+ data_type_name = self.data_type
239
+ if not display_name:
240
+ display_name = field_name
241
+ return VeloxDoubleFieldDefinition(data_type_name, field_name, display_name, min_value, max_value, default_value,
242
+ precision, double_format, kwargs=abstract_info.__dict__)
243
+
244
+ def enum_field(self, field_name: str, options: list[str], default_value: int | None = None,
245
+ abstract_info: AnyFieldInfo | None = None, *, data_type_name: str | None = None,
246
+ display_name: str | None = None) -> VeloxEnumFieldDefinition:
247
+ """
248
+ Create an enum field definition. Enum fields allow for the display of a list of options as a field
249
+ definition without the need of a backing method in the system like pick list or selection lists do. Note that
250
+ when setting the default value or reading the return value of an enum field, the value is an integer
251
+ representing the index of the values list, as opposed to a string for the exact value chosen.
252
+
253
+ Note that this field is mainly here for completeness' sake. You can now use the static_values parameter of a
254
+ selection list to achieve the same thing without needing to worry about the field value being the index of the
255
+ options.
256
+
257
+ :param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
258
+ the display name.
259
+ :param options: The list of strings that the user may select from for this enum field. Note that when a client
260
+ callback returns the value from an enum field, it will be the index of the option in the options list that
261
+ the user chose.
262
+ :param default_value: The default value to display in this field before the user edits it. This is the index of
263
+ the option from the options list that you wish to appear as the default.
264
+ :param abstract_info: The abstract field info for this field, such as whether it is editable or required.
265
+ :param data_type_name: An optional override for the data type name used for this field. If not provided, then
266
+ the data field name of the FieldBuilder is used.
267
+ :param display_name: An optional override for the display name of this field. If not provided, then the data
268
+ field name doubles as the display name.
269
+ :return: An enum field definition with settings from the input criteria.
270
+ """
271
+ if abstract_info is None:
272
+ abstract_info = AnyFieldInfo()
273
+ if not data_type_name:
274
+ data_type_name = self.data_type
275
+ if not display_name:
276
+ display_name = field_name
277
+ return VeloxEnumFieldDefinition(data_type_name, field_name, display_name, default_value, options,
278
+ kwargs=abstract_info.__dict__)
279
+
280
+ def int_field(self, field_name: str, default_value: int | None = None, min_value: int = -2**31,
281
+ max_value: int = 2**31 - 1, unique_value: bool = False, abstract_info: AnyFieldInfo | None = None, *,
282
+ data_type_name: str | None = None, display_name: str | None = None) -> VeloxIntegerFieldDefinition:
283
+ """
284
+ Create an integer field definition. Integer fields are 32-bit whole numbers.
285
+
286
+ :param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
287
+ the display name.
288
+ :param default_value: The default value to display in this field before the user edits it.
289
+ :param min_value: The minimum allowed value in this field.
290
+ :param max_value: The maximum allowed value in this field.
291
+ :param unique_value: Whether the value in this field must be unique across all temp records in the dialog.
292
+ :param abstract_info: The abstract field info for this field, such as whether it is editable or required.
293
+ :param data_type_name: An optional override for the data type name used for this field. If not provided, then
294
+ the data field name of the FieldBuilder is used.
295
+ :param display_name: An optional override for the display name of this field. If not provided, then the data
296
+ field name doubles as the display name.
297
+ :return: An integer field definition with settings from the input criteria.
298
+ """
299
+ if abstract_info is None:
300
+ abstract_info = AnyFieldInfo()
301
+ if not data_type_name:
302
+ data_type_name = self.data_type
303
+ if not display_name:
304
+ display_name = field_name
305
+ return VeloxIntegerFieldDefinition(data_type_name, field_name, display_name, min_value, max_value,
306
+ default_value, unique_value, kwargs=abstract_info.__dict__)
307
+
308
+ def long_field(self, field_name: str, default_value: int | None = None, min_value: int = -2**63,
309
+ max_value: int = 2**63 - 1, unique_value: bool = False, abstract_info: AnyFieldInfo | None = None, *,
310
+ data_type_name: str | None = None, display_name: str | None = None) -> VeloxLongFieldDefinition:
311
+ """
312
+ Create a long field definition. Long fields are 64-bit whole numbers.
313
+
314
+ :param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
315
+ the display name.
316
+ :param default_value: The default value to display in this field before the user edits it.
317
+ :param min_value: The minimum allowed value in this field.
318
+ :param max_value: The maximum allowed value in this field.
319
+ :param unique_value: Whether the value in this field must be unique across all temp records in the dialog.
320
+ :param abstract_info: The abstract field info for this field, such as whether it is editable or required.
321
+ :param data_type_name: An optional override for the data type name used for this field. If not provided, then
322
+ the data field name of the FieldBuilder is used.
323
+ :param display_name: An optional override for the display name of this field. If not provided, then the data
324
+ field name doubles as the display name.
325
+ :return: A long field definition with settings from the input criteria.
326
+ """
327
+ if abstract_info is None:
328
+ abstract_info = AnyFieldInfo()
329
+ if not data_type_name:
330
+ data_type_name = self.data_type
331
+ if not display_name:
332
+ display_name = field_name
333
+ return VeloxLongFieldDefinition(data_type_name, field_name, display_name, min_value, max_value, default_value,
334
+ unique_value, kwargs=abstract_info.__dict__)
335
+
336
+ def pick_list_field(self, field_name: str, pick_list_name: str, default_value: str | None = None,
337
+ direct_edit: bool = False, abstract_info: AnyFieldInfo | None = None, *,
338
+ data_type_name: str | None = None, display_name: str | None = None) -> VeloxPickListFieldDefinition:
339
+ """
340
+ Create a pick list field definition. Pick list fields are string fields that display a drop-down list of options
341
+ when being edited by a user. The list of options is backed by a pick list defined in the list manager sections
342
+ of the app setup.
343
+
344
+ Selection lists can do everything pick lists can do and more, so often it is better to use a selection list.
345
+
346
+ :param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
347
+ the display name.
348
+ :param pick_list_name: The name of the pick list to populate the options of this field.
349
+ :param default_value: The default value to display in this field before the user edits it.
350
+ :param direct_edit: Whether the user may input values not present in the list of options.
351
+ :param abstract_info: The abstract field info for this field, such as whether it is editable or required.
352
+ :param data_type_name: An optional override for the data type name used for this field. If not provided, then
353
+ the data field name of the FieldBuilder is used.
354
+ :param display_name: An optional override for the display name of this field. If not provided, then the data
355
+ field name doubles as the display name.
356
+ :return: A pick list field definition with settings from the input criteria.
357
+ """
358
+ if abstract_info is None:
359
+ abstract_info = AnyFieldInfo()
360
+ if not data_type_name:
361
+ data_type_name = self.data_type
362
+ if not display_name:
363
+ display_name = field_name
364
+ return VeloxPickListFieldDefinition(data_type_name, field_name, display_name, pick_list_name, default_value,
365
+ direct_edit, kwargs=abstract_info.__dict__)
366
+
367
+ def selection_list_field(self, field_name: str, default_value: str | None = None, direct_edit: bool = False,
368
+ multi_select: bool = False, unique_value: bool = False,
369
+ abstract_info: AnyFieldInfo | None = None, *, pick_list_name: str | None = None,
370
+ custom_report_name: str | None = None, plugin_name: str | None = None,
371
+ static_values: list[str] | None = None, user_list: bool = False,
372
+ user_group_list: bool = False, non_api_user_list: bool = False,
373
+ data_type_name: str | None = None, display_name: str | None = None) \
374
+ -> VeloxSelectionFieldDefinition:
375
+ """
376
+ Create a selection list field definition. Selection list fields are string fields that display a drop-down list
377
+ of options when being edited by a user. The list of options can be populated from a number of locations,
378
+ including pick lists, predefined searches (custom reports), all usernames or groups in the system, and more.
379
+
380
+ Note that the different list types are mutually exclusive with one another. You must only provide the parameter
381
+ necessary for a singular selection list type.
382
+
383
+ :param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
384
+ the display name.
385
+ :param default_value: The default value to display in this field before the user edits it.
386
+ :param direct_edit: Whether the user may input values not present in the list of options.
387
+ :param multi_select: Whether the user may select multiple options from the list of this field.
388
+ :param unique_value: Whether the value in this field must be unique across all temp records in the dialog.
389
+ :param pick_list_name: The name of the pick list to populate the options of this field.
390
+ :param custom_report_name: The name of the custom report (predefined search) to populate the options of this
391
+ field.
392
+ :param plugin_name: The path to the plugin used to populate the options of this field.
393
+ :param static_values: The list of string values used to populate the options of this field.
394
+ :param user_list: Whether this field is populated by a list of all users in the system.
395
+ :param user_group_list: Whether this field is populated by a list of all user groups in the system.
396
+ :param non_api_user_list: Whether this field is populated by a list of all non-API users in the system.
397
+ :param abstract_info: The abstract field info for this field, such as whether it is editable or required.
398
+ :param data_type_name: An optional override for the data type name used for this field. If not provided, then
399
+ the data field name of the FieldBuilder is used.
400
+ :param display_name: An optional override for the display name of this field. If not provided, then the data
401
+ field name doubles as the display name.
402
+ :return: A selection list field definition with settings from the input criteria.
403
+ """
404
+ if abstract_info is None:
405
+ abstract_info = AnyFieldInfo()
406
+ if not data_type_name:
407
+ data_type_name = self.data_type
408
+ if not display_name:
409
+ display_name = field_name
410
+
411
+ list_mode: ListMode | None = None
412
+ if pick_list_name:
413
+ list_mode = ListMode.LIST
414
+ if custom_report_name:
415
+ if list_mode:
416
+ raise SapioException("Unable to set multiple list modes at once for a selection list.")
417
+ list_mode = ListMode.REPORT
418
+ if plugin_name:
419
+ if list_mode:
420
+ raise SapioException("Unable to set multiple list modes at once for a selection list.")
421
+ list_mode = ListMode.PLUGIN
422
+ if user_list:
423
+ if list_mode:
424
+ raise SapioException("Unable to set multiple list modes at once for a selection list.")
425
+ list_mode = ListMode.USER
426
+ if user_group_list:
427
+ if list_mode:
428
+ raise SapioException("Unable to set multiple list modes at once for a selection list.")
429
+ list_mode = ListMode.USER_GROUP
430
+ if non_api_user_list:
431
+ if list_mode:
432
+ raise SapioException("Unable to set multiple list modes at once for a selection list.")
433
+ list_mode = ListMode.NON_API_USER
434
+ if static_values:
435
+ if list_mode:
436
+ raise SapioException("Unable to set multiple list modes at once for a selection list.")
437
+ # Static values don't have a list mode. Evaluate this last so that the multiple list modes check doesn't
438
+ # need to be more complex.
439
+
440
+ if not list_mode and static_values is None:
441
+ raise SapioException("A list mode must be chosen for selection list fields.")
442
+ return VeloxSelectionFieldDefinition(data_type_name, field_name, display_name,
443
+ list_mode, unique_value, multi_select,
444
+ default_value, pick_list_name, custom_report_name,
445
+ plugin_name, direct_edit, static_values,
446
+ kwargs=abstract_info.__dict__)
447
+
448
+ def short_field(self, field_name: str, default_value: int | None = None, min_value: int = -2**15,
449
+ max_value: int = 2**15 - 1, unique_value: bool = False, abstract_info: AnyFieldInfo | None = None,
450
+ *, data_type_name: str | None = None, display_name: str | None = None) -> VeloxShortFieldDefinition:
451
+ """
452
+ Create a short field definition. Short fields are 16-bit whole numbers.
453
+
454
+ :param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
455
+ the display name.
456
+ :param default_value: The default value to display in this field before the user edits it.
457
+ :param min_value: The minimum allowed value in this field.
458
+ :param max_value: The maximum allowed value in this field.
459
+ :param unique_value: Whether the value in this field must be unique across all temp records in the dialog.
460
+ :param abstract_info: The abstract field info for this field, such as whether it is editable or required.
461
+ :param data_type_name: An optional override for the data type name used for this field. If not provided, then
462
+ the data field name of the FieldBuilder is used.
463
+ :param display_name: An optional override for the display name of this field. If not provided, then the data
464
+ field name doubles as the display name.
465
+ :return: A short field definition with settings from the input criteria.
466
+ """
467
+ if abstract_info is None:
468
+ abstract_info = AnyFieldInfo()
469
+ if not data_type_name:
470
+ data_type_name = self.data_type
471
+ if not display_name:
472
+ display_name = field_name
473
+ return VeloxShortFieldDefinition(data_type_name, field_name, display_name, min_value, max_value, default_value,
474
+ unique_value, kwargs=abstract_info.__dict__)
475
+
476
+ def string_field(self, field_name: str,
477
+ default_value: str | None = None, max_length: int = 100, unique_value: bool = False,
478
+ html_editor: bool = False, string_format: SapioStringFormat | None = None, num_lines: int = 1,
479
+ auto_size: bool = False, link_out: dict[str, str] | None = None,
480
+ field_validator: FieldValidator | None = None, abstract_info: AnyFieldInfo | None = None, *,
481
+ data_type_name: str | None = None, display_name: str | None = None) -> VeloxStringFieldDefinition:
482
+ """
483
+ Create a string field definition. String fields represent text, and are highly customizable, allowing the
484
+ field to be plain text or rich HTML, take up one line of space or multiple on a form, format as emails or
485
+ phone numbers, or create links to other websites or other locations in the system.
486
+
487
+ :param field_name: The data field name of this field. Unless a display name is also provided, this doubles as
488
+ the display name.
489
+ :param default_value: The default value to display in this field before the user edits it.
490
+ :param max_length: The maximum allowed character length of this field.
491
+ :param unique_value: Whether the value in this field must be unique across all temp records in the dialog.
492
+ :param html_editor: Whether this field allows the user to use an HTML editor.
493
+ :param string_format: The format that this string field is displayed in. If no value is provided, the field
494
+ display as a normal string.
495
+ :param num_lines: The number of lines of space that this field takes up on a form.
496
+ :param auto_size: Whether this field should auto-size itself to fix the text when taking up space on a form.
497
+ :param link_out: A dictionary where the keys are the display names of the links and the values are the links to
498
+ navigate the user to if this field is clicked on. If the values contain the string "[[LINK_OUT]]" then that
499
+ macro will be replaced with the value of the string field when it is clicked. The display name is only
500
+ important if there is more than one link in the dictionary, in which case all available link out locations
501
+ will display in a dialog with their display names for the user to select. If a non-empty dictionary is
502
+ provided, this becomes a link-out field.
503
+ If the value is not determined to have the appearance of a URL (e.g. it doesn't start with https://), then
504
+ the system will prepend "https://<app-url>/veloxClient/" to the start of the URL. This allows you to create
505
+ links to other locations in the system without needing to know what the app URL is. For example, if you have
506
+ a link out string field that contains a record ID to a Sample, you could set the link value to
507
+ "#dataType=Sample;recordId=[[LINK_OUT]];view=dataRecord" and the client will, seeing that this is not a
508
+ normal looking URL, route the user to
509
+ https://<app-url>/veloxClient/#dataType=Sample;recordId=[[LINK_OUT]];view=dataRecord, which is the form
510
+ page of the Sample corresponding to the record ID recorded by the field value.
511
+ :param field_validator: If provided, the user's input for this field must pass the regex of the given validator.
512
+ :param abstract_info: The abstract field info for this field, such as whether it is editable or required.
513
+ :param data_type_name: An optional override for the data type name used for this field. If not provided, then
514
+ the data field name of the FieldBuilder is used.
515
+ :param display_name: An optional override for the display name of this field. If not provided, then the data
516
+ field name doubles as the display name.
517
+ :return: A string field definition with settings from the input criteria.
518
+ """
519
+ if abstract_info is None:
520
+ abstract_info = AnyFieldInfo()
521
+ if not data_type_name:
522
+ data_type_name = self.data_type
523
+ if not display_name:
524
+ display_name = field_name
525
+ link_out, link_out_url = self._convert_link_out(link_out)
526
+ return VeloxStringFieldDefinition(data_type_name, field_name, display_name, default_value, max_length,
527
+ unique_value, html_editor, string_format, num_lines, auto_size, link_out,
528
+ link_out_url, field_validator, kwargs=abstract_info.__dict__)
529
+
530
+ @staticmethod
531
+ def _convert_link_out(link_out: dict[str, str] | None) -> tuple[bool, str | None]:
532
+ """
533
+ Given a dictionary of link-out URLs, convert them to the string format that the field definition expects.
534
+ """
535
+ if link_out:
536
+ return True, "\t".join([display_name + "\t" + link for display_name, link in link_out.items()])
537
+ return False, None
@@ -9,6 +9,7 @@ indigo.setOption("ignore-stereochemistry-errors", True)
9
9
  indigo.setOption("render-stereo-style", "ext")
10
10
  indigo.setOption("aromaticity-model", "generic")
11
11
  indigo.setOption("render-coloring", True)
12
+ indigo.setOption("molfile-saving-mode", "3000")
12
13
  indigo_inchi = IndigoInchi(indigo);
13
14
 
14
15
 
@@ -1,5 +1,6 @@
1
1
  # Author Yechen Qiao
2
2
  # Common Molecule Utilities for Molecule Transfers with Sapio
3
+ from typing import cast
3
4
 
4
5
  from rdkit import Chem
5
6
  from rdkit.Chem import Crippen, MolToInchi
@@ -20,6 +21,25 @@ tautomer_params.tautomerReassignStereo = False
20
21
  tautomer_params.tautomerRemoveIsotopicHs = True
21
22
  enumerator = rdMolStandardize.TautomerEnumerator(tautomer_params)
22
23
 
24
+
25
+ def get_enhanced_stereo_reg_hash(mol: Mol, enhanced_stereo: bool) -> str:
26
+ """
27
+ Get the Registration Hash for the molecule by the current registration configuration.
28
+ When we are running if we are canonicalization of tautomers or cleaning up any other way, do they first before calling.
29
+ :param mol: The molecule to obtain hash for.
30
+ :param canonical_tautomer: Whether the registry system canonicalize the tautomers.
31
+ :param enhanced_stereo: Whether we are computing enhanced stereo at all.
32
+ :return: The enhanced stereo hash.
33
+ """
34
+ if enhanced_stereo:
35
+ from rdkit.Chem.RegistrationHash import GetMolLayers, GetMolHash, HashScheme
36
+ layers = GetMolLayers(mol, enable_tautomer_hash_v2=True)
37
+ hash_scheme: HashScheme = HashScheme.TAUTOMER_INSENSITIVE_LAYERS
38
+ return GetMolHash(layers, hash_scheme=hash_scheme)
39
+ else:
40
+ return ""
41
+
42
+
23
43
  def neutralize_atoms(mol) -> Mol:
24
44
  """
25
45
  Neutralize atoms per https://baoilleach.blogspot.com/2019/12/no-charge-simple-approach-to.html
@@ -86,7 +106,6 @@ def mol_to_img(mol_str: str) -> str:
86
106
  return renderer.renderToString(mol)
87
107
 
88
108
 
89
-
90
109
  def mol_to_sapio_partial_pojo(mol: Mol):
91
110
  """
92
111
  Get the minimum information about molecule to Sapio, just its SMILES, V3000, and image data.
@@ -96,7 +115,7 @@ def mol_to_sapio_partial_pojo(mol: Mol):
96
115
  Chem.SanitizeMol(mol)
97
116
  mol.UpdatePropertyCache()
98
117
  smiles = Chem.MolToSmiles(mol)
99
- molBlock = Chem.MolToMolBlock(mol)
118
+ molBlock = Chem.MolToMolBlock(mol, forceV3000=True)
100
119
  img = mol_to_img(mol)
101
120
  molecule = dict()
102
121
  molecule["smiles"] = smiles
@@ -105,23 +124,52 @@ def mol_to_sapio_partial_pojo(mol: Mol):
105
124
  return molecule
106
125
 
107
126
 
108
- def mol_to_sapio_substance(mol: Mol, include_stereoisomers: bool = False,
127
+ def get_cxs_smiles_hash(mol: Mol, enhanced_stereo: bool) -> str:
128
+ """
129
+ Return the SHA1 CXS Smiles hash for the canonical, isomeric CXS SMILES of the molecule.
130
+ """
131
+ if not enhanced_stereo:
132
+ return ""
133
+ import hashlib
134
+ return hashlib.sha1(Chem.MolToCXSmiles(mol, canonical=True, isomericSmiles=True).encode()).hexdigest()
135
+
136
+
137
+ def get_has_or_group(mol: Mol, enhanced_stereo: bool) -> bool:
138
+ """
139
+ Return true if and only if: enhanced stereochemistry is enabled and there is at least one OR group in mol.
140
+ """
141
+ if not enhanced_stereo:
142
+ return False
143
+ from rdkit.Chem import StereoGroup_vect, STEREO_OR
144
+ stereo_groups: StereoGroup_vect = mol.GetStereoGroups()
145
+ for stereo_group in stereo_groups:
146
+ if stereo_group.GetGroupType() == STEREO_OR:
147
+ return True
148
+ return False
149
+
150
+
151
+ def mol_to_sapio_substance(mol: Mol, include_stereoisomers=False,
109
152
  normalize: bool = False, remove_salt: bool = False, make_images: bool = False,
110
- salt_def: str | None = None, canonical_tautomer: bool = True):
153
+ salt_def: str | None = None, canonical_tautomer: bool = True,
154
+ enhanced_stereo: bool = False, remove_atom_map: bool = True):
111
155
  """
112
156
  Convert a molecule in RDKit to a molecule POJO in Sapio.
113
157
 
114
158
  :param mol: The molecule in RDKit.
115
- :param include_stereoisomers: If true, will compute all stereoisomer permutations of this molecule.
116
159
  :param normalize If true, will normalize the functional groups and return normalized result.
117
160
  :param remove_salt If true, we will remove salts iteratively from the molecule before returning their data.
118
161
  We will also populate desaltedList with molecules we deleted.
162
+ :param make_images Whether to make images as part of the result without having another script to resolve it.
119
163
  :param salt_def: if not none, specifies custom salt to be used during the desalt process.
120
164
  :param canonical_tautomer: if True, we will attempt to compute canonical tautomer for the molecule. Slow!
121
165
  This is needed for a registry. Note it stops after enumeration of 1000.
166
+ :param enhanced_stereo: If enabled, enhanced stereo hash will be produced.
167
+ :param remove_atom_map: When set, clear all atom AAM maps that were set had it been merged into some reactions earlier.
122
168
  :return: The molecule POJO for Sapio.
123
169
  """
124
170
  molecule = dict()
171
+ if remove_atom_map:
172
+ [a.SetAtomMapNum(0) for a in mol.GetAtoms()]
125
173
  Chem.SanitizeMol(mol)
126
174
  mol.UpdatePropertyCache()
127
175
  Chem.GetSymmSSSR(mol)
@@ -157,7 +205,7 @@ def mol_to_sapio_substance(mol: Mol, include_stereoisomers: bool = False,
157
205
  exactMass = Descriptors.ExactMolWt(mol)
158
206
  molFormula = rdMolDescriptors.CalcMolFormula(mol)
159
207
  charge = Chem.GetFormalCharge(mol)
160
- molBlock = Chem.MolToMolBlock(mol)
208
+ molBlock = Chem.MolToMolBlock(mol, forceV3000=True)
161
209
 
162
210
  molecule["cLogP"] = cLogP
163
211
  molecule["tpsa"] = tpsa
@@ -181,28 +229,38 @@ def mol_to_sapio_substance(mol: Mol, include_stereoisomers: bool = False,
181
229
  # We need to test the INCHI can be loaded back to indigo.
182
230
  indigo_mol = indigo.loadMolecule(molBlock)
183
231
  indigo_mol.aromatize()
184
- indigo_inchi.resetOptions()
185
- indigo_inchi_str = indigo_inchi.getInchi(indigo_mol)
186
- molecule["inchi"] = indigo_inchi_str
187
- indigo_inchi_key_str = indigo_inchi.getInchiKey(indigo_inchi_str)
188
- molecule["inchiKey"] = indigo_inchi_key_str
232
+ if enhanced_stereo:
233
+ # Remove enhanced stereo layer when generating InChI as the stereo hash is generated separately for reg.
234
+ mol_copy: Mol = Chem.Mol(mol)
235
+ Chem.CanonicalizeEnhancedStereo(mol_copy)
236
+ molecule["inchi"] = Chem.MolToInchi(mol_copy)
237
+ molecule["inchiKey"] = Chem.MolToInchiKey(mol_copy)
238
+ else:
239
+ indigo_inchi.resetOptions()
240
+ indigo_inchi_str = indigo_inchi.getInchi(indigo_mol)
241
+ molecule["inchi"] = indigo_inchi_str
242
+ indigo_inchi_key_str = indigo_inchi.getInchiKey(indigo_inchi_str)
243
+ molecule["inchiKey"] = indigo_inchi_key_str
189
244
  molecule["smiles"] = indigo_mol.smiles()
245
+ molecule["reg_hash"] = get_enhanced_stereo_reg_hash(mol, enhanced_stereo=enhanced_stereo)
246
+ molecule["cxsmiles_hash"] = get_cxs_smiles_hash(mol, enhanced_stereo=enhanced_stereo)
247
+ molecule["has_or_group"] = get_has_or_group(mol, enhanced_stereo=enhanced_stereo)
190
248
 
191
- if include_stereoisomers and has_chiral_centers(mol):
192
- stereoisomers = find_all_possible_stereoisomers(mol, only_unassigned=False, try_embedding=False, unique=True)
193
- molecule["stereoisomers"] = [mol_to_sapio_partial_pojo(x) for x in stereoisomers]
194
249
  return molecule
195
250
 
196
251
 
197
- def mol_to_sapio_compound(mol: Mol, include_stereoisomers: bool = False,
252
+ def mol_to_sapio_compound(mol: Mol, include_stereoisomers=False, enhanced_stereo: bool = False,
198
253
  salt_def: str | None = None, resolve_canonical: bool = True,
199
- make_images: bool = False, canonical_tautomer: bool = True):
254
+ make_images: bool = False, canonical_tautomer: bool = True,
255
+ remove_atom_map: bool = True):
200
256
  ret = dict()
201
- ret['originalMol'] = mol_to_sapio_substance(mol, include_stereoisomers,
257
+ ret['originalMol'] = mol_to_sapio_substance(mol, include_stereoisomers=False,
202
258
  normalize=False, remove_salt=False, make_images=make_images,
203
- canonical_tautomer=canonical_tautomer)
259
+ canonical_tautomer=canonical_tautomer,
260
+ enhanced_stereo=enhanced_stereo, remove_atom_map=remove_atom_map)
204
261
  if resolve_canonical:
205
262
  ret['canonicalMol'] = mol_to_sapio_substance(mol, include_stereoisomers=False,
206
263
  normalize=True, remove_salt=True, make_images=make_images,
207
- salt_def=salt_def, canonical_tautomer=canonical_tautomer)
264
+ salt_def=salt_def, canonical_tautomer=canonical_tautomer,
265
+ enhanced_stereo=enhanced_stereo, remove_atom_map=remove_atom_map)
208
266
  return ret
@@ -427,7 +427,7 @@ class ExperimentReportUtil:
427
427
  return ret_val
428
428
 
429
429
  @staticmethod
430
- def get_experiments_by_creator(context: UserIdentifier, created_by: str, *,
430
+ def get_experiments_by_creator(context: UserIdentifier, created_by: str,
431
431
  criteria: ExperimentReportCriteria = ExperimentReportCriteria()) \
432
432
  -> list[ElnExperiment]:
433
433
  """
@@ -442,7 +442,7 @@ class ExperimentReportUtil:
442
442
  return ExperimentReportUtil.get_experiments_by_creators(context, [created_by], criteria=criteria)[created_by]
443
443
 
444
444
  @staticmethod
445
- def get_experiments_by_creators(context: UserIdentifier, created_by: list[str], *,
445
+ def get_experiments_by_creators(context: UserIdentifier, created_by: list[str],
446
446
  criteria: ExperimentReportCriteria = ExperimentReportCriteria()) \
447
447
  -> dict[str, list[ElnExperiment]]:
448
448
  """
@@ -0,0 +1,77 @@
1
+ from __future__ import annotations
2
+
3
+ from weakref import WeakValueDictionary
4
+
5
+ from sapiopylib.rest.User import SapioUser
6
+ from databind.json import dumps
7
+
8
+ from sapiopycommons.flowcyto.flowcyto_data import FlowJoWorkspaceInputJson, UploadFCSInputJson, \
9
+ ComputeFlowStatisticsInputJson
10
+
11
+
12
+ class FlowCytoManager:
13
+ """
14
+ This manager includes flow cytometry analysis tools that would require FlowCyto license to use.
15
+ """
16
+ _user: SapioUser
17
+
18
+ __instances: WeakValueDictionary[SapioUser, FlowCytoManager] = WeakValueDictionary()
19
+ __initialized: bool
20
+
21
+ def __new__(cls, user: SapioUser):
22
+ """
23
+ Observes singleton pattern per record model manager object.
24
+
25
+ :param user: The user that will make the webservice request to the application.
26
+ """
27
+ obj = cls.__instances.get(user)
28
+ if not obj:
29
+ obj = object.__new__(cls)
30
+ obj.__initialized = False
31
+ cls.__instances[user] = obj
32
+ return obj
33
+
34
+ def __init__(self, user: SapioUser):
35
+ if self.__initialized:
36
+ return
37
+ self._user = user
38
+ self.__initialized = True
39
+
40
+ def create_flowjo_workspace(self, workspace_input: FlowJoWorkspaceInputJson) -> int:
41
+ """
42
+ Create FlowJo Workspace and return the workspace record ID of workspace root record,
43
+ after successful creation.
44
+ :param workspace_input: the request data payload.
45
+ :return: The new workspace record ID.
46
+ """
47
+ payload = dumps(workspace_input, FlowJoWorkspaceInputJson)
48
+ response = self._user.plugin_post("flowcyto/workspace", payload=payload, is_payload_plain_text=True)
49
+ self._user.raise_for_status(response)
50
+ return int(response.json())
51
+
52
+ def upload_fcs_for_sample(self, upload_input: UploadFCSInputJson) -> int:
53
+ """
54
+ Upload FCS file as root of the sample FCS.
55
+ :param upload_input: The request data payload
56
+ :return: The root FCS file uploaded under sample.
57
+ """
58
+ payload = dumps(upload_input, UploadFCSInputJson)
59
+ response = self._user.plugin_post("flowcyto/fcs", payload=payload, is_payload_plain_text=True)
60
+ self._user.raise_for_status(response)
61
+ return int(response.json())
62
+
63
+ def compute_statistics(self, stat_compute_input: ComputeFlowStatisticsInputJson) -> list[int]:
64
+ """
65
+ Requests to compute flow cytometry statistics.
66
+ The children are of type FCSStatistic.
67
+ If the FCS files have not been evaluated yet,
68
+ then the lazy evaluation will be performed immediately prior to computing statistics, which can take longer.
69
+ If any new statistics are computed as children of FCS, they will be returned in the result record id list.
70
+ Note: if input has multiple FCS files, the client should try to get parent FCS file from each record to figure out which one is for which FCS.
71
+ :param stat_compute_input:
72
+ :return:
73
+ """
74
+ payload = dumps(stat_compute_input, ComputeFlowStatisticsInputJson)
75
+ response = self._user.plugin_post("flowcyto/statistics", payload=payload, is_payload_plain_text=True)
76
+ self._user.raise_for_status(response)
77
+ return list(response.json())
@@ -0,0 +1,75 @@
1
+ import base64
2
+ from enum import Enum
3
+
4
+ from databind.core.dataclasses import dataclass
5
+
6
+
7
+ class ChannelStatisticType(Enum):
8
+ """
9
+ All supported channel statistics type.
10
+ """
11
+ MEAN = "(Mean) MFI"
12
+ MEDIAN = "(Median) MFI"
13
+ STD_EV = "Std. Dev."
14
+ COEFFICIENT_OF_VARIATION = "CV"
15
+
16
+ display_name: str
17
+
18
+ def __init__(self, display_name: str):
19
+ self.display_name = display_name
20
+
21
+
22
+ @dataclass
23
+ class ChannelStatisticsParameterJSON:
24
+ channelNameList: list[str]
25
+ statisticsType: ChannelStatisticType
26
+
27
+ def __init__(self, channel_name_list: list[str], stat_type: ChannelStatisticType):
28
+ self.channelNameList = channel_name_list
29
+ self.statisticsType = stat_type
30
+
31
+
32
+ @dataclass
33
+ class ComputeFlowStatisticsInputJson:
34
+ fcsFileRecordIdList: list[int]
35
+ statisticsParameterList: list[ChannelStatisticsParameterJSON]
36
+
37
+ def __init__(self, fcs_file_record_id_list: list[int], statistics_parameter_list: list[ChannelStatisticsParameterJSON]):
38
+ self.fcsFileRecordIdList = fcs_file_record_id_list
39
+ self.statisticsParameterList = statistics_parameter_list
40
+
41
+
42
+ @dataclass
43
+ class FlowJoWorkspaceInputJson:
44
+ filePath: str
45
+ base64Data: str
46
+
47
+ def __init__(self, filePath: str, file_data: bytes):
48
+ self.filePath = filePath
49
+ self.base64Data = base64.b64encode(file_data).decode('utf-8')
50
+
51
+
52
+ @dataclass
53
+ class UploadFCSInputJson:
54
+ """
55
+ Request to upload new FCS file
56
+ Attributes:
57
+ filePath: The file name of the FCS file to be uploaded. For FlowJo workspace, this is important to match the file in group (via file names).
58
+ attachmentDataType: the attachment data type that contains already-uploaded FCS data.
59
+ attachmentRecordId: the attachment record ID that contains already-uploaded FCS data.
60
+ associatedRecordDataType: the "parent" association for the FCS. Can either be a workspace or a sample record.
61
+ associatedRecordId: the "parent" association for the FCS. Can either be a workspace or a sample record.
62
+ """
63
+ filePath: str
64
+ attachmentDataType: str
65
+ attachmentRecordId: int
66
+ associatedRecordDataType: str
67
+ associatedRecordId: int
68
+
69
+ def __init__(self, associated_record_data_type: str, associated_record_id: int,
70
+ file_path: str, attachment_data_type: str, attachment_record_id: int):
71
+ self.filePath = file_path
72
+ self.attachmentDataType = attachment_data_type
73
+ self.attachmentRecordId = attachment_record_id
74
+ self.associatedRecordDataType = associated_record_data_type
75
+ self.associatedRecordId = associated_record_id
@@ -38,6 +38,9 @@ class PyMolecule:
38
38
  normError: str | None
39
39
  desaltError: str | None
40
40
  desaltedList: list[str] | None
41
+ registrationHash: str | None
42
+ hasOrGroup: bool
43
+ CXSMILESHash: str | None
41
44
 
42
45
 
43
46
  @dataclass
@@ -100,9 +103,9 @@ class PyMoleculeLoaderResult:
100
103
  compoundList: the compounds successfully loaded.
101
104
  errorList: an error record is added here for each one we failed to load in Sapio.
102
105
  """
103
- compoundByStr: dict[str, PyCompound]
104
- compoundList: list[PyCompound]
105
- errorList: list[ChemLoadingError]
106
+ compoundByStr: dict[str, PyCompound] | None
107
+ compoundList: list[PyCompound] | None
108
+ errorList: list[ChemLoadingError] | None
106
109
 
107
110
 
108
111
  @dataclass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sapiopycommons
3
- Version: 2024.10.29a346
3
+ Version: 2024.10.30a348
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>
@@ -1,8 +1,9 @@
1
1
  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
- sapiopycommons/chem/IndigoMolecules.py,sha256=QqFDi9CKERj6sn_ZwVcS2xZq4imlkaTeCrpq1iNcEJA,1992
5
- sapiopycommons/chem/Molecules.py,sha256=t80IsQBPJ9mwE8ZxnWomAGrZDhdsOuPvLaTPb_N6jGU,8639
4
+ sapiopycommons/callbacks/field_builder.py,sha256=8n0jcbMgtMUHjie4C1-IkpAuHz4zBxbZtWpr4y0kABU,36868
5
+ sapiopycommons/chem/IndigoMolecules.py,sha256=3f-aig3AJkKJhRmhlQ0cI-5G8oeaQk_3foJTDZCvoko,2040
6
+ sapiopycommons/chem/Molecules.py,sha256=SQKnqdZnhYj_6HGtEZmE_1DormonRR1-nBAQ__z4gms,11485
6
7
  sapiopycommons/chem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
8
  sapiopycommons/customreport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
9
  sapiopycommons/customreport/column_builder.py,sha256=0RO53e9rKPZ07C--KcepN6_tpRw_FxF3O9vdG0ilKG8,3014
@@ -14,7 +15,7 @@ sapiopycommons/datatype/data_fields.py,sha256=g8Ib6LH8ikNu9AzeVJs8Z2qS8A-cplACeF
14
15
  sapiopycommons/datatype/pseudo_data_types.py,sha256=Fe75Rnq5evyeJM1nC0sLkLGKAC74g2-GEeTdMeId80o,27649
15
16
  sapiopycommons/eln/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
17
  sapiopycommons/eln/experiment_handler.py,sha256=VJPBQSP_4QmhSGDCRDOb5neOjGM6sZ9krvJEmDkpVV8,69282
17
- sapiopycommons/eln/experiment_report_util.py,sha256=MmRQIS3oURnnFJ9tFue7yS-_0gtajNUr7PeShPqmSHY,37685
18
+ sapiopycommons/eln/experiment_report_util.py,sha256=9wWV6oEdKtfn2rI5V0BtmuW9OJlGFd9U07FIf889Gjw,37679
18
19
  sapiopycommons/eln/plate_designer.py,sha256=FYJfhhNq8hdfuXgDYOYHy6g0m2zNwQXZWF_MTPzElDg,7184
19
20
  sapiopycommons/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
21
  sapiopycommons/files/complex_data_loader.py,sha256=T39veNhvYl6j_uZjIIJ8Mk5Aa7otR5RB-g8XlAdkksA,1421
@@ -24,6 +25,8 @@ sapiopycommons/files/file_data_handler.py,sha256=SCsjODMJIPEBSsahzXUeOM7CfSCmYwP
24
25
  sapiopycommons/files/file_util.py,sha256=wbL3rxcFc-t2mXaPWWkoFWYGopvTcQts9Wf-L5GkhT8,29498
25
26
  sapiopycommons/files/file_validator.py,sha256=4OvY98ueJWPJdpndwnKv2nqVvLP9S2W7Il_dM0Y0ojo,28709
26
27
  sapiopycommons/files/file_writer.py,sha256=96Xl8TTT46Krxe_J8rmmlEMtel4nzZB961f5Yqtl1-I,17616
28
+ sapiopycommons/flowcyto/flow_cyto.py,sha256=YlkKJR_zEHYRuNW0bnTqlTyZeXs0lOaeSCfG2fnfD7E,3227
29
+ sapiopycommons/flowcyto/flowcyto_data.py,sha256=mYKFuLbtpJ-EsQxLGtu4tNHVlygTxKixgJxJqD68F58,2596
27
30
  sapiopycommons/general/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
31
  sapiopycommons/general/accession_service.py,sha256=HYgyOsH_UaoRnoury-c2yTW8SeG4OtjLemdpCzoV4R8,13484
29
32
  sapiopycommons/general/aliases.py,sha256=tdDBNWSGx6s39eHJ3n2kscc4xxW3ZYaUfDftct6FmJE,12910
@@ -35,7 +38,7 @@ sapiopycommons/general/sapio_links.py,sha256=o9Z-8y2rz6AI0Cy6tq58ElPge9RBnisGc9N
35
38
  sapiopycommons/general/storage_util.py,sha256=ovmK_jN7v09BoX07XxwShpBUC5WYQOM7dbKV_VeLXJU,8892
36
39
  sapiopycommons/general/time_util.py,sha256=jUAWmQLNcLHZa4UYB4ht_I3d6uoi63VxYdo7T80Ydw0,7458
37
40
  sapiopycommons/multimodal/multimodal.py,sha256=A1QsC8QTPmgZyPr7KtMbPRedn2Ie4WIErodUvQ9otgU,6724
38
- sapiopycommons/multimodal/multimodal_data.py,sha256=p_caXW0vrURkzDHHspUptEI7lVFpZUrmyF7foz2fAvA,14983
41
+ sapiopycommons/multimodal/multimodal_data.py,sha256=t-0uY4cVgm88uXaSOL4ZeB6zmdHufowXuLFlMk61wFg,15087
39
42
  sapiopycommons/processtracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
43
  sapiopycommons/processtracking/custom_workflow_handler.py,sha256=0Si5RQ1YFmqmcZWV8jNDKTffix2iZnQJ6b97fn31pbc,23859
41
44
  sapiopycommons/processtracking/endpoints.py,sha256=w5bziI2xC7450M95rCF8JpRwkoni1kEDibyAux9B12Q,10848
@@ -50,7 +53,7 @@ sapiopycommons/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
50
53
  sapiopycommons/webhook/webhook_context.py,sha256=D793uLsb1691SalaPnBUk3rOSxn_hYLhdvkaIxjNXss,1909
51
54
  sapiopycommons/webhook/webhook_handlers.py,sha256=JTquLBln49L1pJ9txJ4oc4Hpzy9kYtMKs0m4SLaFx78,18363
52
55
  sapiopycommons/webhook/webservice_handlers.py,sha256=1J56zFI0pWl5MHoNTznvcZumITXgAHJMluj8-2BqYEw,3315
53
- sapiopycommons-2024.10.29a346.dist-info/METADATA,sha256=uuwRweqff1sOl8f3xxmdruTfvovfEHNSsjc17It7Uhw,3177
54
- sapiopycommons-2024.10.29a346.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
55
- sapiopycommons-2024.10.29a346.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
56
- sapiopycommons-2024.10.29a346.dist-info/RECORD,,
56
+ sapiopycommons-2024.10.30a348.dist-info/METADATA,sha256=U8UVQE9LmFPyIDkAZka9MQcyFTd7QGBqH7TSlyx2MKE,3177
57
+ sapiopycommons-2024.10.30a348.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
58
+ sapiopycommons-2024.10.30a348.dist-info/licenses/LICENSE,sha256=HyVuytGSiAUQ6ErWBHTqt1iSGHhLmlC8fO7jTCuR8dU,16725
59
+ sapiopycommons-2024.10.30a348.dist-info/RECORD,,