libokj commited on
Commit
8f135eb
·
1 Parent(s): eb777d5

Minor bug fixes

Browse files
Files changed (5) hide show
  1. app/fn.py +1 -20
  2. app/main.py +31 -20
  3. inference.py +2 -4
  4. requirements.txt +1 -0
  5. results/job_db.json +1 -15
app/fn.py CHANGED
@@ -12,14 +12,13 @@ import tempfile
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
@@ -425,29 +424,11 @@ def create_result_table_html(summary_df, result_info, opts=(), progress=gr.Progr
425
  image_zoom_formatter = HTMLTemplateFormatter(
426
  template='<img src="data:image/svg+xml,<%= value %>" alt="Molecule" class="zoom-img">'
427
  )
428
- uniprot_id_formatter = HTMLTemplateFormatter(
429
- template='<% if (value == value) { ' # Check if value is not NaN
430
- '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)) '
431
- # Check if value is a valid UniProt ID
432
- '{ %><a href="https://www.uniprot.org/uniprotkb/<%= value %>" target="_blank"><%= value %></a><% '
433
- # Else treat it as a sequence or other plain-text string, line-warping every 60 characters
434
- '} else { %><div style="white-space: pre-wrap;"><%= value.match(/.{1,60}/g).join("<br>") '
435
- '%></div><% } %><% } else { %><% } %>' # Output empty string if value is NaN
436
- )
437
- pubchem_id_formatter = HTMLTemplateFormatter(
438
- template='<% if (value == value) { ' # Check if value is not NaN
439
- '%><a href="https://pubchem.ncbi.nlm.nih.gov/#query=<%= value %>" '
440
- 'target="_blank"><%= value %></a>'
441
- '<% } else { %><% } %>' # Output empty string if value is NaN
442
- )
443
  bool_formatters = {col: BooleanFormatter() for col in bool_cols}
444
  float_formatters = {col: NumberFormatter(format='0.000') for col in html_df.select_dtypes('floating').columns}
445
  other_formatters = {
446
  'Compound': image_zoom_formatter,
447
  # 'Scaffold': image_zoom_formatter,
448
- # 'Target FASTA': {'type': 'textarea', 'width': 60},
449
- 'Target ID': uniprot_id_formatter,
450
- # 'Compound ID': pubchem_id_formatter, ## TODO: add link to click for adding mol to the viewer
451
  'Pose': {'type': 'molDisplayButtonFormatter'},
452
  }
453
  formatters = {**bool_formatters, **float_formatters, **other_formatters}
 
12
 
13
  import pandas as pd
14
  from bokeh.models import NumberFormatter, BooleanFormatter, HTMLTemplateFormatter
 
15
  import gradio as gr
16
  import pytz
17
  import panel as pn
18
  import seaborn as sns
19
  from markdown import markdown
20
  from rdkit import Chem, RDConfig
21
+ from rdkit.Chem import Crippen, Descriptors, rdMolDescriptors, Lipinski, rdmolops
22
  import requests
23
 
24
  from app import static
 
424
  image_zoom_formatter = HTMLTemplateFormatter(
425
  template='<img src="data:image/svg+xml,<%= value %>" alt="Molecule" class="zoom-img">'
426
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
427
  bool_formatters = {col: BooleanFormatter() for col in bool_cols}
428
  float_formatters = {col: NumberFormatter(format='0.000') for col in html_df.select_dtypes('floating').columns}
429
  other_formatters = {
430
  'Compound': image_zoom_formatter,
431
  # 'Scaffold': image_zoom_formatter,
 
 
 
432
  'Pose': {'type': 'molDisplayButtonFormatter'},
433
  }
434
  formatters = {**bool_formatters, **float_formatters, **other_formatters}
app/main.py CHANGED
@@ -1,7 +1,6 @@
1
- import io
2
-
3
  import spaces
4
 
 
5
  import os
6
  import tempfile
7
  import uuid
@@ -11,6 +10,7 @@ from pathlib import Path
11
  from time import sleep, time
12
  import urllib.parse
13
 
 
14
  import torch
15
  from email_validator import validate_email, EmailNotValidError
16
  from Bio import SeqIO
@@ -19,15 +19,17 @@ from gradio_rangeslider import RangeSlider
19
  from omegaconf import OmegaConf
20
  import pandas as pd
21
  from rdkit import Chem
22
- from rdkit.Chem import PandasTools, Draw
23
 
24
  from inference import (read_fragment_library, process_fragment_library, extract_pockets,
25
  dock_fragments, generate_linkers, select_fragment_pairs)
26
  from app import static, fn, db
27
 
28
 
 
29
  Path(tempfile.gettempdir(), 'gradio').mkdir(exist_ok=True)
30
- gr.set_static_paths(paths=["data/", "results/", "app/"])
 
31
  job_db = db.init_job_db()
32
  os.chmod('./fpocket', 0o755)
33
 
@@ -82,6 +84,8 @@ It might take a few minutes up to a few hours depending on the input size and th
82
  You may keep the page open and wait for job completion, or close the page and revisit later to look up the job status
83
  using the job id. You will also receive an email notification once the job is done.
84
  ''',
 
 
85
  }
86
  if job['status'] == "COMPLETED":
87
  stop = True
@@ -94,7 +98,9 @@ using the job id. You will also receive an email notification once the job is do
94
  yield {
95
  pred_lookup_status: msg,
96
  tabs: gr.Tabs(selected='result'),
97
- result_state: job
 
 
98
  }
99
  if job['status'] == "FAILED":
100
  stop = True
@@ -118,7 +124,7 @@ using the job id. You will also receive an email notification once the job is do
118
  yield {
119
  pred_lookup_status: msg,
120
  pred_lookup_btn: gr.Button(visible=stop),
121
- pred_lookup_stop_btn: gr.Button(visible=stop),
122
  }
123
  sleep(5)
124
 
@@ -197,23 +203,22 @@ def dock_link(
197
  linker_frag_dist, linker_strategy, linker_n_mols, linker_size, linker_steps,
198
  job_info
199
  ):
200
- update_info = {}
201
  job_id = job_info['id']
 
 
 
 
202
  pocket_extract_method = job_info['pocket_extraction_method']
203
  pocket_path_dict = job_info['protein_pocket_files']
 
204
 
205
  config = OmegaConf.load('configs/gen_fbdd_v1.yaml')
206
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
207
  print(f'Using device: {device}')
208
  date_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
209
- out_dir = f'results/{date_time}_{job_id}'
210
  frag_lib['X2'] = prot
211
- frag_lib['ID2'] = Path(prot).stem
212
-
213
- job_db.job_update(
214
- job_id=job_id,
215
- update_info={'status': 'RUNNING'},
216
- )
217
 
218
  try:
219
  docking_df = dock_fragments(
@@ -272,7 +277,7 @@ def dock_link(
272
  update_info = {
273
  'status': "COMPLETED",
274
  'error': None,
275
- 'output_dir': out_dir,
276
  'type': job_type,
277
  }
278
  # return {result_state: job_info | update_info, run_state: {}}
@@ -544,7 +549,7 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
544
  info="Your job ID is a UUID4 string that you receive after submitting a job on the "
545
  "page or in the email notification.")
546
  pred_lookup_example = gr.Button('Example', elem_classes=['example'], scale=1)
547
- pred_lookup_btn = gr.Button(value='Lookup the Job Status', variant='primary', visible=True)
548
  pred_lookup_stop_btn = gr.Button(value='Stop Tracking', variant='stop', visible=False)
549
  pred_lookup_status = gr.Markdown("**Job Status**", container=True)
550
 
@@ -645,7 +650,7 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
645
  raise ValueError(f"Unsupported method: {method}")
646
  return {input_prot_file: gr.File(str(file), visible=True)}
647
  except Exception as e:
648
- raise gr.Error(f"Query error: {str(e)}")
649
 
650
  prot_query_btn.click(
651
  fn=pdb_query,
@@ -759,7 +764,7 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
759
  outputs=loader_html,
760
  )
761
 
762
- job_valid.success(
763
  fn=lambda job: [job['id'], gr.Tabs(selected='job')],
764
  inputs=[run_state],
765
  outputs=[pred_lookup_id, tabs],
@@ -767,7 +772,8 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
767
  lambda: '<div class="loader"></ div>',
768
  outputs=loader_html,
769
  )
770
- auto_job_lookup = job_valid.success(
 
771
  fn=query_job_status,
772
  inputs=pred_lookup_id,
773
  outputs=[pred_lookup_status, tabs, result_state, pred_lookup_btn, pred_lookup_stop_btn],
@@ -815,11 +821,13 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
815
  else:
816
  raise gr.Error('Invalid result type')
817
 
 
818
  draw_opts = Draw.rdMolDraw2D.MolDrawOptions()
819
  draw_opts.clearBackground = False
820
  draw_opts.bondLineWidth = 0.5
821
  draw_opts.explicitMethyl = True
822
  draw_opts.singleColourWedgeBonds = True
 
823
  draw_opts.useCDKAtomPalette()
824
  PandasTools.drawOptions = draw_opts
825
  PandasTools.molSize = (90, 56)
@@ -918,5 +926,8 @@ with gr.Blocks(theme=THEME, title='GenFBDD', css=static.CSS, delete_cache=(3600,
918
  demo.launch(
919
  server_name='0.0.0.0',
920
  max_file_size="5mb",
921
- ssr_mode=False
 
 
 
922
  )
 
 
 
1
  import spaces
2
 
3
+ import io
4
  import os
5
  import tempfile
6
  import uuid
 
10
  from time import sleep, time
11
  import urllib.parse
12
 
13
+ from dotenv import load_dotenv
14
  import torch
15
  from email_validator import validate_email, EmailNotValidError
16
  from Bio import SeqIO
 
19
  from omegaconf import OmegaConf
20
  import pandas as pd
21
  from rdkit import Chem
22
+ from rdkit.Chem import PandasTools, Draw, rdDepictor
23
 
24
  from inference import (read_fragment_library, process_fragment_library, extract_pockets,
25
  dock_fragments, generate_linkers, select_fragment_pairs)
26
  from app import static, fn, db
27
 
28
 
29
+ load_dotenv()
30
  Path(tempfile.gettempdir(), 'gradio').mkdir(exist_ok=True)
31
+ SERVER_DATA_DIR = os.getenv('DATA', 'results')
32
+ gr.set_static_paths(paths=["data/", SERVER_DATA_DIR, "app/"])
33
  job_db = db.init_job_db()
34
  os.chmod('./fpocket', 0o755)
35
 
 
84
  You may keep the page open and wait for job completion, or close the page and revisit later to look up the job status
85
  using the job id. You will also receive an email notification once the job is done.
86
  ''',
87
+ pred_lookup_btn: gr.Button(visible=False),
88
+ pred_lookup_stop_btn: gr.Button(visible=True),
89
  }
90
  if job['status'] == "COMPLETED":
91
  stop = True
 
98
  yield {
99
  pred_lookup_status: msg,
100
  tabs: gr.Tabs(selected='result'),
101
+ result_state: job,
102
+ pred_lookup_btn: gr.Button(visible=True),
103
+ pred_lookup_stop_btn: gr.Button(visible=False),
104
  }
105
  if job['status'] == "FAILED":
106
  stop = True
 
124
  yield {
125
  pred_lookup_status: msg,
126
  pred_lookup_btn: gr.Button(visible=stop),
127
+ pred_lookup_stop_btn: gr.Button(visible= not stop),
128
  }
129
  sleep(5)
130
 
 
203
  linker_frag_dist, linker_strategy, linker_n_mols, linker_size, linker_steps,
204
  job_info
205
  ):
 
