forked from APAJanssen/Alphafold2import
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path__init__.py
300 lines (237 loc) · 9.48 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
import pymol
from pymol import cmd
import tempfile
import os,math,re
import sys
import requests
from pymol.cmd import _cmd, DEFAULT_ERROR, DEFAULT_SUCCESS, _raising, is_ok, is_error, is_list, space_sc, safe_list_eval, is_string, loadable
def __init_plugin__(app=None):
'''
Add an entry to the PyMOL "Plugin" menu
'''
from pymol.plugins import addmenuitemqt
addmenuitemqt('Import Alphafold2 model from EMBL', run_plugin_gui)
# function to get UniprotID (P31749) given SwissProtID (AKT1_HUMAN), which is easier to remember
# Roland Dunbrack 06/28/2023) with ChatGPT's help
def get_uniprot_accession_id(swissprot_id):
url = f'https://www.uniprot.org/uniprot/{swissprot_id}.txt'
response = requests.get(url)
if response.status_code == 200:
lines = response.text.split('\n')
for line in lines:
if line.startswith("AC"):
accession = line.split()[1].strip(';')
return accession
else:
print("Error: HTTP request to UniProt failed with status code ", response.status_code)
return None
# global reference to avoid garbage collection of our dialog
dialog = None
def run_plugin_gui():
'''
Open our custom dialog
'''
global dialog
if dialog is None:
dialog = make_dialog()
dialog.show()
def make_dialog():
# entry point to PyMOL's API
from pymol import cmd
# pymol.Qt provides the PyQt5 interface, but may support PyQt4
# and/or PySide as well
from pymol.Qt import QtWidgets
from pymol.Qt.utils import loadUi
from pymol.Qt.utils import getSaveFileNameWithExt
# create a new Window
dialog = QtWidgets.QDialog()
# populate the Window from our *.ui file which was created with the Qt Designer
uifile = os.path.join(os.path.dirname(__file__), 'Alphafold2import.ui')
form = loadUi(uifile, dialog)
# callback for the OK button
def importAF():
## Import user input from the widget
code = form.code.text()
name = form.name.text()
if form.cif.isChecked():
type = 'cif'
elif form.pdb.isChecked():
type = 'pdb'
else:
type = ''
fetchAF2(code=code, name=name, type=type)
# hook up button callbacks
form.pushButton.clicked.connect(importAF)
form.closeButton.clicked.connect(dialog.close)
return dialog
def _fetchAF2(code, name, state, finish, discrete, multiplex, zoom, type, path,
file, quiet, _self=cmd):
'''
code = str: single UniProt identifier
name = str: object name
state = int: object state
finish =
discrete = bool: make discrete multi-state object
multiplex = bool: split states into objects (like split_states)
zoom = int: zoom to new loaded object
type = str: pdb, mmcif
path = str: fetch_path
file = str or file: file name or open file handle
'''
r = DEFAULT_ERROR
# added by Roland Dunbrack 06/28/2023 for support of SwissprotIDs by user (AKT1_HUMAN)
swissprot=None
if "_" in code:
swissprot=code
code=get_uniprot_accession_id(swissprot)
name=swissprot
# file types can be: pdb, cif
# bioType is the string representation of the type
# nameFmt is the file name pattern after download
bioType = type
nameFmt = '{code}-AF-v{version}.{type}'
if type == 'pdb':
pass
elif type == 'cif':
pass
elif re.match(r'pdb\d+$', type):
bioType = 'bio'
else:
raise ValueError('type')
url = 'https://alphafold.ebi.ac.uk/files/AF-{code}-F1-model_v{version}.{type}'
versions = [9, 8, 7, 6, 5, 4, 3, 2, 1] #Dirty fix, but should hold up for the foreseeable future
for version in versions:
response = requests.get(url.format(code=code,version=version, type=bioType))
if response.status_code == 200:
latest_version = version
break
fobj = None
contents = None
if not file or file in (1, '1', 'auto'):
file = os.path.join(path, nameFmt.format(code=code, version=latest_version, type=bioType))
if not is_string(file):
fobj = file
file = None
elif os.path.exists(file):
# skip downloading
url = nameFmt
url = url.format(code=code, version=latest_version, type=bioType)
try:
contents = _self.file_read(url)
# assume HTML content means error on server side without error HTTP code
if b'<html' in contents[:500].lower():
raise pymol.CmdException
except pymol.CmdException:
if not quiet:
print(" Warning: failed to fetch from %s" % (url,))
if file:
try:
fobj = open(file, 'wb')
except IOError:
print(' Warning: Cannot write to "%s"' % file)
if fobj:
fobj.write(contents)
fobj.flush()
if file:
fobj.close()
if not file:
return DEFAULT_SUCCESS
if os.path.exists(file):
r = _self.load(file, name, state, '',
finish, discrete, quiet, multiplex, zoom)
elif contents and bioType in ('pdb', 'bio'):
r = _self.read_pdbstr(contents, name, state,
finish, discrete, quiet, zoom, multiplex)
elif contents and bioType in ('cif', 'cc'):
r = _self.load_raw(contents, 'cif', name, state,
finish, discrete, quiet, multiplex, zoom)
#Define exact colors as used on alphafold database as hosted by EMBL
cmd.set_color('AF_darkblue', [0., 0.325490, 0.839216])
cmd.set_color('AF_lightblue', [0.396078, 0.796078, 0.952941])
cmd.set_color('AF_yellow', [1., 0.858824, 0.074510])
cmd.set_color('AF_orange', [1., 0.490196, 0.270588])
#Color loaded model by pLDDT value (stored in b factor column)
cmd.color('AF_darkblue', f'{name} & b > 90')
cmd.color('AF_lightblue', f'{name} & (b > 70 & b < 90 | b = 90)')
cmd.color('AF_yellow', f'{name} & (b > 50 & b < 70 | b = 70)')
cmd.color('AF_orange', f'{name} & (b < 50 | b = 50)')
if not _self.is_error(r):
return name
print(" Error-fetch: unable to load '%s'." % code)
return DEFAULT_ERROR
def _multifetchAF2(code,name,state,finish,discrete,multiplex,zoom,type,path,file,quiet,_self):
import string
r = DEFAULT_SUCCESS
code_list = code.split()
name = name.strip()
if (name!='') and (len(code_list)>1) and (discrete<0):
discrete = 1 # by default, select discrete when loading
# multiple PDB entries into a single object
all_type = type
for obj_code in code_list:
obj_name = name
type = all_type
if not type:
if 1 < len(obj_code) < 4:
type = 'cc'
else:
type = _self.get('fetch_type_default')
if not obj_name:
obj_name = obj_code
obj_name = _self.get_legal_name(obj_name)
r = _fetchAF2(obj_code, obj_name, state, finish,
discrete, multiplex, zoom, type, path, file, quiet, _self)
return r
@cmd.extend
def fetchAF2(code, name='', state=0, finish=1, discrete=-1,
multiplex=-2, zoom=-1, type='', async_=0, path='',
file=None, quiet=1, _self=cmd, **kwargs):
'''
DESCRIPTION
"fetch" downloads a file from the internet (if possible)
USAGE
fetchAF2 code [, name [, state [, finish [, discrete [, multiplex
[, zoom [, type [, async [, path ]]]]]]]]]
ARGUMENTS
code = a single UniProt identifier or a list of identifiers.
name = the object name into which the file should be loaded.
state = the state number into which the file should loaded.
type = str: cif, pdb {default: cif}
async_ = 0/1: download in the background and do not block the PyMOL
command line {default: 0 -- changed in PyMOL 2.3}
PYMOL API
cmd.fetchAF2(string code, string name, int state, init finish,
int discrete, int multiplex, int zoom, string type,
int async, string path, string file, int quiet)
NOTES
When running in interactive mode, the fetch command loads
structures asyncronously by default, meaning that the next command
may get executed before the structures have been loaded. If you
need synchronous behavior in order to insure that all structures
are loaded before the next command is executed, please provide the
optional argument "async=0".
Fetch requires a direct connection to the internet and thus may
not work behind certain types of network firewalls.
'''
state, finish, discrete = int(state), int(finish), int(discrete)
multiplex, zoom = int(multiplex), int(zoom)
async_, quiet = int(kwargs.pop('async', async_)), int(quiet)
if kwargs:
raise pymol.CmdException('unknown argument: ' + ', '.join(kwargs))
r = DEFAULT_SUCCESS
if not path:
# blank paths need to be reset to '.'
path = _self.get('fetch_path') or '.'
if async_ < 0: # by default, run asynch when interactive, sync when not
async_ = not quiet
args = (code, name, state, finish, discrete, multiplex, zoom, type, path, file, quiet, _self)
kwargs = { '_self' : _self }
if async_:
_self.async_(_multifetchAF2, *args, **kwargs)
else:
try:
_self.block_flush(_self)
r = _multifetchAF2(*args)
finally:
_self.unblock_flush(_self)
return r