silterra commited on
Commit
5c0a4b6
1 Parent(s): 0303515

Complete overhaul of the UI.

Browse files

* Use Blocks instead of Interface.

* Show protein in nice 3dmol viewer after calculation is finished.

Files changed (3) hide show
  1. main.py +51 -36
  2. mol_viewer.py +64 -0
  3. run_utils.py +35 -13
main.py CHANGED
@@ -1,54 +1,69 @@
1
- import logging
2
- import os.path
3
 
4
  import gradio as gr
5
 
 
6
  import run_utils
7
 
8
 
9
- def run_wrapper(protein_file, ligand_file, other_args_file, *args, **kwargs) -> str:
 
 
 
 
 
10
  if other_args_file is not None:
11
  kwargs["other_arg_file"] = other_args_file.name
12
- return run_utils.run_cli_command(
 
13
  protein_file.name, ligand_file.name, *args, **kwargs
14
  )
15
 
 
 
 
 
 
 
 
16
 
17
  def run():
18
- iface = gr.Interface(
19
- fn=run_wrapper,
20
- inputs=[
21
- gr.File(label="Protein PDB", file_types=[".pdb"]),
22
- gr.File(label="Ligand SDF", file_types=[".sdf"]),
23
- gr.File(label="Other arguments (Optional, YML)", file_types=[".yml", ".yaml"], value=None),
24
- gr.Number(
25
- label="Samples Per Complex",
26
- value=1,
27
- minimum=1,
28
- maximum=100,
29
- precision=0,
30
- ),
31
- gr.Checkbox(label="Keep Local Structures", value=True),
32
- gr.Checkbox(label="Save Visualisation", value=True),
33
- ],
34
- outputs=gr.File(label="Result"),
35
- title="DiffDock-Pocket",
36
- description="""
37
- Run [DiffDock-Pocket](https://github.com/plainerman/DiffDock-Pocket) for a single protein and ligand.
38
- We have provided the most important inputs as UI elements.
39
- Additional values can be included in "Other arguments", and will be passed
40
  to [inference.py](https://github.com/plainerman/DiffDock-Pocket/blob/main/inference.py).
41
- Must be a YAML file without any nesting.
42
- For example, to specify a pocket of (0.0, 1.0, 2.0), the YAML file should contain:
43
- ```
44
- pocket_center_x: 0.0
45
- pocket_center_y: 1.0
46
- pocket_center_z: 2.0
47
- ```
48
- """
49
- )
 
 
 
 
 
 
 
 
50
 
51
- iface.launch(server_name="0.0.0.0", server_port=7860, share=False)
52
 
53
 
54
  if __name__ == "__main__":
 
1
+ import datetime
2
+ from typing import Tuple, Optional
3
 
4
  import gradio as gr
5
 
6
+ import mol_viewer
7
  import run_utils
8
 
9
 
10
+ def run_wrapper(protein_file, ligand_file, other_args_file, *args, **kwargs) -> Tuple[str, Optional[str], str]:
11
+ if protein_file is None:
12
+ return "Protein file is missing! Must provide a protein file in PDB format", None, ""
13
+ if ligand_file is None:
14
+ return "Ligand file is missing! Must provide a ligand file in SDF format", None, ""
15
+
16
  if other_args_file is not None:
17
  kwargs["other_arg_file"] = other_args_file.name
18
+
19
+ output_file, output_pdb_text = run_utils.run_cli_command(
20
  protein_file.name, ligand_file.name, *args, **kwargs
21
  )
22
 
23
+ output_viz = "No visualisation created"
24
+ if output_pdb_text:
25
+ output_viz = mol_viewer.gen_3dmol_vis(output_pdb_text)
26
+
27
+ message = f"Calculation completed at {datetime.datetime.now()}"
28
+ return message, output_file, output_viz
29
+
30
 
31
  def run():
