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