206
  job_id = job_info['id']
207
+ job_db.job_update(
208
+ job_id=job_id,
209
+ update_info={'status': 'RUNNING'},
210
+ )
211
  pocket_extract_method = job_info['pocket_extraction_method']
212
  pocket_path_dict = job_info['protein_pocket_files']
213
+ update_info = {}
214
 
215
  config = OmegaConf.load('configs/gen_fbdd_v1.yaml')
216
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
217
  print(f'Using device: {device}')
218
  date_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
219
+ out_dir = Path(SERVER_DATA_DIR, f'{date_time}_{job_id}')
220
  frag_lib['X2'] = prot
221
+ frag_lib['ID2'] = str(Path(prot).stem)
 
 
 
 
 
222
 
223
  try:
224
  docking_df = dock_fragments(
 
277
  update_info = {
278
  'status': "COMPLETED",
279
  'error': None,
280
+ 'output_dir': str(out_dir),
281
  'type': job_type,
282
  }
283
  # return {result_state: job_info | update_info, run_state: {}}
 
549
  info="Your job ID is a UUID4 string that you receive after submitting a job on the "
550
  "page or in the email notification.")
551
  pred_lookup_example = gr.Button('Example', elem_classes=['example'], scale=1)
552
+ pred_lookup_btn = gr.Button(value='Query Status', variant='primary', visible=True)
553
  pred_lookup_stop_btn = gr.Button(value='Stop Tracking', variant='stop', visible=False)
