sapiopycommons 2024.6.29a275__tar.gz → 2024.7.2a279__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sapiopycommons might be problematic. Click here for more details.
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/PKG-INFO +1 -1
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/pyproject.toml +1 -1
- sapiopycommons-2024.7.2a279/src/sapiopycommons/general/accession_service.py +375 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/multimodal/multimodal.py +22 -2
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/multimodal/multimodal_data.py +23 -0
- sapiopycommons-2024.7.2a279/tests/accession_test.py +41 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/tests/chem_test.py +27 -2
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/.gitignore +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/LICENSE +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/README.md +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/__init__.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/callbacks/__init__.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/callbacks/callback_util.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/chem/IndigoMolecules.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/chem/Molecules.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/chem/__init__.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/datatype/__init__.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/datatype/attachment_util.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/eln/__init__.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/eln/experiment_handler.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/eln/plate_designer.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/files/__init__.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/files/complex_data_loader.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/files/file_bridge.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/files/file_bridge_handler.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/files/file_data_handler.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/files/file_util.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/files/file_validator.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/files/file_writer.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/general/__init__.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/general/aliases.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/general/custom_report_util.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/general/exceptions.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/general/popup_util.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/general/storage_util.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/general/time_util.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/processtracking/__init__.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/processtracking/endpoints.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/recordmodel/__init__.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/recordmodel/record_handler.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/rules/__init__.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/rules/eln_rule_handler.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/rules/on_save_rule_handler.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/webhook/__init__.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/webhook/webhook_handlers.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/tests/_do_not_add_init_py_here +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/tests/bio_reg_test.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/tests/data_type_models.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/tests/kappa.chains.fasta +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/tests/mafft_test.py +0 -0
- {sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/tests/test.gb +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: sapiopycommons
|
|
3
|
-
Version: 2024.
|
|
3
|
+
Version: 2024.7.2a279
|
|
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>
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any
|
|
5
|
+
from weakref import WeakValueDictionary
|
|
6
|
+
|
|
7
|
+
from sapiopylib.rest.User import SapioUser
|
|
8
|
+
|
|
9
|
+
_STR_JAVA_TYPE = "java.lang.String"
|
|
10
|
+
_INT_JAVA_TYPE = "java.lang.Integer"
|
|
11
|
+
_BOOL_JAVA_TYPE = "java.lang.Boolean"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AbstractAccessionServiceOperator(ABC):
|
|
15
|
+
"""
|
|
16
|
+
Abstract class to define an accession service operator.
|
|
17
|
+
The default one in sapiopycommon only includes the out of box operators.
|
|
18
|
+
More can be added via java plugins for global operators.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def op_class_name(self) -> str:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def op_param_value_list(self) -> list[Any] | None:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def op_param_class_name_list(self) -> list[str] | None:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def default_accessor_name(self) -> str:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class AccessionWithPrefixSuffix(AbstractAccessionServiceOperator):
|
|
43
|
+
"""
|
|
44
|
+
Local operator for accessioning prefix and suffix format.
|
|
45
|
+
"""
|
|
46
|
+
_prefix: str | None
|
|
47
|
+
_suffix: str | None
|
|
48
|
+
_num_of_digits: int | None
|
|
49
|
+
_start_num: int
|
|
50
|
+
_strict_mode: bool
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def prefix(self):
|
|
54
|
+
return self._prefix
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def suffix(self):
|
|
58
|
+
return self._suffix
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def num_of_digits(self):
|
|
62
|
+
return self._num_of_digits
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def start_num(self):
|
|
66
|
+
return self._start_num
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def strict_mode(self):
|
|
70
|
+
return self._strict_mode
|
|
71
|
+
|
|
72
|
+
def __init__(self, prefix: str | None, suffix: str | None, num_of_digits: int | None = None,
|
|
73
|
+
start_num: int = 1, strict_mode: bool = False):
|
|
74
|
+
if prefix is None:
|
|
75
|
+
prefix = ""
|
|
76
|
+
if suffix is None:
|
|
77
|
+
suffix = ""
|
|
78
|
+
self._prefix = prefix
|
|
79
|
+
self._suffix = suffix
|
|
80
|
+
self._num_of_digits = num_of_digits
|
|
81
|
+
self._start_num = start_num
|
|
82
|
+
self._strict_mode = strict_mode
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def op_param_value_list(self):
|
|
86
|
+
return [self._prefix, self._suffix, self._num_of_digits, self._start_num, self._strict_mode]
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def op_param_class_name_list(self):
|
|
90
|
+
return [_STR_JAVA_TYPE, _STR_JAVA_TYPE, _INT_JAVA_TYPE, _INT_JAVA_TYPE, _BOOL_JAVA_TYPE]
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def op_class_name(self):
|
|
94
|
+
return "com.velox.accessionservice.operators.AccessionWithPrefixSuffix"
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def default_accessor_name(self):
|
|
98
|
+
return "PREFIX_AND_SUFFIX" + "(" + self.prefix + "," + self.suffix + ")";
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class AccessionGlobalPrefixSuffix(AbstractAccessionServiceOperator):
|
|
102
|
+
"""
|
|
103
|
+
Global operator for accessioning prefix and suffix format.
|
|
104
|
+
"""
|
|
105
|
+
_prefix: str | None
|
|
106
|
+
_suffix: str | None
|
|
107
|
+
_num_of_digits: int | None
|
|
108
|
+
_start_num: int
|
|
109
|
+
_strict_mode: bool
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def prefix(self):
|
|
113
|
+
return self._prefix
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def suffix(self):
|
|
117
|
+
return self._suffix
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def num_of_digits(self):
|
|
121
|
+
return self._num_of_digits
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def start_num(self):
|
|
125
|
+
return self._start_num
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def strict_mode(self):
|
|
129
|
+
return self._strict_mode
|
|
130
|
+
|
|
131
|
+
def __init__(self, prefix: str | None, suffix: str | None, num_of_digits: int | None = None,
|
|
132
|
+
start_num: int = 1, strict_mode: bool = False):
|
|
133
|
+
if prefix is None:
|
|
134
|
+
prefix = ""
|
|
135
|
+
if suffix is None:
|
|
136
|
+
suffix = ""
|
|
137
|
+
self._prefix = prefix
|
|
138
|
+
self._suffix = suffix
|
|
139
|
+
self._num_of_digits = num_of_digits
|
|
140
|
+
self._start_num = start_num
|
|
141
|
+
self._strict_mode = strict_mode
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def op_param_value_list(self):
|
|
145
|
+
return [self._prefix, self._suffix, self._num_of_digits, self._start_num, self._strict_mode]
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def op_param_class_name_list(self):
|
|
149
|
+
return [_STR_JAVA_TYPE, _STR_JAVA_TYPE, _INT_JAVA_TYPE, _INT_JAVA_TYPE, _BOOL_JAVA_TYPE]
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def op_class_name(self):
|
|
153
|
+
return "com.velox.accessionservice.operators.sapio.AccessionGlobalPrefixSuffix"
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def default_accessor_name(self):
|
|
157
|
+
return "PREFIX_AND_SUFFIX" + "(" + self._prefix + "," + self._suffix + ")"
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class AccessionNextBarcode(AbstractAccessionServiceOperator):
|
|
161
|
+
"""
|
|
162
|
+
From Java description:
|
|
163
|
+
This will start accessioning at the getNextBarcode() when there's no system preference to be backward compatible.
|
|
164
|
+
However, once it completes setting the first ID, it will start increment by its own preference and disregards getNextBarcode().
|
|
165
|
+
|
|
166
|
+
Recommend using AccessionServiceBasicManager to accession next barcode.
|
|
167
|
+
To avoid ambiguity in preference cache.
|
|
168
|
+
|
|
169
|
+
This should not be used unless we are using something legacy such as plate mapping template record creation
|
|
170
|
+
(Note: not 3D plating, I'm talking about the older aliquoter).
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def op_param_value_list(self):
|
|
175
|
+
return []
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def op_param_class_name_list(self):
|
|
179
|
+
return []
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def op_class_name(self):
|
|
183
|
+
return "com.velox.accessionservice.operators.sapio.AccessionNextBarcode"
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def default_accessor_name(self):
|
|
187
|
+
return "Barcode"
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class AccessionRequestId(AbstractAccessionServiceOperator):
|
|
191
|
+
"""
|
|
192
|
+
This class implements the accessioning operator for com.velox.sapioutils.shared.managers.DataRecordUtilManager.getNextRequestId()
|
|
193
|
+
and getNextRequestId(int numberOfCharacters).
|
|
194
|
+
|
|
195
|
+
Operation: For 4 characters start with A001, increment by 1 until A999. Then We use B001.
|
|
196
|
+
After Z999 we start with AA01 until we get to AA99, etc.
|
|
197
|
+
|
|
198
|
+
Exception: Skips I and O to prevent confusions with 1 and 0 when incrementing letters.
|
|
199
|
+
|
|
200
|
+
Properties:
|
|
201
|
+
numberOfCharacters: Number of characters maximum in the request ID.
|
|
202
|
+
accessorName: This is a legacy variable from drum.getNextIdListByMapName(), which allows setting different "accessorName" from old system. We need this for compability patch for converting these to the new preference format.
|
|
203
|
+
"""
|
|
204
|
+
_num_of_characters: int
|
|
205
|
+
_accessor_name: str
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def num_of_characters(self):
|
|
209
|
+
return self._num_of_characters
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def accessor_name(self):
|
|
213
|
+
return self._accessor_name
|
|
214
|
+
|
|
215
|
+
def __init__(self, num_of_characters: int = 4, accessor_name: str = None):
|
|
216
|
+
self._num_of_characters = num_of_characters
|
|
217
|
+
if not accessor_name:
|
|
218
|
+
accessor_name = self.default_accessor_name
|
|
219
|
+
self._accessor_name = accessor_name
|
|
220
|
+
|
|
221
|
+
@property
|
|
222
|
+
def op_class_name(self):
|
|
223
|
+
return "com.velox.accessionservice.operators.sapio.AccessionRequestId"
|
|
224
|
+
|
|
225
|
+
@property
|
|
226
|
+
def op_param_value_list(self):
|
|
227
|
+
return [self._num_of_characters, self._accessor_name]
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def op_param_class_name_list(self):
|
|
231
|
+
return [_INT_JAVA_TYPE, _STR_JAVA_TYPE]
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def default_accessor_name(self):
|
|
235
|
+
return "SapioNextRequestIdMap"
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class AccessionServiceDescriptor:
|
|
239
|
+
"""
|
|
240
|
+
Describes a single accession service's accessioning request
|
|
241
|
+
|
|
242
|
+
Attributes:
|
|
243
|
+
opClassName: The accession service operator class name as in Java
|
|
244
|
+
opParamValueList: Ordered list of parameter values to construct the accession service operator.
|
|
245
|
+
opParamClassNameList: Ordered list of FQCN of java classes in order of parameter value list.
|
|
246
|
+
dataTypeName: The data type to accession. Should be blank if opClassName resolves to a global operator.
|
|
247
|
+
dataFieldName: The data field to accession. Should be blank if opClassName resolves to a global operator.
|
|
248
|
+
accessorName: The accessor cache name to be used for accessioning.
|
|
249
|
+
numIds: The number of IDs to accession.
|
|
250
|
+
"""
|
|
251
|
+
op: AbstractAccessionServiceOperator
|
|
252
|
+
dataTypeName: str | None
|
|
253
|
+
dataFieldName: str | None
|
|
254
|
+
accessorName: str
|
|
255
|
+
numIds: int
|
|
256
|
+
|
|
257
|
+
def __init__(self, accessor_name: str, op: AbstractAccessionServiceOperator, num_ids: int,
|
|
258
|
+
data_type_name: str | None, data_field_name: str | None):
|
|
259
|
+
self.accessorName = accessor_name
|
|
260
|
+
self.op = op
|
|
261
|
+
self.dataTypeName = data_type_name
|
|
262
|
+
self.dataFieldName = data_field_name
|
|
263
|
+
self.numIds = num_ids
|
|
264
|
+
|
|
265
|
+
def to_json(self):
|
|
266
|
+
return {
|
|
267
|
+
"opClassName": self.op.op_class_name,
|
|
268
|
+
"opParamValueList": self.op.op_param_value_list,
|
|
269
|
+
"opParamClassNameList": self.op.op_param_class_name_list,
|
|
270
|
+
"accessorName": self.accessorName,
|
|
271
|
+
"numIds": self.numIds,
|
|
272
|
+
"dataTypeName": self.dataTypeName,
|
|
273
|
+
"dataFieldName": self.dataFieldName
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class AccessionService:
|
|
278
|
+
"""
|
|
279
|
+
Provides Sapio Foundations Accession Service functionalities.
|
|
280
|
+
"""
|
|
281
|
+
_user: SapioUser
|
|
282
|
+
|
|
283
|
+
__instances: WeakValueDictionary[SapioUser, AccessionService] = WeakValueDictionary()
|
|
284
|
+
__initialized: bool
|
|
285
|
+
|
|
286
|
+
@property
|
|
287
|
+
def user(self) -> SapioUser:
|
|
288
|
+
return self._user
|
|
289
|
+
|
|
290
|
+
def __new__(cls, user: SapioUser):
|
|
291
|
+
"""
|
|
292
|
+
Observes singleton pattern per record model manager object.
|
|
293
|
+
|
|
294
|
+
:param user: The user that will make the webservice request to the application.
|
|
295
|
+
"""
|
|
296
|
+
obj = cls.__instances.get(user)
|
|
297
|
+
if not obj:
|
|
298
|
+
obj = object.__new__(cls)
|
|
299
|
+
obj.__initialized = False
|
|
300
|
+
cls.__instances[user] = obj
|
|
301
|
+
return obj
|
|
302
|
+
|
|
303
|
+
def __init__(self, user: SapioUser):
|
|
304
|
+
if self.__initialized:
|
|
305
|
+
return
|
|
306
|
+
self._user = user
|
|
307
|
+
self.__initialized = True
|
|
308
|
+
|
|
309
|
+
def accession_with_config(self, data_type_name: str, data_field_name: str, num_ids: int) -> list[str]:
|
|
310
|
+
"""
|
|
311
|
+
Accession with Configuration Manager => Accession Service configuration (This is not visible to regular users in SaaS)
|
|
312
|
+
"""
|
|
313
|
+
payload = {
|
|
314
|
+
"dataTypeName": data_type_name,
|
|
315
|
+
"dataFieldName": data_field_name,
|
|
316
|
+
"numIds": num_ids
|
|
317
|
+
}
|
|
318
|
+
response = self.user.plugin_post("accessionservice/accession_with_config", payload=payload)
|
|
319
|
+
self.user.raise_for_status(response)
|
|
320
|
+
return list(response.json())
|
|
321
|
+
|
|
322
|
+
def accession_in_batch(self, descriptor: AccessionServiceDescriptor) -> list[str]:
|
|
323
|
+
"""
|
|
324
|
+
This is the most flexible way to make use of accession service: directly via a descriptor object.
|
|
325
|
+
"""
|
|
326
|
+
payload = descriptor.to_json()
|
|
327
|
+
response = self.user.plugin_post("accessionservice/accession", payload=payload)
|
|
328
|
+
self.user.raise_for_status(response)
|
|
329
|
+
return list(response.json())
|
|
330
|
+
|
|
331
|
+
def accession_next_request_id_list(self, num_of_characters: int, num_ids: int) -> list[str]:
|
|
332
|
+
"""
|
|
333
|
+
Accession Request ID by old LIMS format. This is usually deprecated today.
|
|
334
|
+
:param num_of_characters: Number of characters minimum in request ID.
|
|
335
|
+
:param num_ids: Number of request IDs to accession.
|
|
336
|
+
"""
|
|
337
|
+
op = AccessionRequestId(num_of_characters)
|
|
338
|
+
descriptor = AccessionServiceDescriptor(op.default_accessor_name, op, num_ids, None, None)
|
|
339
|
+
return self.accession_in_batch(descriptor)
|
|
340
|
+
|
|
341
|
+
def get_affixed_id_in_batch(self, data_type_name: str, data_field_name: str, num_ids: int, prefix: str | None,
|
|
342
|
+
suffix: str | None, num_digits: int | None, start_num: int = 1) -> list[str]:
|
|
343
|
+
"""
|
|
344
|
+
Get the batch affixed IDs that are maximal in cache and contiguious for a particular datatype.datafield under a given format.
|
|
345
|
+
:param data_type_name: The datatype name to look for max ID
|
|
346
|
+
:param data_field_name: The datafield name to look for max ID
|
|
347
|
+
:param num_ids: The number of IDs to accession.
|
|
348
|
+
:param prefix: leave it empty string "" if no prefix. Otherwise, specifies the prefix of ID.
|
|
349
|
+
:param suffix: leave it empty string "" if no suffix. Otherwise, specifies the suffix of ID.
|
|
350
|
+
:param num_digits: None if unlimited with no leading zeros.
|
|
351
|
+
:param start_num The number to begin accessioning if this is the first time.
|
|
352
|
+
:return:
|
|
353
|
+
"""
|
|
354
|
+
op = AccessionWithPrefixSuffix(prefix, suffix, num_digits, start_num)
|
|
355
|
+
descriptor = AccessionServiceDescriptor(op.default_accessor_name, op, num_ids, data_type_name, data_field_name)
|
|
356
|
+
return self.accession_in_batch(descriptor)
|
|
357
|
+
|
|
358
|
+
def get_global_affixed_id_in_batch(
|
|
359
|
+
self, num_ids: int, prefix: str | None, suffix: str | None, num_digits: int | None, start_num: int = 1) -> list[str]:
|
|
360
|
+
"""
|
|
361
|
+
Get the next numOfIds affixed IDs using system preference cache that's maximum across all datatype and datafields and maximal for the format.
|
|
362
|
+
This method allows users to customize a start number instead of always starting at 1.
|
|
363
|
+
:param num_ids: The number of IDs to accession.
|
|
364
|
+
:param prefix: leave it empty string "" if no prefix. Otherwise, specifies the prefix of ID.
|
|
365
|
+
:param suffix: leave it empty string "" if no suffix. Otherwise, specifies the suffix of ID.
|
|
366
|
+
:param num_digits: None if unlimited with no leading zeros.
|
|
367
|
+
:param start_num The number to begin accessioning if this is the first time.
|
|
368
|
+
"""
|
|
369
|
+
op: AbstractAccessionServiceOperator
|
|
370
|
+
if not prefix and not suffix:
|
|
371
|
+
op = AccessionNextBarcode()
|
|
372
|
+
else:
|
|
373
|
+
op = AccessionGlobalPrefixSuffix(prefix, suffix, num_digits, start_num)
|
|
374
|
+
descriptor = AccessionServiceDescriptor(op.default_accessor_name, op, num_ids, None, None)
|
|
375
|
+
return self.accession_in_batch(descriptor)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# Multimodal registration client
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
import io
|
|
4
5
|
from weakref import WeakValueDictionary
|
|
5
6
|
|
|
6
7
|
from databind.json import dumps, loads
|
|
7
8
|
from sapiopylib.rest.User import SapioUser
|
|
8
9
|
|
|
10
|
+
from sapiopycommons.general.exceptions import SapioException
|
|
9
11
|
from sapiopycommons.multimodal.multimodal_data import *
|
|
10
12
|
|
|
11
13
|
|
|
@@ -103,7 +105,7 @@ class MultiModalManager:
|
|
|
103
105
|
self._user.raise_for_status(response)
|
|
104
106
|
return loads(response.text, ChemSearchResponsePojo)
|
|
105
107
|
|
|
106
|
-
def run_multi_sequence_alignment(self, request: MultiSequenceAlignmentRequestPojo):
|
|
108
|
+
def run_multi_sequence_alignment(self, request: MultiSequenceAlignmentRequestPojo) -> list[MultiSequenceAlignmentSeqPojo]:
|
|
107
109
|
"""
|
|
108
110
|
Run a multi-sequence alignment using the specified tool and strategy.
|
|
109
111
|
:param request: The request object containing the sequences and alignment parameters. The parameters inside it can be the pojo dict of one of the options.
|
|
@@ -115,7 +117,7 @@ class MultiModalManager:
|
|
|
115
117
|
self._user.raise_for_status(response)
|
|
116
118
|
return loads(response.text, list[MultiSequenceAlignmentSeqPojo])
|
|
117
119
|
|
|
118
|
-
def register_bio(self, request: BioFileRegistrationRequest):
|
|
120
|
+
def register_bio(self, request: BioFileRegistrationRequest) -> BioFileRegistrationResponse:
|
|
119
121
|
"""
|
|
120
122
|
Register to bioregistry of a file.
|
|
121
123
|
"""
|
|
@@ -124,3 +126,21 @@ class MultiModalManager:
|
|
|
124
126
|
self._user.raise_for_status(response)
|
|
125
127
|
return loads(response.text, BioFileRegistrationResponse)
|
|
126
128
|
|
|
129
|
+
def export_to_sdf(self, request: ChemExportSDFRequest) -> str:
|
|
130
|
+
"""
|
|
131
|
+
Export the SDF files
|
|
132
|
+
:param request: The request for exporting SDF file.
|
|
133
|
+
:return: the SDF plain text data.
|
|
134
|
+
"""
|
|
135
|
+
payload = dumps(request, ChemExportSDFRequest)
|
|
136
|
+
response = self._user.plugin_post("chemistry/export_sdf", payload=payload, is_payload_plain_text=True)
|
|
137
|
+
self._user.raise_for_status(response)
|
|
138
|
+
gzip_base64: str = response.text
|
|
139
|
+
if not gzip_base64:
|
|
140
|
+
raise SapioException("Returning data from server is blank for export SDF.")
|
|
141
|
+
decoded_bytes = base64.b64decode(gzip_base64)
|
|
142
|
+
with io.BytesIO(decoded_bytes) as bytes_io:
|
|
143
|
+
import gzip
|
|
144
|
+
with gzip.GzipFile(fileobj=bytes_io, mode='rb') as f:
|
|
145
|
+
ret: str = f.read().decode()
|
|
146
|
+
return ret
|
|
@@ -462,3 +462,26 @@ class BioFileRegistrationResponse:
|
|
|
462
462
|
newRecordIdList: list[int]
|
|
463
463
|
oldRecordIdList: list[int]
|
|
464
464
|
|
|
465
|
+
|
|
466
|
+
@dataclass
|
|
467
|
+
class ChemExportSDFRequest:
|
|
468
|
+
"""
|
|
469
|
+
A request to export SDF data from Sapio to Python REST client.
|
|
470
|
+
"""
|
|
471
|
+
partDataTypeName: str
|
|
472
|
+
partRecordIdList: list[int]
|
|
473
|
+
|
|
474
|
+
forceV3000: bool
|
|
475
|
+
fieldNameList: list[str] | None
|
|
476
|
+
assayNameList: list[str] | None
|
|
477
|
+
additionalPropertiesByRecordId: dict[int, dict[str, Any]] | None
|
|
478
|
+
|
|
479
|
+
def __init__(self, partDataTypeName: str, partRecordIdList: list[int], forceV3000: bool = True,
|
|
480
|
+
fieldNameList: list[str] | None = None, assayNameList: list[str] | None = None,
|
|
481
|
+
additionalPropertiesByRecordId: dict[int, dict[str, Any]] | None = None):
|
|
482
|
+
self.partDataTypeName = partDataTypeName
|
|
483
|
+
self.partRecordIdList = partRecordIdList
|
|
484
|
+
self.forceV3000 = forceV3000
|
|
485
|
+
self.fieldNameList = fieldNameList
|
|
486
|
+
self.assayNameList = assayNameList
|
|
487
|
+
self.additionalPropertiesByRecordId = additionalPropertiesByRecordId
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from sapiopylib.rest.User import SapioUser
|
|
4
|
+
|
|
5
|
+
from sapiopycommons.general.accession_service import AccessionService
|
|
6
|
+
from data_type_models import SampleModel
|
|
7
|
+
|
|
8
|
+
user = SapioUser(url="https://linux-vm:8443/webservice/api", verify_ssl_cert=False,
|
|
9
|
+
guid="3c232543-f407-4828-aae5-b33d4cd31fa7",
|
|
10
|
+
username="yqiao_api", password="Password1!")
|
|
11
|
+
|
|
12
|
+
accession_service = AccessionService(user)
|
|
13
|
+
|
|
14
|
+
# This test needs to be run together with accessioning happening separately inside Sapio for creating samples/plates in 3D plater.
|
|
15
|
+
# Set plate to auto accession mode and ensure plate ID is non-unique to trigger global accessioning logic.
|
|
16
|
+
class AccessionServiceTest(unittest.TestCase):
|
|
17
|
+
|
|
18
|
+
def test_sample_accession_with_config(self):
|
|
19
|
+
"""
|
|
20
|
+
This is how platform accessions new samples that isn't an aliquot or pool.
|
|
21
|
+
"""
|
|
22
|
+
id_list = accession_service.accession_with_config(SampleModel.DATA_TYPE_NAME, SampleModel.SAMPLEID__FIELD_NAME.field_name, 5)
|
|
23
|
+
print("New Sample IDs: " + str(id_list) +
|
|
24
|
+
". Accession some more samples in ELN grabber and run this again and check continuity.")
|
|
25
|
+
self.assertEqual(len(id_list), 5)
|
|
26
|
+
|
|
27
|
+
def test_plate_id_nonunique_accession(self):
|
|
28
|
+
"""
|
|
29
|
+
This will test the platform accessioning plate IDs.
|
|
30
|
+
"""
|
|
31
|
+
# This is from MultiLayerPlatingConfigManager
|
|
32
|
+
id_list = accession_service.get_global_affixed_id_in_batch(5, "", "", 4, 1000)
|
|
33
|
+
print("New Plate IDs: " + str(id_list) + ". Check continuty in ELN using 3d plating auto accession plate ID, then make sure the plate ID is non-unique.")
|
|
34
|
+
self.assertEqual(len(id_list), 5)
|
|
35
|
+
|
|
36
|
+
def test_accession_pool_ids(self):
|
|
37
|
+
# This is from CreateBoolBase, and MultiLayerPlatingRecordManager
|
|
38
|
+
id_list = accession_service.get_affixed_id_in_batch(SampleModel.DATA_TYPE_NAME, SampleModel.SAMPLEID__FIELD_NAME.field_name,
|
|
39
|
+
10, "Pool-", "", None, 1000)
|
|
40
|
+
print("New Pool IDs: " + str(id_list))
|
|
41
|
+
self.assertEqual(len(id_list), 10)
|
|
@@ -6,11 +6,12 @@ from sapiopylib.rest.ClientCallbackService import ClientCallback
|
|
|
6
6
|
from sapiopylib.rest.DataMgmtService import DataMgmtServer
|
|
7
7
|
from sapiopylib.rest.DataTypeService import DataTypeManager
|
|
8
8
|
from sapiopylib.rest.WebhookService import AbstractWebhookHandler, WebhookConfiguration, WebhookServerFactory
|
|
9
|
+
from sapiopylib.rest.pojo.CustomReport import RawReportTerm, RawTermOperation
|
|
9
10
|
from sapiopylib.rest.pojo.datatype.FieldDefinition import VeloxDoubleFieldDefinition
|
|
10
11
|
from sapiopylib.rest.pojo.webhook.ClientCallbackRequest import *
|
|
11
12
|
from sapiopylib.rest.pojo.webhook.WebhookContext import SapioWebhookContext
|
|
12
13
|
from sapiopylib.rest.pojo.webhook.WebhookResult import SapioWebhookResult
|
|
13
|
-
from sapiopylib.rest.utils.autopaging import QueryDataRecordByIdListAutoPager
|
|
14
|
+
from sapiopylib.rest.utils.autopaging import QueryDataRecordByIdListAutoPager, QueryAllRecordsOfTypeAutoPager
|
|
14
15
|
|
|
15
16
|
from data_type_models import *
|
|
16
17
|
from sapiopycommons.multimodal.multimodal import MultiModalManager
|
|
@@ -173,6 +174,28 @@ def structure_search(context: SapioWebhookContext) -> SapioWebhookResult:
|
|
|
173
174
|
return SapioWebhookResult(True)
|
|
174
175
|
|
|
175
176
|
|
|
177
|
+
def export_sdf(context: SapioWebhookContext) -> SapioWebhookResult:
|
|
178
|
+
user = context.user
|
|
179
|
+
custom_report_manager = DataMgmtServer.get_custom_report_manager(user)
|
|
180
|
+
client_callback = DataMgmtServer.get_client_callback(user)
|
|
181
|
+
report = custom_report_manager.run_quick_report(RawReportTerm(ChemicalReagentPartModel.DATA_TYPE_NAME, "RecordId", RawTermOperation.GREATER_THAN_OPERATOR, "0"), 100, 0)
|
|
182
|
+
result_table: list[list[Any]] = report.result_table
|
|
183
|
+
record_id_col_index: int = [idx for idx, x in enumerate(report.column_list) if x.data_field_name == "RecordId"][0]
|
|
184
|
+
record_id_list = [int(x[record_id_col_index]) for x in result_table if x[record_id_col_index] is not None]
|
|
185
|
+
if not record_id_list:
|
|
186
|
+
client_callback.display_warning("There are no Chemical Reagent Part to test with. Please register at least 1 such record and test again.")
|
|
187
|
+
return SapioWebhookResult(True)
|
|
188
|
+
custom_payload: dict[int, dict[str, str]] = dict()
|
|
189
|
+
for i, record_id in enumerate(record_id_list):
|
|
190
|
+
custom_payload[record_id] = {"pyIndex": str(i)}
|
|
191
|
+
request = ChemExportSDFRequest(ChemicalReagentPartModel.DATA_TYPE_NAME, record_id_list,
|
|
192
|
+
additionalPropertiesByRecordId=custom_payload)
|
|
193
|
+
sdf_text = MultiModalManager(user).export_to_sdf(request)
|
|
194
|
+
with BytesIO(sdf_text.encode()) as io:
|
|
195
|
+
client_callback.send_file("output.sdf", False, io)
|
|
196
|
+
return SapioWebhookResult(True)
|
|
197
|
+
|
|
198
|
+
|
|
176
199
|
class ChemistryRegistrationTestHandler(AbstractWebhookHandler):
|
|
177
200
|
"""
|
|
178
201
|
This one will test the facilities to parse a chemical structure and return a svg file to user's browser.
|
|
@@ -180,7 +203,7 @@ class ChemistryRegistrationTestHandler(AbstractWebhookHandler):
|
|
|
180
203
|
|
|
181
204
|
def run(self, context: SapioWebhookContext) -> SapioWebhookResult:
|
|
182
205
|
options = ['Download Image', 'Register Non-Interactively', 'Structure Search',
|
|
183
|
-
'Register Reaction']
|
|
206
|
+
'Register Reaction', "Export SDF"]
|
|
184
207
|
user = context.user
|
|
185
208
|
client_callback = DataMgmtServer.get_client_callback(user)
|
|
186
209
|
selected = client_callback.show_option_dialog(OptionDialogRequest(
|
|
@@ -195,6 +218,8 @@ class ChemistryRegistrationTestHandler(AbstractWebhookHandler):
|
|
|
195
218
|
return structure_search(context)
|
|
196
219
|
elif selected == 3:
|
|
197
220
|
return register_reaction(context)
|
|
221
|
+
elif selected == 4:
|
|
222
|
+
return export_sdf(context)
|
|
198
223
|
return SapioWebhookResult(False)
|
|
199
224
|
|
|
200
225
|
config: WebhookConfiguration = WebhookConfiguration(verify_sapio_cert=False, debug=True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/chem/Molecules.py
RENAMED
|
File without changes
|
{sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/chem/__init__.py
RENAMED
|
File without changes
|
{sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/datatype/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/eln/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/files/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/files/file_bridge.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/files/file_util.py
RENAMED
|
File without changes
|
|
File without changes
|
{sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/files/file_writer.py
RENAMED
|
File without changes
|
{sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/general/__init__.py
RENAMED
|
File without changes
|
{sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/general/aliases.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/general/time_util.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/rules/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sapiopycommons-2024.6.29a275 → sapiopycommons-2024.7.2a279}/src/sapiopycommons/webhook/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|