Spaces:
Sleeping
Sleeping
Update result view and fix bugs
Browse files- app/fn.py +55 -155
- app/main.py +174 -95
- app/static.py +134 -643
- app/static/medium-zoom.min.js +2 -0
- app/{panel.css → static/panel.css} +18 -20
- app/{panel.js → static/panel.js} +10 -13
- app/static/zooming.min.js +1 -0
- configs/gen_fbdd_v1.yaml +42 -0
- inference.py +9 -2
app/fn.py
CHANGED
@@ -3,6 +3,7 @@ from email.mime.multipart import MIMEMultipart
|
|
3 |
from email.mime.text import MIMEText
|
4 |
from email.utils import formatdate, make_msgid
|
5 |
from functools import cache
|
|
|
6 |
import os
|
7 |
from pathlib import Path
|
8 |
import smtplib
|
@@ -11,13 +12,14 @@ import tempfile
|
|
11 |
|
12 |
import pandas as pd
|
13 |
from bokeh.models import NumberFormatter, BooleanFormatter, HTMLTemplateFormatter
|
|
|
14 |
import gradio as gr
|
15 |
import pytz
|
16 |
import panel as pn
|
17 |
import seaborn as sns
|
18 |
from markdown import markdown
|
19 |
from rdkit import Chem, RDConfig
|
20 |
-
from rdkit.Chem import
|
21 |
import requests
|
22 |
|
23 |
from app import static
|
@@ -153,7 +155,7 @@ def bms(mol):
|
|
153 |
|
154 |
|
155 |
SCORE_MAP = {
|
156 |
-
'
|
157 |
'LogP': Crippen.MolLogP,
|
158 |
'Molecular Weight': Descriptors.MolWt,
|
159 |
'Number of Atoms': rdMolDescriptors.CalcNumAtoms,
|
@@ -180,16 +182,6 @@ FILTER_MAP = {
|
|
180 |
}
|
181 |
|
182 |
|
183 |
-
def validate_columns(df, mandatory_cols):
|
184 |
-
missing_cols = [col for col in mandatory_cols if col not in df.columns]
|
185 |
-
if missing_cols:
|
186 |
-
error_message = (f"The following mandatory columns are missing "
|
187 |
-
f"in the uploaded dataset: {str(mandatory_cols).strip('[]')}.")
|
188 |
-
raise ValueError(error_message)
|
189 |
-
else:
|
190 |
-
return
|
191 |
-
|
192 |
-
|
193 |
def get_timezone_by_ip(ip, session):
|
194 |
try:
|
195 |
data = session.get(f'https://worldtimeapi.org/api/ip/{ip}').json()
|
@@ -289,20 +281,6 @@ def read_molecule_file(in_file, allowed_extentions):
|
|
289 |
return content, extension, None
|
290 |
|
291 |
|
292 |
-
def show_target(in_protein):
|
293 |
-
molecule, extension, html = read_molecule_file(in_protein, allowed_extentions=['pdb'])
|
294 |
-
if molecule is not None:
|
295 |
-
html = static.TARGET_RENDERING_TEMPLATE.format(molecule=molecule, fmt=extension)
|
296 |
-
|
297 |
-
return static.IFRAME_TEMPLATE.format(html=html)
|
298 |
-
|
299 |
-
def show_complex(complex_path):
|
300 |
-
protein_complex, extension, html = read_molecule_file(complex_path, allowed_extentions=['pdb'])
|
301 |
-
if protein_complex is not None:
|
302 |
-
html = static.COMPLEX_RENDERING_TEMPLATE.format(complex=protein_complex, fmt=extension)
|
303 |
-
|
304 |
-
return static.IFRAME_TEMPLATE.format(html=html)
|
305 |
-
|
306 |
# def create_complex_view_html(
|
307 |
# complex_path, pocket_path_dict=None,
|
308 |
# interactive_ligands=True, interactive_pockets=True
|
@@ -413,23 +391,24 @@ def show_complex(complex_path):
|
|
413 |
# html = static.COMPLEX_RENDERING_TEMPLATE.format(viewer_models=viewer_models)
|
414 |
# return static.IFRAME_TEMPLATE.format(html=html)
|
415 |
|
416 |
-
def create_result_table_html(summary_df, opts=(), progress=gr.Progress(track_tqdm=True)):
|
417 |
-
html_df = summary_df.copy()
|
418 |
column_aliases = {
|
|
|
|
|
419 |
'ID1': 'Compound ID',
|
420 |
'ID2': 'Target ID',
|
421 |
-
'X1': '
|
422 |
-
'
|
423 |
-
'
|
424 |
}
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
html_df.rename(columns=column_aliases, inplace=True)
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
|
434 |
# if 'Scaffold' in html_df.columns and 'Exclude Scaffold Graph' not in opts:
|
435 |
# html_df['Scaffold'] = html_df['Scaffold'].parallel_apply(
|
@@ -437,13 +416,13 @@ def create_result_table_html(summary_df, opts=(), progress=gr.Progress(track_tqd
|
|
437 |
# else:
|
438 |
# html_df.drop(['Scaffold'], axis=1, inplace=True)
|
439 |
|
440 |
-
# html_df.index.name = 'Index'
|
441 |
-
|
442 |
num_cols = html_df.select_dtypes('number').columns
|
443 |
num_col_colors = sns.color_palette('husl', len(num_cols))
|
444 |
bool_cols = html_df.select_dtypes(bool).columns
|
445 |
|
446 |
-
image_zoom_formatter = HTMLTemplateFormatter(
|
|
|
|
|
447 |
uniprot_id_formatter = HTMLTemplateFormatter(
|
448 |
template='<% if (value == value) { ' # Check if value is not NaN
|
449 |
'if (/^[OPQ][0-9][A-Z0-9]{3}[0-9]|[A-NR-Z][0-9]([A-Z][A-Z0-9]{2}[0-9]){1,2}$/.test(value)) '
|
@@ -473,12 +452,27 @@ def create_result_table_html(summary_df, opts=(), progress=gr.Progress(track_tqd
|
|
473 |
|
474 |
# html = df.to_html(file)
|
475 |
# return html
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
476 |
|
477 |
report_table = pn.widgets.Tabulator(
|
478 |
html_df, formatters=formatters,
|
479 |
-
frozen_columns=['Compound ID', 'Compound'],
|
480 |
hidden_columns=hidden_cols,
|
481 |
-
|
|
|
|
|
|
|
|
|
|
|
482 |
)
|
483 |
|
484 |
for i, col in enumerate(num_cols):
|
@@ -488,7 +482,7 @@ def create_result_table_html(summary_df, opts=(), progress=gr.Progress(track_tqd
|
|
488 |
subset=html_df.columns == col, cmap=cmap)
|
489 |
|
490 |
# TODO change this to use commonn substructures
|
491 |
-
pie_charts = {}
|
492 |
# for y in html_df.columns.intersection(['Interaction Probability', 'Binding Affinity (IC50 [nM])']):
|
493 |
# for category in categories:
|
494 |
# pie_charts[y][category] = []
|
@@ -505,68 +499,6 @@ def create_result_table_html(summary_df, opts=(), progress=gr.Progress(track_tqd
|
|
505 |
# pie_charts[y] = {k: v for k, v in pie_charts[y].items() if any(v)}
|
506 |
# pie_charts = {k: v for k, v in pie_charts.items() if any(v)}
|
507 |
|
508 |
-
panel_css = """
|
509 |
-
.tabulator {
|
510 |
-
font-family: Courier New !important;
|
511 |
-
font-weight: normal !important;
|
512 |
-
font-size: 12px !important;
|
513 |
-
}
|
514 |
-
|
515 |
-
.tabulator-cell {
|
516 |
-
overflow: visible !important;
|
517 |
-
align-content: center !important;
|
518 |
-
}
|
519 |
-
|
520 |
-
.tabulator-cell:hover {
|
521 |
-
z-index: 1000 !important;
|
522 |
-
}
|
523 |
-
|
524 |
-
.image-zoom-viewer {
|
525 |
-
display: inline-block;
|
526 |
-
overflow: visible;
|
527 |
-
z-index: 1000;
|
528 |
-
}
|
529 |
-
|
530 |
-
.image-zoom-viewer::after {
|
531 |
-
content: "";
|
532 |
-
top: 0;
|
533 |
-
left: 0;
|
534 |
-
width: 100%;
|
535 |
-
height: 100%;
|
536 |
-
pointer-events: none;
|
537 |
-
}
|
538 |
-
|
539 |
-
.image-zoom-viewer:hover::after {
|
540 |
-
pointer-events: all;
|
541 |
-
}
|
542 |
-
|
543 |
-
/* When hovering over the container, scale its child (the SVG) */
|
544 |
-
.tabulator-cell:hover .image-zoom-viewer svg {
|
545 |
-
padding: 3px;
|
546 |
-
position: absolute;
|
547 |
-
background-color: rgba(250, 250, 250, 0.854);
|
548 |
-
box-shadow: 0 0 10px rgba(0, 0, 0, 0.618);
|
549 |
-
border-radius: 3px;
|
550 |
-
transform: scale(3); /* Scale up the SVG */
|
551 |
-
transition: transform 0.3s ease;
|
552 |
-
pointer-events: none; /* Prevents the SVG from blocking mouse interactions */
|
553 |
-
z-index: 1000;
|
554 |
-
}
|
555 |
-
"""
|
556 |
-
|
557 |
-
pn.extension(
|
558 |
-
raw_css=[panel_css],
|
559 |
-
js_files={'panel_custom': 'app/panel.js'},
|
560 |
-
# js_modules={'3Dmol': 'static/3Dmol-min.js'},
|
561 |
-
inline=True,
|
562 |
-
)
|
563 |
-
|
564 |
-
template = pn.template.VanillaTemplate(
|
565 |
-
sidebar=[],
|
566 |
-
header=False,
|
567 |
-
busy_indicator=None,
|
568 |
-
)
|
569 |
-
|
570 |
# stats_pane = pn.Column()
|
571 |
# if pie_charts:
|
572 |
# for score_name, figure_dict in pie_charts.items():
|
@@ -584,64 +516,34 @@ def create_result_table_html(summary_df, opts=(), progress=gr.Progress(track_tqd
|
|
584 |
# template.main.append(
|
585 |
# pn.Card(stats_pane, sizing_mode='stretch_width', title='Summary Statistics', margin=10)
|
586 |
# )
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
)
|
592 |
|
593 |
with tempfile.TemporaryDirectory() as tmpdir:
|
594 |
file = Path(tmpdir) / 'report.html'
|
595 |
-
|
596 |
-
|
597 |
-
|
|
|
598 |
return iframe_html
|
599 |
|
600 |
|
601 |
-
def pdb_query(query, method):
|
602 |
-
"""Downloads protein structure data or searches FASTA sequence."""
|
603 |
-
gr.Info(f'Querying protein by {method}...')
|
604 |
-
try:
|
605 |
-
if method == 'PDB ID':
|
606 |
-
url = f"https://files.rcsb.org/download/{query}.pdb"
|
607 |
-
return download_file(url)
|
608 |
-
elif method == 'UniProt ID':
|
609 |
-
pdb_ids = uniprot_to_pdb(query)
|
610 |
-
if pdb_ids:
|
611 |
-
# Download the first associated PDB file
|
612 |
-
return download_file(f"https://files.rcsb.org/download/{pdb_ids[0]}.pdb")
|
613 |
-
else:
|
614 |
-
raise ValueError(f"No PDB IDs found for UniProt ID: {query}")
|
615 |
-
elif method == 'FASTA Sequence':
|
616 |
-
pdb_ids = fasta_to_pdb(query)
|
617 |
-
if pdb_ids:
|
618 |
-
# Download the first associated PDB file
|
619 |
-
return download_file(f"https://files.rcsb.org/download/{pdb_ids[0]}.pdb")
|
620 |
-
else:
|
621 |
-
raise ValueError("No PDB IDs found for the provided FASTA sequence.")
|
622 |
-
else:
|
623 |
-
raise ValueError(f"Unsupported method: {method}")
|
624 |
-
except Exception as e:
|
625 |
-
gr.Warning(f"Error downloading PDB file: {e}")
|
626 |
-
return None
|
627 |
-
|
628 |
-
|
629 |
def download_file(url):
|
630 |
"""Downloads a small file to a temporary location, preserving its filename."""
|
631 |
-
|
632 |
-
|
633 |
-
|
|
|
634 |
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
|
639 |
-
|
640 |
|
641 |
-
|
642 |
-
except Exception as e:
|
643 |
-
gr.Error(f"Download error: {e}")
|
644 |
-
return None
|
645 |
|
646 |
|
647 |
def uniprot_to_pdb(uniprot_id):
|
@@ -681,7 +583,6 @@ def uniprot_to_pdb(uniprot_id):
|
|
681 |
data = response.json()
|
682 |
return [entry["identifier"] for entry in data.get("result_set", [])]
|
683 |
except Exception as e:
|
684 |
-
print(f"Error querying UniProt ID: {e}")
|
685 |
return []
|
686 |
|
687 |
|
@@ -711,5 +612,4 @@ def fasta_to_pdb(fasta_sequence):
|
|
711 |
data = response.json()
|
712 |
return [entry["identifier"] for entry in data.get("result_set", [])]
|
713 |
except Exception as e:
|
714 |
-
print(f"Error querying FASTA sequence: {e}")
|
715 |
return []
|
|
|
3 |
from email.mime.text import MIMEText
|
4 |
from email.utils import formatdate, make_msgid
|
5 |
from functools import cache
|
6 |
+
import html
|
7 |
import os
|
8 |
from pathlib import Path
|
9 |
import smtplib
|
|
|
12 |
|
13 |
import pandas as pd
|
14 |
from bokeh.models import NumberFormatter, BooleanFormatter, HTMLTemplateFormatter
|
15 |
+
from bokeh.resources import INLINE
|
16 |
import gradio as gr
|
17 |
import pytz
|
18 |
import panel as pn
|
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
|
23 |
import requests
|
24 |
|
25 |
from app import static
|
|
|
155 |
|
156 |
|
157 |
SCORE_MAP = {
|
158 |
+
'Synthetic Accessibility': sascorer.calculateScore,
|
159 |
'LogP': Crippen.MolLogP,
|
160 |
'Molecular Weight': Descriptors.MolWt,
|
161 |
'Number of Atoms': rdMolDescriptors.CalcNumAtoms,
|
|
|
182 |
}
|
183 |
|
184 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
185 |
def get_timezone_by_ip(ip, session):
|
186 |
try:
|
187 |
data = session.get(f'https://worldtimeapi.org/api/ip/{ip}').json()
|
|
|
281 |
return content, extension, None
|
282 |
|
283 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
284 |
# def create_complex_view_html(
|
285 |
# complex_path, pocket_path_dict=None,
|
286 |
# interactive_ligands=True, interactive_pockets=True
|
|
|
391 |
# html = static.COMPLEX_RENDERING_TEMPLATE.format(viewer_models=viewer_models)
|
392 |
# return static.IFRAME_TEMPLATE.format(html=html)
|
393 |
|
394 |
+
def create_result_table_html(summary_df, result_info, opts=(), progress=gr.Progress(track_tqdm=True)):
|
395 |
+
html_df = summary_df.copy().drop(columns=['mol'])
|
396 |
column_aliases = {
|
397 |
+
'out_path': 'Pose',
|
398 |
+
'ligand_conf_path': 'Pose',
|
399 |
'ID1': 'Compound ID',
|
400 |
'ID2': 'Target ID',
|
401 |
+
'X1': 'Fragment SMILES',
|
402 |
+
'X1^': 'Compound SMILES',
|
403 |
+
'name': 'Complex Name',
|
404 |
}
|
405 |
+
output_dir = Path(result_info['output_dir'])
|
406 |
+
job_type = result_info['type']
|
|
|
407 |
html_df.rename(columns=column_aliases, inplace=True)
|
408 |
+
html_df['Pose'] = html_df['Pose'].apply(lambda x: str(output_dir / job_type / x))
|
409 |
+
# drop remaining columns ending with '_path'
|
410 |
+
hidden_cols = [col for col in html_df.columns if col.endswith('_path')]
|
411 |
+
html_df.index.name = 'Index'
|
412 |
|
413 |
# if 'Scaffold' in html_df.columns and 'Exclude Scaffold Graph' not in opts:
|
414 |
# html_df['Scaffold'] = html_df['Scaffold'].parallel_apply(
|
|
|
416 |
# else:
|
417 |
# html_df.drop(['Scaffold'], axis=1, inplace=True)
|
418 |
|
|
|
|
|
419 |
num_cols = html_df.select_dtypes('number').columns
|
420 |
num_col_colors = sns.color_palette('husl', len(num_cols))
|
421 |
bool_cols = html_df.select_dtypes(bool).columns
|
422 |
|
423 |
+
image_zoom_formatter = HTMLTemplateFormatter(
|
424 |
+
template='<img src="data:image/svg+xml,<%= value %>" alt="Molecule" class="zoom-img">'
|
425 |
+
)
|
426 |
uniprot_id_formatter = HTMLTemplateFormatter(
|
427 |
template='<% if (value == value) { ' # Check if value is not NaN
|
428 |
'if (/^[OPQ][0-9][A-Z0-9]{3}[0-9]|[A-NR-Z][0-9]([A-Z][A-Z0-9]{2}[0-9]){1,2}$/.test(value)) '
|
|
|
452 |
|
453 |
# html = df.to_html(file)
|
454 |
# return html
|
455 |
+
static_url = "gradio_api/file=app/static/"
|
456 |
+
pn.extension(
|
457 |
+
design='material',
|
458 |
+
css_files=[
|
459 |
+
static_url + 'panel.css'
|
460 |
+
],
|
461 |
+
js_files={
|
462 |
+
'panel_custom': static_url + 'panel.js',
|
463 |
+
},
|
464 |
+
)
|
465 |
|
466 |
report_table = pn.widgets.Tabulator(
|
467 |
html_df, formatters=formatters,
|
468 |
+
frozen_columns=['Pose', 'Compound ID', 'Compound'],
|
469 |
hidden_columns=hidden_cols,
|
470 |
+
sizing_mode='stretch_both',
|
471 |
+
disabled=True, selectable=False,
|
472 |
+
pagination='local',
|
473 |
+
configuration={
|
474 |
+
'rowHeight': 60,
|
475 |
+
},
|
476 |
)
|
477 |
|
478 |
for i, col in enumerate(num_cols):
|
|
|
482 |
subset=html_df.columns == col, cmap=cmap)
|
483 |
|
484 |
# TODO change this to use commonn substructures
|
485 |
+
# pie_charts = {}
|
486 |
# for y in html_df.columns.intersection(['Interaction Probability', 'Binding Affinity (IC50 [nM])']):
|
487 |
# for category in categories:
|
488 |
# pie_charts[y][category] = []
|
|
|
499 |
# pie_charts[y] = {k: v for k, v in pie_charts[y].items() if any(v)}
|
500 |
# pie_charts = {k: v for k, v in pie_charts.items() if any(v)}
|
501 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
502 |
# stats_pane = pn.Column()
|
503 |
# if pie_charts:
|
504 |
# for score_name, figure_dict in pie_charts.items():
|
|
|
516 |
# template.main.append(
|
517 |
# pn.Card(stats_pane, sizing_mode='stretch_width', title='Summary Statistics', margin=10)
|
518 |
# )
|
519 |
+
report = pn.Column(pn.Accordion(
|
520 |
+
(f'{job_type.title()} Results', report_table),
|
521 |
+
toggle=True, margin=5, active=[0]
|
522 |
+
))
|
|
|
523 |
|
524 |
with tempfile.TemporaryDirectory() as tmpdir:
|
525 |
file = Path(tmpdir) / 'report.html'
|
526 |
+
report.save(file)
|
527 |
+
# iframe_html = static.IFRAME_LINK_TEMPLATE.format(src="gradio_api/file=" + str(file))
|
528 |
+
html_str = file.read_text() # .replace('\'', '\"')
|
529 |
+
iframe_html = static.IFRAME_TEMPLATE.format(srcdoc=html.escape(html_str), aspect_ratio='1.090 / 1')
|
530 |
return iframe_html
|
531 |
|
532 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
533 |
def download_file(url):
|
534 |
"""Downloads a small file to a temporary location, preserving its filename."""
|
535 |
+
response = requests.get(url)
|
536 |
+
if response.status_code == 404:
|
537 |
+
raise ValueError('No record found for the provided PDB ID.')
|
538 |
+
response.raise_for_status()
|
539 |
|
540 |
+
filename = Path(url).name
|
541 |
+
temp_dir = Path(tempfile.gettempdir()) / 'gradio'
|
542 |
+
temp_path = temp_dir / filename
|
543 |
|
544 |
+
temp_path.write_bytes(response.content)
|
545 |
|
546 |
+
return str(temp_path)
|
|
|
|
|
|
|
547 |
|
548 |
|
549 |
def uniprot_to_pdb(uniprot_id):
|
|
|
583 |
data = response.json()
|
584 |
return [entry["identifier"] for entry in data.get("result_set", [])]
|
585 |
except Exception as e:
|
|
|
586 |
return []
|
587 |
|
588 |
|
|
|
612 |
data = response.json()
|
613 |
return [entry["identifier"] for entry in data.get("result_set", [])]
|
614 |
except Exception as e:
|
|
|
615 |
return []
|
app/main.py
CHANGED
@@ -3,6 +3,7 @@ import zipfile
|
|
3 |
from datetime import datetime
|
4 |
from pathlib import Path
|
5 |
from time import sleep, time
|
|
|
6 |
|
7 |
import torch
|
8 |
from email_validator import validate_email, EmailNotValidError
|
@@ -12,17 +13,17 @@ from gradio_rangeslider import RangeSlider
|
|
12 |
from omegaconf import OmegaConf
|
13 |
import pandas as pd
|
14 |
from rdkit import Chem
|
15 |
-
from rdkit.Chem import PandasTools
|
16 |
|
17 |
from inference import (read_fragment_library, process_fragment_library, extract_pockets,
|
18 |
dock_fragments, generate_linkers, select_fragment_pairs)
|
19 |
from app import static, fn, db
|
20 |
|
21 |
|
22 |
-
gr.set_static_paths(paths=["data/", "results/"])
|
23 |
job_db = db.init_job_db()
|
24 |
|
25 |
-
FRAG_LIBS = {
|
26 |
lib_path.stem.replace('_', ' '): str(lib_path) for lib_path in Path('data/fragment_libraries').glob('*')
|
27 |
}
|
28 |
|
@@ -48,20 +49,14 @@ POCKET_EXTRACT_OPTS = {
|
|
48 |
}
|
49 |
|
50 |
|
51 |
-
|
52 |
-
def
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
library_upload,
|
58 |
-
smilesName='X1', molColName='mol',
|
59 |
-
)
|
60 |
-
else:
|
61 |
-
raise gr.Error('Current supported fragment library formats only include CSV and SDF files.')
|
62 |
-
fn.validate_columns(df, ['X1'])
|
63 |
-
return df
|
64 |
|
|
|
65 |
|
66 |
def query_job_status(job_id):
|
67 |
gr.Info('Start querying the job database...')
|
@@ -69,10 +64,10 @@ def query_job_status(job_id):
|
|
69 |
retry = 0
|
70 |
while not stop:
|
71 |
try:
|
72 |
-
sleep(5)
|
73 |
job = job_db.job_lookup(job_id)
|
74 |
if job:
|
75 |
if job['status'] == "RUNNING":
|
|
|
76 |
yield {
|
77 |
pred_lookup_status: f'''
|
78 |
Your job (ID: **{job['id']}**) started at **{job['start_time']}** and is **RUNNING...**
|
@@ -86,7 +81,7 @@ using the job id. You will also receive an email notification once the job is do
|
|
86 |
}
|
87 |
if job['status'] == "COMPLETED":
|
88 |
stop = True
|
89 |
-
msg = f"Your GenFBDD job (ID: {job['id']}) has been
|
90 |
msg += f" at {job['end_time']}" if job.get('end_time') else ""
|
91 |
msg += f" and the results will expire by {job['expiry_time']}." if job.get('expiry_time') else "."
|
92 |
msg += f' Redirecting to the Results page...'
|
@@ -112,13 +107,14 @@ using the job id. You will also receive an email notification once the job is do
|
|
112 |
tabs: gr.Tabs(selected='job'),
|
113 |
}
|
114 |
else:
|
115 |
-
stop = (retry >
|
116 |
if not stop:
|
117 |
msg = f'Job ID {job_id} not found. Retrying... ({retry})'
|
118 |
else:
|
119 |
msg = f'Job ID {job_id} not found after {retry} retries. Please double-check the job ID.'
|
120 |
gr.Info(msg)
|
121 |
retry += 1
|
|
|
122 |
yield {
|
123 |
pred_lookup_status: msg,
|
124 |
pred_lookup_btn: gr.Button(visible=True),
|
@@ -177,7 +173,7 @@ def job_validate(
|
|
177 |
job_id = str(uuid.uuid4())
|
178 |
job_info = {
|
179 |
'id': job_id,
|
180 |
-
'status': '
|
181 |
'fragment_library_file': frag_file,
|
182 |
'protein_structure_file': prot_file,
|
183 |
'pocket_extraction_method': pocket_extraction_method,
|
@@ -200,10 +196,10 @@ def dock_link(
|
|
200 |
linker_frag_dist, linker_strategy, linker_n_mols, linker_size, linker_steps,
|
201 |
job_info
|
202 |
):
|
|
|
203 |
job_id = job_info['id']
|
204 |
pocket_extract_method = job_info['pocket_extraction_method']
|
205 |
pocket_path_dict = job_info['protein_pocket_files']
|
206 |
-
update_info = {}
|
207 |
|
208 |
config = OmegaConf.load('configs/gen_fbdd_v1.yaml')
|
209 |
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
@@ -213,6 +209,11 @@ def dock_link(
|
|
213 |
frag_lib['X2'] = prot
|
214 |
frag_lib['ID2'] = Path(prot).stem
|
215 |
|
|
|
|
|
|
|
|
|
|
|
216 |
try:
|
217 |
docking_df = dock_fragments(
|
218 |
df=frag_lib, out_dir=out_dir,
|
@@ -230,7 +231,8 @@ def dock_link(
|
|
230 |
temp_sigma_data_tr=config.temp_sigma_data_tr,
|
231 |
temp_sigma_data_rot=config.temp_sigma_data_rot,
|
232 |
temp_sigma_data_tor=config.temp_sigma_data_tor,
|
233 |
-
save_docking=pocket_extract_method == 'clustering',
|
|
|
234 |
)
|
235 |
|
236 |
linking_df = select_fragment_pairs(
|
@@ -290,7 +292,6 @@ def dock_link(
|
|
290 |
)
|
291 |
|
292 |
|
293 |
-
|
294 |
def get_session_state(request: gr.Request):
|
295 |
return request
|
296 |
|
@@ -299,7 +300,7 @@ THEME = gr.themes.Base(
|
|
299 |
spacing_size="sm", text_size='md', font=gr.themes.GoogleFont("Roboto"),
|
300 |
primary_hue='emerald', secondary_hue='emerald', neutral_hue='slate',
|
301 |
).set(
|
302 |
-
body_background_fill='*primary_50'
|
303 |
# background_fill_primary='#eef3f9',
|
304 |
# background_fill_secondary='white',
|
305 |
# checkbox_label_background_fill='#eef3f9',
|
@@ -327,7 +328,7 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
327 |
|
328 |
# script_init_frame = gr.HTML(static.PROTEIN_VIEW_IFRAME)
|
329 |
with gr.Tabs() as tabs:
|
330 |
-
with gr.Tab(label='
|
331 |
gr.Markdown('''
|
332 |
# GenFBDD - A Fragment-Based Drug Design Protocol Based on SOTA Molecular Generative Models
|
333 |
|
@@ -342,7 +343,7 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
342 |
frag_lib_dropdown = gr.Dropdown(
|
343 |
label='Select a Preset Fragment Library',
|
344 |
choices=list(FRAG_LIBS.keys()),
|
345 |
-
value=
|
346 |
)
|
347 |
# with gr.Row():
|
348 |
# gr.File(label='Example SDF fragment library',
|
@@ -350,18 +351,20 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
350 |
# gr.File(label='Example CSV fragment library',
|
351 |
# value='data/examples/fragment_library.csv', interactive=False)
|
352 |
frag_lib_upload_btn = gr.UploadButton(
|
353 |
-
label='OR Upload Your Own Library', variant='primary'
|
354 |
)
|
355 |
|
356 |
frag_lib_file = gr.File(
|
357 |
-
label='Fragment Library File (Original)',
|
|
|
|
|
358 |
)
|
359 |
-
frag_lib_orig_df = gr.State(value=pd.DataFrame())
|
360 |
-
frag_lib_mod_df = gr.State(value=pd.DataFrame())
|
361 |
# TODO: Tabulator with gr.HTML() for fragment library preview
|
362 |
frag_lib_view = gr.DataFrame(
|
|
|
363 |
visible=True, interactive=False,
|
364 |
-
elem_id='frag_lib_view',
|
365 |
)
|
366 |
|
367 |
with gr.Group():
|
@@ -375,7 +378,9 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
375 |
value=['Dehalogenate Fragments', 'Discard Inorganic Fragments'],
|
376 |
interactive=True,
|
377 |
)
|
378 |
-
frag_lib_process_btn = gr.Button(
|
|
|
|
|
379 |
# Fragment library preview
|
380 |
|
381 |
with gr.Column(variant='panel'):
|
@@ -394,22 +399,25 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
394 |
)
|
395 |
prot_query_input = gr.Textbox(
|
396 |
show_label=False, placeholder='Enter the protein query here',
|
397 |
-
scale=3,
|
398 |
)
|
399 |
|
400 |
with gr.Row():
|
401 |
-
prot_query_btn = gr.Button(
|
|
|
|
|
|
|
402 |
prot_upload_btn = gr.UploadButton(
|
403 |
label='OR Upload Your PDB/FASTA File', variant='primary',
|
404 |
file_types=['.pdb', '.fasta'],
|
405 |
-
scale=2
|
406 |
)
|
407 |
|
408 |
input_prot_file = gr.File(
|
409 |
-
label='Protein Structure File (Original)',
|
410 |
-
interactive=False, visible=False
|
411 |
)
|
412 |
-
input_prot_view = gr.HTML('<div id="input_protein_view" class="mol-container"></div>')
|
413 |
|
414 |
with gr.Group():
|
415 |
pocket_extract_dropdown = gr.Dropdown(
|
@@ -422,12 +430,14 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
422 |
selected_pocket = gr.Textbox(visible=False)
|
423 |
selected_ligand = gr.Textbox(visible=False)
|
424 |
pocket_files = gr.Files(visible=False)
|
425 |
-
pocket_extract_btn = gr.Button(
|
|
|
|
|
426 |
# Target protein preview
|
427 |
with gr.Row():
|
428 |
with gr.Column(variant='panel'):
|
429 |
gr.Markdown('## Dock Phase Settings')
|
430 |
-
|
431 |
value=5, minimum=1, maximum=20, step=1,
|
432 |
label="Number of conformers to generate per fragment",
|
433 |
interactive=True
|
@@ -450,7 +460,7 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
450 |
)
|
451 |
with gr.Column(variant='panel'):
|
452 |
gr.Markdown('## Link Phase Settings')
|
453 |
-
|
454 |
label='Select a Fragment-Conformer Linking Strategy',
|
455 |
choices=[
|
456 |
'Link Pairs of Fragment-Conformers Contacting the Pocket',
|
@@ -458,12 +468,12 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
458 |
],
|
459 |
value='Link Pairs of Fragment-Conformers Contacting the Pocket',
|
460 |
)
|
461 |
-
|
462 |
value=[2, 8], minimum=1, maximum=10, step=1,
|
463 |
label="Fragment-Conformer Distance Range (Å) Eligible for Linking",
|
464 |
interactive=True
|
465 |
)
|
466 |
-
|
467 |
value=10, minimum=1, maximum=20, step=1,
|
468 |
label="Number of molecules to generate per fragment conformer combination",
|
469 |
interactive=True
|
@@ -474,13 +484,13 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
474 |
choices=['DiffLinker'],
|
475 |
interactive=True,
|
476 |
)
|
477 |
-
|
478 |
minimum=0, maximum=20, step=1,
|
479 |
label="Linker Size",
|
480 |
info="0: automatically predicted; >=1: fixed size",
|
481 |
interactive=True
|
482 |
)
|
483 |
-
|
484 |
minimum=100, maximum=500, step=10,
|
485 |
label="Number of Denoising Steps for Generating Linkers",
|
486 |
interactive=True
|
@@ -489,14 +499,16 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
489 |
email_input =gr.Textbox(
|
490 |
label='Email Address (Optional)',
|
491 |
info="Your email address will be used to notify you of the status of your job. "
|
492 |
-
"If you cannot receive the email, please check your spam/junk folder."
|
|
|
493 |
)
|
494 |
with gr.Column():
|
495 |
-
|
496 |
-
value='Reset Inputs',
|
497 |
-
|
|
|
|
|
498 |
)
|
499 |
-
run_btn = gr.Button(value='Run GenFBDD', variant='primary')
|
500 |
with gr.Tab(label='Results', id='result'):
|
501 |
# Results
|
502 |
result_state = gr.State(value={})
|
@@ -509,14 +521,14 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
509 |
filters = gr.CheckboxGroup(list(fn.FILTER_MAP.keys()), label='Compound Filters')
|
510 |
with gr.Row():
|
511 |
prop_clr_btn = gr.ClearButton(value='Clear Properties', interactive=False)
|
512 |
-
prop_calc_btn = gr.Button(value='Calculate Properties', interactive=False)
|
513 |
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
|
521 |
with gr.Tab(label='Job Status', id='job'):
|
522 |
gr.Markdown('''
|
@@ -540,10 +552,10 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
540 |
pred_lookup_status = gr.Markdown()
|
541 |
|
542 |
# Event handlers
|
543 |
-
##
|
544 |
### Fragment Library
|
545 |
-
frag_lib_dropdown.change(
|
546 |
-
fn=lambda lib: gr.File(FRAG_LIBS[lib], visible=
|
547 |
inputs=[frag_lib_dropdown],
|
548 |
outputs=[frag_lib_file],
|
549 |
)
|
@@ -554,8 +566,8 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
554 |
)
|
555 |
|
556 |
# Changing the file updates the original df, the modified df, and the view
|
557 |
-
frag_lib_file.change(
|
558 |
-
fn=read_fragment_library,
|
559 |
inputs=[frag_lib_file],
|
560 |
outputs=[frag_lib_orig_df],
|
561 |
).success(
|
@@ -586,7 +598,7 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
586 |
}
|
587 |
elif filepath.suffix == '.fasta':
|
588 |
seq = next(SeqIO.parse(file, 'fasta')).seq
|
589 |
-
filepath =
|
590 |
return {
|
591 |
input_prot_file: gr.File(str(filepath), visible=True),
|
592 |
prot_query_input: seq,
|
@@ -611,17 +623,45 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
611 |
outputs=[input_prot_file, prot_query_dropdown, prot_query_input],
|
612 |
)
|
613 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
614 |
prot_query_btn.click(
|
615 |
-
fn=
|
616 |
inputs=[prot_query_input, prot_query_dropdown],
|
617 |
outputs=[input_prot_file],
|
618 |
)
|
619 |
|
620 |
-
input_prot_file.change(
|
621 |
-
fn=lambda
|
|
|
|
|
622 |
inputs=[input_prot_file, input_prot_view],
|
623 |
-
|
624 |
-
js=static.CREATE_MOL_VIEW,
|
625 |
)
|
626 |
|
627 |
#### Pocket Extraction
|
@@ -652,7 +692,7 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
652 |
outputs=[pocket_files, selected_ligand, selected_pocket],
|
653 |
).success(
|
654 |
fn=lambda x, y: gr.Info('Pocket extraction completed.'),
|
655 |
-
js=static.
|
656 |
inputs=[pocket_files, input_prot_view],
|
657 |
)
|
658 |
|
@@ -676,18 +716,40 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
676 |
fn=dock_link,
|
677 |
inputs=[
|
678 |
frag_lib_mod_df, input_prot_file,
|
679 |
-
dock_steps,
|
680 |
-
|
681 |
-
|
682 |
run_state
|
683 |
],
|
684 |
outputs=[result_state, run_state],
|
685 |
concurrency_limit=1, concurrency_id="gpu_queue"
|
686 |
)
|
687 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
688 |
### Job Status
|
689 |
user_job_lookup = pred_lookup_btn.click(
|
690 |
-
lambda: '<div class="loader"></ div>',
|
691 |
outputs=loader_html,
|
692 |
).success(
|
693 |
fn=query_job_status,
|
@@ -734,29 +796,44 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
734 |
protein_structure_file = Path(result_info['protein_structure_file'])
|
735 |
if result_type == 'docking':
|
736 |
result_df = pd.read_csv(result_dir / 'docking_summary.csv')
|
737 |
-
result_df['
|
738 |
elif result_type == 'linking':
|
739 |
result_df = pd.read_csv(result_dir / 'linking_summary.csv')
|
740 |
result_df = result_df[~result_df['X1^'].str.contains('.', regex=False)]
|
741 |
-
result_df['
|
742 |
-
result_df.dropna(subset=['
|
743 |
else:
|
744 |
raise gr.Error('Invalid result type')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
745 |
return {
|
746 |
result_table_orig_df: result_df,
|
747 |
result_table_mod_df: result_df.copy(deep=True),
|
748 |
result_protein_file: str(protein_structure_file),
|
|
|
749 |
}
|
750 |
|
751 |
def update_table(orig_df, score_list, filter_list, progress=gr.Progress(track_tqdm=True)):
|
|
|
752 |
mod_df = orig_df.copy()
|
753 |
try:
|
754 |
for filter_name in filter_list:
|
755 |
-
mod_df[filter_name] = mod_df['
|
756 |
lambda x: fn.FILTER_MAP[filter_name](x) if not pd.isna(x) else x)
|
757 |
|
758 |
for score_name in score_list:
|
759 |
-
mod_df[score_name] = mod_df['
|
760 |
lambda x: fn.SCORE_MAP[score_name](x) if not pd.isna(x) else x)
|
761 |
|
762 |
return {result_table_mod_df: mod_df}
|
@@ -772,45 +849,47 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
|
|
772 |
)
|
773 |
|
774 |
result_protein_file.change(
|
775 |
-
fn=lambda x, y:
|
776 |
-
js=static.
|
777 |
inputs=[result_protein_file, result_prot_view],
|
778 |
-
outputs=[result_protein_file],
|
779 |
)
|
780 |
result_table_mod_df.change(
|
781 |
fn=fn.create_result_table_html,
|
782 |
-
inputs=[result_table_mod_df],
|
783 |
outputs=[result_table_view]
|
784 |
).success(
|
785 |
-
fn=lambda x: gr.Button(visible=True),
|
786 |
inputs=[result_file_btn],
|
787 |
-
outputs=[result_file_btn],
|
788 |
)
|
789 |
prop_calc_btn.click(
|
790 |
fn=update_table,
|
791 |
inputs=[result_table_orig_df, scores, filters],
|
792 |
outputs=[result_table_mod_df],
|
793 |
-
show_progress='full',
|
794 |
)
|
795 |
prop_clr_btn.click(
|
796 |
-
fn=lambda orig_df: [orig_df, [], [], gr.
|
797 |
inputs=[result_table_orig_df],
|
798 |
-
outputs=[result_table_mod_df, scores, filters,
|
799 |
-
show_progress='full',
|
800 |
)
|
801 |
|
802 |
-
|
803 |
def generate_result_zip(result_info, compound_mod_df, protein_file):
|
804 |
-
|
805 |
-
zip_path =
|
806 |
-
|
|
|
|
|
|
|
807 |
with zipfile.ZipFile(zip_path, 'w') as zip_file:
|
808 |
-
for file in
|
809 |
-
|
810 |
-
|
|
|
811 |
zip_file.write(Path(protein_file), arcname=Path(protein_file).name)
|
|
|
812 |
return gr.File(str(zip_path), visible=True)
|
813 |
|
|
|
814 |
result_file_btn.click(
|
815 |
fn=generate_result_zip,
|
816 |
inputs=[result_state, result_table_mod_df, result_protein_file],
|
|
|
3 |
from datetime import datetime
|
4 |
from pathlib import Path
|
5 |
from time import sleep, time
|
6 |
+
import urllib.parse
|
7 |
|
8 |
import torch
|
9 |
from email_validator import validate_email, EmailNotValidError
|
|
|
13 |
from omegaconf import OmegaConf
|
14 |
import pandas as pd
|
15 |
from rdkit import Chem
|
16 |
+
from rdkit.Chem import PandasTools, Draw
|
17 |
|
18 |
from inference import (read_fragment_library, process_fragment_library, extract_pockets,
|
19 |
dock_fragments, generate_linkers, select_fragment_pairs)
|
20 |
from app import static, fn, db
|
21 |
|
22 |
|
23 |
+
gr.set_static_paths(paths=["data/", "results/", "app/"])
|
24 |
job_db = db.init_job_db()
|
25 |
|
26 |
+
FRAG_LIBS = {'': None} | {
|
27 |
lib_path.stem.replace('_', ' '): str(lib_path) for lib_path in Path('data/fragment_libraries').glob('*')
|
28 |
}
|
29 |
|
|
|
49 |
}
|
50 |
|
51 |
|
52 |
+
def gr_error_wrapper(func):
|
53 |
+
def wrapper(*args, **kwargs):
|
54 |
+
try:
|
55 |
+
return func(*args, **kwargs)
|
56 |
+
except Exception as e:
|
57 |
+
raise gr.Error(str(e))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
+
return wrapper
|
60 |
|
61 |
def query_job_status(job_id):
|
62 |
gr.Info('Start querying the job database...')
|
|
|
64 |
retry = 0
|
65 |
while not stop:
|
66 |
try:
|
|
|
67 |
job = job_db.job_lookup(job_id)
|
68 |
if job:
|
69 |
if job['status'] == "RUNNING":
|
70 |
+
sleep(5)
|
71 |
yield {
|
72 |
pred_lookup_status: f'''
|
73 |
Your job (ID: **{job['id']}**) started at **{job['start_time']}** and is **RUNNING...**
|
|
|
81 |
}
|
82 |
if job['status'] == "COMPLETED":
|
83 |
stop = True
|
84 |
+
msg = f"Your GenFBDD job (ID: {job['id']}) has been COMPLETED"
|
85 |
msg += f" at {job['end_time']}" if job.get('end_time') else ""
|
86 |
msg += f" and the results will expire by {job['expiry_time']}." if job.get('expiry_time') else "."
|
87 |
msg += f' Redirecting to the Results page...'
|
|
|
107 |
tabs: gr.Tabs(selected='job'),
|
108 |
}
|
109 |
else:
|
110 |
+
stop = (retry > 2)
|
111 |
if not stop:
|
112 |
msg = f'Job ID {job_id} not found. Retrying... ({retry})'
|
113 |
else:
|
114 |
msg = f'Job ID {job_id} not found after {retry} retries. Please double-check the job ID.'
|
115 |
gr.Info(msg)
|
116 |
retry += 1
|
117 |
+
sleep(5)
|
118 |
yield {
|
119 |
pred_lookup_status: msg,
|
120 |
pred_lookup_btn: gr.Button(visible=True),
|
|
|
173 |
job_id = str(uuid.uuid4())
|
174 |
job_info = {
|
175 |
'id': job_id,
|
176 |
+
'status': 'QUEUED',
|
177 |
'fragment_library_file': frag_file,
|
178 |
'protein_structure_file': prot_file,
|
179 |
'pocket_extraction_method': pocket_extraction_method,
|
|
|
196 |
linker_frag_dist, linker_strategy, linker_n_mols, linker_size, linker_steps,
|
197 |
job_info
|
198 |
):
|
199 |
+
update_info = {}
|
200 |
job_id = job_info['id']
|
201 |
pocket_extract_method = job_info['pocket_extraction_method']
|
202 |
pocket_path_dict = job_info['protein_pocket_files']
|
|
|
203 |
|
204 |
config = OmegaConf.load('configs/gen_fbdd_v1.yaml')
|
205 |
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
|
|
209 |
frag_lib['X2'] = prot
|
210 |
frag_lib['ID2'] = Path(prot).stem
|
211 |
|
212 |
+
job_db.job_update(
|
213 |
+
job_id=job_id,
|
214 |
+
update_info={'status': 'RUNNING'},
|
215 |
+
)
|
216 |
+
|
217 |
try:
|
218 |
docking_df = dock_fragments(
|
219 |
df=frag_lib, out_dir=out_dir,
|
|
|
231 |
temp_sigma_data_tr=config.temp_sigma_data_tr,
|
232 |
temp_sigma_data_rot=config.temp_sigma_data_rot,
|
233 |
temp_sigma_data_tor=config.temp_sigma_data_tor,
|
234 |
+
save_docking=(pocket_extract_method == 'clustering'),
|
235 |
+
device=device,
|
236 |
)
|
237 |
|
238 |
linking_df = select_fragment_pairs(
|
|
|
292 |
)
|
293 |
|
294 |
|
|
|
295 |
def get_session_state(request: gr.Request):
|
296 |
return request
|
297 |
|
|
|
300 |
spacing_size="sm", text_size='md', font=gr.themes.GoogleFont("Roboto"),
|
301 |
primary_hue='emerald', secondary_hue='emerald', neutral_hue='slate',
|
302 |
).set(
|
303 |
+
# body_background_fill='*primary_50'
|
304 |
# background_fill_primary='#eef3f9',
|
305 |
# background_fill_secondary='white',
|
306 |
# checkbox_label_background_fill='#eef3f9',
|
|
|
328 |
|
329 |
# script_init_frame = gr.HTML(static.PROTEIN_VIEW_IFRAME)
|
330 |
with gr.Tabs() as tabs:
|
331 |
+
with gr.Tab(label='Start', id='start'):
|
332 |
gr.Markdown('''
|
333 |
# GenFBDD - A Fragment-Based Drug Design Protocol Based on SOTA Molecular Generative Models
|
334 |
|
|
|
343 |
frag_lib_dropdown = gr.Dropdown(
|
344 |
label='Select a Preset Fragment Library',
|
345 |
choices=list(FRAG_LIBS.keys()),
|
346 |
+
value='',
|
347 |
)
|
348 |
# with gr.Row():
|
349 |
# gr.File(label='Example SDF fragment library',
|
|
|
351 |
# gr.File(label='Example CSV fragment library',
|
352 |
# value='data/examples/fragment_library.csv', interactive=False)
|
353 |
frag_lib_upload_btn = gr.UploadButton(
|
354 |
+
label='OR Upload Your Own Library', variant='primary', interactive=True,
|
355 |
)
|
356 |
|
357 |
frag_lib_file = gr.File(
|
358 |
+
value=None, label='Fragment Library File (Original)',
|
359 |
+
file_count='single', file_types=['.sdf', '.csv'],
|
360 |
+
interactive=False, visible=False
|
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 |
# TODO: Tabulator with gr.HTML() for fragment library preview
|
365 |
frag_lib_view = gr.DataFrame(
|
366 |
+
value=pd.DataFrame(columns=['X1', 'ID1']), elem_id='frag_lib_view',
|
367 |
visible=True, interactive=False,
|
|
|
368 |
)
|
369 |
|
370 |
with gr.Group():
|
|
|
378 |
value=['Dehalogenate Fragments', 'Discard Inorganic Fragments'],
|
379 |
interactive=True,
|
380 |
)
|
381 |
+
frag_lib_process_btn = gr.Button(
|
382 |
+
value='Process Fragments', variant='primary', interactive=True,
|
383 |
+
)
|
384 |
# Fragment library preview
|
385 |
|
386 |
with gr.Column(variant='panel'):
|
|
|
399 |
)
|
400 |
prot_query_input = gr.Textbox(
|
401 |
show_label=False, placeholder='Enter the protein query here',
|
402 |
+
scale=3, interactive=True
|
403 |
)
|
404 |
|
405 |
with gr.Row():
|
406 |
+
prot_query_btn = gr.Button(
|
407 |
+
value='Query', variant='primary',
|
408 |
+
scale=1, interactive=True
|
409 |
+
)
|
410 |
prot_upload_btn = gr.UploadButton(
|
411 |
label='OR Upload Your PDB/FASTA File', variant='primary',
|
412 |
file_types=['.pdb', '.fasta'],
|
413 |
+
scale=2, interactive=True,
|
414 |
)
|
415 |
|
416 |
input_prot_file = gr.File(
|
417 |
+
value=None, label='Protein Structure File (Original)',
|
418 |
+
interactive=False, visible=False, file_count='single',
|
419 |
)
|
420 |
+
input_prot_view = gr.HTML(value='<div id="input_protein_view" class="mol-container"></div>')
|
421 |
|
422 |
with gr.Group():
|
423 |
pocket_extract_dropdown = gr.Dropdown(
|
|
|
430 |
selected_pocket = gr.Textbox(visible=False)
|
431 |
selected_ligand = gr.Textbox(visible=False)
|
432 |
pocket_files = gr.Files(visible=False)
|
433 |
+
pocket_extract_btn = gr.Button(
|
434 |
+
value='Extract Pocket', variant='primary', interactive=True
|
435 |
+
)
|
436 |
# Target protein preview
|
437 |
with gr.Row():
|
438 |
with gr.Column(variant='panel'):
|
439 |
gr.Markdown('## Dock Phase Settings')
|
440 |
+
dock_n_poses = gr.Slider(
|
441 |
value=5, minimum=1, maximum=20, step=1,
|
442 |
label="Number of conformers to generate per fragment",
|
443 |
interactive=True
|
|
|
460 |
)
|
461 |
with gr.Column(variant='panel'):
|
462 |
gr.Markdown('## Link Phase Settings')
|
463 |
+
link_frag_pose_strategy = gr.Radio(
|
464 |
label='Select a Fragment-Conformer Linking Strategy',
|
465 |
choices=[
|
466 |
'Link Pairs of Fragment-Conformers Contacting the Pocket',
|
|
|
468 |
],
|
469 |
value='Link Pairs of Fragment-Conformers Contacting the Pocket',
|
470 |
)
|
471 |
+
link_frag_dist_range = RangeSlider(
|
472 |
value=[2, 8], minimum=1, maximum=10, step=1,
|
473 |
label="Fragment-Conformer Distance Range (Å) Eligible for Linking",
|
474 |
interactive=True
|
475 |
)
|
476 |
+
link_n_mols = gr.Slider(
|
477 |
value=10, minimum=1, maximum=20, step=1,
|
478 |
label="Number of molecules to generate per fragment conformer combination",
|
479 |
interactive=True
|
|
|
484 |
choices=['DiffLinker'],
|
485 |
interactive=True,
|
486 |
)
|
487 |
+
link_linker_size = gr.Slider(
|
488 |
minimum=0, maximum=20, step=1,
|
489 |
label="Linker Size",
|
490 |
info="0: automatically predicted; >=1: fixed size",
|
491 |
interactive=True
|
492 |
)
|
493 |
+
link_steps = gr.Slider(
|
494 |
minimum=100, maximum=500, step=10,
|
495 |
label="Number of Denoising Steps for Generating Linkers",
|
496 |
interactive=True
|
|
|
499 |
email_input =gr.Textbox(
|
500 |
label='Email Address (Optional)',
|
501 |
info="Your email address will be used to notify you of the status of your job. "
|
502 |
+
"If you cannot receive the email, please check your spam/junk folder.",
|
503 |
+
type='email'
|
504 |
)
|
505 |
with gr.Column():
|
506 |
+
start_clr_btn = gr.ClearButton(
|
507 |
+
value='Reset Inputs', interactive=True,
|
508 |
+
)
|
509 |
+
run_btn = gr.Button(
|
510 |
+
value='Run GenFBDD', variant='primary', interactive=True,
|
511 |
)
|
|
|
512 |
with gr.Tab(label='Results', id='result'):
|
513 |
# Results
|
514 |
result_state = gr.State(value={})
|
|
|
521 |
filters = gr.CheckboxGroup(list(fn.FILTER_MAP.keys()), label='Compound Filters')
|
522 |
with gr.Row():
|
523 |
prop_clr_btn = gr.ClearButton(value='Clear Properties', interactive=False)
|
524 |
+
prop_calc_btn = gr.Button(value='Calculate Properties', interactive=False, variant='primary')
|
525 |
|
526 |
+
with gr.Row():
|
527 |
+
result_table_view = gr.HTML('<div id="result_view" class="fancy-table"></div>')
|
528 |
+
with gr.Column():
|
529 |
+
result_prot_view = gr.HTML('<div id="result_protein_view" class="mol-container"></div>')
|
530 |
+
result_file_btn = gr.Button(value='Create Result File', visible=False, variant='primary')
|
531 |
+
result_download_file = gr.File(label='Download Result File', visible=False)
|
532 |
|
533 |
with gr.Tab(label='Job Status', id='job'):
|
534 |
gr.Markdown('''
|
|
|
552 |
pred_lookup_status = gr.Markdown()
|
553 |
|
554 |
# Event handlers
|
555 |
+
## Start tab
|
556 |
### Fragment Library
|
557 |
+
frag_lib_dropdown_change = frag_lib_dropdown.change(
|
558 |
+
fn=lambda lib: gr.File(FRAG_LIBS[lib], visible=bool(lib)),
|
559 |
inputs=[frag_lib_dropdown],
|
560 |
outputs=[frag_lib_file],
|
561 |
)
|
|
|
566 |
)
|
567 |
|
568 |
# Changing the file updates the original df, the modified df, and the view
|
569 |
+
frag_lib_file_change = frag_lib_file.change(
|
570 |
+
fn=gr_error_wrapper(read_fragment_library),
|
571 |
inputs=[frag_lib_file],
|
572 |
outputs=[frag_lib_orig_df],
|
573 |
).success(
|
|
|
598 |
}
|
599 |
elif filepath.suffix == '.fasta':
|
600 |
seq = next(SeqIO.parse(file, 'fasta')).seq
|
601 |
+
filepath = pdb_query(seq, method='FASTA Sequence')
|
602 |
return {
|
603 |
input_prot_file: gr.File(str(filepath), visible=True),
|
604 |
prot_query_input: seq,
|
|
|
623 |
outputs=[input_prot_file, prot_query_dropdown, prot_query_input],
|
624 |
)
|
625 |
|
626 |
+
def pdb_query(query, method):
|
627 |
+
"""Downloads protein structure data or searches FASTA sequence."""
|
628 |
+
gr.Info(f'Querying protein by {method}...')
|
629 |
+
try:
|
630 |
+
if method == 'PDB ID':
|
631 |
+
url = f"https://files.rcsb.org/download/{query}.pdb"
|
632 |
+
file = fn.download_file(url)
|
633 |
+
elif method == 'UniProt ID':
|
634 |
+
pdb_ids = fn.uniprot_to_pdb(query)
|
635 |
+
if pdb_ids:
|
636 |
+
# Download the first associated PDB file
|
637 |
+
file = fn.download_file(f"https://files.rcsb.org/download/{pdb_ids[0]}.pdb")
|
638 |
+
else:
|
639 |
+
raise ValueError(f"No PDB IDs found for UniProt ID: {query}")
|
640 |
+
elif method == 'FASTA Sequence':
|
641 |
+
pdb_ids = fn.fasta_to_pdb(query)
|
642 |
+
if pdb_ids:
|
643 |
+
# Download the first associated PDB file
|
644 |
+
file = fn.download_file(f"https://files.rcsb.org/download/{pdb_ids[0]}.pdb")
|
645 |
+
else:
|
646 |
+
raise ValueError("No PDB IDs found for the provided FASTA sequence.")
|
647 |
+
else:
|
648 |
+
raise ValueError(f"Unsupported method: {method}")
|
649 |
+
return {input_prot_file: gr.File(str(file), visible=True)}
|
650 |
+
except Exception as e:
|
651 |
+
gr.Error(f"Query error: {str(e)}")
|
652 |
+
|
653 |
prot_query_btn.click(
|
654 |
+
fn=pdb_query,
|
655 |
inputs=[prot_query_input, prot_query_dropdown],
|
656 |
outputs=[input_prot_file],
|
657 |
)
|
658 |
|
659 |
+
input_prot_file_change = input_prot_file.change(
|
660 |
+
fn=lambda: gr.Info('Rendering 3DMol view...'),
|
661 |
+
).then(
|
662 |
+
fn=lambda x, y: gr.Info('3DMol view rendered.'),
|
663 |
inputs=[input_prot_file, input_prot_view],
|
664 |
+
js=static.CREATE_INPUT_MOL_VIEW,
|
|
|
665 |
)
|
666 |
|
667 |
#### Pocket Extraction
|
|
|
692 |
outputs=[pocket_files, selected_ligand, selected_pocket],
|
693 |
).success(
|
694 |
fn=lambda x, y: gr.Info('Pocket extraction completed.'),
|
695 |
+
js=static.UPDATE_MOL_VIEW,
|
696 |
inputs=[pocket_files, input_prot_view],
|
697 |
)
|
698 |
|
|
|
716 |
fn=dock_link,
|
717 |
inputs=[
|
718 |
frag_lib_mod_df, input_prot_file,
|
719 |
+
dock_steps, dock_n_poses, dock_confidence_cutoff,
|
720 |
+
link_frag_dist_range, link_frag_pose_strategy, link_n_mols,
|
721 |
+
link_linker_size, link_steps,
|
722 |
run_state
|
723 |
],
|
724 |
outputs=[result_state, run_state],
|
725 |
concurrency_limit=1, concurrency_id="gpu_queue"
|
726 |
)
|
727 |
|
728 |
+
start_reset_components=[
|
729 |
+
frag_lib_dropdown, frag_lib_process_opts,
|
730 |
+
prot_query_dropdown, prot_query_input, input_prot_file,
|
731 |
+
dock_n_poses, dock_confidence_cutoff, dock_model, dock_steps,
|
732 |
+
link_frag_pose_strategy, link_n_mols, link_frag_dist_range, link_model, link_linker_size, link_steps,
|
733 |
+
email_input
|
734 |
+
]
|
735 |
+
|
736 |
+
def reset_components(components):
|
737 |
+
return [
|
738 |
+
type(component)(
|
739 |
+
value=component.value,
|
740 |
+
visible=component.visible,
|
741 |
+
) for component in components
|
742 |
+
]
|
743 |
+
|
744 |
+
start_clr_btn.click(
|
745 |
+
fn=lambda: reset_components(start_reset_components),
|
746 |
+
outputs=start_reset_components,
|
747 |
+
show_progress='hidden',
|
748 |
+
)
|
749 |
+
|
750 |
### Job Status
|
751 |
user_job_lookup = pred_lookup_btn.click(
|
752 |
+
fn=lambda: '<div class="loader"></ div>',
|
753 |
outputs=loader_html,
|
754 |
).success(
|
755 |
fn=query_job_status,
|
|
|
796 |
protein_structure_file = Path(result_info['protein_structure_file'])
|
797 |
if result_type == 'docking':
|
798 |
result_df = pd.read_csv(result_dir / 'docking_summary.csv')
|
799 |
+
result_df['mol'] = result_df['X1'].apply(Chem.MolFromSmiles)
|
800 |
elif result_type == 'linking':
|
801 |
result_df = pd.read_csv(result_dir / 'linking_summary.csv')
|
802 |
result_df = result_df[~result_df['X1^'].str.contains('.', regex=False)]
|
803 |
+
result_df['mol'] = result_df['X1^'].apply(Chem.MolFromSmiles)
|
804 |
+
result_df.dropna(subset=['mol'], inplace=True)
|
805 |
else:
|
806 |
raise gr.Error('Invalid result type')
|
807 |
+
|
808 |
+
draw_opts = Draw.rdMolDraw2D.MolDrawOptions()
|
809 |
+
draw_opts.clearBackground = False
|
810 |
+
draw_opts.bondLineWidth = 0.5
|
811 |
+
draw_opts.explicitMethyl = True
|
812 |
+
draw_opts.singleColourWedgeBonds = True
|
813 |
+
draw_opts.useCDKAtomPalette()
|
814 |
+
PandasTools.drawOptions = draw_opts
|
815 |
+
PandasTools.molSize = (90, 56)
|
816 |
+
PandasTools.molRepresentation = 'svg'
|
817 |
+
# Convert to URI-formatted inline SVG
|
818 |
+
result_df['Compound'] = result_df['mol'].apply(PandasTools.PrintAsImageString).apply(urllib.parse.quote)
|
819 |
+
|
820 |
return {
|
821 |
result_table_orig_df: result_df,
|
822 |
result_table_mod_df: result_df.copy(deep=True),
|
823 |
result_protein_file: str(protein_structure_file),
|
824 |
+
result_download_file: gr.File('', visible=False),
|
825 |
}
|
826 |
|
827 |
def update_table(orig_df, score_list, filter_list, progress=gr.Progress(track_tqdm=True)):
|
828 |
+
gr.Info('Calculating properties...')
|
829 |
mod_df = orig_df.copy()
|
830 |
try:
|
831 |
for filter_name in filter_list:
|
832 |
+
mod_df[filter_name] = mod_df['mol'].apply(
|
833 |
lambda x: fn.FILTER_MAP[filter_name](x) if not pd.isna(x) else x)
|
834 |
|
835 |
for score_name in score_list:
|
836 |
+
mod_df[score_name] = mod_df['mol'].apply(
|
837 |
lambda x: fn.SCORE_MAP[score_name](x) if not pd.isna(x) else x)
|
838 |
|
839 |
return {result_table_mod_df: mod_df}
|
|
|
849 |
)
|
850 |
|
851 |
result_protein_file.change(
|
852 |
+
fn=lambda x, y: gr.Info('Rendering result table and 3DMol view...'),
|
853 |
+
js=static.CREATE_OUTPUT_MOL_VIEW,
|
854 |
inputs=[result_protein_file, result_prot_view],
|
|
|
855 |
)
|
856 |
result_table_mod_df.change(
|
857 |
fn=fn.create_result_table_html,
|
858 |
+
inputs=[result_table_mod_df, result_state],
|
859 |
outputs=[result_table_view]
|
860 |
).success(
|
861 |
+
fn=lambda x: [gr.Button(visible=True), gr.Button(interactive=True), gr.Button(interactive=True)],
|
862 |
inputs=[result_file_btn],
|
863 |
+
outputs=[result_file_btn, prop_calc_btn, prop_clr_btn],
|
864 |
)
|
865 |
prop_calc_btn.click(
|
866 |
fn=update_table,
|
867 |
inputs=[result_table_orig_df, scores, filters],
|
868 |
outputs=[result_table_mod_df],
|
|
|
869 |
)
|
870 |
prop_clr_btn.click(
|
871 |
+
fn=lambda orig_df: [orig_df, [], [], gr.File(visible=False)],
|
872 |
inputs=[result_table_orig_df],
|
873 |
+
outputs=[result_table_mod_df, scores, filters, result_download_file],
|
|
|
874 |
)
|
875 |
|
|
|
876 |
def generate_result_zip(result_info, compound_mod_df, protein_file):
|
877 |
+
result_path = Path(result_info['output_dir'])
|
878 |
+
zip_path = result_path.with_suffix('.zip')
|
879 |
+
cols_to_drop = ['mol', 'Compound', 'protein_path']
|
880 |
+
compound_mod_df.drop(columns=[col for col in cols_to_drop if col in compound_mod_df.columns], inplace=True)
|
881 |
+
compound_mod_df.to_csv(result_path / f'{result_info["type"]}_summary.csv', index=False)
|
882 |
+
|
883 |
with zipfile.ZipFile(zip_path, 'w') as zip_file:
|
884 |
+
for file in result_path.rglob('*'):
|
885 |
+
if file.is_file(): # Skip directories
|
886 |
+
archive_path = file.relative_to(result_path)
|
887 |
+
zip_file.write(file, arcname=archive_path)
|
888 |
zip_file.write(Path(protein_file), arcname=Path(protein_file).name)
|
889 |
+
|
890 |
return gr.File(str(zip_path), visible=True)
|
891 |
|
892 |
+
|
893 |
result_file_btn.click(
|
894 |
fn=generate_result_zip,
|
895 |
inputs=[result_state, result_table_mod_df, result_protein_file],
|
app/static.py
CHANGED
@@ -143,640 +143,50 @@ footer {
|
|
143 |
.mol-container {
|
144 |
width: 100%;
|
145 |
aspect-ratio: 1.618 / 1;
|
146 |
-
position: relative;
|
147 |
-
box-sizing: border-box;
|
148 |
}
|
149 |
-
"""
|
150 |
-
|
151 |
-
IFRAME_TEMPLATE = """
|
152 |
-
<iframe style="width: 100%; aspect-ratio: 1.618 / 1;" name="result" allow="display-capture; encrypted-media;"
|
153 |
-
sandbox="allow-modals allow-forms allow-scripts allow-same-origin allow-popups
|
154 |
-
allow-top-navigation-by-user-activation allow-downloads" allowfullscreen=""
|
155 |
-
allowpaymentrequest="" frameborder="0" srcdoc='{html}'></iframe>
|
156 |
-
"""
|
157 |
-
|
158 |
-
PROTEIN_VIEW_IFRAME = IFRAME_TEMPLATE.format(html="""
|
159 |
-
<!DOCTYPE html>
|
160 |
-
<html>
|
161 |
-
<head>
|
162 |
-
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
163 |
-
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
164 |
-
<script src="https://3Dmol.org/build/3Dmol.js"></script>
|
165 |
-
<style>
|
166 |
-
html, body {
|
167 |
-
height: 100%;
|
168 |
-
margin: 0;
|
169 |
-
overflow: hidden;
|
170 |
-
}
|
171 |
-
.mol-container {
|
172 |
-
width: 100%;
|
173 |
-
height: 100%;
|
174 |
-
position: relative;
|
175 |
-
box-sizing: border-box;
|
176 |
-
}
|
177 |
-
.mol-container select {
|
178 |
-
background-image: none;
|
179 |
-
}
|
180 |
-
</style>
|
181 |
-
</head>
|
182 |
-
<body>
|
183 |
-
<div id="container" class="mol-container"></div>
|
184 |
-
<script>
|
185 |
-
var viewer = null;
|
186 |
-
$(document).ready(function() {
|
187 |
-
let element = $("#container");
|
188 |
-
let config = { backgroundColor: "white" };
|
189 |
-
viewer = $3Dmol.createViewer(element, config);
|
190 |
-
let ligandStyle = { stick: { colorscheme: "greenCarbon" } };
|
191 |
-
let proteinStyle = { cartoon: { colorscheme: "Jmol" } };
|
192 |
-
let pocketStyle = { sphere: { colorscheme: "blueCarbon", opacity: 0.618 } };
|
193 |
-
});
|
194 |
-
</script>
|
195 |
-
</body>
|
196 |
-
</html>
|
197 |
-
""")
|
198 |
-
|
199 |
-
COMPLEX_RENDERING_TEMPLATE = """
|
200 |
-
<!DOCTYPE html>
|
201 |
-
<html>
|
202 |
-
<head>
|
203 |
-
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
204 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
205 |
-
<script src="https://3Dmol.org/build/3Dmol.js"></script>
|
206 |
-
<style>
|
207 |
-
html, body {{
|
208 |
-
height: 100%;
|
209 |
-
margin: 0;
|
210 |
-
overflow: hidden;
|
211 |
-
}}
|
212 |
-
.mol-container {{
|
213 |
-
width: 100%;
|
214 |
-
height: 100%;
|
215 |
-
position: relative;
|
216 |
-
box-sizing: border-box;
|
217 |
-
}}
|
218 |
-
.mol-container select {{
|
219 |
-
background-image: none;
|
220 |
-
}}
|
221 |
-
</style>
|
222 |
-
</head>
|
223 |
-
<body>
|
224 |
-
<div id="container" class="mol-container"></div>
|
225 |
-
<script>
|
226 |
-
var viewer = null;
|
227 |
-
$(document).ready(function() {{
|
228 |
-
let element = $("#container");
|
229 |
-
let config = {{ backgroundColor: "white" }};
|
230 |
-
let viewer = $3Dmol.createViewer(element, config);
|
231 |
-
let ligandStyle = {{ stick: {{ colorscheme: "greenCarbon" }} }};
|
232 |
-
let proteinStyle = {{ cartoon: {{ colorscheme: "Jmol" }} }};
|
233 |
-
let pocketStyle = {{ sphere: {{ colorscheme: "blueCarbon", opacity: 0.618 }} }};
|
234 |
-
|
235 |
-
{viewer_models}
|
236 |
-
|
237 |
-
viewer.zoomTo({{ "model": 0 }});
|
238 |
-
viewer.render();
|
239 |
-
}});
|
240 |
-
</script>
|
241 |
-
</body>
|
242 |
-
</html>
|
243 |
-
"""
|
244 |
-
|
245 |
-
|
246 |
-
FRAGMENTS_RENDERING_TEMPLATE = """<!DOCTYPE html>
|
247 |
-
<html>
|
248 |
-
<head>
|
249 |
-
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
250 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
251 |
-
<script src="https://3Dmol.org/build/3Dmol.js"></script>
|
252 |
-
<style>
|
253 |
-
html, body {{
|
254 |
-
height: 100%;
|
255 |
-
margin: 0;
|
256 |
-
overflow: hidden;
|
257 |
-
}}
|
258 |
-
.mol-container {{
|
259 |
-
width: 100%;
|
260 |
-
height: 100%;
|
261 |
-
position: relative;
|
262 |
-
box-sizing: border-box;
|
263 |
-
}}
|
264 |
-
.mol-container select{{
|
265 |
-
background-image:None;
|
266 |
-
}}
|
267 |
-
</style>
|
268 |
-
</head>
|
269 |
-
|
270 |
-
<body>
|
271 |
-
<div id="container" class="mol-container"></div>
|
272 |
-
<script>
|
273 |
-
$(document).ready(function() {{
|
274 |
-
let element = $("#container");
|
275 |
-
let config = {{ backgroundColor: "white" }};
|
276 |
-
let viewer = $3Dmol.createViewer(element, config);
|
277 |
-
let defaultStyle = {{ stick: {{ colorscheme: "greenCarbon" }} }};
|
278 |
-
viewer.addModel(`{molecule}`, "{fmt}");
|
279 |
-
viewer.getModel(0).setStyle(defaultStyle);
|
280 |
-
|
281 |
-
viewer.getModel(0).setClickable(
|
282 |
-
{{}},
|
283 |
-
true,
|
284 |
-
function (_atom, _viewer, _event, _container) {{
|
285 |
-
if (!_atom.isClicked) {{
|
286 |
-
_atom.isClicked = true;
|
287 |
-
_viewer.addStyle(
|
288 |
-
{{"serial": _atom.serial, "model": 0}},
|
289 |
-
{{"sphere": {{"color": "magenta", "radius": 0.4}} }}
|
290 |
-
);
|
291 |
-
window.parent.postMessage({{
|
292 |
-
name: "atom_selection",
|
293 |
-
data: {{"atom": _atom.serial, "add": true}}
|
294 |
-
}}, "*");
|
295 |
-
}} else {{
|
296 |
-
delete _atom.isClicked;
|
297 |
-
_viewer.setStyle({{"serial": _atom.serial, "model": 0}}, defaultStyle);
|
298 |
-
window.parent.postMessage({{
|
299 |
-
name: "atom_selection",
|
300 |
-
data: {{"atom": _atom.serial, "add": false}}
|
301 |
-
}}, "*");
|
302 |
-
}}
|
303 |
-
_viewer.render();
|
304 |
-
}}
|
305 |
-
);
|
306 |
-
|
307 |
-
viewer.zoomTo({{ "model": 0 }});
|
308 |
-
viewer.zoom(0.7);
|
309 |
-
viewer.render();
|
310 |
-
}});
|
311 |
-
</script>
|
312 |
-
</body>
|
313 |
-
</html>
|
314 |
-
"""
|
315 |
-
|
316 |
-
TARGET_RENDERING_TEMPLATE = """<!DOCTYPE html>
|
317 |
-
<html>
|
318 |
-
<head>
|
319 |
-
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
320 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
321 |
-
<script src="https://3Dmol.org/build/3Dmol.js"></script>
|
322 |
-
<style>
|
323 |
-
html, body {{
|
324 |
-
height: 100%;
|
325 |
-
margin: 0;
|
326 |
-
overflow: hidden;
|
327 |
-
}}
|
328 |
-
.mol-container {{
|
329 |
-
width: 100%;
|
330 |
-
height: 100%;
|
331 |
-
position: relative;
|
332 |
-
box-sizing: border-box;
|
333 |
-
}}
|
334 |
-
.mol-container select {{
|
335 |
-
background-image: none;
|
336 |
-
}}
|
337 |
-
</style>
|
338 |
-
</head>
|
339 |
-
|
340 |
-
<body>
|
341 |
-
<div id="container" class="mol-container"></div>
|
342 |
-
<script>
|
343 |
-
$(document).ready(function() {{
|
344 |
-
let element = $("#container");
|
345 |
-
let config = {{ backgroundColor: "white" }};
|
346 |
-
let viewer = $3Dmol.createViewer(element, config);
|
347 |
-
let proteinStyle = {{ cartoon: {{ colorscheme: "ssPyMOL" }} }};
|
348 |
-
viewer.addModel(`{molecule}`, "{fmt}");
|
349 |
-
viewer.getModel(0).setStyle(proteinStyle);
|
350 |
-
|
351 |
-
viewer.zoomTo({{ "model": 0 }});
|
352 |
-
viewer.zoom(0.7);
|
353 |
-
viewer.render();
|
354 |
-
}});
|
355 |
-
</script>
|
356 |
-
</body>
|
357 |
-
</html>
|
358 |
-
"""
|
359 |
-
|
360 |
-
COMPLEX_RENDERING_TEMPLATE_OLD = """<!DOCTYPE html>
|
361 |
-
<html>
|
362 |
-
<head>
|
363 |
-
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
364 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
365 |
-
<script src="https://3Dmol.org/build/3Dmol.js"></script>
|
366 |
-
<style>
|
367 |
-
html, body {{
|
368 |
-
height: 100%;
|
369 |
-
margin: 0;
|
370 |
-
overflow: hidden;
|
371 |
-
}}
|
372 |
-
.mol-container {{
|
373 |
-
width: 100%;
|
374 |
-
height: 100%;
|
375 |
-
position: relative;
|
376 |
-
box-sizing: border-box;
|
377 |
-
}}
|
378 |
-
.mol-container select {{
|
379 |
-
background-image: none;
|
380 |
-
}}
|
381 |
-
</style>
|
382 |
-
</head>
|
383 |
-
|
384 |
-
<body>
|
385 |
-
<div id="container" class="mol-container"></div>
|
386 |
-
<script>
|
387 |
-
$(document).ready(function() {{
|
388 |
-
let element = $("#container");
|
389 |
-
let config = {{ backgroundColor: "white" }};
|
390 |
-
let viewer = $3Dmol.createViewer(element, config);
|
391 |
-
let ligandStyle = {{ stick: {{ colorscheme: "greenCarbon" }} }};
|
392 |
-
let proteinStyle = {{ cartoon: {{ colorscheme: "Jmol" }} }};
|
393 |
-
let defaultStyle = {{ stick: {{ colorscheme: "greenCarbon" }} }}; // Store default style
|
394 |
-
let selectedLigand = null; // Keep track of currently selected ligand
|
395 |
-
|
396 |
-
viewer.setStyle({{ hetflag: false }}, proteinStyle);
|
397 |
-
viewer.setStyle({{ hetflag: true }}, ligandStyle);
|
398 |
-
viewer.addModel(`{complex}`, "{fmt}");
|
399 |
-
|
400 |
-
viewer.getModel(0).setClickable(
|
401 |
-
{{ hetflag: true, byres: true }},
|
402 |
-
true,
|
403 |
-
function (_atom, _viewer, _event, _container) {{
|
404 |
-
let currentLigand = {{ resn: _atom.resn, chain: _atom.chain, resi: _atom.resi }};
|
405 |
-
|
406 |
-
if (selectedLigand === currentLigand) {{
|
407 |
-
// Deselect ligand
|
408 |
-
selectedLigand = null;
|
409 |
-
_viewer.setStyle({{ resn: _atom.resn, chain: _atom.chain, resi: _atom.resi }}, defaultStyle);
|
410 |
-
console.log("Deselected Residue:", currentLigand);
|
411 |
-
window.parent.postMessage({{
|
412 |
-
name: "ligand_selection",
|
413 |
-
data: {{ residue: currentLigand, add: false }}
|
414 |
-
}}, "*");
|
415 |
-
}} else {{
|
416 |
-
// Select ligand and deselect previous
|
417 |
-
if (selectedLigand) {{
|
418 |
-
_viewer.setStyle({{ resn: selectedLigand.resn, chain: selectedLigand.chain, resi: selectedLigand.resi }}, defaultStyle);
|
419 |
-
}}
|
420 |
-
selectedLigand = currentLigand;
|
421 |
-
_viewer.setStyle({{ resn: _atom.resn, chain: _atom.chain, resi: _atom.resi }}, {{ stick: {{ color: "red", radius: 0.4}} }});
|
422 |
-
console.log("Selected Residue:", currentLigand);
|
423 |
-
window.parent.postMessage({{
|
424 |
-
name: "ligand_selection",
|
425 |
-
data: {{ residue: currentLigand, add: true }}
|
426 |
-
}}, "*");
|
427 |
-
}}
|
428 |
-
_viewer.render();
|
429 |
-
}}
|
430 |
-
);
|
431 |
-
|
432 |
-
viewer.render();
|
433 |
-
}});
|
434 |
-
</script>
|
435 |
-
</body>
|
436 |
-
</html>
|
437 |
-
"""
|
438 |
-
|
439 |
-
COMPLEX_WITH_POCKETS_RENDERING_TEMPLATE = """<!DOCTYPE html>
|
440 |
-
<html>
|
441 |
-
<head>
|
442 |
-
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
443 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
444 |
-
<script src="https://3Dmol.org/build/3Dmol.js"></script>
|
445 |
-
<style>
|
446 |
-
html, body {{
|
447 |
-
height: 100%;
|
448 |
-
margin: 0;
|
449 |
-
overflow: hidden;
|
450 |
-
}}
|
451 |
-
.mol-container {{
|
452 |
-
width: 100%;
|
453 |
-
height: 100%;
|
454 |
-
position: relative;
|
455 |
-
box-sizing: border-box;
|
456 |
-
}}
|
457 |
-
.mol-container select {{
|
458 |
-
background-image: none;
|
459 |
-
}}
|
460 |
-
</style>
|
461 |
-
</head>
|
462 |
-
|
463 |
-
<body>
|
464 |
-
<div id="container" class="mol-container"></div>
|
465 |
-
<script>
|
466 |
-
$(document).ready(function() {{
|
467 |
-
let element = $("#container");
|
468 |
-
let config = {{ backgroundColor: "white" }};
|
469 |
-
let viewer = $3Dmol.createViewer(element, config);
|
470 |
-
let ligandStyle = {{ stick: {{ colorscheme: "greenCarbon" }} }};
|
471 |
-
let proteinStyle = {{ cartoon: {{ colorscheme: "Jmol" }} }};
|
472 |
-
let defaultStyle = {{ stick: {{ colorscheme: "greenCarbon" }} }}; // Store default style
|
473 |
-
let selectedLigand = null; // Keep track of currently selected ligand
|
474 |
-
|
475 |
-
viewer.addModel(`{complex}`, "{fmt}");
|
476 |
-
viewer.setStyle({{ hetflag: false }}, proteinStyle);
|
477 |
-
viewer.setStyle({{ hetflag: true }}, ligandStyle);
|
478 |
-
|
479 |
-
viewer.getModel(0).setClickable(
|
480 |
-
{{ hetflag: true, byres: true }},
|
481 |
-
true,
|
482 |
-
function (_atom, _viewer, _event, _container) {{
|
483 |
-
let currentLigand = {{ resn: _atom.resn, chain: _atom.chain, resi: _atom.resi }};
|
484 |
-
|
485 |
-
if (selectedLigand === currentLigand) {{
|
486 |
-
// Deselect ligand
|
487 |
-
selectedLigand = null;
|
488 |
-
_viewer.setStyle({{ resn: _atom.resn, chain: _atom.chain, resi: _atom.resi }}, defaultStyle);
|
489 |
-
console.log("Deselected Residue:", currentLigand);
|
490 |
-
window.parent.postMessage({{
|
491 |
-
name: "ligand_selection",
|
492 |
-
data: {{ residue: currentLigand, add: false }}
|
493 |
-
}}, "*");
|
494 |
-
}} else {{
|
495 |
-
// Select ligand and deselect previous
|
496 |
-
if (selectedLigand) {{
|
497 |
-
_viewer.setStyle({{ resn: selectedLigand.resn, chain: selectedLigand.chain, resi: selectedLigand.resi }}, defaultStyle);
|
498 |
-
}}
|
499 |
-
selectedLigand = currentLigand;
|
500 |
-
_viewer.setStyle({{ resn: _atom.resn, chain: _atom.chain, resi: _atom.resi }}, {{ stick: {{ color: "red", radius: 0.4}} }});
|
501 |
-
console.log("Selected Residue:", currentLigand);
|
502 |
-
window.parent.postMessage({{
|
503 |
-
name: "ligand_selection",
|
504 |
-
data: {{ residue: currentLigand, add: true }}
|
505 |
-
}}, "*");
|
506 |
-
}}
|
507 |
-
_viewer.render();
|
508 |
-
}}
|
509 |
-
);
|
510 |
-
// Add pockets
|
511 |
-
viewer.addModel
|
512 |
-
|
513 |
-
viewer.zoomTo({{ "model": 0 }});
|
514 |
-
viewer.zoom(0.7);
|
515 |
-
viewer.render();
|
516 |
-
}});
|
517 |
-
</script>
|
518 |
-
</body>
|
519 |
-
</html>
|
520 |
-
"""
|
521 |
-
|
522 |
-
|
523 |
-
FRAGMENTS_AND_TARGET_RENDERING_TEMPLATE = """
|
524 |
-
<!DOCTYPE html>
|
525 |
-
<html>
|
526 |
-
<head>
|
527 |
-
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
528 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
529 |
-
<script src="https://3Dmol.org/build/3Dmol.js"></script>
|
530 |
-
<style>
|
531 |
-
html, body {{
|
532 |
-
height: 100%;
|
533 |
-
margin: 0;
|
534 |
-
overflow: hidden;
|
535 |
-
}}
|
536 |
-
.mol-container {{
|
537 |
-
width: 100%;
|
538 |
-
height: 100%;
|
539 |
-
position: relative;
|
540 |
-
box-sizing: border-box;
|
541 |
-
}}
|
542 |
-
.mol-container select{{
|
543 |
-
background-image:None;
|
544 |
-
}}
|
545 |
-
</style>
|
546 |
-
</head>
|
547 |
-
|
548 |
-
<body>
|
549 |
-
<div id="container" class="mol-container"></div>
|
550 |
-
<script>
|
551 |
-
$(document).ready(function() {{
|
552 |
-
let element = $("#container");
|
553 |
-
let config = {{ backgroundColor: "white" }};
|
554 |
-
let viewer = $3Dmol.createViewer(element, config);
|
555 |
-
let defaultStyle = {{ stick: {{ colorscheme: "greenCarbon" }} }};
|
556 |
-
let proteinStyle = {{ cartoon: {{ colorscheme: "ssPyMOL" }} }};
|
557 |
-
|
558 |
-
viewer.addModel(`{molecule}`, "{fmt}");
|
559 |
-
viewer.getModel(0).setStyle(defaultStyle);
|
560 |
-
viewer.getModel(0).setClickable(
|
561 |
-
{{}},
|
562 |
-
true,
|
563 |
-
function (_atom, _viewer, _event, _container) {{
|
564 |
-
if (!_atom.isClicked) {{
|
565 |
-
_atom.isClicked = true;
|
566 |
-
_viewer.addStyle(
|
567 |
-
{{"serial": _atom.serial, "model": 0}},
|
568 |
-
{{"sphere": {{"color": "magenta", "radius": 0.4}} }}
|
569 |
-
);
|
570 |
-
window.parent.postMessage({{
|
571 |
-
name: "atom_selection",
|
572 |
-
data: {{"atom": _atom.serial, "add": true}}
|
573 |
-
}}, "*");
|
574 |
-
}} else {{
|
575 |
-
delete _atom.isClicked;
|
576 |
-
_viewer.setStyle({{"serial": _atom.serial, "model": 0}}, defaultStyle);
|
577 |
-
window.parent.postMessage({{
|
578 |
-
name: "atom_selection",
|
579 |
-
data: {{"atom": _atom.serial, "add": false}}
|
580 |
-
}}, "*");
|
581 |
-
}}
|
582 |
-
_viewer.render();
|
583 |
-
}}
|
584 |
-
);
|
585 |
-
|
586 |
-
viewer.addModel(`{target}`, "{target_fmt}");
|
587 |
-
viewer.getModel(1).setStyle(proteinStyle);
|
588 |
-
|
589 |
-
viewer.zoomTo({{ "model": 0 }});
|
590 |
-
viewer.zoom(0.7);
|
591 |
-
viewer.render();
|
592 |
-
}});
|
593 |
-
</script>
|
594 |
-
</body>
|
595 |
-
</html>
|
596 |
-
"""
|
597 |
-
|
598 |
-
SAMPLES_RENDERING_TEMPLATE = """<!DOCTYPE html>
|
599 |
-
<html>
|
600 |
-
<head>
|
601 |
-
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
602 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
603 |
-
<script src="https://3Dmol.org/build/3Dmol.js"></script>
|
604 |
-
<style>
|
605 |
-
html, body {{
|
606 |
-
height: 100%;
|
607 |
-
margin: 0;
|
608 |
-
overflow: hidden;
|
609 |
-
}}
|
610 |
-
.mol-container {{
|
611 |
-
width: 100%;
|
612 |
-
height: 100%;
|
613 |
-
position: relative;
|
614 |
-
box-sizing: border-box;
|
615 |
-
}}
|
616 |
-
.mol-container select{{
|
617 |
-
background-image:None;
|
618 |
-
}}
|
619 |
-
</style>
|
620 |
-
</head>
|
621 |
-
|
622 |
-
<body>
|
623 |
-
<div id="container" class="mol-container"></div>
|
624 |
-
<br>
|
625 |
-
<button id="fragments">Input Fragments</button>
|
626 |
-
<button id="molecule">Output Molecule</button>
|
627 |
-
<script>
|
628 |
-
let element = $("#container");
|
629 |
-
let config = {{ backgroundColor: "white" }};
|
630 |
-
let viewer = $3Dmol.createViewer( element, config );
|
631 |
-
|
632 |
-
$(document).ready(function() {{
|
633 |
-
viewer.addModel(`{fragments}`, "{fragments_fmt}")
|
634 |
-
viewer.getModel().setStyle({{ stick: {{ colorscheme:"greenCarbon" }} }})
|
635 |
-
viewer.getModel().hide();
|
636 |
-
viewer.addModel(`{molecule}`, "{molecule_fmt}")
|
637 |
-
viewer.getModel().setStyle({{ stick: {{ colorscheme:"greenCarbon" }} }})
|
638 |
-
viewer.zoomTo({{ "model": 0 }});
|
639 |
-
viewer.zoom(0.7);
|
640 |
-
viewer.render();
|
641 |
-
}});
|
642 |
-
$("#fragments").click(function() {{
|
643 |
-
viewer.getModel(0).show();
|
644 |
-
viewer.getModel(1).hide();
|
645 |
-
viewer.render();
|
646 |
-
}});
|
647 |
-
$("#molecule").click(function() {{
|
648 |
-
viewer.getModel(1).show();
|
649 |
-
viewer.getModel(0).hide();
|
650 |
-
viewer.render();
|
651 |
-
}});
|
652 |
-
</script>
|
653 |
-
</body>
|
654 |
-
</html>
|
655 |
-
"""
|
656 |
-
|
657 |
-
SAMPLES_WITH_TARGET_RENDERING_TEMPLATE = """
|
658 |
-
<!DOCTYPE html>
|
659 |
-
<html>
|
660 |
-
<head>
|
661 |
-
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
662 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.3/jquery.min.js" integrity="sha512-STof4xm1wgkfm7heWqFJVn58Hm3EtS31XFaagaa8VMReCXAkQnJZ+jEy8PCC/iT18dFy95WcExNHFTqLyp72eQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
663 |
-
<script src="https://3Dmol.org/build/3Dmol.js"></script>
|
664 |
-
<style>
|
665 |
-
html, body {{
|
666 |
-
height: 100%;
|
667 |
-
margin: 0;
|
668 |
-
overflow: hidden;
|
669 |
-
}}
|
670 |
-
.mol-container {{
|
671 |
-
width: 100%;
|
672 |
-
height: 100%;
|
673 |
-
position: relative;
|
674 |
-
box-sizing: border-box;
|
675 |
-
}}
|
676 |
-
.mol-container select{{
|
677 |
-
background-image:None;
|
678 |
-
}}
|
679 |
-
</style>
|
680 |
-
</head>
|
681 |
-
|
682 |
-
<body>
|
683 |
-
<div id="container" class="mol-container"></div>
|
684 |
-
<br>
|
685 |
-
<button id="fragments">Input Fragments</button>
|
686 |
-
<button id="molecule">Output Molecule</button>
|
687 |
-
<button id="show-target">Show Target</button>
|
688 |
-
<button id="hide-target">Hide Target</button>
|
689 |
-
<script>
|
690 |
-
let element = $("#container");
|
691 |
-
let config = {{ backgroundColor: "white" }};
|
692 |
-
let viewer = $3Dmol.createViewer( element, config );
|
693 |
-
|
694 |
-
$(document).ready(function() {{
|
695 |
-
viewer.addModel(`{fragments}`, "{fragments_fmt}")
|
696 |
-
viewer.getModel(0).setStyle({{ stick: {{ colorscheme:"greenCarbon" }} }})
|
697 |
-
viewer.getModel(0).hide();
|
698 |
-
|
699 |
-
viewer.addModel(`{molecule}`, "{molecule_fmt}")
|
700 |
-
viewer.getModel(1).setStyle({{ stick: {{ colorscheme:"greenCarbon" }} }})
|
701 |
-
|
702 |
-
viewer.addModel(`{target}`, "{target_fmt}")
|
703 |
-
viewer.getModel(2).setStyle({{ cartoon: {{ colorscheme: "ssPyMOL" }} }})
|
704 |
-
|
705 |
-
viewer.zoomTo({{ "model": 0 }});
|
706 |
-
viewer.zoom(0.7);
|
707 |
-
viewer.render();
|
708 |
-
}});
|
709 |
-
$("#fragments").click(function() {{
|
710 |
-
viewer.getModel(0).show();
|
711 |
-
viewer.getModel(1).hide();
|
712 |
-
viewer.render();
|
713 |
-
}});
|
714 |
-
$("#molecule").click(function() {{
|
715 |
-
viewer.getModel(1).show();
|
716 |
-
viewer.getModel(0).hide();
|
717 |
-
viewer.render();
|
718 |
-
}});
|
719 |
-
$("#show-target").click(function() {{
|
720 |
-
viewer.getModel(2).show();
|
721 |
-
viewer.render();
|
722 |
-
}});
|
723 |
-
$("#hide-target").click(function() {{
|
724 |
-
viewer.getModel(2).hide();
|
725 |
-
viewer.render();
|
726 |
-
}});
|
727 |
-
</script>
|
728 |
-
</body>
|
729 |
-
</html>
|
730 |
-
"""
|
731 |
|
|
|
|
|
|
|
732 |
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
|
|
744 |
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
|
|
|
|
761 |
"""
|
762 |
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
<style>
|
769 |
-
body{{
|
770 |
-
font-family:sans-serif
|
771 |
-
}}
|
772 |
-
</style>
|
773 |
-
</head>
|
774 |
-
|
775 |
-
<body>
|
776 |
-
<h3>Error:</h3>
|
777 |
-
{message}
|
778 |
-
</body>
|
779 |
-
</html>
|
780 |
"""
|
781 |
|
782 |
SETUP_JS = """
|
@@ -902,7 +312,7 @@ RETURN_ATOM_SELECTION_JS = """
|
|
902 |
}
|
903 |
"""
|
904 |
|
905 |
-
|
906 |
(mol_file, view_html) => {
|
907 |
try {
|
908 |
let viewer;
|
@@ -915,20 +325,24 @@ CREATE_MOL_VIEW = """
|
|
915 |
const element_id = idMatch[1];
|
916 |
const element = document.getElementById(element_id);
|
917 |
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
922 |
-
|
923 |
-
|
924 |
-
|
925 |
-
|
926 |
-
|
927 |
-
"atoms": [],
|
928 |
-
"ligand": {name: null, resn: null, chain: null, resi: null},
|
929 |
-
"pocket": {name: null, id: null}
|
930 |
-
}
|
931 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
932 |
model = viewer.addModel(molContent, fmt);
|
933 |
model.setStyle({ hetflag: false }, proteinStyle);
|
934 |
model.setStyle({ hetflag: true }, ligandStyle);
|
@@ -977,7 +391,7 @@ CREATE_MOL_VIEW = """
|
|
977 |
}
|
978 |
"""
|
979 |
|
980 |
-
|
981 |
(mol_files, view_html) => {
|
982 |
try {
|
983 |
let viewer;
|
@@ -1064,3 +478,80 @@ UPDATE_PROT_VIEW = """
|
|
1064 |
}
|
1065 |
"""
|
1066 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
.mol-container {
|
144 |
width: 100%;
|
145 |
aspect-ratio: 1.618 / 1;
|
|
|
|
|
146 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
|
148 |
+
.mol-container > canvas {
|
149 |
+
position: relative ! important;
|
150 |
+
}
|
151 |
|
152 |
+
.gr-btn-grp {
|
153 |
+
display: flex;
|
154 |
+
flex-wrap: wrap;
|
155 |
+
gap: var(--layout-gap);
|
156 |
+
width: var(--size-full);
|
157 |
+
position: relative
|
158 |
+
border-radius: 0
|
159 |
+
border-radius: var(--container-radius);
|
160 |
+
background: var(--background-fill-secondary);
|
161 |
+
padding: var(--size-2)
|
162 |
+
align-items: stretch
|
163 |
+
}
|
164 |
|
165 |
+
.gr-btn-grp > button {
|
166 |
+
flex: 1 1 0;
|
167 |
+
display: inline-flex;
|
168 |
+
justify-content: center;
|
169 |
+
align-items: center;
|
170 |
+
transition: var(--button-transition);
|
171 |
+
padding: var(--size-0-5) var(--size-2);
|
172 |
+
text-align: center;
|
173 |
+
border: var(--button-border-width) solid var(--button-secondary-border-color);
|
174 |
+
background: var(--button-secondary-background-fill);
|
175 |
+
color: var(--button-secondary-text-color);
|
176 |
+
box-shadow: var(--button-secondary-shadow)
|
177 |
+
border-radius: var(--button-large-radius);
|
178 |
+
padding: calc(var(--button-large-padding) - 1px);
|
179 |
+
font-family: monospace;
|
180 |
+
font-weight: var(--button-large-text-weight);
|
181 |
+
font-size: calc(var(--button-large-text-size) - 1px);
|
182 |
+
}
|
183 |
"""
|
184 |
|
185 |
+
IFRAME_TEMPLATE = """
|
186 |
+
<iframe style="width: 100%; aspect-ratio: {aspect_ratio}; overflow: visible; border: none;" scrolling="no"
|
187 |
+
frameborder="0" allow="display-capture; encrypted-media;"
|
188 |
+
sandbox="allow-modals allow-forms allow-scripts allow-same-origin allow-popups
|
189 |
+
allow-top-navigation-by-user-activation allow-downloads" srcdoc='{srcdoc}'></iframe>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
190 |
"""
|
191 |
|
192 |
SETUP_JS = """
|
|
|
312 |
}
|
313 |
"""
|
314 |
|
315 |
+
CREATE_INPUT_MOL_VIEW = """
|
316 |
(mol_file, view_html) => {
|
317 |
try {
|
318 |
let viewer;
|
|
|
325 |
const element_id = idMatch[1];
|
326 |
const element = document.getElementById(element_id);
|
327 |
|
328 |
+
if (!element.querySelector('canvas')) {
|
329 |
+
viewer = $3Dmol.createViewer(element, viewerConfig);
|
330 |
+
} else {
|
331 |
+
viewer = element.querySelector('canvas')._3dmol_viewer;
|
332 |
+
viewer.clear();
|
333 |
+
selectedElements = {
|
334 |
+
"atoms": [],
|
335 |
+
"ligand": {name: null, resn: null, chain: null, resi: null},
|
336 |
+
"pocket": {name: null, id: null}
|
|
|
|
|
|
|
|
|
337 |
}
|
338 |
+
}
|
339 |
+
|
340 |
+
if (mol_file == null) {
|
341 |
+
return;
|
342 |
+
}
|
343 |
+
|
344 |
+
$.get(mol_file.url, function(molContent) {
|
345 |
+
fmt = mol_file.path.split('.').pop();
|
346 |
model = viewer.addModel(molContent, fmt);
|
347 |
model.setStyle({ hetflag: false }, proteinStyle);
|
348 |
model.setStyle({ hetflag: true }, ligandStyle);
|
|
|
391 |
}
|
392 |
"""
|
393 |
|
394 |
+
UPDATE_MOL_VIEW = """
|
395 |
(mol_files, view_html) => {
|
396 |
try {
|
397 |
let viewer;
|
|
|
478 |
}
|
479 |
"""
|
480 |
|
481 |
+
CREATE_OUTPUT_MOL_VIEW = """
|
482 |
+
(mol_file, view_html) => {
|
483 |
+
try {
|
484 |
+
let viewer;
|
485 |
+
const idMatch = view_html.match(/id="(\w+)"/);
|
486 |
+
if (!idMatch || !idMatch[1]) {
|
487 |
+
console.error("Invalid view_html: No ID found.");
|
488 |
+
return;
|
489 |
+
}
|
490 |
+
const element_id = idMatch[1];
|
491 |
+
const element = document.getElementById(element_id);
|
492 |
+
|
493 |
+
fmt = mol_file.path.split('.').pop();
|
494 |
+
$.get(mol_file.url, function(molContent) {
|
495 |
+
if (!element.querySelector('canvas')) {
|
496 |
+
viewer = $3Dmol.createViewer(element, viewerConfig);
|
497 |
+
} else {
|
498 |
+
viewer = element.querySelector('canvas')._3dmol_viewer;
|
499 |
+
viewer.clear();
|
500 |
+
}
|
501 |
+
model = viewer.addModel(molContent, fmt);
|
502 |
+
model.setStyle({ hetflag: false }, proteinStyle);
|
503 |
+
model.setStyle({ hetflag: true }, ligandStyle);
|
504 |
+
viewer.zoomTo();
|
505 |
+
viewer.render();
|
506 |
+
|
507 |
+
const container = document.createElement("div");
|
508 |
+
container.classList.add("gr-btn-grp");
|
509 |
+
|
510 |
+
// Molecule Button
|
511 |
+
const toggleMoleculeButton = document.createElement("button");
|
512 |
+
toggleMoleculeButton.textContent = "Hide Generated Molecule";
|
513 |
+
let moleculeIsHidden = false;
|
514 |
+
toggleMoleculeButton.onclick = function() {
|
515 |
+
if (viewer.models.length === 2) {
|
516 |
+
moleculeIsHidden = !moleculeIsHidden;
|
517 |
+
viewer.addStyle({model: 1}, {stick: {hidden: moleculeIsHidden} });
|
518 |
+
toggleMoleculeButton.textContent = moleculeIsHidden ? "Show Generated Molecule" : "Hide Generated Molecule";
|
519 |
+
viewer.render();
|
520 |
+
}
|
521 |
+
};
|
522 |
+
container.appendChild(toggleMoleculeButton);
|
523 |
+
|
524 |
+
// Ligand Button
|
525 |
+
if (viewer.getAtomsFromSel({model: 0, hetflag : true}).length > 0) {
|
526 |
+
const toggleLigandButton = document.createElement("button");
|
527 |
+
toggleLigandButton.textContent = "Hide Co-Crystallized Ligand";
|
528 |
+
let ligandIsHidden = false;
|
529 |
+
toggleLigandButton.onclick = function() {
|
530 |
+
ligandIsHidden = !ligandIsHidden;
|
531 |
+
viewer.addStyle({model: 0, hetflag: true}, {stick: {hidden: ligandIsHidden} });
|
532 |
+
toggleLigandButton.textContent = ligandIsHidden ? "Show Co-Crystallized Ligand" : "Hide Co-Crystallized Ligand";
|
533 |
+
viewer.render();
|
534 |
+
};
|
535 |
+
container.appendChild(toggleLigandButton);
|
536 |
+
}
|
537 |
+
|
538 |
+
// Protein Button
|
539 |
+
const toggleProteinButton = document.createElement("button");
|
540 |
+
toggleProteinButton.textContent = "Hide Target Protein";
|
541 |
+
let proteinIsHidden = false;
|
542 |
+
toggleProteinButton.onclick = function() {
|
543 |
+
proteinIsHidden = !proteinIsHidden;
|
544 |
+
viewer.addStyle({model: 0, hetflag: false}, {cartoon: {hidden: proteinIsHidden} });
|
545 |
+
toggleProteinButton.textContent = proteinIsHidden ? "Show Target Protein" : "Hide Target Protein";
|
546 |
+
viewer.render();
|
547 |
+
};
|
548 |
+
container.appendChild(toggleProteinButton);
|
549 |
+
element.parentElement.appendChild(container);
|
550 |
+
}).fail(function(error) {
|
551 |
+
console.error("Error loading molecule:", error);
|
552 |
+
});
|
553 |
+
} catch (error) {
|
554 |
+
console.error("An error occurred:", error);
|
555 |
+
}
|
556 |
+
}
|
557 |
+
"""
|
app/static/medium-zoom.min.js
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
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/{panel.css → static/panel.css}
RENAMED
@@ -4,39 +4,37 @@
|
|
4 |
font-size: 12px !important;
|
5 |
}
|
6 |
|
7 |
-
.tabulator
|
8 |
overflow: visible !important;
|
9 |
}
|
10 |
|
|
|
|
|
|
|
|
|
11 |
|
12 |
-
.
|
13 |
-
|
14 |
-
overflow: visible;
|
15 |
-
z-index: 1000;
|
16 |
}
|
17 |
|
18 |
-
.
|
19 |
-
|
20 |
-
top: 0;
|
21 |
-
left: 0;
|
22 |
-
width: 100%;
|
23 |
-
height: 100%;
|
24 |
-
pointer-events: none;
|
25 |
}
|
26 |
|
27 |
-
.
|
28 |
-
|
|
|
29 |
}
|
30 |
|
31 |
/* When hovering over the container, scale its child (the SVG) */
|
32 |
-
.tabulator-cell:hover .
|
33 |
padding: 3px;
|
34 |
-
position:
|
|
|
35 |
background-color: rgba(250, 250, 250, 0.854);
|
36 |
box-shadow: 0 0 10px rgba(0, 0, 0, 0.618);
|
37 |
border-radius: 3px;
|
38 |
-
transform: scale(3); /* Scale up the SVG */
|
39 |
transition: transform 0.3s ease;
|
40 |
-
pointer-events: none; /*
|
41 |
-
z-index:
|
42 |
-
}
|
|
|
4 |
font-size: 12px !important;
|
5 |
}
|
6 |
|
7 |
+
.tabulator:hover {
|
8 |
overflow: visible !important;
|
9 |
}
|
10 |
|
11 |
+
.tabulator-cell:has(> img) {
|
12 |
+
padding: 2px !important;
|
13 |
+
height: 60px !important;
|
14 |
+
}
|
15 |
|
16 |
+
.tabulator-cell:hover {
|
17 |
+
overflow: visible !important;
|
|
|
|
|
18 |
}
|
19 |
|
20 |
+
.tabulator-row:hover {
|
21 |
+
z-index: 10000;
|
|
|
|
|
|
|
|
|
|
|
22 |
}
|
23 |
|
24 |
+
.zoom-img {
|
25 |
+
position: relative;
|
26 |
+
transition: transform 0.3s ease;
|
27 |
}
|
28 |
|
29 |
/* When hovering over the container, scale its child (the SVG) */
|
30 |
+
.tabulator-cell:hover .zoom-img {
|
31 |
padding: 3px;
|
32 |
+
position: fixed;
|
33 |
+
transform: translate(100%, 0%) scale(3); /* Center and scale */
|
34 |
background-color: rgba(250, 250, 250, 0.854);
|
35 |
box-shadow: 0 0 10px rgba(0, 0, 0, 0.618);
|
36 |
border-radius: 3px;
|
|
|
37 |
transition: transform 0.3s ease;
|
38 |
+
pointer-events: none; /* Prevent the image from blocking interactions */
|
39 |
+
z-index: 10000; /* Ensure it appears above other elements */
|
40 |
+
}
|
app/{panel.js → static/panel.js}
RENAMED
@@ -28,34 +28,31 @@ const molDisplayButtonFormatter = function (cell, formatterParams, onRendered) {
|
|
28 |
try {
|
29 |
let viewer;
|
30 |
const elementId = 'result_protein_view'; // Ensure this element exists in your HTML
|
31 |
-
const element = document.getElementById(elementId);
|
32 |
|
33 |
// Check if the element exists and contains a canvas
|
34 |
if (element && element.querySelector('canvas')) {
|
35 |
viewer = element.querySelector('canvas')._3dmol_viewer;
|
36 |
-
|
37 |
-
// Remove all models except the first one
|
38 |
-
for (let i = 1; i < viewer.models.length; i++) {
|
39 |
-
viewer.removeModel(i);
|
40 |
-
}
|
41 |
} else {
|
42 |
console.error("Invalid element_id: No canvas found.");
|
43 |
return;
|
44 |
}
|
45 |
|
46 |
// Get the file format from the cell content (assumed to be a property of molFile)
|
47 |
-
const fmt = molFile.
|
48 |
-
|
49 |
-
console.log("File:", filename, "Format:", fmt); // Log file name and format
|
50 |
|
51 |
// Fetch the molecule content using the URL
|
52 |
-
|
53 |
// Add the model to the viewer and set the style
|
|
|
|
|
|
|
54 |
const model = viewer.addModel(molContent, fmt);
|
55 |
-
model.setStyle({}, {
|
56 |
|
57 |
console.log("Rendering protein view.");
|
58 |
-
viewer.zoomTo();
|
59 |
viewer.render();
|
60 |
});
|
61 |
} catch (error) {
|
@@ -69,5 +66,5 @@ const molDisplayButtonFormatter = function (cell, formatterParams, onRendered) {
|
|
69 |
|
70 |
Tabulator.extendModule("format", "formatters", {
|
71 |
executeScriptFormatter: executeScriptFormatter,
|
72 |
-
|
73 |
});
|
|
|
28 |
try {
|
29 |
let viewer;
|
30 |
const elementId = 'result_protein_view'; // Ensure this element exists in your HTML
|
31 |
+
const element = window.parent.document.getElementById(elementId);
|
32 |
|
33 |
// Check if the element exists and contains a canvas
|
34 |
if (element && element.querySelector('canvas')) {
|
35 |
viewer = element.querySelector('canvas')._3dmol_viewer;
|
|
|
|
|
|
|
|
|
|
|
36 |
} else {
|
37 |
console.error("Invalid element_id: No canvas found.");
|
38 |
return;
|
39 |
}
|
40 |
|
41 |
// Get the file format from the cell content (assumed to be a property of molFile)
|
42 |
+
const fmt = molFile.split('.').pop();
|
43 |
+
molUrl = 'gradio_api/file=' + molFile;
|
|
|
44 |
|
45 |
// Fetch the molecule content using the URL
|
46 |
+
window.parent.$.get(molUrl, function(molContent) {
|
47 |
// Add the model to the viewer and set the style
|
48 |
+
for (let i = viewer.models.length - 1; i > 0; i--) {
|
49 |
+
viewer.removeModel(i); // Or viewer.models.splice(i, 1);
|
50 |
+
}
|
51 |
const model = viewer.addModel(molContent, fmt);
|
52 |
+
model.setStyle({}, { stick: {colorscheme: 'magentaCarbon' } }); // Change style as needed
|
53 |
|
54 |
console.log("Rendering protein view.");
|
55 |
+
viewer.zoomTo({model: model});
|
56 |
viewer.render();
|
57 |
});
|
58 |
} catch (error) {
|
|
|
66 |
|
67 |
Tabulator.extendModule("format", "formatters", {
|
68 |
executeScriptFormatter: executeScriptFormatter,
|
69 |
+
molDisplayButtonFormatter: molDisplayButtonFormatter
|
70 |
});
|
app/static/zooming.min.js
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
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}()});
|
configs/gen_fbdd_v1.yaml
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
score_ckpt: resources/checkpoints/DiffDock/score_model/best_ema_inference_epoch_model.pt
|
2 |
+
confidence_ckpt: resources/checkpoints/DiffDock/confidence_model/best_model_epoch75.pt
|
3 |
+
docking_batch_size: 20
|
4 |
+
#confidence_model_dir: ./workdir/v1.1/confidence_model
|
5 |
+
different_schedules: false
|
6 |
+
inf_sched_alpha: 1
|
7 |
+
inf_sched_beta: 1
|
8 |
+
inference_steps: 20
|
9 |
+
initial_noise_std_proportion: 1.4601642460337794
|
10 |
+
limit_failures: 5
|
11 |
+
#model_dir: ./workdir/v1.1/score_model
|
12 |
+
#comment
|
13 |
+
no_final_step_noise: true
|
14 |
+
no_model: false
|
15 |
+
no_random: false
|
16 |
+
no_random_pocket: false
|
17 |
+
ode: false
|
18 |
+
old_filtering_model: true
|
19 |
+
old_score_model: false
|
20 |
+
resample_rdkit: false
|
21 |
+
samples_per_complex: 10
|
22 |
+
sigma_schedule: expbeta
|
23 |
+
temp_psi_rot: 0.9022615585677628
|
24 |
+
temp_psi_tor: 0.5946212391366862
|
25 |
+
temp_psi_tr: 0.727287304570729
|
26 |
+
temp_sampling_rot: 2.06391612594481
|
27 |
+
temp_sampling_tor: 7.044261621607846
|
28 |
+
temp_sampling_tr: 1.170050527854316
|
29 |
+
temp_sigma_data_rot: 0.7464326999906034
|
30 |
+
temp_sigma_data_tor: 0.6943254174849822
|
31 |
+
temp_sigma_data_tr: 0.9299802531572672
|
32 |
+
|
33 |
+
rmsd_threshold: 1.5
|
34 |
+
|
35 |
+
linker_ckpt:
|
36 |
+
pocket_full: resources/checkpoints/DiffLinker/pockets_difflinker_full_no_anchors_fc_pdb_excluded.ckpt
|
37 |
+
pocket_bb: resources/checkpoints/DiffLinker/pockets_difflinker_backbone.ckpt
|
38 |
+
geom: resources/checkpoints/DiffLinker/geom_difflinker.ckpt
|
39 |
+
size_ckpt: resources/checkpoints/DiffLinker/geom_size_gnn.ckpt
|
40 |
+
linker_condition: 'none' # pocket
|
41 |
+
linker_batch_size: 64
|
42 |
+
linker_steps: 1000
|
inference.py
CHANGED
@@ -69,9 +69,15 @@ pandarallel.initialize(nb_workers=nb_workers, progress_bar=progress_bar)
|
|
69 |
|
70 |
|
71 |
def read_fragment_library(file_path):
|
|
|
|
|
72 |
file_path = Path(file_path)
|
73 |
if file_path.suffix == '.csv':
|
74 |
df = pd.read_csv(file_path)
|
|
|
|
|
|
|
|
|
75 |
PandasTools.AddMoleculeColumnToFrame(df, smilesCol='X1', molCol='mol')
|
76 |
elif file_path.suffix == '.sdf':
|
77 |
df = PandasTools.LoadSDF(file_path, smilesName='X1', molColName='mol')
|
@@ -606,7 +612,7 @@ def process_docking_results(
|
|
606 |
linking_df.drop(columns=['fragment_mol']).to_csv(Path(args.out_dir, 'linking_summary.csv'), index=False)
|
607 |
return linking_df
|
608 |
else:
|
609 |
-
raise ValueError('No eligible fragment pairs found for linking.')
|
610 |
|
611 |
|
612 |
def extract_pockets(protein_path, ligand_residue=None, top_pockets=None):
|
@@ -1340,7 +1346,8 @@ if __name__ == "__main__":
|
|
1340 |
out_dir=args.out_dir,
|
1341 |
)
|
1342 |
if linking_df is None or len(linking_df) == 0:
|
1343 |
-
|
|
|
1344 |
|
1345 |
generate_linkers(
|
1346 |
linking_df,
|
|
|
69 |
|
70 |
|
71 |
def read_fragment_library(file_path):
|
72 |
+
if file_path is None:
|
73 |
+
return pd.DataFrame(columns=['X1', 'ID1', 'mol'])
|
74 |
file_path = Path(file_path)
|
75 |
if file_path.suffix == '.csv':
|
76 |
df = pd.read_csv(file_path)
|
77 |
+
# Validate columns
|
78 |
+
for col in ['X1', 'ID1']:
|
79 |
+
if col not in df.columns:
|
80 |
+
raise ValueError(f"Column '{col}' not found in CSV file.")
|
81 |
PandasTools.AddMoleculeColumnToFrame(df, smilesCol='X1', molCol='mol')
|
82 |
elif file_path.suffix == '.sdf':
|
83 |
df = PandasTools.LoadSDF(file_path, smilesName='X1', molColName='mol')
|
|
|
612 |
linking_df.drop(columns=['fragment_mol']).to_csv(Path(args.out_dir, 'linking_summary.csv'), index=False)
|
613 |
return linking_df
|
614 |
else:
|
615 |
+
raise ValueError('No eligible fragment pose pairs found for linking.')
|
616 |
|
617 |
|
618 |
def extract_pockets(protein_path, ligand_residue=None, top_pockets=None):
|
|
|
1346 |
out_dir=args.out_dir,
|
1347 |
)
|
1348 |
if linking_df is None or len(linking_df) == 0:
|
1349 |
+
log.error('No eligible fragment pose pairs found for linking.')
|
1350 |
+
sys.exit()
|
1351 |
|
1352 |
generate_linkers(
|
1353 |
linking_df,
|