554
  pred_lookup_status = gr.Markdown("**Job Status**", container=True)
555
 
 
650
  raise ValueError(f"Unsupported method: {method}")
651
  return {input_prot_file: gr.File(str(file), visible=True)}
652
  except Exception as e:
653
+ gr.Warning(f"Query error: {str(e)}")
654
 
655
  prot_query_btn.click(
656
  fn=pdb_query,
 
764
  outputs=loader_html,
765
  )
766
 
767
+ job_valid_success = job_valid.success(
768
  fn=lambda job: [job['id'], gr.Tabs(selected='job')],
769
  inputs=[run_state],
770
  outputs=[pred_lookup_id, tabs],
 
772
  lambda: '<div class="loader"></ div>',
773
  outputs=loader_html,
774
  )
775
+
776
+ auto_job_lookup = job_valid_success.success(
777
  fn=query_job_status,
778
  inputs=pred_lookup_id,
779
  outputs=[pred_lookup_status, tabs, result_state, pred_lookup_btn, pred_lookup_stop_btn],
 
821
  else:
822
  raise gr.Error('Invalid result type')
823
 
824
+ rdDepictor.SetPreferCoordGen(True)
825
  draw_opts = Draw.rdMolDraw2D.MolDrawOptions()
826
  draw_opts.clearBackground = False
827
  draw_opts.bondLineWidth = 0.5
828
  draw_opts.explicitMethyl = True
829
  draw_opts.singleColourWedgeBonds = True
830
+ draw_opts.addStereoAnnotation = False
831
  draw_opts.useCDKAtomPalette()
832
  PandasTools.drawOptions = draw_opts
833
  PandasTools.molSize = (90, 56)
 
926
  demo.launch(
927
  server_name='0.0.0.0',
928
  max_file_size="5mb",
929
+ ssr_mode=False,
930
+ share=True,
931
+ share_server_address="ciddr-lab.ac.cn:7000",
932
+ share_server_protocol="https"
933
  )
inference.py CHANGED
@@ -443,15 +443,13 @@ def dock_fragments(
443
  # TODO Use index instead of confidence in filename
444
  if save_docking:
445
  sample_df['ligand_conf_path'] = [
446
- Path(
447
- docking_out_dir, f"{df['name'].iloc[idx]}_{i}-confidence{confidence[i]:.2f}.sdf"
448
- ) for i in range(n_samples)
449
  ]
450
  sample_df['ligand_mol']= [
451
  create_mol_with_coords(
452
  mol=RemoveAllHs(copy.deepcopy(lig)),
453
  new_coords=pos,
454
- path=sample_df['ligand_conf_path'].iloc[i] if save_docking else None
455
  ) for i, pos in enumerate(ligand_pos)
456
  ]
457
  # sample_df['ligand_pos'] = list(ligand_pos)
 
443
  # TODO Use index instead of confidence in filename
444
  if save_docking:
445
  sample_df['ligand_conf_path'] = [
446
+ f"{df['name'].iloc[idx]}_{i}-confidence{confidence[i]:.2f}.sdf" for i in range(n_samples)
 
 
447
  ]
448
  sample_df['ligand_mol']= [
449
  create_mol_with_coords(
450
  mol=RemoveAllHs(copy.deepcopy(lig)),
451
  new_coords=pos,
452
+ path=Path(docking_out_dir, sample_df['ligand_conf_path'].iloc[i]) if save_docking else None
453
  ) for i, pos in enumerate(ligand_pos)
454
  ]
455
  # sample_df['ligand_pos'] = list(ligand_pos)
requirements.txt CHANGED
@@ -17,6 +17,7 @@ hydra-core
17
  pandas
18
  pandarallel
19
  panel
 
20
  seaborn
21
  apscheduler
22
  tinydb
 
17
  pandas
18
  pandarallel
19
  panel
20
+ python-dotenv
21
  seaborn
22
  apscheduler
23
  tinydb
results/job_db.json CHANGED
@@ -1,15 +1 @@
1
- {
2
- "_default": {
3
- "1": {
4
- "id": "80cf2658-7a1c-48d6-8372-61b978177fe6",
5
- "type": "linking",
6
- "status": "COMPLETED",
7
- "output_dir": "results/2025-01-01_01-01-01_80cf2658-7a1c-48d6-8372-61b978177fe6",
8
- "protein_structure_file": "results/2025-01-01_01-01-01_80cf2658-7a1c-48d6-8372-61b978177fe6/1xkk.pdb",
9
- "start_time": 1735693261,
10
- "end_time": 1735693321,
11
- "expiry_time": 1893459721,
12
- "ip": "1.1.1.1"
13
- }
14
- }
15
- }
 
1
+ {"_default": {"1": {"id": "80cf2658-7a1c-48d6-8372-61b978177fe6", "type": "linking", "status": "COMPLETED", "output_dir": "results/2025-01-01_01-01-01_80cf2658-7a1c-48d6-8372-61b978177fe6", "protein_structure_file": "results/2025-01-01_01-01-01_80cf2658-7a1c-48d6-8372-61b978177fe6/1xkk.pdb", "start_time": 1735693261, "end_time": 1735693321, "expiry_time": 1893459721, "ip": "1.1.1.1"}}}