32
+
33
+ with gr.Blocks(title="DiffDock-Pocket Web") as demo:
34
+ gr.Markdown("# DiffDock-Pocket")
35
+ gr.Markdown("""Run [DiffDock-Pocket](https://github.com/plainerman/DiffDock-Pocket) for a single protein and ligand.
36
+ We have provided the most important inputs as UI elements. """)
37
+ with gr.Row():
38
+ protein_pdb = gr.File(label="Protein PDB (required)", file_types=[".pdb"])
39
+ ligand_sdf = gr.File(label="Ligand SDF (required)", file_types=[".sdf"])
40
+ with gr.Row():
41
+ samples_per_complex = gr.Number(label="Samples Per Complex", value=1, minimum=1, maximum=100, precision=0)
42
+ with gr.Column():
43
+ keep_local_structures = gr.Checkbox(label="Keep Local Structures", value=True)
44
+ save_vis = gr.Checkbox(label="Save Visualisation", value=True)
45
+ with gr.Row():
46
+ gr.Markdown("""Additional values can be included in "Other arguments", and will be passed
 
 
 
 
 
 
 
47
  to [inference.py](https://github.com/plainerman/DiffDock-Pocket/blob/main/inference.py).
48
+ Must be a YAML file without any nesting. """)
49
+ other_args = gr.File(label="Other arguments (Optional, YML)", file_types=[".yml", ".yaml"], value=None)
50
+
51
+ with gr.Row():
52
+ run_btn = gr.Button("Run DiffDock-Pocket")
53
+
54
+ with gr.Row():
55
+ message = gr.Text(label="Run message", interactive=False)
56
+ with gr.Row():
57
+ output_file = gr.File(label="Output Files")
58
+ with gr.Row():
59
+ init_value = "Rank1 Reverse Processed Protein will be visualised here"
60
+ viewer = gr.HTML(value=init_value, label="Protein Viewer", show_label=True)
61
+
62
+ _inputs = [protein_pdb, ligand_sdf, other_args, samples_per_complex, keep_local_structures, save_vis]
63
+ _outputs = [message, output_file, viewer]
64
+ run_btn.click(fn=run_wrapper, inputs=_inputs, outputs=_outputs)
65
 
66
+ demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
67
 
68
 
69
  if __name__ == "__main__":
mol_viewer.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ viewer_description = """
2
+ PDB viewer using 3Dmol.js
3
+ If using please cite:
4
+ 3Dmol.js: Molecular visualization with WebGL, Nicholas Rego, David Koes , Bioinformatics, Volume 31, Issue 8, April 2015, Pages 1322–1324, https://doi.org/10.1093/bioinformatics/btu829
5
+
6
+ See also:
7
+ https://huggingface.co/blog/spaces_3dmoljs
8
+ https://huggingface.co/spaces/simonduerr/3dmol.js/blob/main/app.py
9
+ """
10
+
11
+
12
+ def gen_3dmol_vis(pdb_text: str):
13
+ mol = pdb_text
14
+
15
+ x = (
16
+ """<!DOCTYPE html>
17
+ <html>
18
+ <head>
19
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
20
+ <style>
21
+ body{
22
+ font-family:sans-serif
23
+ }
24
+ .mol-container {
25
+ width: 100%;
26
+ height: 600px;
27
+ position: relative;
28
+ }
29
+ .mol-container select{
30
+ background-image:None;
31
+ }
32
+ </style>
33
+ <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>
34
+ <script src="https://3Dmol.csb.pitt.edu/build/3Dmol-min.js"></script>
35
+ </head>
36
+ <body>
37
+
38
+ <div id="container" class="mol-container"></div>
39
+
40
+ <script>
41
+ let pdb = `"""
42
+ + mol
43
+ + """`
44
+
45
+ $(document).ready(function () {
46
+ let element = $("#container");
47
+ let config = { backgroundColor: "white" };
48
+ let viewer = $3Dmol.createViewer(element, config);
49
+ viewer.addModel(pdb, "pdb");
50
+ viewer.getModel(0).setStyle({}, { cartoon: { colorscheme:"whiteCarbon" } });
51
+ viewer.zoomTo();
52
+ viewer.render();
53
+ viewer.zoom(0.8, 2000);
54
+ })
55
+ </script>
56
+ </body></html>"""
57
+ )
58
+
59
+ return f"""<iframe style="width: 100%; height: 600px" name="result" allow="midi; geolocation; microphone; camera;
60
+ display-capture; encrypted-media;" sandbox="allow-modals allow-forms
61
+ allow-scripts allow-same-origin allow-popups
62
+ allow-top-navigation-by-user-activation allow-downloads" allowfullscreen=""
63
+ allowpaymentrequest="" frameborder="0" srcdoc='{x}'></iframe>"""
64
+
run_utils.py CHANGED
@@ -57,13 +57,20 @@ def kwargs_to_cli_args(**kwargs) -> List[str]:
57
  return cli_args
