rcsb.workflow 0.64__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.
- rcsb/__init__.py +1 -0
- rcsb/workflow/__init__.py +1 -0
- rcsb/workflow/bcif/__init__.py +0 -0
- rcsb/workflow/bcif/task_functions.py +333 -0
- rcsb/workflow/chem/ChemCompFileWorkflow.py +96 -0
- rcsb/workflow/chem/ChemCompImageWorkflow.py +133 -0
- rcsb/workflow/chem/ChemCompSearchIndexWorkflow.py +99 -0
- rcsb/workflow/chem/__init__.py +0 -0
- rcsb/workflow/cli/BcifExec.py +179 -0
- rcsb/workflow/cli/ChemSearchIndexExec.py +65 -0
- rcsb/workflow/cli/ExDbExec.py +240 -0
- rcsb/workflow/cli/ImgExec.py +64 -0
- rcsb/workflow/cli/__init__.py +0 -0
- rcsb/workflow/targets/ProteinTargetSequenceExecutionWorkflow.py +361 -0
- rcsb/workflow/targets/ProteinTargetSequenceWorkflow.py +615 -0
- rcsb/workflow/targets/__init__.py +0 -0
- rcsb/workflow/wuw/BcifWorkflow.py +87 -0
- rcsb/workflow/wuw/ExDbWorkflow.py +505 -0
- rcsb/workflow/wuw/PdbCsmImageWorkflow.py +198 -0
- rcsb/workflow/wuw/WuwUtils.py +23 -0
- rcsb/workflow/wuw/__init__.py +0 -0
- rcsb_workflow-0.64.dist-info/METADATA +77 -0
- rcsb_workflow-0.64.dist-info/RECORD +26 -0
- rcsb_workflow-0.64.dist-info/WHEEL +4 -0
- rcsb_workflow-0.64.dist-info/entry_points.txt +5 -0
- rcsb_workflow-0.64.dist-info/licenses/LICENSE +201 -0
rcsb/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
|
|
File without changes
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
##
|
|
2
|
+
# File: task_functions.py
|
|
3
|
+
# Author: James Smith
|
|
4
|
+
# Date: 21-Feb-2025
|
|
5
|
+
##
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
Workflow task descriptors.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__docformat__ = "google en"
|
|
12
|
+
__author__ = "James Smith"
|
|
13
|
+
__email__ = "james.smith@rcsb.org"
|
|
14
|
+
__license__ = "Apache 2.0"
|
|
15
|
+
|
|
16
|
+
import multiprocessing
|
|
17
|
+
import os
|
|
18
|
+
import shutil
|
|
19
|
+
import tempfile
|
|
20
|
+
import logging
|
|
21
|
+
from enum import Enum
|
|
22
|
+
from typing import Optional
|
|
23
|
+
from mmcif.api.DictionaryApi import DictionaryApi
|
|
24
|
+
from mmcif.io.IoAdapterPy import IoAdapterPy as IoAdapter
|
|
25
|
+
from rcsb.utils.io.MarshalUtil import MarshalUtil
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
logger.setLevel(logging.INFO)
|
|
29
|
+
|
|
30
|
+
ContentTypeEnum = Enum(
|
|
31
|
+
"ContentTypeEnum",
|
|
32
|
+
[("EXPERIMENTAL", "pdb"), ("COMPUTATIONAL", "csm"), ("INTEGRATIVE", "ihm")],
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def convertCifFilesToBcif(
|
|
37
|
+
listFileName: str,
|
|
38
|
+
listFileBase: str,
|
|
39
|
+
remotePath: str,
|
|
40
|
+
updateBase: str,
|
|
41
|
+
outfileSuffix: str,
|
|
42
|
+
contentType: str,
|
|
43
|
+
outputContentType: bool,
|
|
44
|
+
outputHash: bool,
|
|
45
|
+
inputHash: bool,
|
|
46
|
+
batchSize: int,
|
|
47
|
+
maxFiles: int,
|
|
48
|
+
pdbxDict: str,
|
|
49
|
+
maDict: str,
|
|
50
|
+
rcsbDict: str,
|
|
51
|
+
ihmDict: str,
|
|
52
|
+
flrDict: str,
|
|
53
|
+
):
|
|
54
|
+
"""
|
|
55
|
+
Converts CIF files to BCIF format based on a given list file.
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
FileNotFoundError: If the specified list file does not exist.
|
|
59
|
+
ValueError: If the list file is empty.
|
|
60
|
+
RuntimeError: If processing fails for some files.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
# read sublist
|
|
64
|
+
listfilepath = os.path.join(listFileBase, listFileName)
|
|
65
|
+
logger.info(
|
|
66
|
+
"reading list file %s and remote path %s",
|
|
67
|
+
listfilepath,
|
|
68
|
+
remotePath,
|
|
69
|
+
)
|
|
70
|
+
files = []
|
|
71
|
+
if not os.path.exists(listfilepath):
|
|
72
|
+
logger.warning("No list file found at %s", listfilepath)
|
|
73
|
+
return
|
|
74
|
+
f = open(listfilepath, "r", encoding="utf-8")
|
|
75
|
+
for line in f:
|
|
76
|
+
files.append(line.strip())
|
|
77
|
+
if 0 < maxFiles <= len(files):
|
|
78
|
+
break
|
|
79
|
+
f.close()
|
|
80
|
+
if len(files) < 1:
|
|
81
|
+
raise ValueError("No files found in %s" % listfilepath)
|
|
82
|
+
|
|
83
|
+
# determine batch size
|
|
84
|
+
if (batchSize is None) or not str(batchSize).isdigit():
|
|
85
|
+
batchSize = 1
|
|
86
|
+
batchSize = int(batchSize)
|
|
87
|
+
if batchSize <= 0:
|
|
88
|
+
batchSize = 1
|
|
89
|
+
logger.info("distributing %d files across %d sublists", len(files), batchSize)
|
|
90
|
+
|
|
91
|
+
# form dictionary object
|
|
92
|
+
dictionaryApi = getDictionaryApi(pdbxDict, maDict, rcsbDict, ihmDict, flrDict)
|
|
93
|
+
|
|
94
|
+
# traverse sublist and send each input file to converter
|
|
95
|
+
temppath = tempfile.mkdtemp()
|
|
96
|
+
if batchSize == 1:
|
|
97
|
+
# process one file at a time
|
|
98
|
+
for line in files:
|
|
99
|
+
args = (
|
|
100
|
+
line,
|
|
101
|
+
remotePath,
|
|
102
|
+
updateBase,
|
|
103
|
+
outfileSuffix,
|
|
104
|
+
outputContentType,
|
|
105
|
+
outputHash,
|
|
106
|
+
inputHash,
|
|
107
|
+
contentType,
|
|
108
|
+
dictionaryApi,
|
|
109
|
+
temppath,
|
|
110
|
+
)
|
|
111
|
+
singleTask(*args)
|
|
112
|
+
elif batchSize > 1:
|
|
113
|
+
pool = multiprocessing.Pool(processes=batchSize)
|
|
114
|
+
results = []
|
|
115
|
+
for line in files:
|
|
116
|
+
entry = line.strip()
|
|
117
|
+
args = (
|
|
118
|
+
entry,
|
|
119
|
+
remotePath,
|
|
120
|
+
updateBase,
|
|
121
|
+
outfileSuffix,
|
|
122
|
+
outputContentType,
|
|
123
|
+
outputHash,
|
|
124
|
+
inputHash,
|
|
125
|
+
contentType,
|
|
126
|
+
dictionaryApi,
|
|
127
|
+
temppath,
|
|
128
|
+
)
|
|
129
|
+
results.append(pool.apply_async(singleTask, args))
|
|
130
|
+
for r in results:
|
|
131
|
+
r.get(timeout=60 * 5)
|
|
132
|
+
pool.close()
|
|
133
|
+
results.clear()
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
if os.path.exists(temppath):
|
|
137
|
+
shutil.rmtree(temppath)
|
|
138
|
+
except Exception as e:
|
|
139
|
+
logger.error(str(e))
|
|
140
|
+
|
|
141
|
+
logger.info("Done with CIF to BCIF conversion")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def getDictionaryApi(
|
|
145
|
+
pdbxDict: str, maDict: str, rcsbDict: str, ihmDict: str, flrDict: str
|
|
146
|
+
) -> DictionaryApi:
|
|
147
|
+
paths = [pdbxDict, maDict, rcsbDict, ihmDict, flrDict]
|
|
148
|
+
try:
|
|
149
|
+
adapter = IoAdapter(raiseExceptions=True)
|
|
150
|
+
containers = []
|
|
151
|
+
for path in paths:
|
|
152
|
+
containers += adapter.readFile(inputFilePath=path)
|
|
153
|
+
dictionaryApi = DictionaryApi(containerList=containers, consolidate=True)
|
|
154
|
+
except Exception as e:
|
|
155
|
+
raise FileNotFoundError("failed to create dictionary api: %s" % str(e)) from e
|
|
156
|
+
return dictionaryApi
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def singleTask(
|
|
160
|
+
pdbId: str,
|
|
161
|
+
remotePath: str,
|
|
162
|
+
updateBase: str,
|
|
163
|
+
outfileSuffix: str,
|
|
164
|
+
outputContentType: bool,
|
|
165
|
+
outputHash: bool,
|
|
166
|
+
inputHash: bool,
|
|
167
|
+
contentType: str,
|
|
168
|
+
dictionaryApi: DictionaryApi,
|
|
169
|
+
temppath: str,
|
|
170
|
+
) -> None:
|
|
171
|
+
remoteFileName = "%s%s" % (
|
|
172
|
+
getInputPdbId(pdbId, contentType),
|
|
173
|
+
outfileSuffix.replace(".bcif.gz", ".cif.gz").replace(".bcif", ".cif"),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# form input cifFilePath
|
|
177
|
+
if not remotePath.startswith("http"):
|
|
178
|
+
# local file
|
|
179
|
+
cifFilePath = os.path.join(remotePath, remoteFileName)
|
|
180
|
+
if inputHash:
|
|
181
|
+
cifFilePath = os.path.join(
|
|
182
|
+
remotePath,
|
|
183
|
+
getHash(getInputPdbId(pdbId, contentType), contentType),
|
|
184
|
+
remoteFileName,
|
|
185
|
+
)
|
|
186
|
+
if not os.path.exists(cifFilePath):
|
|
187
|
+
logger.warning("%s not found", cifFilePath)
|
|
188
|
+
return
|
|
189
|
+
else:
|
|
190
|
+
cifFilePath = getRemoteFilePath(
|
|
191
|
+
getInputPdbId(pdbId, contentType), contentType, remotePath, remoteFileName
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# form output bcifFilePath
|
|
195
|
+
bcifFilePath = getBcifFilePath(
|
|
196
|
+
pdbId, outfileSuffix, updateBase, contentType, outputContentType, outputHash
|
|
197
|
+
)
|
|
198
|
+
if not bcifFilePath:
|
|
199
|
+
raise ValueError("could not form bcif file path for %s" % pdbId)
|
|
200
|
+
|
|
201
|
+
if os.path.exists(bcifFilePath):
|
|
202
|
+
# earlier timestamp ... overwrite
|
|
203
|
+
os.unlink(bcifFilePath)
|
|
204
|
+
if os.path.exists(bcifFilePath):
|
|
205
|
+
raise PermissionError("file %s not removed" % bcifFilePath)
|
|
206
|
+
|
|
207
|
+
# make nested directories
|
|
208
|
+
dirs = os.path.dirname(bcifFilePath)
|
|
209
|
+
if not os.path.exists(dirs):
|
|
210
|
+
os.makedirs(dirs)
|
|
211
|
+
|
|
212
|
+
# convert to bcif
|
|
213
|
+
result = convert(cifFilePath, bcifFilePath, temppath, dictionaryApi)
|
|
214
|
+
if not result:
|
|
215
|
+
raise Exception("failed to convert %s" % cifFilePath)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def getInputPdbId(pdbId: str, contentType: str) -> str:
|
|
219
|
+
"""determine upper or lowercase id names for remote and local .cif input files"""
|
|
220
|
+
if contentType == ContentTypeEnum.COMPUTATIONAL.value:
|
|
221
|
+
return pdbId.upper()
|
|
222
|
+
return pdbId.lower()
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def getOutputPdbId(pdbId: str) -> str:
|
|
226
|
+
"""determine upper or lowercase id names for local .bcif output files"""
|
|
227
|
+
return pdbId.lower()
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def getOutputContentType(contentType: str) -> str:
|
|
231
|
+
"""prepended output folder name for local .bcif output files"""
|
|
232
|
+
if contentType in [ContentTypeEnum.EXPERIMENTAL.value, ContentTypeEnum.INTEGRATIVE.value]:
|
|
233
|
+
return "pdb"
|
|
234
|
+
return "csm"
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def getHash(pdbId: str, contentType: str) -> str:
|
|
238
|
+
"""get upper/lowercase pdb id prior to getting hash"""
|
|
239
|
+
result = pdbId[1:3]
|
|
240
|
+
if contentType == ContentTypeEnum.COMPUTATIONAL.value:
|
|
241
|
+
result = os.path.join(pdbId[0:2], pdbId[-6:-4], pdbId[-4:-2])
|
|
242
|
+
return result
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def getRemoteFilePath(
|
|
246
|
+
pdbId: str, contentType: str, remotePath: str, remoteFileName: str
|
|
247
|
+
) -> str:
|
|
248
|
+
"""form hashed pash for remote cif file, only for input files"""
|
|
249
|
+
pdbId = getInputPdbId(pdbId, contentType)
|
|
250
|
+
result = os.path.join(remotePath, getHash(pdbId, contentType), remoteFileName)
|
|
251
|
+
if contentType == ContentTypeEnum.INTEGRATIVE.value:
|
|
252
|
+
result = os.path.join(
|
|
253
|
+
remotePath, getHash(pdbId, contentType), pdbId, "structures", remoteFileName
|
|
254
|
+
)
|
|
255
|
+
return result
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def getBcifFilePath(
|
|
259
|
+
pdbId: str,
|
|
260
|
+
outfileSuffix: str,
|
|
261
|
+
updateBase: str,
|
|
262
|
+
contentType: str,
|
|
263
|
+
outputContentType: bool,
|
|
264
|
+
outputHash: bool,
|
|
265
|
+
) -> Optional[str]:
|
|
266
|
+
"""form optionally hashed path for local bcif file, only for output files"""
|
|
267
|
+
pdbId = getOutputPdbId(pdbId)
|
|
268
|
+
bcifFileName = "%s%s" % (pdbId, outfileSuffix)
|
|
269
|
+
bcifFilePath = None
|
|
270
|
+
if contentType in [
|
|
271
|
+
ContentTypeEnum.EXPERIMENTAL.value,
|
|
272
|
+
ContentTypeEnum.INTEGRATIVE.value,
|
|
273
|
+
]:
|
|
274
|
+
if outputContentType and outputHash:
|
|
275
|
+
bcifFilePath = os.path.join(
|
|
276
|
+
updateBase, getOutputContentType(contentType), getHash(pdbId, contentType), bcifFileName
|
|
277
|
+
)
|
|
278
|
+
elif outputContentType:
|
|
279
|
+
bcifFilePath = os.path.join(updateBase, getOutputContentType(contentType), bcifFileName)
|
|
280
|
+
elif outputHash:
|
|
281
|
+
bcifFilePath = os.path.join(
|
|
282
|
+
updateBase, getHash(pdbId, contentType), bcifFileName
|
|
283
|
+
)
|
|
284
|
+
else:
|
|
285
|
+
bcifFilePath = os.path.join(updateBase, bcifFileName)
|
|
286
|
+
elif contentType == ContentTypeEnum.COMPUTATIONAL.value:
|
|
287
|
+
if outputContentType and outputHash:
|
|
288
|
+
bcifFilePath = os.path.join(
|
|
289
|
+
updateBase,
|
|
290
|
+
getOutputContentType(contentType),
|
|
291
|
+
getHash(pdbId, contentType),
|
|
292
|
+
bcifFileName,
|
|
293
|
+
)
|
|
294
|
+
elif outputContentType:
|
|
295
|
+
bcifFilePath = os.path.join(
|
|
296
|
+
updateBase,
|
|
297
|
+
getOutputContentType(contentType),
|
|
298
|
+
bcifFileName,
|
|
299
|
+
)
|
|
300
|
+
elif outputHash:
|
|
301
|
+
bcifFilePath = os.path.join(
|
|
302
|
+
updateBase,
|
|
303
|
+
getHash(pdbId, contentType),
|
|
304
|
+
bcifFileName,
|
|
305
|
+
)
|
|
306
|
+
else:
|
|
307
|
+
bcifFilePath = os.path.join(
|
|
308
|
+
updateBase,
|
|
309
|
+
bcifFileName,
|
|
310
|
+
)
|
|
311
|
+
return bcifFilePath
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def convert(
|
|
315
|
+
infile: str, outfile: str, workpath: str, dictionaryApi: DictionaryApi
|
|
316
|
+
) -> bool:
|
|
317
|
+
mu = MarshalUtil(workPath=workpath)
|
|
318
|
+
data = mu.doImport(infile, fmt="mmcif")
|
|
319
|
+
result = mu.doExport(outfile, data, fmt="bcif", dictionaryApi=dictionaryApi)
|
|
320
|
+
if not result:
|
|
321
|
+
return False
|
|
322
|
+
return True
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def deconvert(
|
|
326
|
+
infile: str, outfile: str, workpath: str, dictionaryApi: DictionaryApi
|
|
327
|
+
) -> bool:
|
|
328
|
+
mu = MarshalUtil(workPath=workpath)
|
|
329
|
+
data = mu.doImport(infile, fmt="bcif")
|
|
330
|
+
result = mu.doExport(outfile, data, fmt="mmcif", dictionaryApi=dictionaryApi)
|
|
331
|
+
if not result:
|
|
332
|
+
return False
|
|
333
|
+
return True
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
##
|
|
2
|
+
# File: ChemCompFileWorkflow.py
|
|
3
|
+
# Date: 10-Mar-2020 jdw
|
|
4
|
+
#
|
|
5
|
+
# Workflow wrapper -- chemical component file conversion generator --
|
|
6
|
+
#
|
|
7
|
+
# Updates:
|
|
8
|
+
#
|
|
9
|
+
##
|
|
10
|
+
__docformat__ = "google en"
|
|
11
|
+
__author__ = "John Westbrook"
|
|
12
|
+
__email__ = "jwest@rcsb.rutgers.edu"
|
|
13
|
+
__license__ = "Apache 2.0"
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
|
|
18
|
+
from rcsb.utils.chem.OeIoUtils import OeIoUtils
|
|
19
|
+
from rcsb.utils.chem.OeMoleculeProvider import OeMoleculeProvider
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ChemCompFileWorkflow(object):
|
|
25
|
+
def __init__(self, **kwargs):
|
|
26
|
+
"""Module entry point for chemical component definition 2D image generation
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
ccUrlTarget (str): URL or path for concatenated chemical component dictionary (default: public wwPDB ftp)
|
|
30
|
+
birdUrlTarget (str): URL or path for concatenated BIRD dictionary (default: public wwPDB ftp)
|
|
31
|
+
licenseFilePath (str) = path to OpenEye license text file
|
|
32
|
+
fileDirPath(str): directory containing generated image tree
|
|
33
|
+
cachePath(str): cache directory for temporary files
|
|
34
|
+
molBuildType(str): build type for constructing OE moleclues ('ideal-xyz', 'model-xyz' default: 'ideal-xyz')
|
|
35
|
+
"""
|
|
36
|
+
#
|
|
37
|
+
ccUrlTarget = kwargs.get("ccUrlTarget", None)
|
|
38
|
+
birdUrlTarget = kwargs.get("birdUrlTarget", None)
|
|
39
|
+
self.__licensePath = kwargs.get("licenseFilePath", "oe_license.txt")
|
|
40
|
+
self.__fileDirPath = kwargs.get("fileDirPath", ".")
|
|
41
|
+
self.__molBuildType = kwargs.get("molBuildType", "ideal-xyz")
|
|
42
|
+
cachePath = kwargs.get("cachePath", ".")
|
|
43
|
+
cachePath = os.path.abspath(cachePath)
|
|
44
|
+
|
|
45
|
+
#
|
|
46
|
+
self.__oemp = OeMoleculeProvider(
|
|
47
|
+
ccUrlTarget=ccUrlTarget,
|
|
48
|
+
birdUrlTarget=birdUrlTarget,
|
|
49
|
+
ccFileNamePrefix="cc-full",
|
|
50
|
+
cachePath=cachePath,
|
|
51
|
+
molBuildType=self.__molBuildType,
|
|
52
|
+
useCache=True,
|
|
53
|
+
oeFileNamePrefix="oe-full",
|
|
54
|
+
)
|
|
55
|
+
self.__oeMolD = self.__oemp.getOeMolD()
|
|
56
|
+
|
|
57
|
+
def __setLicense(self, licensePath):
|
|
58
|
+
ok = False
|
|
59
|
+
try:
|
|
60
|
+
if os.environ.get("OE_LICENSE") and os.access(os.environ["OE_LICENSE"], os.R_OK):
|
|
61
|
+
logger.info("Using license from environment %r", os.environ["OE_LICENSE"])
|
|
62
|
+
ok = True
|
|
63
|
+
elif os.access(licensePath, os.R_OK):
|
|
64
|
+
os.environ["OE_LICENSE"] = licensePath
|
|
65
|
+
logger.info("Setting environmenal variable OE_LICENSE to %r", os.environ["OE_LICENSE"])
|
|
66
|
+
ok = True
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logger.error("Setting license file %r failing %s", licensePath, str(e))
|
|
69
|
+
return ok
|
|
70
|
+
|
|
71
|
+
def testCache(self):
|
|
72
|
+
return self.__oemp.testCache() if self.__oemp else False
|
|
73
|
+
|
|
74
|
+
def makeFiles(self, fmt="sdf"):
|
|
75
|
+
"""Create files (mol, mol2) for all public chemical components."""
|
|
76
|
+
try:
|
|
77
|
+
|
|
78
|
+
if fmt not in ["mol", "mol2", "mol2h", "sdf"]:
|
|
79
|
+
return False
|
|
80
|
+
if not self.__setLicense(self.__licensePath):
|
|
81
|
+
logger.error("Invalid license details - exiting")
|
|
82
|
+
return False
|
|
83
|
+
for ccId, oeMol in self.__oeMolD.items():
|
|
84
|
+
if self.__molBuildType == "ideal-xyz":
|
|
85
|
+
filePath = os.path.join(self.__fileDirPath, fmt, ccId[0], ccId + "_ideal." + fmt)
|
|
86
|
+
oeioU = OeIoUtils()
|
|
87
|
+
oeioU.write(filePath, oeMol, constantMol=True)
|
|
88
|
+
else:
|
|
89
|
+
filePath = os.path.join(self.__fileDirPath, fmt, ccId[0], ccId + "_model." + fmt)
|
|
90
|
+
oeioU = OeIoUtils()
|
|
91
|
+
oeioU.write(filePath, oeMol, constantMol=True)
|
|
92
|
+
|
|
93
|
+
return True
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.exception("Failing with %s", str(e))
|
|
96
|
+
return False
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
##
|
|
2
|
+
# File: ChemCompImageWorkflow.py
|
|
3
|
+
# Date: 10-Mar-2020 jdw
|
|
4
|
+
#
|
|
5
|
+
# Workflow wrapper -- chemical component image generation --
|
|
6
|
+
#
|
|
7
|
+
# Updates:
|
|
8
|
+
#
|
|
9
|
+
##
|
|
10
|
+
__docformat__ = "google en"
|
|
11
|
+
__author__ = "John Westbrook"
|
|
12
|
+
__email__ = "jwest@rcsb.rutgers.edu"
|
|
13
|
+
__license__ = "Apache 2.0"
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
|
|
18
|
+
from rcsb.utils.chem.OeDepict import OeDepict
|
|
19
|
+
from rcsb.utils.chem.OeMoleculeProvider import OeMoleculeProvider
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ChemCompImageWorkflow(object):
|
|
25
|
+
def __init__(self, **kwargs):
|
|
26
|
+
"""Module entry point for chemical component definition 2D image generation
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
ccUrlTarget (str): URL or path for concatenated chemical component dictionary (default: public wwPDB ftp)
|
|
30
|
+
birdUrlTarget (str): URL or path for concatenated BIRD dictionary (default: public wwPDB ftp)
|
|
31
|
+
licenseFilePath (str) = path to OpenEye license text file
|
|
32
|
+
imagePath(str): directory containing generated image tree
|
|
33
|
+
cachePath(str): cache directory for temporary files
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
#
|
|
37
|
+
ccUrlTarget = kwargs.get("ccUrlTarget", None)
|
|
38
|
+
birdUrlTarget = kwargs.get("birdUrlTarget", None)
|
|
39
|
+
self.__licensePath = kwargs.get("licenseFilePath", "oe_license.txt")
|
|
40
|
+
self.__imagePath = kwargs.get("imagePath", ".")
|
|
41
|
+
cachePath = kwargs.get("cachePath", ".")
|
|
42
|
+
cachePath = os.path.abspath(cachePath)
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
# changed molBuildType from "model-xyz" to "connection-table" per ticket RO-3041
|
|
46
|
+
self.__oemp = OeMoleculeProvider(
|
|
47
|
+
ccUrlTarget=ccUrlTarget,
|
|
48
|
+
birdUrlTarget=birdUrlTarget,
|
|
49
|
+
ccFileNamePrefix="cc-full",
|
|
50
|
+
cachePath=cachePath,
|
|
51
|
+
molBuildType="connection-table",
|
|
52
|
+
useCache=True,
|
|
53
|
+
oeFileNamePrefix="oe-full",
|
|
54
|
+
)
|
|
55
|
+
self.__oeMolD = self.__oemp.getOeMolD()
|
|
56
|
+
|
|
57
|
+
def __setLicense(self, licensePath):
|
|
58
|
+
ok = False
|
|
59
|
+
try:
|
|
60
|
+
if os.environ.get("OE_LICENSE") and os.access(os.environ["OE_LICENSE"], os.R_OK):
|
|
61
|
+
logger.info("Using license from environment %r", os.environ["OE_LICENSE"])
|
|
62
|
+
ok = True
|
|
63
|
+
elif os.access(licensePath, os.R_OK):
|
|
64
|
+
os.environ["OE_LICENSE"] = licensePath
|
|
65
|
+
logger.info("Setting environmenal variable OE_LICENSE to %r", os.environ["OE_LICENSE"])
|
|
66
|
+
ok = True
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logger.error("Setting license file %r failing %s", licensePath, str(e))
|
|
69
|
+
return ok
|
|
70
|
+
|
|
71
|
+
def testCache(self):
|
|
72
|
+
return self.__oemp.testCache() if self.__oemp else False
|
|
73
|
+
|
|
74
|
+
def makeImages(self):
|
|
75
|
+
"""Create images for all public chemical components with and without atom labels."""
|
|
76
|
+
try:
|
|
77
|
+
if not self.__setLicense(self.__licensePath):
|
|
78
|
+
logger.error("Invalid license details - exiting")
|
|
79
|
+
return False
|
|
80
|
+
for ccId, oeMol in self.__oeMolD.items():
|
|
81
|
+
imagePath = os.path.join(self.__imagePath, "image", ccId[0], ccId + ".svg")
|
|
82
|
+
oed = OeDepict()
|
|
83
|
+
title = ""
|
|
84
|
+
oed.setMolTitleList([(ccId, oeMol, title)])
|
|
85
|
+
# ---
|
|
86
|
+
bondDisplayWidth = 10.0
|
|
87
|
+
numAtoms = oeMol.NumAtoms()
|
|
88
|
+
if numAtoms > 100 and numAtoms <= 200:
|
|
89
|
+
bondDisplayWidth = 6.0
|
|
90
|
+
elif numAtoms > 200:
|
|
91
|
+
bondDisplayWidth = 4.0
|
|
92
|
+
# ---
|
|
93
|
+
oed.setDisplayOptions(
|
|
94
|
+
labelAtomName=False,
|
|
95
|
+
labelAtomCIPStereo=True,
|
|
96
|
+
labelAtomIndex=False,
|
|
97
|
+
labelBondIndex=False,
|
|
98
|
+
labelBondCIPStereo=True,
|
|
99
|
+
cellBorders=False,
|
|
100
|
+
bondDisplayWidth=bondDisplayWidth,
|
|
101
|
+
)
|
|
102
|
+
oed.setGridOptions(rows=1, cols=1, cellBorders=False)
|
|
103
|
+
oed.prepare()
|
|
104
|
+
oed.write(imagePath)
|
|
105
|
+
for ccId, oeMol in self.__oeMolD.items():
|
|
106
|
+
imagePath = os.path.join(self.__imagePath, "image_labeled", ccId[0], ccId + ".svg")
|
|
107
|
+
oed = OeDepict()
|
|
108
|
+
title = ""
|
|
109
|
+
oed.setMolTitleList([(ccId, oeMol, title)])
|
|
110
|
+
# ---
|
|
111
|
+
bondDisplayWidth = 10.0
|
|
112
|
+
numAtoms = oeMol.NumAtoms()
|
|
113
|
+
if numAtoms > 100 and numAtoms <= 200:
|
|
114
|
+
bondDisplayWidth = 6.0
|
|
115
|
+
elif numAtoms > 200:
|
|
116
|
+
bondDisplayWidth = 4.0
|
|
117
|
+
# ---
|
|
118
|
+
oed.setDisplayOptions(
|
|
119
|
+
labelAtomName=True,
|
|
120
|
+
labelAtomCIPStereo=True,
|
|
121
|
+
labelAtomIndex=False,
|
|
122
|
+
labelBondIndex=False,
|
|
123
|
+
labelBondCIPStereo=True,
|
|
124
|
+
cellBorders=False,
|
|
125
|
+
bondDisplayWidth=bondDisplayWidth,
|
|
126
|
+
)
|
|
127
|
+
oed.setGridOptions(rows=1, cols=1, cellBorders=False)
|
|
128
|
+
oed.prepare()
|
|
129
|
+
oed.write(imagePath)
|
|
130
|
+
return True
|
|
131
|
+
except Exception as e:
|
|
132
|
+
logger.exception("Failing with %s", str(e))
|
|
133
|
+
return False
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
##
|
|
2
|
+
# File: ChemCompIndexWorkflow.py
|
|
3
|
+
# Date: 2-Jun-2020 jdw
|
|
4
|
+
#
|
|
5
|
+
# Workflow wrapper -- generate chemical component and BIRD search indices --
|
|
6
|
+
#
|
|
7
|
+
# Updates:
|
|
8
|
+
# 10-Jun-2020 jdw Hookup to ChemCompSearchWrapper()
|
|
9
|
+
##
|
|
10
|
+
__docformat__ = "google en"
|
|
11
|
+
__author__ = "John Westbrook"
|
|
12
|
+
__email__ = "jwest@rcsb.rutgers.edu"
|
|
13
|
+
__license__ = "Apache 2.0"
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
|
|
18
|
+
from rcsb.utils.chem.ChemCompSearchWrapper import ChemCompSearchWrapper
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ChemCompSearchIndexWorkflow(object):
|
|
24
|
+
def __init__(self, **kwargs):
|
|
25
|
+
"""Module entry point for chemical component and BIRD search index generation workflow.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
cachePath(str, optional): cache directory to store indices and temporary files (default: '.')
|
|
29
|
+
licenseFilePath (str, optional) = path to OpenEye license text file (default: use enviroment OE_LICENSE setting)
|
|
30
|
+
ccFilNamePrefix (str, optional) = index prefix (default: "cc-full")
|
|
31
|
+
"""
|
|
32
|
+
self.__licensePath = kwargs.get("licenseFilePath", "oe_license.txt")
|
|
33
|
+
cachePath = kwargs.get("cachePath", ".")
|
|
34
|
+
self.__cachePath = os.path.abspath(cachePath)
|
|
35
|
+
self.__ccFileNamePrefix = kwargs.get("ccFilNamePrefix", "cc-full")
|
|
36
|
+
self.__ccsw = ChemCompSearchWrapper(cachePath=self.__cachePath, ccFileNamePrefix=self.__ccFileNamePrefix)
|
|
37
|
+
|
|
38
|
+
def __setLicense(self, licensePath):
|
|
39
|
+
ok = False
|
|
40
|
+
try:
|
|
41
|
+
if os.environ.get("OE_LICENSE") and os.access(os.environ["OE_LICENSE"], os.R_OK):
|
|
42
|
+
logger.info("Using license from environment %r", os.environ["OE_LICENSE"])
|
|
43
|
+
ok = True
|
|
44
|
+
elif os.access(licensePath, os.R_OK):
|
|
45
|
+
os.environ["OE_LICENSE"] = licensePath
|
|
46
|
+
logger.info("Setting environmenal variable OE_LICENSE to %r", os.environ["OE_LICENSE"])
|
|
47
|
+
ok = True
|
|
48
|
+
except Exception as e:
|
|
49
|
+
logger.error("Setting Openeye license file %r failing %s", licensePath, str(e))
|
|
50
|
+
return ok
|
|
51
|
+
|
|
52
|
+
def testCache(self):
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
def makeIndices(self, ccUrlTarget, birdUrlTarget, **kwargs):
|
|
56
|
+
"""Build chemical component and BIRD search indices.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
ccUrlTarget (str): URL or path for concatenated chemical component dictionary
|
|
60
|
+
birdUrlTarget (str): URL or path for concatenated BIRD dictionary
|
|
61
|
+
|
|
62
|
+
Other arguments may be supplied to change defaults for index generators (testing/troubleshooting)
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
bool: True for success or False otherwise
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
ok = False
|
|
69
|
+
if not self.__setLicense(self.__licensePath):
|
|
70
|
+
logger.error("Invalid OpenEye license details - exiting")
|
|
71
|
+
return ok
|
|
72
|
+
#
|
|
73
|
+
try:
|
|
74
|
+
ok = self.__ccsw.buildDependenices(ccUrlTarget, birdUrlTarget, **kwargs)
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.exception("Failing with %s", str(e))
|
|
77
|
+
return ok
|
|
78
|
+
|
|
79
|
+
def stashIndices(self, url, dirPath, bundleLabel="A", userName=None, pw=None):
|
|
80
|
+
"""Store a copy of the bundled search dependencies remotely -
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
url (str): URL string for the destination host (e.g. sftp://myserver.net or None for a local file)
|
|
84
|
+
dirPath (str): directory path on the remote resource
|
|
85
|
+
bundleLabel (str, optional): optional label preppended to the stashed dependency bundle artifact (default='A')
|
|
86
|
+
userName (str, optional): optional access information. Defaults to None.
|
|
87
|
+
password (str, optional): optional access information. Defaults to None.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
bool: True for success or False otherwise
|
|
91
|
+
|
|
92
|
+
"""
|
|
93
|
+
#
|
|
94
|
+
ok = False
|
|
95
|
+
try:
|
|
96
|
+
ok = self.__ccsw.stashDependencies(url, dirPath, bundleLabel=bundleLabel, userName=userName, pw=pw)
|
|
97
|
+
except Exception as e:
|
|
98
|
+
logger.exception("Failing with %s", str(e))
|
|
99
|
+
return ok
|
|
File without changes
|