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.

Files changed (52) hide show
  1. sapiopycommons/callbacks/__init__.py +0 -0
  2. sapiopycommons/callbacks/callback_util.py +2041 -0
  3. sapiopycommons/callbacks/field_builder.py +545 -0
  4. sapiopycommons/chem/IndigoMolecules.py +52 -5
  5. sapiopycommons/chem/Molecules.py +114 -30
  6. sapiopycommons/customreport/__init__.py +0 -0
  7. sapiopycommons/customreport/column_builder.py +60 -0
  8. sapiopycommons/customreport/custom_report_builder.py +137 -0
  9. sapiopycommons/customreport/term_builder.py +315 -0
  10. sapiopycommons/datatype/attachment_util.py +17 -15
  11. sapiopycommons/datatype/data_fields.py +61 -0
  12. sapiopycommons/datatype/pseudo_data_types.py +440 -0
  13. sapiopycommons/eln/experiment_handler.py +390 -90
  14. sapiopycommons/eln/experiment_report_util.py +649 -0
  15. sapiopycommons/eln/plate_designer.py +152 -0
  16. sapiopycommons/files/complex_data_loader.py +31 -0
  17. sapiopycommons/files/file_bridge.py +153 -25
  18. sapiopycommons/files/file_bridge_handler.py +555 -0
  19. sapiopycommons/files/file_data_handler.py +633 -0
  20. sapiopycommons/files/file_util.py +270 -158
  21. sapiopycommons/files/file_validator.py +569 -0
  22. sapiopycommons/files/file_writer.py +377 -0
  23. sapiopycommons/flowcyto/flow_cyto.py +77 -0
  24. sapiopycommons/flowcyto/flowcyto_data.py +75 -0
  25. sapiopycommons/general/accession_service.py +375 -0
  26. sapiopycommons/general/aliases.py +259 -18
  27. sapiopycommons/general/audit_log.py +185 -0
  28. sapiopycommons/general/custom_report_util.py +252 -31
  29. sapiopycommons/general/directive_util.py +86 -0
  30. sapiopycommons/general/exceptions.py +69 -7
  31. sapiopycommons/general/popup_util.py +85 -18
  32. sapiopycommons/general/sapio_links.py +50 -0
  33. sapiopycommons/general/storage_util.py +148 -0
  34. sapiopycommons/general/time_util.py +97 -7
  35. sapiopycommons/multimodal/multimodal.py +146 -0
  36. sapiopycommons/multimodal/multimodal_data.py +490 -0
  37. sapiopycommons/processtracking/__init__.py +0 -0
  38. sapiopycommons/processtracking/custom_workflow_handler.py +406 -0
  39. sapiopycommons/processtracking/endpoints.py +192 -0
  40. sapiopycommons/recordmodel/record_handler.py +653 -149
  41. sapiopycommons/rules/eln_rule_handler.py +89 -8
  42. sapiopycommons/rules/on_save_rule_handler.py +89 -12
  43. sapiopycommons/sftpconnect/__init__.py +0 -0
  44. sapiopycommons/sftpconnect/sftp_builder.py +70 -0
  45. sapiopycommons/webhook/webhook_context.py +39 -0
  46. sapiopycommons/webhook/webhook_handlers.py +617 -69
  47. sapiopycommons/webhook/webservice_handlers.py +317 -0
  48. {sapiopycommons-2024.3.18a156.dist-info → sapiopycommons-2025.1.17a402.dist-info}/METADATA +5 -4
  49. sapiopycommons-2025.1.17a402.dist-info/RECORD +60 -0
  50. {sapiopycommons-2024.3.18a156.dist-info → sapiopycommons-2025.1.17a402.dist-info}/WHEEL +1 -1
  51. sapiopycommons-2024.3.18a156.dist-info/RECORD +0 -28
  52. {sapiopycommons-2024.3.18a156.dist-info → sapiopycommons-2025.1.17a402.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,490 @@
1
+ # Includes general Multimodal registration data structures for specific endpoints.
2
+ # Author: yqiao
3
+ import base64
4
+ from enum import Enum
5
+ from typing import Any
6
+
7
+ from databind.core import ExtraKeys
8
+ from databind.core.dataclasses import dataclass
9
+
10
+
11
+ @dataclass
12
+ class PySimpleMoleculePojo:
13
+ imageSVG: str | None
14
+ smiles: str
15
+ molBlock: str
16
+
17
+
18
+ @dataclass
19
+ @ExtraKeys()
20
+ class PyMolecule:
21
+ """
22
+ Describes a deserialized molecule for Java.
23
+ """
24
+ imageSVG: str | None
25
+ smiles: str
26
+ clogP: float
27
+ tpsa: float
28
+ amw: float
29
+ exactMass: float
30
+ numHBondDonors: int
31
+ numHBondAcceptors: int
32
+ molFormula: str
33
+ charge: int
34
+ molBlock: str
35
+ inchi: str
36
+ inchiKey: str
37
+ stereoisomers: list[PySimpleMoleculePojo] | None
38
+ normError: str | None
39
+ desaltError: str | None
40
+ desaltedList: list[str] | None
41
+ registrationHash: str | None
42
+ hasOrGroup: bool
43
+ CXSMILESHash: str | None
44
+
45
+
46
+ @dataclass
47
+ class PyCompound:
48
+ """
49
+ class PyCompound
50
+ """
51
+
52
+ # The original substance data.
53
+ originalMol: PyMolecule # since May 24, 2023
54
+
55
+ # The normalized, desalted abstract compound data.
56
+ canonicalMol: PyMolecule | None # since May 24, 2023
57
+
58
+ props: dict[str, object] | None
59
+
60
+
61
+ class ChemFileType(Enum):
62
+ CSV = 0
63
+ SDF = 1
64
+
65
+
66
+ class ChemLoadType(Enum):
67
+ SMILES_LIST = 0
68
+ INCHI_LIST = 1
69
+ MOL_BLOCK_LIST = 2
70
+ SDF_FILE = 3
71
+
72
+
73
+ class ChemSearchType(Enum):
74
+ """
75
+ All possible chemistry quicksearch types.
76
+ """
77
+ COMPOUND_SUBSTRUCTURE = 0
78
+ COMPOUND_SIMILARITY = 1
79
+ REACTION_SUBSTRUCTURE = 2
80
+
81
+
82
+ @dataclass
83
+ class ChemLoadingError:
84
+ """
85
+ Describes a single record of compound loading error, this will be produced by interactive loader of compounds.
86
+ Attributes:
87
+ original: The original data that makes up the molecule so user can find it in the file.
88
+ errorMsg: Why the import has failed. (normalization/desalting/sanitization/kekulize)
89
+ properties: The additional attributes in the import. (for mols in SDF)
90
+ """
91
+ original: str
92
+ errorMsg: str
93
+ properties: dict
94
+
95
+
96
+ @dataclass
97
+ class PyMoleculeLoaderResult:
98
+ """
99
+ The results of a loading operation.
100
+
101
+ Attributes:
102
+ compoundByStr: may be blank if irrelevant. The map of source data string to the compound loaded.
103
+ compoundList: the compounds successfully loaded.
104
+ errorList: an error record is added here for each one we failed to load in Sapio.
105
+ """
106
+ compoundByStr: dict[str, PyCompound] | None
107
+ compoundList: list[PyCompound] | None
108
+ errorList: list[ChemLoadingError] | None
109
+
110
+
111
+ @dataclass
112
+ class ChemCompleteImportPojo:
113
+ # Variables declaration with type hints
114
+ dataTypeName: str
115
+ successPartRecordIdList: list[int] | None
116
+ successSampleRecordIdList: list[int] | None
117
+ errors: list[ChemLoadingError] | None
118
+ registeredOriginalParts: list[int]
119
+ allRegisteredPartsNoDuplicates: list[int]
120
+ newPartList: list[int]
121
+ numOldParts: int
122
+
123
+
124
+ @dataclass
125
+ class ChemInteractiveRegisterRequestPojo:
126
+ dataType: str
127
+ fileType: ChemFileType
128
+ fileDataEncodedBase64: str | None
129
+ addingItems: bool
130
+
131
+ def __init__(self, data_type: str, file_type: ChemFileType, is_adding_items: bool, file_data: bytes):
132
+ self.dataType = data_type
133
+ self.fileType = file_type
134
+ self.addingItems = is_adding_items
135
+ self.fileDataEncodedBase64 = base64.b64encode(file_data).decode()
136
+
137
+
138
+ @dataclass
139
+ class CompoundLoadRequestPojo:
140
+ """
141
+ Describes a load request for the Load Compound Endpoint.
142
+
143
+ Attributes:
144
+ dataType: The data type of records to be registered in Sapio.
145
+ loadType: The source data's type you are loading.
146
+ dataList: If the source data is not a file, here you specify a list of string describing molecule for that src.
147
+ fileDataBase64: If the source data is a file, the file's base64 data content.
148
+ """
149
+ dataType: str
150
+ loadType: ChemLoadType
151
+ dataList: list[str] | None
152
+ fileDataBase64: str | None
153
+
154
+ def __init__(self, data_type: str, load_type: ChemLoadType, data_list: list[str] | None = None,
155
+ file_data: bytes | None = None):
156
+ self.dataType = data_type
157
+ self.loadType = load_type
158
+ if load_type is ChemLoadType.SDF_FILE:
159
+ self.dataList = None
160
+ if file_data is None:
161
+ raise ValueError("The file data must be specify the the load type is of a file type.")
162
+ self.fileDataBase64 = base64.b64encode(file_data).decode()
163
+ else:
164
+ self.dataList = data_list
165
+ self.fileDataBase64 = None
166
+
167
+
168
+ @dataclass
169
+ class ChemRegisterRequestPojo:
170
+ """
171
+ Data payload to send to webservice to request registration in RegisterCompoundEndpoint
172
+
173
+ Attributes:
174
+ dataType: The data type of records to be registered in Sapio.
175
+ registrationList: This list must be of correct data structure suitable for the type. For example, for CompoundPart data type the canonical form must be resolved by earlier call.
176
+ """
177
+ dataType: str
178
+ registrationList: list[PyCompound]
179
+
180
+ def __init__(self, data_type: str, registration_list: list[PyCompound]):
181
+ self.dataType = data_type
182
+ self.registrationList = registration_list
183
+
184
+
185
+ @dataclass
186
+ class ImageDataRequestPojo:
187
+ """
188
+ Payload to request in endpoint loading of image data of a compound or a reaction in Sapio's unified drawing format.
189
+
190
+ Attributes:
191
+ dataList: The list of data about the images of the molecules or reactions. This can be SMILES, MOL, or INCHI. SMILES is expected. INCHI can be ambiguous to chrality as well as tautomers.
192
+ isReaction: true if the underlying data is of RxN format of reaction.
193
+ """
194
+ dataList: list[str]
195
+ reaction: bool
196
+
197
+ def __init__(self, data_list: list[str], is_reaction: bool):
198
+ self.dataList = data_list
199
+ self.reaction = is_reaction
200
+
201
+
202
+ @dataclass
203
+ class PyIndigoReactionPojo:
204
+ """
205
+ The result of loading a reaction.
206
+
207
+ Attributes:
208
+ products: products of the reaction.
209
+ reactants: reactants of the reaction.
210
+ reactionSmiles: the SMILES representation of this reaction.
211
+ reactionRxn: the RxN of an arotmized reaction
212
+ reactionRenderRxn: the RxN of no-automapping, DE-aromotized reaction, with 2D coordinates of atoms computed.
213
+ """
214
+ products: list[PyCompound]
215
+ reactants: list[PyCompound]
216
+ reactionSmiles: str
217
+ reactionRxn: str
218
+ reactionRenderRxn: str
219
+
220
+
221
+ @dataclass
222
+ class ChemQuickSearchContextData:
223
+ """
224
+ Do not directly make use of this class. The only use for this class is to pass in the "next page"
225
+ produced by the endpoint.
226
+
227
+ When obtaining the first page, this parameter argument should not be passed at all (not created with default values).
228
+ """
229
+ previousPageSearchAfterJsonStack: list[str] | None
230
+ nextPageSearchAfter: str | None
231
+ pitId: str | None
232
+ query: str | None
233
+ joinSapioPartType: str | None
234
+ simUpperLimit: float | None
235
+
236
+
237
+ @dataclass
238
+ class ChemSearchRequestPojo:
239
+ """
240
+ Payload to send to endpoint to request a chemical search in Sapio.
241
+ This can be a substructure search or similarity search.
242
+
243
+ Attributes:
244
+ searchStr: The search string of SMILES or SMARTS you are searching
245
+ searchType: The type of search you are doing.
246
+ joinMethod: The registry you are using to join with Sapio record. This is not relevant for reactions.
247
+ contextData: The context data of the current page passed to you by result of previous page. If this is the first page you are querying, leave this as None.
248
+ simSearchUpperLimit: similarity search upper limit, between 0.0 to 1.0, valid only to similarity searches.
249
+ """
250
+ searchStr: str
251
+ searchType: ChemSearchType
252
+ joinSapioType: str | None
253
+ contextData: ChemQuickSearchContextData | None
254
+ simSearchUpperLimit: float | None
255
+
256
+ def __init__(self, search_str: str, search_type: ChemSearchType, join_sapio_type: str | None = None,
257
+ context_data: ChemQuickSearchContextData | None = None, sim_search_upper: float | None = None):
258
+ self.searchStr = search_str
259
+ self.searchType = search_type
260
+ self.joinSapioType = join_sapio_type
261
+ self.contextData = context_data
262
+ self.simSearchUpperLimit = sim_search_upper
263
+
264
+
265
+ @dataclass
266
+ class ChemSearchResponsePojo:
267
+ """
268
+ A response object of a chemistry quick search.
269
+ NOTE: It's possible to have next page while the current records list is blank (although without additional query conditions, this should be rare).
270
+
271
+ Attributes:
272
+ recordsOfPage: The records returned in this page.
273
+ nextPageAvailable: Whether there is still a next page to query. If this is filled, then the context to query next page is on nextPageContext.
274
+
275
+ """
276
+ recordIdListOfPage: list[int]
277
+ nextPageAvailable: bool
278
+ nextPageContext: ChemQuickSearchContextData
279
+
280
+
281
+ class MAFFTStrategy(Enum):
282
+ """
283
+ Select one of the strategies for MAFFT multi sequence alignment.
284
+ """
285
+ # Accuracy-Orientated options
286
+ L_INS_i = 0
287
+ G_INS_i = 1
288
+ E_INS_i = 2
289
+ # Speed-Orientated options
290
+ AUTO = 3
291
+ FFT_NS_i = 4
292
+ FFT_NS_2 = 5
293
+ FFT_NS_1 = 6
294
+ NW_NS_i = 7
295
+ NW_NS_2 = 8
296
+ NW_NS_PartTree_1 = 9
297
+
298
+
299
+ class MultiSequenceAlignmentTool(Enum):
300
+ MAFFT = 0
301
+
302
+
303
+ class MultiSeqAlignemntSeqType(Enum):
304
+ nucleic = 0
305
+ amino = 1
306
+
307
+
308
+ @dataclass
309
+ class MultiSequenceAlignmentSeqPojo:
310
+ seqId: str
311
+ seqString: str | None
312
+ hyphSeqString: str | None
313
+
314
+ @staticmethod
315
+ def create(id: str, seq: str):
316
+ ret = MultiSequenceAlignmentSeqPojo(seqId=id, seqString=seq, hyphSeqString=None)
317
+ return ret
318
+
319
+
320
+ class MAFFTRunOptions:
321
+ strategy: MAFFTStrategy
322
+ noScore: bool
323
+ op: float
324
+ lop: float
325
+ lep: float
326
+ lexp: float
327
+ LOP: float
328
+ LEXP: float
329
+ fmodel: bool
330
+
331
+ def __init__(self, strategy: MAFFTStrategy = MAFFTStrategy.AUTO, noScore: bool = True,
332
+ op: float = 1.53, lop: float = -2.0, lep: float = 0.1, lexp: float = -0.1,
333
+ LOP: float = -6.0, LEXP: float = 0.00, fmodel: bool = False):
334
+ self.strategy = strategy
335
+ self.noScore = noScore
336
+ self.op = op
337
+ self.lop = lop
338
+ self.lep = lep
339
+ self.lexp = lexp
340
+ self.LOP = LOP
341
+ self.LEXP = LEXP
342
+ self.fmodel = fmodel
343
+
344
+ def to_json(self) -> dict[str, Any]:
345
+ return {
346
+ "strategy": self.strategy.name,
347
+ "noScore": self.noScore,
348
+ "op": self.op,
349
+ "lop": self.lop,
350
+ "lep": self.lep,
351
+ "lexp": self.lexp,
352
+ "LOP": self.LOP,
353
+ "LEXP": self.LEXP,
354
+ "fmodel": self.fmodel
355
+ }
356
+
357
+
358
+ class ClustalOOptions:
359
+ dealign: bool
360
+ numCombinedIterations: int | None
361
+ numGuideTreeIterations: int | None
362
+ numHMMIterations: int | None
363
+
364
+ def __init__(self, dealign: bool = False, numCombinedIterations: int | None = None,
365
+ numGuideTreeIterations: int | None = None, numHMMIterations: int | None = None):
366
+ self.dealign = dealign
367
+ self.numCombinedIterations = numCombinedIterations
368
+ self.numGuideTreeIterations = numGuideTreeIterations
369
+ self.numHMMIterations = numHMMIterations
370
+
371
+ def to_json(self) -> dict[str, Any]:
372
+ return {
373
+ "dealign": self.dealign,
374
+ "numCombinedIterations": self.numCombinedIterations,
375
+ "numGuideTreeIterations": self.numGuideTreeIterations,
376
+ "numHMMIterations": self.numHMMIterations
377
+ }
378
+
379
+
380
+ class KAlignOptions:
381
+ gapOpenPenalty: float | None
382
+ gapExtensionPenalty: float | None
383
+ terminalGapPenalty: float | None
384
+
385
+ def __init__(self, gapOpenPenalty: float | None = None, gapExtensionPenalty: float | None = None,
386
+ terminalGapPenalty: float | None = None):
387
+ self.gapOpenPenalty = gapOpenPenalty
388
+ self.gapExtensionPenalty = gapExtensionPenalty
389
+ self.terminalGapPenalty = terminalGapPenalty
390
+
391
+ def to_json(self) -> dict[str, Any]:
392
+ return {
393
+ "gapOpenPenalty": self.gapOpenPenalty,
394
+ "gapExtensionPenalty": self.gapExtensionPenalty,
395
+ "terminalGapPenalty": self.terminalGapPenalty
396
+ }
397
+
398
+
399
+ class MuscleOptions:
400
+ consiters: int
401
+ refineiters: int
402
+
403
+ def __init__(self, consiters: int = 2, refineiters: int = 100):
404
+ self.consiters = consiters
405
+ self.refineiters = refineiters
406
+
407
+ def to_json(self) -> dict[str, Any]:
408
+ return {
409
+ "consiters": self.consiters,
410
+ "refineiters": self.refineiters
411
+ }
412
+
413
+
414
+ @dataclass
415
+ class MultiSequenceAlignmentRequestPojo:
416
+ tool: MultiSequenceAlignmentTool
417
+ seqType: MultiSeqAlignemntSeqType
418
+ inputSeqs: list[MultiSequenceAlignmentSeqPojo]
419
+ parameters: dict[str, Any] | None
420
+
421
+ def __init__(self, tool: MultiSequenceAlignmentTool, seq_type: MultiSeqAlignemntSeqType,
422
+ input_sequences: list[MultiSequenceAlignmentSeqPojo],
423
+ parameters: dict[str, Any]):
424
+ self.tool = tool
425
+ self.seqType = seq_type
426
+ self.inputSeqs = input_sequences
427
+ self.parameters = parameters
428
+
429
+
430
+ class BioFileType(Enum):
431
+ """
432
+ Different bio registry supported file types.
433
+ """
434
+ FASTA = 0
435
+ GENBANK = 1
436
+ PDB = 2
437
+ CIF = 3
438
+
439
+
440
+ @dataclass
441
+ class BioFileRegistrationRequest:
442
+ """
443
+ A request object for a single bio-registration request on parts.
444
+ """
445
+ dataTypeName: str
446
+ fileType: BioFileType
447
+ prefilledFieldMapList: list[dict[str, Any]] | None
448
+ overwriteExisting: bool
449
+ fileContent: str
450
+
451
+ def __init__(self, data_type_name: str, file_type: BioFileType, file_content: str,
452
+ prefilled_field_map_list: list[dict[str, Any]] | None = None, overwrite: bool = False):
453
+ self.dataTypeName = data_type_name
454
+ self.fileType = file_type
455
+ self.fileContent = file_content
456
+ self.prefilledFieldMapList = prefilled_field_map_list
457
+ self.overwriteExisting = overwrite
458
+
459
+
460
+ @dataclass
461
+ class BioFileRegistrationResponse:
462
+ """
463
+ A response object for a single bio-registration request on parts.
464
+ """
465
+ newRecordIdList: list[int]
466
+ oldRecordIdList: list[int]
467
+
468
+
469
+ @dataclass
470
+ class ChemExportSDFRequest:
471
+ """
472
+ A request to export SDF data from Sapio to Python REST client.
473
+ """
474
+ partDataTypeName: str
475
+ partRecordIdList: list[int]
476
+
477
+ forceV3000: bool
478
+ fieldNameList: list[str] | None
479
+ assayNameList: list[str] | None
480
+ additionalPropertiesByRecordId: dict[int, dict[str, Any]] | None
481
+
482
+ def __init__(self, partDataTypeName: str, partRecordIdList: list[int], forceV3000: bool = True,
483
+ fieldNameList: list[str] | None = None, assayNameList: list[str] | None = None,
484
+ additionalPropertiesByRecordId: dict[int, dict[str, Any]] | None = None):
485
+ self.partDataTypeName = partDataTypeName
486
+ self.partRecordIdList = partRecordIdList
487
+ self.forceV3000 = forceV3000
488
+ self.fieldNameList = fieldNameList
489
+ self.assayNameList = assayNameList
490
+ self.additionalPropertiesByRecordId = additionalPropertiesByRecordId
File without changes