58
 
59
 
 
 
 
 
 
 
 
60
  def run_cli_command(
61
  protein_path: str,
62
  ligand: str,
63
  samples_per_complex: int,
64
  keep_local_structures: bool,
65
  save_visualisation: bool,
66
- other_arg_file: str,
67
  work_dir=None,
68
  ):
69
  if work_dir is None:
@@ -98,20 +105,35 @@ def run_cli_command(
98
 
99
  # Running the command
100
  try:
101
- result = subprocess.run(
102
- command,
103
- cwd=work_dir,
104
- check=False,
105
- text=True,
106
- capture_output=True,
107
- env=os.environ,
108
- )
109
- logging.debug(f"Command output:\n{result.stdout}")
110
- if result.stderr:
111
- logging.error(f"Command error:\n{result.stderr}")
 
 
112
  except subprocess.CalledProcessError as e:
113
  logging.error(f"An error occurred while executing the command: {e}")
114
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  # Zip the output directory
116
  # Generate a unique filename using a timestamp and a UUID
117
  timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
@@ -122,7 +144,7 @@ def run_cli_command(
122
 
123
  logging.debug(f"Directory '{temp_dir}' zipped to '{full_zip_path}'")
124
 
125
- return full_zip_path
126
 
127
 
128
  if __name__ == "__main__":
 
57
  return cli_args
58
 
59
 
60
+ def read_file_lines(fi_path: str):
61
+ with open(fi_path, "r") as fp:
62
+ lines = fp.readlines()
63
+ mol = "".join(lines)
64
+ return mol
65
+
66
+
67
  def run_cli_command(
68
  protein_path: str,
69
  ligand: str,
70
  samples_per_complex: int,
71
  keep_local_structures: bool,
72
  save_visualisation: bool,
73
+ other_arg_file: str = None,
74
  work_dir=None,
75
  ):
76
  if work_dir is None:
 
105
 
106
  # Running the command
107
  try:
108
+ skip_running = False
109
+ if not skip_running:
110
+ result = subprocess.run(
111
+ command,
112
+ cwd=work_dir,
113
+ check=False,
114
+ text=True,
115
+ capture_output=True,
116
+ env=os.environ,
117
+ )
118
+ logging.debug(f"Command output:\n{result.stdout}")
119
+ if result.stderr:
120
+ logging.error(f"Command error:\n{result.stderr}")
121
  except subprocess.CalledProcessError as e:
122
  logging.error(f"An error occurred while executing the command: {e}")
123
 
124
+ # If there's a file for viewing, load it and view it.
125
+ sub_dirs = [os.path.join(temp_dir_path, x) for x in os.listdir(temp_dir_path)]
126
+ sub_dirs = list(filter(lambda x: os.path.isdir(x), sub_dirs))
127
+ display_path = display_pdb_text = None
128
+ if len(sub_dirs) == 1:
129
+ display_path = os.path.join(sub_dirs[0], "rank1_reverseprocess_protein.pdb")
130
+
131
+ if display_path and os.path.exists(display_path):
132
+ display_pdb_text = read_file_lines(display_path)
133
+
134
+ logging.debug(f"Display path: {display_path}")
135
+ # logging.debug(f"Display pdb text:\n{display_pdb_text}")
136
+
137
  # Zip the output directory
138
  # Generate a unique filename using a timestamp and a UUID
139
  timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
 
144
 
145
  logging.debug(f"Directory '{temp_dir}' zipped to '{full_zip_path}'")
146
 
147
+ return full_zip_path, display_pdb_text
148
 
149
 
150
  if __name__ == "__main__":