Spaces:
Sleeping
Sleeping
Bug fixes
Browse files- app/fn.py +62 -12
- app/main.py +167 -169
- app/static.py +49 -27
- app/static/medium-zoom.min.js +0 -2
- app/static/zooming.min.js +0 -1
- inference.py +6 -5
- utils/inference_utils.py +11 -1
app/fn.py
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
from datetime import datetime
|
2 |
from email.mime.multipart import MIMEMultipart
|
3 |
from email.mime.text import MIMEText
|
@@ -18,7 +19,7 @@ import panel as pn
|
|
18 |
import seaborn as sns
|
19 |
from markdown import markdown
|
20 |
from rdkit import Chem, RDConfig
|
21 |
-
from rdkit.Chem import Crippen, Descriptors, rdMolDescriptors, Lipinski, rdmolops
|
22 |
import requests
|
23 |
|
24 |
from app import static
|
@@ -37,6 +38,16 @@ COL_ALIASES = {
|
|
37 |
'name': 'Complex Name',
|
38 |
}
|
39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
|
41 |
def lipinski(mol):
|
42 |
"""
|
@@ -401,14 +412,46 @@ def read_molecule_file(in_file, allowed_extentions):
|
|
401 |
# html = static.COMPLEX_RENDERING_TEMPLATE.format(viewer_models=viewer_models)
|
402 |
# return static.IFRAME_TEMPLATE.format(html=html)
|
403 |
|
404 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
405 |
html_df = summary_df.copy().drop(columns=['mol'])
|
406 |
-
output_dir = Path(result_info['output_dir'])
|
407 |
-
job_type = result_info['type']
|
408 |
html_df.rename(columns=COL_ALIASES, inplace=True)
|
409 |
-
|
410 |
-
|
|
|
|
|
411 |
hidden_cols = [col for col in html_df.columns if col.endswith('_path')]
|
|
|
|
|
|
|
|
|
412 |
html_df.index.name = 'Index'
|
413 |
|
414 |
# if 'Scaffold' in html_df.columns and 'Exclude Scaffold Graph' not in opts:
|
@@ -448,7 +491,7 @@ def create_result_table_html(summary_df, result_info, opts=(), progress=gr.Progr
|
|
448 |
|
449 |
report_table = pn.widgets.Tabulator(
|
450 |
html_df, formatters=formatters,
|
451 |
-
frozen_columns=['Pose', 'Compound ID', 'Compound'],
|
452 |
hidden_columns=hidden_cols,
|
453 |
sizing_mode='stretch_both',
|
454 |
disabled=True, selectable=False,
|
@@ -499,17 +542,24 @@ def create_result_table_html(summary_df, result_info, opts=(), progress=gr.Progr
|
|
499 |
# template.main.append(
|
500 |
# pn.Card(stats_pane, sizing_mode='stretch_width', title='Summary Statistics', margin=10)
|
501 |
# )
|
502 |
-
|
503 |
-
(f
|
504 |
-
|
505 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
506 |
|
507 |
with tempfile.TemporaryDirectory() as tmpdir:
|
508 |
file = Path(tmpdir) / 'report.html'
|
509 |
report.save(file)
|
510 |
# iframe_html = static.IFRAME_LINK_TEMPLATE.format(src="gradio_api/file=" + str(file))
|
511 |
html_str = file.read_text() # .replace('\'', '\"')
|
512 |
-
iframe_html = static.IFRAME_TEMPLATE.format(srcdoc=html.escape(html_str), aspect_ratio=
|
513 |
return iframe_html
|
514 |
|
515 |
|
|
|
1 |
+
import urllib.parse
|
2 |
from datetime import datetime
|
3 |
from email.mime.multipart import MIMEMultipart
|
4 |
from email.mime.text import MIMEText
|
|
|
19 |
import seaborn as sns
|
20 |
from markdown import markdown
|
21 |
from rdkit import Chem, RDConfig
|
22 |
+
from rdkit.Chem import Crippen, Descriptors, rdMolDescriptors, Lipinski, rdmolops, Draw, rdDepictor
|
23 |
import requests
|
24 |
|
25 |
from app import static
|
|
|
38 |
'name': 'Complex Name',
|
39 |
}
|
40 |
|
41 |
+
COL_DTYPE = {
|
42 |
+
'out_path': 'str',
|
43 |
+
'ligand_conf_path': 'str',
|
44 |
+
'ID1': 'str',
|
45 |
+
'ID2': 'str',
|
46 |
+
'X1': 'str',
|
47 |
+
'X1^': 'str',
|
48 |
+
'name': 'str',
|
49 |
+
}
|
50 |
+
|
51 |
|
52 |
def lipinski(mol):
|
53 |
"""
|
|
|
412 |
# html = static.COMPLEX_RENDERING_TEMPLATE.format(viewer_models=viewer_models)
|
413 |
# return static.IFRAME_TEMPLATE.format(html=html)
|
414 |
|
415 |
+
def prepare_df_for_table(result_df):
|
416 |
+
result_df.dropna(subset=['mol'], inplace=True)
|
417 |
+
|
418 |
+
rdDepictor.SetPreferCoordGen(True)
|
419 |
+
draw_opts = Draw.rdMolDraw2D.MolDrawOptions()
|
420 |
+
draw_opts.clearBackground = False
|
421 |
+
draw_opts.bondLineWidth = 0.5
|
422 |
+
draw_opts.explicitMethyl = True
|
423 |
+
draw_opts.singleColourWedgeBonds = True
|
424 |
+
draw_opts.addStereoAnnotation = False
|
425 |
+
draw_opts.useCDKAtomPalette()
|
426 |
+
|
427 |
+
def draw_mol(mol):
|
428 |
+
# Create a new drawer instance for each molecule (for efficiency)
|
429 |
+
drawer = Draw.MolDraw2DSVG(90, 56)
|
430 |
+
drawer.SetDrawOptions(draw_opts)
|
431 |
+
|
432 |
+
# Draw the molecule and return the SVG as a URI
|
433 |
+
drawer.DrawMolecule(mol)
|
434 |
+
drawer.FinishDrawing()
|
435 |
+
return urllib.parse.quote(drawer.GetDrawingText())
|
436 |
+
|
437 |
+
# Convert to URI-formatted inline SVG
|
438 |
+
result_df['Compound'] = result_df['mol'].apply(draw_mol)
|
439 |
+
|
440 |
+
return result_df
|
441 |
+
|
442 |
+
|
443 |
+
def create_result_table_html(summary_df, result_info=None, opts=(), progress=gr.Progress(track_tqdm=True)):
|
444 |
html_df = summary_df.copy().drop(columns=['mol'])
|
|
|
|
|
445 |
html_df.rename(columns=COL_ALIASES, inplace=True)
|
446 |
+
if result_info:
|
447 |
+
output_dir = Path(result_info['output_dir'])
|
448 |
+
job_type = result_info['type']
|
449 |
+
html_df['Pose'] = html_df['Pose'].apply(lambda x: str(output_dir / job_type / x))
|
450 |
hidden_cols = [col for col in html_df.columns if col.endswith('_path')]
|
451 |
+
rightmost_cols = ['Complex Name', 'Fragment SMILES', 'Compound SMILES']
|
452 |
+
col_order = ([col for col in html_df.columns if col not in rightmost_cols] +
|
453 |
+
[col for col in html_df.columns if col in rightmost_cols])
|
454 |
+
html_df = html_df[col_order]
|
455 |
html_df.index.name = 'Index'
|
456 |
|
457 |
# if 'Scaffold' in html_df.columns and 'Exclude Scaffold Graph' not in opts:
|
|
|
491 |
|
492 |
report_table = pn.widgets.Tabulator(
|
493 |
html_df, formatters=formatters,
|
494 |
+
frozen_columns=['Index', 'Pose', 'Compound ID', 'Compound'],
|
495 |
hidden_columns=hidden_cols,
|
496 |
sizing_mode='stretch_both',
|
497 |
disabled=True, selectable=False,
|
|
|
542 |
# template.main.append(
|
543 |
# pn.Card(stats_pane, sizing_mode='stretch_width', title='Summary Statistics', margin=10)
|
544 |
# )
|
545 |
+
if result_info:
|
546 |
+
table_title = (f"{job_type.title()} Results "
|
547 |
+
f"({'No Linkable Pairs' if job_type != 'linking' else 'Generated Molecules'})")
|
548 |
+
report = pn.Column(pn.Accordion(
|
549 |
+
(table_title, report_table),
|
550 |
+
toggle=True, margin=5, active=[0]
|
551 |
+
))
|
552 |
+
aspect_ratio = '1.090 / 1'
|
553 |
+
else:
|
554 |
+
report = report_table
|
555 |
+
aspect_ratio = '1.618 / 1'
|
556 |
|
557 |
with tempfile.TemporaryDirectory() as tmpdir:
|
558 |
file = Path(tmpdir) / 'report.html'
|
559 |
report.save(file)
|
560 |
# iframe_html = static.IFRAME_LINK_TEMPLATE.format(src="gradio_api/file=" + str(file))
|
561 |
html_str = file.read_text() # .replace('\'', '\"')
|
562 |
+
iframe_html = static.IFRAME_TEMPLATE.format(srcdoc=html.escape(html_str), aspect_ratio=aspect_ratio)
|
563 |
return iframe_html
|
564 |
|
565 |
|
app/main.py
CHANGED
@@ -1,3 +1,7 @@
|
|
|
|
|
|
|
|
|
|
1 |
import spaces
|
2 |
|
3 |
import io
|
@@ -7,8 +11,7 @@ import uuid
|
|
7 |
import zipfile
|
8 |
from datetime import datetime
|
9 |
from pathlib import Path
|
10 |
-
from time import
|
11 |
-
import urllib.parse
|
12 |
|
13 |
from dotenv import load_dotenv
|
14 |
import torch
|
@@ -19,7 +22,6 @@ from gradio_rangeslider import RangeSlider
|
|
19 |
from omegaconf import OmegaConf
|
20 |
import pandas as pd
|
21 |
from rdkit import Chem
|
22 |
-
from rdkit.Chem import PandasTools, Draw, rdDepictor
|
23 |
|
24 |
from inference import (read_fragment_library, process_fragment_library, extract_pockets,
|
25 |
dock_fragments, generate_linkers, select_fragment_pairs)
|
@@ -27,12 +29,35 @@ from app import static, fn, db
|
|
27 |
|
28 |
|
29 |
load_dotenv()
|
30 |
-
|
31 |
-
|
32 |
-
|
|
|
|
|
|
|
33 |
job_db = db.init_job_db()
|
|
|
|
|
|
|
34 |
os.chmod('./fpocket', 0o755)
|
35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
FRAG_LIBS = {'': None} | {
|
37 |
lib_path.stem.replace('_', ' '): str(lib_path) for lib_path in Path('data/fragment_libraries').glob('*')
|
38 |
}
|
@@ -58,6 +83,7 @@ POCKET_EXTRACT_OPTS = {
|
|
58 |
}
|
59 |
}
|
60 |
|
|
|
61 |
def gr_error_wrapper(func):
|
62 |
def wrapper(*args, **kwargs):
|
63 |
try:
|
@@ -67,69 +93,71 @@ def gr_error_wrapper(func):
|
|
67 |
|
68 |
return wrapper
|
69 |
|
70 |
-
|
71 |
-
|
72 |
stop = False
|
|
|
73 |
retry = 0
|
|
|
74 |
while not stop:
|
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 |
-
tabs: gr.Tabs(selected='result'),
|
101 |
-
result_state: job,
|
102 |
-
pred_lookup_btn: gr.Button(visible=True),
|
103 |
-
pred_lookup_stop_btn: gr.Button(visible=False),
|
104 |
-
}
|
105 |
-
if job['status'] == "FAILED":
|
106 |
-
stop = True
|
107 |
-
msg = f'Your GenFBDD job (ID: {job_id}) has **FAILED**'
|
108 |
-
msg += f" at {job['end_time']}" if job.get('end_time') else ''
|
109 |
-
msg += f" due to error: {job['error']}." if job.get('expiry_time') else '.'
|
110 |
-
gr.Info(msg)
|
111 |
-
yield {
|
112 |
-
pred_lookup_status: msg,
|
113 |
-
pred_lookup_btn: gr.Button(visible=True),
|
114 |
-
pred_lookup_stop_btn: gr.Button(visible=False),
|
115 |
-
}
|
116 |
-
else:
|
117 |
-
stop = (retry > 2)
|
118 |
-
if not stop:
|
119 |
-
msg = f'Job ID {job_id} not found. Retrying... ({retry})'
|
120 |
-
else:
|
121 |
-
msg = f'Job ID {job_id} not found after {retry} retries. Please double-check the job ID.'
|
122 |
-
gr.Info(msg)
|
123 |
-
retry += 1
|
124 |
yield {
|
125 |
pred_lookup_status: msg,
|
126 |
-
|
127 |
-
|
|
|
|
|
128 |
}
|
129 |
-
sleep(5)
|
130 |
|
131 |
-
|
132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
|
134 |
|
135 |
def checkbox_group_selections_to_kwargs(selected_options, option_mapping):
|
@@ -140,10 +168,12 @@ def checkbox_group_selections_to_kwargs(selected_options, option_mapping):
|
|
140 |
return kwargs
|
141 |
|
142 |
|
143 |
-
def
|
144 |
-
|
|
|
|
|
145 |
pocket_name, pocket_method, pocket_fs,
|
146 |
-
email,
|
147 |
):
|
148 |
if len(frag_df) == 0 or not frag_file:
|
149 |
raise gr.Error("Please provide a valid fragment library.")
|
@@ -168,14 +198,10 @@ def job_validate(
|
|
168 |
except EmailNotValidError as e:
|
169 |
raise gr.Error(f"Invalid email address: {str(e)}.")
|
170 |
|
171 |
-
if run_info:
|
172 |
-
raise gr.Error(f"You already have a running prediction job (ID: {run_info['id']}) under this session. "
|
173 |
-
"Please wait for it to complete before submitting another job.")
|
174 |
if check := job_db.check_user_running_job(email, session_info):
|
175 |
raise gr.Error(check)
|
176 |
|
177 |
-
gr.Info('Finished processing inputs. Initiating the GenFBDD job... '
|
178 |
-
'You will be redirected to Job Status page.')
|
179 |
job_id = str(uuid.uuid4())
|
180 |
job_info = {
|
181 |
'id': job_id,
|
@@ -194,7 +220,17 @@ def job_validate(
|
|
194 |
}
|
195 |
job_db.insert(job_info)
|
196 |
|
197 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
|
199 |
@spaces.GPU(duration=600)
|
200 |
def dock_link(
|
@@ -216,7 +252,7 @@ def dock_link(
|
|
216 |
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
217 |
print(f'Using device: {device}')
|
218 |
date_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
219 |
-
out_dir = Path(
|
220 |
frag_lib['X2'] = prot
|
221 |
frag_lib['ID2'] = str(Path(prot).stem)
|
222 |
|
@@ -280,7 +316,6 @@ def dock_link(
|
|
280 |
'output_dir': str(out_dir),
|
281 |
'type': job_type,
|
282 |
}
|
283 |
-
# return {result_state: job_info | update_info, run_state: {}}
|
284 |
|
285 |
except Exception as e:
|
286 |
gr.Warning(f"Job failed due to error: {str(e)}")
|
@@ -289,7 +324,6 @@ def dock_link(
|
|
289 |
'error': str(e),
|
290 |
'output_dir': None
|
291 |
}
|
292 |
-
# return {result_state: {}, run_state: {}}
|
293 |
|
294 |
finally:
|
295 |
job_db.job_update(
|
@@ -298,10 +332,6 @@ def dock_link(
|
|
298 |
)
|
299 |
|
300 |
|
301 |
-
def get_session_state(request: gr.Request):
|
302 |
-
return request
|
303 |
-
|
304 |
-
|
305 |
THEME = gr.themes.Base(
|
306 |
spacing_size="sm", text_size='md', font=gr.themes.GoogleFont("Roboto"),
|
307 |
primary_hue='emerald', secondary_hue='emerald', neutral_hue='slate',
|
@@ -329,8 +359,6 @@ THEME = gr.themes.Base(
|
|
329 |
)
|
330 |
|
331 |
with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600, 48 * 3600)) as demo:
|
332 |
-
run_state = gr.State(value={})
|
333 |
-
session_state = gr.State(value={})
|
334 |
with gr.Column(variant='panel'):
|
335 |
with gr.Tabs() as tabs:
|
336 |
with gr.Tab(label='Start', id='start'):
|
@@ -361,11 +389,11 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
361 |
)
|
362 |
frag_lib_orig_df = gr.State(value=pd.DataFrame(columns=['X1', 'ID1', 'mol']))
|
363 |
frag_lib_mod_df = gr.State(value=pd.DataFrame(columns=['X1', 'ID1', 'mol']))
|
364 |
-
#
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
)
|
369 |
|
370 |
with gr.Group():
|
371 |
frag_lib_process_opts = gr.CheckboxGroup(
|
@@ -510,6 +538,27 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
510 |
value='Run GenFBDD', variant='primary', interactive=True,
|
511 |
)
|
512 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
513 |
with gr.Tab(label='Results', id='result'):
|
514 |
# Results
|
515 |
result_state = gr.State(value={})
|
@@ -520,6 +569,7 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
520 |
with gr.Row():
|
521 |
scores = gr.CheckboxGroup(list(fn.SCORE_MAP.keys()), label='Compound Scores')
|
522 |
filters = gr.CheckboxGroup(list(fn.FILTER_MAP.keys()), label='Compound Filters')
|
|
|
523 |
with gr.Row():
|
524 |
prop_clr_btn = gr.ClearButton(value='Clear Properties', interactive=False)
|
525 |
prop_calc_btn = gr.Button(value='Calculate Properties', interactive=False, variant='primary')
|
@@ -531,28 +581,6 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
531 |
result_file_btn = gr.Button(value='Create Result File', visible=False, variant='primary')
|
532 |
result_download_file = gr.File(label='Download Result File', visible=False)
|
533 |
|
534 |
-
with gr.Tab(label='Job Status', id='job'):
|
535 |
-
gr.Markdown('''
|
536 |
-
To check the status of an in-progress or historical job using the job ID and retrieve the predictions
|
537 |
-
if the job has completed. Note that predictions are only kept for 48 hours upon job completion.
|
538 |
-
|
539 |
-
You will be redirected to `Results` for carrying out further analysis and
|
540 |
-
generating the full report when the job is done. If the the query fails to respond, please wait for a
|
541 |
-
few minutes and refresh the page to try again.
|
542 |
-
''')
|
543 |
-
with gr.Row():
|
544 |
-
with gr.Column(scale=1):
|
545 |
-
loader_html = gr.HTML('<div class="loader first-frame"></div>', visible=False)
|
546 |
-
with gr.Column(scale=4):
|
547 |
-
pred_lookup_id = gr.Textbox(
|
548 |
-
label='Input Your Job ID', placeholder='e.g., e9dfd149-3f5c-48a6-b797-c27d027611ac',
|
549 |
-
info="Your job ID is a UUID4 string that you receive after submitting a job on the "
|
550 |
-
"page or in the email notification.")
|
551 |
-
pred_lookup_example = gr.Button('Example', elem_classes=['example'], scale=1)
|
552 |
-
pred_lookup_btn = gr.Button(value='Query Status', variant='primary', visible=True)
|
553 |
-
pred_lookup_stop_btn = gr.Button(value='Stop Tracking', variant='stop', visible=False)
|
554 |
-
pred_lookup_status = gr.Markdown("**Job Status**", container=True)
|
555 |
-
|
556 |
# Event handlers
|
557 |
## Start tab
|
558 |
### Fragment Library
|
@@ -573,7 +601,7 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
573 |
inputs=[frag_lib_file],
|
574 |
outputs=[frag_lib_orig_df],
|
575 |
).success(
|
576 |
-
fn=lambda df: [df,
|
577 |
inputs=[frag_lib_orig_df],
|
578 |
outputs=[frag_lib_mod_df, frag_lib_view],
|
579 |
)
|
@@ -586,7 +614,7 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
586 |
new_df:=process_fragment_library(
|
587 |
df, **checkbox_group_selections_to_kwargs(opts, FRAG_LIB_PROCESS_OPTS)
|
588 |
),
|
589 |
-
|
590 |
],
|
591 |
inputs=[frag_lib_orig_df, frag_lib_process_opts],
|
592 |
outputs=[frag_lib_mod_df, frag_lib_view],
|
@@ -705,26 +733,16 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
705 |
inputs=[selected_ligand, selected_pocket],
|
706 |
outputs=[selected_ligand, selected_pocket],
|
707 |
).success(
|
708 |
-
fn=
|
709 |
-
inputs=[
|
710 |
-
frag_lib_file, frag_lib_mod_df, input_prot_file,
|
711 |
-
selected_pocket, pocket_extract_dropdown, pocket_files,
|
712 |
-
email_input, run_state
|
713 |
-
],
|
714 |
-
outputs=[run_state],
|
715 |
-
)
|
716 |
-
|
717 |
-
job_valid.success(
|
718 |
-
fn=dock_link,
|
719 |
inputs=[
|
720 |
-
frag_lib_mod_df, input_prot_file,
|
721 |
dock_steps, dock_n_poses, dock_confidence_cutoff,
|
722 |
link_frag_dist_range, link_frag_pose_strategy, link_n_mols,
|
723 |
link_linker_size, link_steps,
|
724 |
-
|
|
|
725 |
],
|
726 |
-
|
727 |
-
concurrency_limit=1, concurrency_id="gpu_queue"
|
728 |
)
|
729 |
|
730 |
start_reset_components=[
|
@@ -750,57 +768,49 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
750 |
)
|
751 |
|
752 |
### Job Status
|
753 |
-
pred_lookup_btn.click(
|
754 |
-
fn=lambda: '<div class="loader"></ div>',
|
755 |
-
outputs=loader_html,
|
756 |
-
)
|
757 |
user_job_lookup = pred_lookup_btn.click(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
758 |
fn=query_job_status,
|
759 |
inputs=[pred_lookup_id],
|
760 |
outputs=[pred_lookup_status, tabs, result_state, pred_lookup_btn, pred_lookup_stop_btn],
|
761 |
show_progress='minimal',
|
762 |
-
).success(
|
763 |
-
lambda: '<div class="loader first-frame"></ div>',
|
764 |
-
outputs=loader_html,
|
765 |
)
|
766 |
|
767 |
-
|
768 |
-
fn=lambda
|
769 |
-
|
770 |
-
|
|
|
|
|
|
|
771 |
).success(
|
772 |
-
lambda: '<div class="loader"></ div>',
|
773 |
-
outputs=loader_html,
|
774 |
-
)
|
775 |
-
|
776 |
-
auto_job_lookup = job_valid_success.success(
|
777 |
fn=query_job_status,
|
778 |
inputs=pred_lookup_id,
|
779 |
outputs=[pred_lookup_status, tabs, result_state, pred_lookup_btn, pred_lookup_stop_btn],
|
780 |
show_progress='minimal',
|
781 |
cancels=[user_job_lookup],
|
782 |
-
).success(
|
783 |
-
lambda: '<div class="loader first-frame"></ div>',
|
784 |
-
outputs=loader_html,
|
785 |
)
|
786 |
|
787 |
pred_lookup_stop_btn.click(
|
788 |
fn=lambda: [gr.Button(visible=True), gr.Button(visible=False)],
|
789 |
outputs=[pred_lookup_btn, pred_lookup_stop_btn],
|
790 |
cancels=[user_job_lookup, auto_job_lookup],
|
791 |
-
).success(
|
792 |
-
lambda: '<div class="loader first-frame"></ div>',
|
793 |
-
outputs=loader_html,
|
794 |
)
|
795 |
|
796 |
-
|
797 |
fn=lambda: '80cf2658-7a1c-48d6-8372-61b978177fe6',
|
798 |
outputs=[pred_lookup_id],
|
799 |
show_progress='hidden'
|
800 |
).success(
|
801 |
fn=query_job_status,
|
802 |
inputs=pred_lookup_id,
|
803 |
-
outputs=[pred_lookup_status, tabs, result_state],
|
804 |
show_progress='minimal',
|
805 |
cancels=[user_job_lookup, auto_job_lookup],
|
806 |
)
|
@@ -811,29 +821,16 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
811 |
result_type = result_info['type']
|
812 |
protein_structure_file = Path(result_info['protein_structure_file'])
|
813 |
if result_type == 'docking':
|
814 |
-
result_df = pd.read_csv(result_dir / 'docking_summary.csv')
|
815 |
result_df['mol'] = result_df['X1'].apply(Chem.MolFromSmiles)
|
816 |
elif result_type == 'linking':
|
817 |
-
result_df = pd.read_csv(result_dir / 'linking_summary.csv')
|
818 |
-
result_df = result_df[~result_df['X1^'].str.contains('.', regex=False)]
|
819 |
result_df['mol'] = result_df['X1^'].apply(Chem.MolFromSmiles)
|
820 |
-
result_df
|
821 |
else:
|
822 |
raise gr.Error('Invalid result type')
|
823 |
|
824 |
-
|
825 |
-
draw_opts = Draw.rdMolDraw2D.MolDrawOptions()
|
826 |
-
draw_opts.clearBackground = False
|
827 |
-
draw_opts.bondLineWidth = 0.5
|
828 |
-
draw_opts.explicitMethyl = True
|
829 |
-
draw_opts.singleColourWedgeBonds = True
|
830 |
-
draw_opts.addStereoAnnotation = False
|
831 |
-
draw_opts.useCDKAtomPalette()
|
832 |
-
PandasTools.drawOptions = draw_opts
|
833 |
-
PandasTools.molSize = (90, 56)
|
834 |
-
PandasTools.molRepresentation = 'svg'
|
835 |
-
# Convert to URI-formatted inline SVG
|
836 |
-
result_df['Compound'] = result_df['mol'].apply(PandasTools.PrintAsImageString).apply(urllib.parse.quote)
|
837 |
|
838 |
return {
|
839 |
result_table_orig_df: result_df,
|
@@ -921,13 +918,14 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
921 |
outputs=[result_download_file],
|
922 |
)
|
923 |
|
924 |
-
demo.load(
|
925 |
|
|
|
|
|
926 |
demo.launch(
|
927 |
server_name='0.0.0.0',
|
928 |
max_file_size="5mb",
|
929 |
ssr_mode=False,
|
930 |
-
|
931 |
-
|
932 |
-
share_server_protocol="https"
|
933 |
)
|
|
|
1 |
+
import asyncio
|
2 |
+
import queue
|
3 |
+
import threading
|
4 |
+
|
5 |
import spaces
|
6 |
|
7 |
import io
|
|
|
11 |
import zipfile
|
12 |
from datetime import datetime
|
13 |
from pathlib import Path
|
14 |
+
from time import time
|
|
|
15 |
|
16 |
from dotenv import load_dotenv
|
17 |
import torch
|
|
|
22 |
from omegaconf import OmegaConf
|
23 |
import pandas as pd
|
24 |
from rdkit import Chem
|
|
|
25 |
|
26 |
from inference import (read_fragment_library, process_fragment_library, extract_pockets,
|
27 |
dock_fragments, generate_linkers, select_fragment_pairs)
|
|
|
29 |
|
30 |
|
31 |
load_dotenv()
|
32 |
+
RESULTS_DIR = os.getenv('RESULTS', 'results')
|
33 |
+
MAX_CONCURRENT_JOBS = 1
|
34 |
+
|
35 |
+
task_queue = queue.Queue()
|
36 |
+
semaphore = threading.Semaphore(MAX_CONCURRENT_JOBS)
|
37 |
+
lock = threading.Lock()
|
38 |
job_db = db.init_job_db()
|
39 |
+
|
40 |
+
Path(tempfile.gettempdir(), 'gradio').mkdir(exist_ok=True)
|
41 |
+
gr.set_static_paths(paths=["data/", RESULTS_DIR, "app/"])
|
42 |
os.chmod('./fpocket', 0o755)
|
43 |
|
44 |
+
def task_worker():
|
45 |
+
"""Worker function to process tasks from the queue with concurrency limit."""
|
46 |
+
while True:
|
47 |
+
data = task_queue.get() # Get the next task from the queue
|
48 |
+
with semaphore: # Ensure only 'MAX_CONCURRENT_JOBS' tasks run at once
|
49 |
+
with lock: # Ensure only one task is processed at a time (for shared state)
|
50 |
+
dock_link(*data)
|
51 |
+
task_queue.task_done()
|
52 |
+
|
53 |
+
|
54 |
+
worker_threads = []
|
55 |
+
for _ in range(MAX_CONCURRENT_JOBS):
|
56 |
+
worker_thread = threading.Thread(target=task_worker, daemon=True)
|
57 |
+
worker_threads.append(worker_thread)
|
58 |
+
worker_thread.start()
|
59 |
+
|
60 |
+
|
61 |
FRAG_LIBS = {'': None} | {
|
62 |
lib_path.stem.replace('_', ' '): str(lib_path) for lib_path in Path('data/fragment_libraries').glob('*')
|
63 |
}
|
|
|
83 |
}
|
84 |
}
|
85 |
|
86 |
+
|
87 |
def gr_error_wrapper(func):
|
88 |
def wrapper(*args, **kwargs):
|
89 |
try:
|
|
|
93 |
|
94 |
return wrapper
|
95 |
|
96 |
+
|
97 |
+
async def query_job_status(job_id):
|
98 |
stop = False
|
99 |
+
interval = 3 # Check every 3 seconds for better responsiveness
|
100 |
retry = 0
|
101 |
+
|
102 |
while not stop:
|
103 |
+
# Wait for a short interval before checking again
|
104 |
+
await asyncio.sleep(interval) # Non-blocking sleep
|
105 |
+
job = job_db.job_lookup(job_id)
|
106 |
+
|
107 |
+
if job: # If the job exists
|
108 |
+
if job['status'] == "RUNNING": # If the job is still running
|
109 |
+
yield {
|
110 |
+
pred_lookup_status: f'''
|
111 |
+
Your job (ID: **{job['id']}**) started at **{job['start_time']}** and is **RUNNING...**
|
112 |
+
|
113 |
+
It might take a few minutes to a few hours depending on the input size and the queue status.
|
114 |
+
You may keep the page open or close it and revisit later using the job ID.
|
115 |
+
You will receive an email notification once the job is done.
|
116 |
+
''',
|
117 |
+
pred_lookup_btn: gr.update(visible=False),
|
118 |
+
pred_lookup_stop_btn: gr.update(visible=True),
|
119 |
+
}
|
120 |
+
|
121 |
+
elif job['status'] == "COMPLETED": # If the job is complete
|
122 |
+
stop = True
|
123 |
+
msg = f"Your job (ID: {job['id']}) has been **COMPLETED**"
|
124 |
+
msg += f" at **{job['end_time']}**" if job.get('end_time') else ""
|
125 |
+
msg += f" and the results will **EXPIRE** by **{job['expiry_time']}**." if job.get('expiry_time') else "."
|
126 |
+
msg += " Redirecting to the results page..."
|
127 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
yield {
|
129 |
pred_lookup_status: msg,
|
130 |
+
tabs: gr.Tabs(selected='result'),
|
131 |
+
result_state: job,
|
132 |
+
pred_lookup_btn: gr.update(visible=True),
|
133 |
+
pred_lookup_stop_btn: gr.update(visible=False),
|
134 |
}
|
|
|
135 |
|
136 |
+
elif job['status'] == "FAILED": # If the job failed
|
137 |
+
stop = True
|
138 |
+
msg = f'Your job (ID: {job_id}) has **FAILED**'
|
139 |
+
msg += f" at {job['end_time']}" if job.get('end_time') else ''
|
140 |
+
msg += f" due to error: {job['error']}." if job.get('error') else '.'
|
141 |
+
|
142 |
+
yield {
|
143 |
+
pred_lookup_status: msg,
|
144 |
+
pred_lookup_btn: gr.update(visible=True),
|
145 |
+
pred_lookup_stop_btn: gr.update(visible=False),
|
146 |
+
}
|
147 |
+
|
148 |
+
else: # If the job is not found
|
149 |
+
stop = retry > 2 # Stop after 3 retries
|
150 |
+
if not stop:
|
151 |
+
msg = f'Job ID {job_id} not found. Retrying... ({retry})'
|
152 |
+
else:
|
153 |
+
msg = f'Job ID {job_id} not found after {retry} retries. Please double-check the job ID.'
|
154 |
+
|
155 |
+
retry += 1
|
156 |
+
yield {
|
157 |
+
pred_lookup_status: msg,
|
158 |
+
pred_lookup_btn: gr.update(visible=stop),
|
159 |
+
pred_lookup_stop_btn: gr.update(visible=not stop),
|
160 |
+
}
|
161 |
|
162 |
|
163 |
def checkbox_group_selections_to_kwargs(selected_options, option_mapping):
|
|
|
168 |
return kwargs
|
169 |
|
170 |
|
171 |
+
def job_submit(
|
172 |
+
frag_df, frag_file, prot_file,
|
173 |
+
dock_n_steps, dock_n_poses, dock_confidence_threshold,
|
174 |
+
linker_frag_dist, linker_strategy, linker_n_mols, linker_size, linker_steps,
|
175 |
pocket_name, pocket_method, pocket_fs,
|
176 |
+
email, session_info: gr.Request
|
177 |
):
|
178 |
if len(frag_df) == 0 or not frag_file:
|
179 |
raise gr.Error("Please provide a valid fragment library.")
|
|
|
198 |
except EmailNotValidError as e:
|
199 |
raise gr.Error(f"Invalid email address: {str(e)}.")
|
200 |
|
|
|
|
|
|
|
201 |
if check := job_db.check_user_running_job(email, session_info):
|
202 |
raise gr.Error(check)
|
203 |
|
204 |
+
gr.Info('Finished processing inputs. Initiating the GenFBDD job... You will be redirected to Job Status page.')
|
|
|
205 |
job_id = str(uuid.uuid4())
|
206 |
job_info = {
|
207 |
'id': job_id,
|
|
|
220 |
}
|
221 |
job_db.insert(job_info)
|
222 |
|
223 |
+
task_queue.put((
|
224 |
+
frag_df, prot_file,
|
225 |
+
dock_n_steps, dock_n_poses, dock_confidence_threshold,
|
226 |
+
linker_frag_dist, linker_strategy, linker_n_mols, linker_size, linker_steps,
|
227 |
+
job_info
|
228 |
+
))
|
229 |
+
|
230 |
+
return {
|
231 |
+
pred_lookup_id: job_id,
|
232 |
+
tabs: gr.Tabs(selected='job'),
|
233 |
+
}
|
234 |
|
235 |
@spaces.GPU(duration=600)
|
236 |
def dock_link(
|
|
|
252 |
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
253 |
print(f'Using device: {device}')
|
254 |
date_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
255 |
+
out_dir = Path(RESULTS_DIR, f'{date_time}_{job_id}')
|
256 |
frag_lib['X2'] = prot
|
257 |
frag_lib['ID2'] = str(Path(prot).stem)
|
258 |
|
|
|
316 |
'output_dir': str(out_dir),
|
317 |
'type': job_type,
|
318 |
}
|
|
|
319 |
|
320 |
except Exception as e:
|
321 |
gr.Warning(f"Job failed due to error: {str(e)}")
|
|
|
324 |
'error': str(e),
|
325 |
'output_dir': None
|
326 |
}
|
|
|
327 |
|
328 |
finally:
|
329 |
job_db.job_update(
|
|
|
332 |
)
|
333 |
|
334 |
|
|
|
|
|
|
|
|
|
335 |
THEME = gr.themes.Base(
|
336 |
spacing_size="sm", text_size='md', font=gr.themes.GoogleFont("Roboto"),
|
337 |
primary_hue='emerald', secondary_hue='emerald', neutral_hue='slate',
|
|
|
359 |
)
|
360 |
|
361 |
with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600, 48 * 3600)) as demo:
|
|
|
|
|
362 |
with gr.Column(variant='panel'):
|
363 |
with gr.Tabs() as tabs:
|
364 |
with gr.Tab(label='Start', id='start'):
|
|
|
389 |
)
|
390 |
frag_lib_orig_df = gr.State(value=pd.DataFrame(columns=['X1', 'ID1', 'mol']))
|
391 |
frag_lib_mod_df = gr.State(value=pd.DataFrame(columns=['X1', 'ID1', 'mol']))
|
392 |
+
# frag_lib_view = gr.DataFrame(
|
393 |
+
# value=pd.DataFrame(columns=['X1', 'ID1']), elem_id='frag_lib_view',
|
394 |
+
# visible=True, interactive=False,
|
395 |
+
# )
|
396 |
+
frag_lib_view = gr.HTML(static.IFRAME_TEMPLATE.format(aspect_ratio='1.618 /1', srcdoc=''))
|
397 |
|
398 |
with gr.Group():
|
399 |
frag_lib_process_opts = gr.CheckboxGroup(
|
|
|
538 |
value='Run GenFBDD', variant='primary', interactive=True,
|
539 |
)
|
540 |
|
541 |
+
with gr.Tab(label='Jobs', id='job'):
|
542 |
+
gr.Markdown('''
|
543 |
+
To check the status of an in-progress or historical job using the job ID and retrieve the predictions
|
544 |
+
if the job has completed. Note that predictions are only kept for 48 hours upon job completion.
|
545 |
+
|
546 |
+
You will be redirected to `Results` for carrying out further analysis and
|
547 |
+
generating the full report when the job is done. If the the query fails to respond, please wait for a
|
548 |
+
few minutes and refresh the page to try again.
|
549 |
+
''')
|
550 |
+
with gr.Row():
|
551 |
+
with gr.Column(scale=1):
|
552 |
+
loader_html = gr.HTML('<div class="loader first-frame"></div>', visible=False)
|
553 |
+
with gr.Column(scale=4):
|
554 |
+
pred_lookup_id = gr.Textbox(
|
555 |
+
label='Input Your Job ID', placeholder='e.g., e9dfd149-3f5c-48a6-b797-c27d027611ac',
|
556 |
+
info="Your job ID is a UUID4 string that you receive after submitting a job on the "
|
557 |
+
"page or in the email notification.")
|
558 |
+
pred_lookup_btn = gr.Button(value='Query Status', variant='primary', visible=True)
|
559 |
+
pred_lookup_stop_btn = gr.Button(value='Stop Tracking', variant='stop', visible=False)
|
560 |
+
pred_lookup_status = gr.Markdown("**Job Status**", container=True)
|
561 |
+
|
562 |
with gr.Tab(label='Results', id='result'):
|
563 |
# Results
|
564 |
result_state = gr.State(value={})
|
|
|
569 |
with gr.Row():
|
570 |
scores = gr.CheckboxGroup(list(fn.SCORE_MAP.keys()), label='Compound Scores')
|
571 |
filters = gr.CheckboxGroup(list(fn.FILTER_MAP.keys()), label='Compound Filters')
|
572 |
+
result_example = gr.Button('Example', elem_classes=['example'])
|
573 |
with gr.Row():
|
574 |
prop_clr_btn = gr.ClearButton(value='Clear Properties', interactive=False)
|
575 |
prop_calc_btn = gr.Button(value='Calculate Properties', interactive=False, variant='primary')
|
|
|
581 |
result_file_btn = gr.Button(value='Create Result File', visible=False, variant='primary')
|
582 |
result_download_file = gr.File(label='Download Result File', visible=False)
|
583 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
584 |
# Event handlers
|
585 |
## Start tab
|
586 |
### Fragment Library
|
|
|
601 |
inputs=[frag_lib_file],
|
602 |
outputs=[frag_lib_orig_df],
|
603 |
).success(
|
604 |
+
fn=lambda df: [df.copy(), fn.create_result_table_html(fn.prepare_df_for_table(df))],
|
605 |
inputs=[frag_lib_orig_df],
|
606 |
outputs=[frag_lib_mod_df, frag_lib_view],
|
607 |
)
|
|
|
614 |
new_df:=process_fragment_library(
|
615 |
df, **checkbox_group_selections_to_kwargs(opts, FRAG_LIB_PROCESS_OPTS)
|
616 |
),
|
617 |
+
fn.create_result_table_html(fn.prepare_df_for_table(new_df))
|
618 |
],
|
619 |
inputs=[frag_lib_orig_df, frag_lib_process_opts],
|
620 |
outputs=[frag_lib_mod_df, frag_lib_view],
|
|
|
733 |
inputs=[selected_ligand, selected_pocket],
|
734 |
outputs=[selected_ligand, selected_pocket],
|
735 |
).success(
|
736 |
+
fn=job_submit,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
737 |
inputs=[
|
738 |
+
frag_lib_mod_df, frag_lib_file, input_prot_file,
|
739 |
dock_steps, dock_n_poses, dock_confidence_cutoff,
|
740 |
link_frag_dist_range, link_frag_pose_strategy, link_n_mols,
|
741 |
link_linker_size, link_steps,
|
742 |
+
selected_pocket, pocket_extract_dropdown, pocket_files,
|
743 |
+
email_input,
|
744 |
],
|
745 |
+
outputs=[pred_lookup_id, tabs],
|
|
|
746 |
)
|
747 |
|
748 |
start_reset_components=[
|
|
|
768 |
)
|
769 |
|
770 |
### Job Status
|
|
|
|
|
|
|
|
|
771 |
user_job_lookup = pred_lookup_btn.click(
|
772 |
+
fn=lambda: [
|
773 |
+
gr.update(value="Start querying the job database..."),
|
774 |
+
gr.update(visible=False),
|
775 |
+
gr.update(visible=True),
|
776 |
+
],
|
777 |
+
outputs=[pred_lookup_status, pred_lookup_btn, pred_lookup_stop_btn],
|
778 |
+
).success(
|
779 |
fn=query_job_status,
|
780 |
inputs=[pred_lookup_id],
|
781 |
outputs=[pred_lookup_status, tabs, result_state, pred_lookup_btn, pred_lookup_stop_btn],
|
782 |
show_progress='minimal',
|
|
|
|
|
|
|
783 |
)
|
784 |
|
785 |
+
auto_job_lookup = job_valid.success(
|
786 |
+
fn=lambda: [
|
787 |
+
gr.update(value="Start querying the job database..."),
|
788 |
+
gr.update(visible=False),
|
789 |
+
gr.update(visible=True),
|
790 |
+
],
|
791 |
+
outputs=[pred_lookup_status, pred_lookup_btn, pred_lookup_stop_btn],
|
792 |
).success(
|
|
|
|
|
|
|
|
|
|
|
793 |
fn=query_job_status,
|
794 |
inputs=pred_lookup_id,
|
795 |
outputs=[pred_lookup_status, tabs, result_state, pred_lookup_btn, pred_lookup_stop_btn],
|
796 |
show_progress='minimal',
|
797 |
cancels=[user_job_lookup],
|
|
|
|
|
|
|
798 |
)
|
799 |
|
800 |
pred_lookup_stop_btn.click(
|
801 |
fn=lambda: [gr.Button(visible=True), gr.Button(visible=False)],
|
802 |
outputs=[pred_lookup_btn, pred_lookup_stop_btn],
|
803 |
cancels=[user_job_lookup, auto_job_lookup],
|
|
|
|
|
|
|
804 |
)
|
805 |
|
806 |
+
result_example.click(
|
807 |
fn=lambda: '80cf2658-7a1c-48d6-8372-61b978177fe6',
|
808 |
outputs=[pred_lookup_id],
|
809 |
show_progress='hidden'
|
810 |
).success(
|
811 |
fn=query_job_status,
|
812 |
inputs=pred_lookup_id,
|
813 |
+
outputs=[pred_lookup_status, tabs, result_state, pred_lookup_btn, pred_lookup_stop_btn],
|
814 |
show_progress='minimal',
|
815 |
cancels=[user_job_lookup, auto_job_lookup],
|
816 |
)
|
|
|
821 |
result_type = result_info['type']
|
822 |
protein_structure_file = Path(result_info['protein_structure_file'])
|
823 |
if result_type == 'docking':
|
824 |
+
result_df = pd.read_csv(result_dir / 'docking_summary.csv', dtype=fn.COL_DTYPE)
|
825 |
result_df['mol'] = result_df['X1'].apply(Chem.MolFromSmiles)
|
826 |
elif result_type == 'linking':
|
827 |
+
result_df = pd.read_csv(result_dir / 'linking_summary.csv', dtype=fn.COL_DTYPE)
|
|
|
828 |
result_df['mol'] = result_df['X1^'].apply(Chem.MolFromSmiles)
|
829 |
+
result_df = result_df[~result_df['X1^'].str.contains('.', regex=False)]
|
830 |
else:
|
831 |
raise gr.Error('Invalid result type')
|
832 |
|
833 |
+
result_df = fn.prepare_df_for_table(result_df)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
834 |
|
835 |
return {
|
836 |
result_table_orig_df: result_df,
|
|
|
918 |
outputs=[result_download_file],
|
919 |
)
|
920 |
|
921 |
+
demo.load(None, None, None, js=static.SETUP_JS)
|
922 |
|
923 |
+
demo.share_token = 'genfbdd'
|
924 |
+
demo.queue(default_concurrency_limit=None)
|
925 |
demo.launch(
|
926 |
server_name='0.0.0.0',
|
927 |
max_file_size="5mb",
|
928 |
ssr_mode=False,
|
929 |
+
show_api=False,
|
930 |
+
enable_monitoring=True,
|
|
|
931 |
)
|
app/static.py
CHANGED
@@ -517,26 +517,41 @@ CREATE_OUTPUT_MOL_VIEW = """
|
|
517 |
viewer.zoomTo();
|
518 |
viewer.render();
|
519 |
|
520 |
-
|
521 |
-
container
|
|
|
|
|
|
|
|
|
522 |
|
523 |
// Molecule Button
|
524 |
-
|
525 |
-
toggleMoleculeButton
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
|
|
|
|
|
|
|
|
536 |
|
537 |
// Ligand Button
|
538 |
-
|
539 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
540 |
toggleLigandButton.textContent = "Hide Co-Crystallized Ligand";
|
541 |
let ligandIsHidden = false;
|
542 |
toggleLigandButton.onclick = function() {
|
@@ -546,20 +561,27 @@ CREATE_OUTPUT_MOL_VIEW = """
|
|
546 |
viewer.render();
|
547 |
};
|
548 |
container.appendChild(toggleLigandButton);
|
|
|
|
|
|
|
549 |
}
|
550 |
|
551 |
// Protein Button
|
552 |
-
|
553 |
-
toggleProteinButton
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
toggleProteinButton.
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
|
|
|
|
|
|
|
|
563 |
}).fail(function(error) {
|
564 |
console.error("Error loading molecule:", error);
|
565 |
});
|
|
|
517 |
viewer.zoomTo();
|
518 |
viewer.render();
|
519 |
|
520 |
+
let container = element.parentElement.querySelector(".gr-btn-grp");
|
521 |
+
if (!container) {
|
522 |
+
container = document.createElement("div");
|
523 |
+
container.classList.add("gr-btn-grp");
|
524 |
+
element.parentElement.appendChild(container);
|
525 |
+
}
|
526 |
|
527 |
// Molecule Button
|
528 |
+
let toggleMoleculeButton = container.querySelector("#toggleMoleculeButton");
|
529 |
+
if (!toggleMoleculeButton) {
|
530 |
+
toggleMoleculeButton = document.createElement("button");
|
531 |
+
toggleMoleculeButton.id = "toggleMoleculeButton"; // Add an ID
|
532 |
+
toggleMoleculeButton.textContent = "Hide Generated Molecule";
|
533 |
+
let moleculeIsHidden = false;
|
534 |
+
toggleMoleculeButton.onclick = function() {
|
535 |
+
if (viewer.models.length === 2) {
|
536 |
+
moleculeIsHidden = !moleculeIsHidden;
|
537 |
+
viewer.addStyle({model: 1}, {stick: {hidden: moleculeIsHidden} });
|
538 |
+
toggleMoleculeButton.textContent = moleculeIsHidden ? "Show Generated Molecule" : "Hide Generated Molecule";
|
539 |
+
viewer.render();
|
540 |
+
}
|
541 |
+
};
|
542 |
+
container.appendChild(toggleMoleculeButton);
|
543 |
+
}
|
544 |
|
545 |
// Ligand Button
|
546 |
+
const ligandButtonId = "toggleLigandButton";
|
547 |
+
let toggleLigandButton = container.querySelector("#" + ligandButtonId);
|
548 |
+
|
549 |
+
// Check for ligands and existing button
|
550 |
+
const hasLigands = viewer.getAtomsFromSel({model: 0, hetflag : true}).length > 0;
|
551 |
+
if (hasLigands && !toggleLigandButton) {
|
552 |
+
// Create button if ligands exist and it doesn't exist yet
|
553 |
+
toggleLigandButton = document.createElement("button");
|
554 |
+
toggleLigandButton.id = ligandButtonId;
|
555 |
toggleLigandButton.textContent = "Hide Co-Crystallized Ligand";
|
556 |
let ligandIsHidden = false;
|
557 |
toggleLigandButton.onclick = function() {
|
|
|
561 |
viewer.render();
|
562 |
};
|
563 |
container.appendChild(toggleLigandButton);
|
564 |
+
} else if (!hasLigands && toggleLigandButton) {
|
565 |
+
// Remove button if no ligands exist and it already exists
|
566 |
+
container.removeChild(toggleLigandButton);
|
567 |
}
|
568 |
|
569 |
// Protein Button
|
570 |
+
let toggleProteinButton = container.querySelector("#toggleProteinButton");
|
571 |
+
if (!toggleProteinButton) {
|
572 |
+
toggleProteinButton = document.createElement("button");
|
573 |
+
toggleProteinButton.id = "toggleProteinButton";
|
574 |
+
toggleProteinButton.textContent = "Hide Target Protein";
|
575 |
+
let proteinIsHidden = false;
|
576 |
+
toggleProteinButton.onclick = function() {
|
577 |
+
proteinIsHidden = !proteinIsHidden;
|
578 |
+
viewer.addStyle({model: 0, hetflag: false}, {cartoon: {hidden: proteinIsHidden} });
|
579 |
+
toggleProteinButton.textContent = proteinIsHidden ? "Show Target Protein" : "Hide Target Protein";
|
580 |
+
viewer.render();
|
581 |
+
};
|
582 |
+
container.appendChild(toggleProteinButton);
|
583 |
+
}
|
584 |
+
|
585 |
}).fail(function(error) {
|
586 |
console.error("Error loading molecule:", error);
|
587 |
});
|
app/static/medium-zoom.min.js
DELETED
@@ -1,2 +0,0 @@
|
|
1 |
-
/*! medium-zoom 1.1.0 | MIT License | https://github.com/francoischalifour/medium-zoom */
|
2 |
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).mediumZoom=t()}(this,(function(){"use strict";var e=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var o=arguments[t];for(var n in o)Object.prototype.hasOwnProperty.call(o,n)&&(e[n]=o[n])}return e},t=function(e){return"IMG"===e.tagName},o=function(e){return e&&1===e.nodeType},n=function(e){return".svg"===(e.currentSrc||e.src).substr(-4).toLowerCase()},i=function(e){try{return Array.isArray(e)?e.filter(t):function(e){return NodeList.prototype.isPrototypeOf(e)}(e)?[].slice.call(e).filter(t):o(e)?[e].filter(t):"string"==typeof e?[].slice.call(document.querySelectorAll(e)).filter(t):[]}catch(e){throw new TypeError("The provided selector is invalid.\nExpects a CSS selector, a Node element, a NodeList or an array.\nSee: https://github.com/francoischalifour/medium-zoom")}},r=function(e){var t=document.createElement("div");return t.classList.add("medium-zoom-overlay"),t.style.background=e,t},d=function(e){var t=e.getBoundingClientRect(),o=t.top,n=t.left,i=t.width,r=t.height,d=e.cloneNode(),a=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,m=window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0;return d.removeAttribute("id"),d.style.position="absolute",d.style.top=o+a+"px",d.style.left=n+m+"px",d.style.width=i+"px",d.style.height=r+"px",d.style.transform="",d},a=function(t,o){var n=e({bubbles:!1,cancelable:!1,detail:void 0},o);if("function"==typeof window.CustomEvent)return new CustomEvent(t,n);var i=document.createEvent("CustomEvent");return i.initCustomEvent(t,n.bubbles,n.cancelable,n.detail),i};return function(e,t){void 0===t&&(t={});var o=t.insertAt;if(e&&"undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],i=document.createElement("style");i.type="text/css","top"===o&&n.firstChild?n.insertBefore(i,n.firstChild):n.appendChild(i),i.styleSheet?i.styleSheet.cssText=e:i.appendChild(document.createTextNode(e))}}(".medium-zoom-overlay{position:fixed;top:0;right:0;bottom:0;left:0;opacity:0;transition:opacity .3s;will-change:opacity}.medium-zoom--opened .medium-zoom-overlay{cursor:pointer;cursor:zoom-out;opacity:1}.medium-zoom-image{cursor:pointer;cursor:zoom-in;transition:transform .3s cubic-bezier(.2,0,.2,1)!important}.medium-zoom-image--hidden{visibility:hidden}.medium-zoom-image--opened{position:relative;cursor:pointer;cursor:zoom-out;will-change:transform}"),function t(m){var l=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},c=window.Promise||function(e){function t(){}e(t,t)},u=function(e){var t=e.target;t!==N?-1!==x.indexOf(t)&&w({target:t}):E()},s=function(){if(!A&&k.original){var e=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;Math.abs(S-e)>T.scrollOffset&&setTimeout(E,150)}},f=function(e){var t=e.key||e.keyCode;"Escape"!==t&&"Esc"!==t&&27!==t||E()},p=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t;if(t.background&&(N.style.background=t.background),t.container&&t.container instanceof Object&&(n.container=e({},T.container,t.container)),t.template){var i=o(t.template)?t.template:document.querySelector(t.template);n.template=i}return T=e({},T,n),x.forEach((function(e){e.dispatchEvent(a("medium-zoom:update",{detail:{zoom:j}}))})),j},g=function(){var o=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return t(e({},T,o))},v=function(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];var n=t.reduce((function(e,t){return[].concat(e,i(t))}),[]);return n.filter((function(e){return-1===x.indexOf(e)})).forEach((function(e){x.push(e),e.classList.add("medium-zoom-image")})),O.forEach((function(e){var t=e.type,o=e.listener,i=e.options;n.forEach((function(e){e.addEventListener(t,o,i)}))})),j},h=function(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];k.zoomed&&E();var n=t.length>0?t.reduce((function(e,t){return[].concat(e,i(t))}),[]):x;return n.forEach((function(e){e.classList.remove("medium-zoom-image"),e.dispatchEvent(a("medium-zoom:detach",{detail:{zoom:j}}))})),x=x.filter((function(e){return-1===n.indexOf(e)})),j},z=function(e,t){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return x.forEach((function(n){n.addEventListener("medium-zoom:"+e,t,o)})),O.push({type:"medium-zoom:"+e,listener:t,options:o}),j},y=function(e,t){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return x.forEach((function(n){n.removeEventListener("medium-zoom:"+e,t,o)})),O=O.filter((function(o){return!(o.type==="medium-zoom:"+e&&o.listener.toString()===t.toString())})),j},b=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},i=t.target,r=function(){var t={width:document.documentElement.clientWidth,height:document.documentElement.clientHeight,left:0,top:0,right:0,bottom:0},i=void 0,r=void 0;if(T.container)if(T.container instanceof Object)i=(t=e({},t,T.container)).width-t.left-t.right-2*T.margin,r=t.height-t.top-t.bottom-2*T.margin;else{var d=(o(T.container)?T.container:document.querySelector(T.container)).getBoundingClientRect(),a=d.width,m=d.height,l=d.left,c=d.top;t=e({},t,{width:a,height:m,left:l,top:c})}i=i||t.width-2*T.margin,r=r||t.height-2*T.margin;var u=k.zoomedHd||k.original,s=n(u)?i:u.naturalWidth||i,f=n(u)?r:u.naturalHeight||r,p=u.getBoundingClientRect(),g=p.top,v=p.left,h=p.width,z=p.height,y=Math.min(Math.max(h,s),i)/h,b=Math.min(Math.max(z,f),r)/z,E=Math.min(y,b),w="scale("+E+") translate3d("+((i-h)/2-v+T.margin+t.left)/E+"px, "+((r-z)/2-g+T.margin+t.top)/E+"px, 0)";k.zoomed.style.transform=w,k.zoomedHd&&(k.zoomedHd.style.transform=w)};return new c((function(e){if(i&&-1===x.indexOf(i))e(j);else{if(k.zoomed)e(j);else{if(i)k.original=i;else{if(!(x.length>0))return void e(j);var t=x;k.original=t[0]}if(k.original.dispatchEvent(a("medium-zoom:open",{detail:{zoom:j}})),S=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,A=!0,k.zoomed=d(k.original),document.body.appendChild(N),T.template){var n=o(T.template)?T.template:document.querySelector(T.template);k.template=document.createElement("div"),k.template.appendChild(n.content.cloneNode(!0)),document.body.appendChild(k.template)}if(k.original.parentElement&&"PICTURE"===k.original.parentElement.tagName&&k.original.currentSrc&&(k.zoomed.src=k.original.currentSrc),document.body.appendChild(k.zoomed),window.requestAnimationFrame((function(){document.body.classList.add("medium-zoom--opened")})),k.original.classList.add("medium-zoom-image--hidden"),k.zoomed.classList.add("medium-zoom-image--opened"),k.zoomed.addEventListener("click",E),k.zoomed.addEventListener("transitionend",(function t(){A=!1,k.zoomed.removeEventListener("transitionend",t),k.original.dispatchEvent(a("medium-zoom:opened",{detail:{zoom:j}})),e(j)})),k.original.getAttribute("data-zoom-src")){k.zoomedHd=k.zoomed.cloneNode(),k.zoomedHd.removeAttribute("srcset"),k.zoomedHd.removeAttribute("sizes"),k.zoomedHd.removeAttribute("loading"),k.zoomedHd.src=k.zoomed.getAttribute("data-zoom-src"),k.zoomedHd.onerror=function(){clearInterval(m),console.warn("Unable to reach the zoom image target "+k.zoomedHd.src),k.zoomedHd=null,r()};var m=setInterval((function(){k.zoomedHd.complete&&(clearInterval(m),k.zoomedHd.classList.add("medium-zoom-image--opened"),k.zoomedHd.addEventListener("click",E),document.body.appendChild(k.zoomedHd),r())}),10)}else if(k.original.hasAttribute("srcset")){k.zoomedHd=k.zoomed.cloneNode(),k.zoomedHd.removeAttribute("sizes"),k.zoomedHd.removeAttribute("loading");var l=k.zoomedHd.addEventListener("load",(function(){k.zoomedHd.removeEventListener("load",l),k.zoomedHd.classList.add("medium-zoom-image--opened"),k.zoomedHd.addEventListener("click",E),document.body.appendChild(k.zoomedHd),r()}))}else r()}}}))},E=function(){return new c((function(e){if(!A&&k.original){A=!0,document.body.classList.remove("medium-zoom--opened"),k.zoomed.style.transform="",k.zoomedHd&&(k.zoomedHd.style.transform=""),k.template&&(k.template.style.transition="opacity 150ms",k.template.style.opacity=0),k.original.dispatchEvent(a("medium-zoom:close",{detail:{zoom:j}})),k.zoomed.addEventListener("transitionend",(function t(){k.original.classList.remove("medium-zoom-image--hidden"),document.body.removeChild(k.zoomed),k.zoomedHd&&document.body.removeChild(k.zoomedHd),document.body.removeChild(N),k.zoomed.classList.remove("medium-zoom-image--opened"),k.template&&document.body.removeChild(k.template),A=!1,k.zoomed.removeEventListener("transitionend",t),k.original.dispatchEvent(a("medium-zoom:closed",{detail:{zoom:j}})),k.original=null,k.zoomed=null,k.zoomedHd=null,k.template=null,e(j)}))}else e(j)}))},w=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.target;return k.original?E():b({target:t})},L=function(){return T},H=function(){return x},C=function(){return k.original},x=[],O=[],A=!1,S=0,T=l,k={original:null,zoomed:null,zoomedHd:null,template:null};"[object Object]"===Object.prototype.toString.call(m)?T=m:(m||"string"==typeof m)&&v(m),T=e({margin:0,background:"#fff",scrollOffset:40,container:null,template:null},T);var N=r(T.background);document.addEventListener("click",u),document.addEventListener("keyup",f),document.addEventListener("scroll",s),window.addEventListener("resize",E);var j={open:b,close:E,toggle:w,update:p,clone:g,attach:v,detach:h,on:z,off:y,getOptions:L,getImages:H,getZoomedImage:C};return j}}));
|
|
|
|
|
|
app/static/zooming.min.js
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Zooming=e()}(this,function(){"use strict";var t="auto",e="zoom-in",i="zoom-out",n="grab",s="move";function o(t,e,i){var n={passive:!1};!(arguments.length>3&&void 0!==arguments[3])||arguments[3]?t.addEventListener(e,i,n):t.removeEventListener(e,i,n)}function r(t,e){if(t){var i=new Image;i.onload=function(){e&&e(i)},i.src=t}}function a(t){return t.dataset.original?t.dataset.original:"A"===t.parentNode.tagName?t.parentNode.getAttribute("href"):null}function l(t,e,i){!function(t){var e=h.transitionProp,i=h.transformProp;if(t.transition){var n=t.transition;delete t.transition,t[e]=n}if(t.transform){var s=t.transform;delete t.transform,t[i]=s}}(e);var n=t.style,s={};for(var o in e)i&&(s[o]=n[o]||""),n[o]=e[o];return s}var h={transitionProp:"transition",transEndEvent:"transitionend",transformProp:"transform",transformCssProp:"transform"},c=h.transformCssProp,u=h.transEndEvent;var d=function(){},f={enableGrab:!0,preloadImage:!1,closeOnWindowResize:!0,transitionDuration:.4,transitionTimingFunction:"cubic-bezier(0.4, 0, 0, 1)",bgColor:"rgb(255, 255, 255)",bgOpacity:1,scaleBase:1,scaleExtra:.5,scrollThreshold:40,zIndex:998,customSize:null,onOpen:d,onClose:d,onGrab:d,onMove:d,onRelease:d,onBeforeOpen:d,onBeforeClose:d,onBeforeGrab:d,onBeforeRelease:d,onImageLoading:d,onImageLoaded:d},p={init:function(t){var e,i;e=this,i=t,Object.getOwnPropertyNames(Object.getPrototypeOf(e)).forEach(function(t){e[t]=e[t].bind(i)})},click:function(t){if(t.preventDefault(),m(t))return window.open(this.target.srcOriginal||t.currentTarget.src,"_blank");this.shown?this.released?this.close():this.release():this.open(t.currentTarget)},scroll:function(){var t=document.documentElement||document.body.parentNode||document.body,e=window.pageXOffset||t.scrollLeft,i=window.pageYOffset||t.scrollTop;null===this.lastScrollPosition&&(this.lastScrollPosition={x:e,y:i});var n=this.lastScrollPosition.x-e,s=this.lastScrollPosition.y-i,o=this.options.scrollThreshold;(Math.abs(s)>=o||Math.abs(n)>=o)&&(this.lastScrollPosition=null,this.close())},keydown:function(t){(function(t){return"Escape"===(t.key||t.code)||27===t.keyCode})(t)&&(this.released?this.close():this.release(this.close))},mousedown:function(t){if(y(t)&&!m(t)){t.preventDefault();var e=t.clientX,i=t.clientY;this.pressTimer=setTimeout(function(){this.grab(e,i)}.bind(this),200)}},mousemove:function(t){this.released||this.move(t.clientX,t.clientY)},mouseup:function(t){y(t)&&!m(t)&&(clearTimeout(this.pressTimer),this.released?this.close():this.release())},touchstart:function(t){t.preventDefault();var e=t.touches[0],i=e.clientX,n=e.clientY;this.pressTimer=setTimeout(function(){this.grab(i,n)}.bind(this),200)},touchmove:function(t){if(!this.released){var e=t.touches[0],i=e.clientX,n=e.clientY;this.move(i,n)}},touchend:function(t){(function(t){t.targetTouches.length})(t)||(clearTimeout(this.pressTimer),this.released?this.close():this.release())},clickOverlay:function(){this.close()},resizeWindow:function(){this.close()}};function y(t){return 0===t.button}function m(t){return t.metaKey||t.ctrlKey}var g={init:function(t){this.el=document.createElement("div"),this.instance=t,this.parent=document.body,l(this.el,{position:"fixed",top:0,left:0,right:0,bottom:0,opacity:0}),this.updateStyle(t.options),o(this.el,"click",t.handler.clickOverlay.bind(t))},updateStyle:function(t){l(this.el,{zIndex:t.zIndex,backgroundColor:t.bgColor,transition:"opacity\n "+t.transitionDuration+"s\n "+t.transitionTimingFunction})},insert:function(){this.parent.appendChild(this.el)},remove:function(){this.parent.removeChild(this.el)},fadeIn:function(){this.el.offsetWidth,this.el.style.opacity=this.instance.options.bgOpacity},fadeOut:function(){this.el.style.opacity=0}},v="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},b=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},w=function(){function t(t,e){for(var i=0;i<e.length;i++){var n=e[i];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(e,i,n){return i&&t(e.prototype,i),n&&t(e,n),e}}(),x=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var i=arguments[e];for(var n in i)Object.prototype.hasOwnProperty.call(i,n)&&(t[n]=i[n])}return t},O={init:function(t,e){this.el=t,this.instance=e,this.srcThumbnail=this.el.getAttribute("src"),this.srcset=this.el.getAttribute("srcset"),this.srcOriginal=a(this.el),this.rect=this.el.getBoundingClientRect(),this.translate=null,this.scale=null,this.styleOpen=null,this.styleClose=null},zoomIn:function(){var t=this.instance.options,e=t.zIndex,s=t.enableGrab,o=t.transitionDuration,r=t.transitionTimingFunction;this.translate=this.calculateTranslate(),this.scale=this.calculateScale(),this.styleOpen={position:"relative",zIndex:e+1,cursor:s?n:i,transition:c+"\n "+o+"s\n "+r,transform:"translate3d("+this.translate.x+"px, "+this.translate.y+"px, 0px)\n scale("+this.scale.x+","+this.scale.y+")",height:this.rect.height+"px",width:this.rect.width+"px"},this.el.offsetWidth,this.styleClose=l(this.el,this.styleOpen,!0)},zoomOut:function(){this.el.offsetWidth,l(this.el,{transform:"none"})},grab:function(t,e,i){var n=k(),o=n.x-t,r=n.y-e;l(this.el,{cursor:s,transform:"translate3d(\n "+(this.translate.x+o)+"px, "+(this.translate.y+r)+"px, 0px)\n scale("+(this.scale.x+i)+","+(this.scale.y+i)+")"})},move:function(t,e,i){var n=k(),s=n.x-t,o=n.y-e;l(this.el,{transition:c,transform:"translate3d(\n "+(this.translate.x+s)+"px, "+(this.translate.y+o)+"px, 0px)\n scale("+(this.scale.x+i)+","+(this.scale.y+i)+")"})},restoreCloseStyle:function(){l(this.el,this.styleClose)},restoreOpenStyle:function(){l(this.el,this.styleOpen)},upgradeSource:function(){if(this.srcOriginal){var t=this.el.parentNode;this.srcset&&this.el.removeAttribute("srcset");var e=this.el.cloneNode(!1);e.setAttribute("src",this.srcOriginal),e.style.position="fixed",e.style.visibility="hidden",t.appendChild(e),setTimeout(function(){this.el.setAttribute("src",this.srcOriginal),t.removeChild(e)}.bind(this),50)}},downgradeSource:function(){this.srcOriginal&&(this.srcset&&this.el.setAttribute("srcset",this.srcset),this.el.setAttribute("src",this.srcThumbnail))},calculateTranslate:function(){var t=k(),e=this.rect.left+this.rect.width/2,i=this.rect.top+this.rect.height/2;return{x:t.x-e,y:t.y-i}},calculateScale:function(){var t=this.el.dataset,e=t.zoomingHeight,i=t.zoomingWidth,n=this.instance.options,s=n.customSize,o=n.scaleBase;if(!s&&e&&i)return{x:i/this.rect.width,y:e/this.rect.height};if(s&&"object"===(void 0===s?"undefined":v(s)))return{x:s.width/this.rect.width,y:s.height/this.rect.height};var r=this.rect.width/2,a=this.rect.height/2,l=k(),h={x:l.x-r,y:l.y-a},c=h.x/r,u=h.y/a,d=o+Math.min(c,u);if(s&&"string"==typeof s){var f=i||this.el.naturalWidth,p=e||this.el.naturalHeight,y=parseFloat(s)*f/(100*this.rect.width),m=parseFloat(s)*p/(100*this.rect.height);if(d>y||d>m)return{x:y,y:m}}return{x:d,y:d}}};function k(){var t=document.documentElement;return{x:Math.min(t.clientWidth,window.innerWidth)/2,y:Math.min(t.clientHeight,window.innerHeight)/2}}function S(t,e,i){["mousedown","mousemove","mouseup","touchstart","touchmove","touchend"].forEach(function(n){o(t,n,e[n],i)})}return function(){function i(t){b(this,i),this.target=Object.create(O),this.overlay=Object.create(g),this.handler=Object.create(p),this.body=document.body,this.shown=!1,this.lock=!1,this.released=!0,this.lastScrollPosition=null,this.pressTimer=null,this.options=x({},f,t),this.overlay.init(this),this.handler.init(this)}return w(i,[{key:"listen",value:function(t){if("string"==typeof t)for(var i=document.querySelectorAll(t),n=i.length;n--;)this.listen(i[n]);else"IMG"===t.tagName&&(t.style.cursor=e,o(t,"click",this.handler.click),this.options.preloadImage&&r(a(t)));return this}},{key:"config",value:function(t){return t?(x(this.options,t),this.overlay.updateStyle(this.options),this):this.options}},{key:"open",value:function(t){var e=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.options.onOpen;if(!this.shown&&!this.lock){var n="string"==typeof t?document.querySelector(t):t;if("IMG"===n.tagName){if(this.options.onBeforeOpen(n),this.target.init(n,this),!this.options.preloadImage){var s=this.target.srcOriginal;null!=s&&(this.options.onImageLoading(n),r(s,this.options.onImageLoaded))}this.shown=!0,this.lock=!0,this.target.zoomIn(),this.overlay.insert(),this.overlay.fadeIn(),o(document,"scroll",this.handler.scroll),o(document,"keydown",this.handler.keydown),this.options.closeOnWindowResize&&o(window,"resize",this.handler.resizeWindow);return o(n,u,function t(){o(n,u,t,!1),e.lock=!1,e.target.upgradeSource(),e.options.enableGrab&&S(document,e.handler,!0),i(n)}),this}}}},{key:"close",value:function(){var e=this,i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.options.onClose;if(this.shown&&!this.lock){var n=this.target.el;this.options.onBeforeClose(n),this.lock=!0,this.body.style.cursor=t,this.overlay.fadeOut(),this.target.zoomOut(),o(document,"scroll",this.handler.scroll,!1),o(document,"keydown",this.handler.keydown,!1),this.options.closeOnWindowResize&&o(window,"resize",this.handler.resizeWindow,!1);return o(n,u,function t(){o(n,u,t,!1),e.shown=!1,e.lock=!1,e.target.downgradeSource(),e.options.enableGrab&&S(document,e.handler,!1),e.target.restoreCloseStyle(),e.overlay.remove(),i(n)}),this}}},{key:"grab",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.options.scaleExtra,n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:this.options.onGrab;if(this.shown&&!this.lock){var s=this.target.el;this.options.onBeforeGrab(s),this.released=!1,this.target.grab(t,e,i);return o(s,u,function t(){o(s,u,t,!1),n(s)}),this}}},{key:"move",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.options.scaleExtra,n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:this.options.onMove;if(this.shown&&!this.lock){this.released=!1,this.body.style.cursor=s,this.target.move(t,e,i);var r=this.target.el;return o(r,u,function t(){o(r,u,t,!1),n(r)}),this}}},{key:"release",value:function(){var e=this,i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.options.onRelease;if(this.shown&&!this.lock){var n=this.target.el;this.options.onBeforeRelease(n),this.lock=!0,this.body.style.cursor=t,this.target.restoreOpenStyle();return o(n,u,function t(){o(n,u,t,!1),e.lock=!1,e.released=!0,i(n)}),this}}}]),i}()});
|
|
|
|
inference.py
CHANGED
@@ -467,16 +467,15 @@ def dock_fragments(
|
|
467 |
log.warning("Failed on", orig_complex_graph["name"], e)
|
468 |
failures += 1
|
469 |
|
470 |
-
#
|
471 |
-
model.cpu()
|
472 |
del model
|
473 |
if confidence_model is not None:
|
474 |
-
confidence_model.cpu()
|
475 |
del confidence_model
|
476 |
del test_dataset
|
477 |
if confidence_test_dataset is not None:
|
478 |
del confidence_test_dataset
|
479 |
del test_loader
|
|
|
480 |
|
481 |
docking_df = pd.concat(docking_dfs, ignore_index=True)
|
482 |
# Save intermediate docking results
|
@@ -691,6 +690,8 @@ def select_fragment_pairs(
|
|
691 |
ligand_residue=None,
|
692 |
):
|
693 |
df = df[df['confidence'] > confidence_threshold].copy()
|
|
|
|
|
694 |
if 'ligand_mol' not in df.columns:
|
695 |
df['ligand_mol'] = df['ligand_conf_path'].apply(read_molecule)
|
696 |
|
@@ -1139,9 +1140,9 @@ def generate_linkers(
|
|
1139 |
# batch_df['out_path'] = str(out_sdf)
|
1140 |
# linking_dfs.append(batch_df)
|
1141 |
|
1142 |
-
#
|
1143 |
-
ddpm.cpu()
|
1144 |
del ddpm
|
|
|
1145 |
|
1146 |
if linking_dfs:
|
1147 |
linking_summary_df = pd.concat(linking_dfs, ignore_index=True)
|
|
|
467 |
log.warning("Failed on", orig_complex_graph["name"], e)
|
468 |
failures += 1
|
469 |
|
470 |
+
# Teardown
|
|
|
471 |
del model
|
472 |
if confidence_model is not None:
|
|
|
473 |
del confidence_model
|
474 |
del test_dataset
|
475 |
if confidence_test_dataset is not None:
|
476 |
del confidence_test_dataset
|
477 |
del test_loader
|
478 |
+
torch.cuda.empty_cache()
|
479 |
|
480 |
docking_df = pd.concat(docking_dfs, ignore_index=True)
|
481 |
# Save intermediate docking results
|
|
|
690 |
ligand_residue=None,
|
691 |
):
|
692 |
df = df[df['confidence'] > confidence_threshold].copy()
|
693 |
+
if 'ligand_conf_path' in df.columns:
|
694 |
+
df['ligand_conf_path'] = df['ligand_conf_path'].apply(Path)
|
695 |
if 'ligand_mol' not in df.columns:
|
696 |
df['ligand_mol'] = df['ligand_conf_path'].apply(read_molecule)
|
697 |
|
|
|
1140 |
# batch_df['out_path'] = str(out_sdf)
|
1141 |
# linking_dfs.append(batch_df)
|
1142 |
|
1143 |
+
# Teardown
|
|
|
1144 |
del ddpm
|
1145 |
+
torch.cuda.empty_cache()
|
1146 |
|
1147 |
if linking_dfs:
|
1148 |
linking_summary_df = pd.concat(linking_dfs, ignore_index=True)
|
utils/inference_utils.py
CHANGED
@@ -62,7 +62,17 @@ def compute_esm_embeddings(model, alphabet, labels, sequences):
|
|
62 |
repr_layers = [33]
|
63 |
truncation_seq_length = 1022
|
64 |
|
65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
batches = dataset.get_batch_indices(toks_per_batch, extra_toks_per_seq=1)
|
67 |
data_loader = torch.utils.data.DataLoader(
|
68 |
dataset, collate_fn=alphabet.get_batch_converter(truncation_seq_length), batch_sampler=batches
|
|
|
62 |
repr_layers = [33]
|
63 |
truncation_seq_length = 1022
|
64 |
|
65 |
+
# Deduplicate by label to avoid redundant ESM computation
|
66 |
+
unique_labels = set()
|
67 |
+
unique_sequences = []
|
68 |
+
for label, sequence in zip(labels, sequences):
|
69 |
+
if label not in unique_labels:
|
70 |
+
unique_labels.add(label)
|
71 |
+
unique_sequences.append(sequence)
|
72 |
+
unique_labels = list(unique_labels)
|
73 |
+
|
74 |
+
# Create the dataset with unique labels and sequences
|
75 |
+
dataset = FastaBatchedDataset(unique_labels, unique_sequences)
|
76 |
batches = dataset.get_batch_indices(toks_per_batch, extra_toks_per_seq=1)
|
77 |
data_loader = torch.utils.data.DataLoader(
|
78 |
dataset, collate_fn=alphabet.get_batch_converter(truncation_seq_length), batch_sampler=batches
|