added single dock functionality, added more information
This commit is contained in:
parent
acc43c4818
commit
29798c7351
|
@ -0,0 +1,235 @@
|
|||
import argparse
|
||||
import logging
|
||||
import multiprocessing
|
||||
import os
|
||||
import sys
|
||||
from argparse import ArgumentParser
|
||||
from collections import namedtuple
|
||||
|
||||
import mysql.connector as con
|
||||
|
||||
mycon = con.connect(host='192.168.1.6',user="curieweb",password="curie-web-russian-54",port=3306,database="curie")
|
||||
mycursor = mycon.cursor()
|
||||
|
||||
sql_select_Query = "SELECT id,email,pdb,ligand_smile,ligand_name,description,date FROM curieweb WHERE pdb IS NOT NULL AND done=0 LIMIT 1"
|
||||
mycursor.execute(sql_select_Query)
|
||||
|
||||
records = mycursor.fetchall()
|
||||
if records == []:
|
||||
print("Empty Set 😳")
|
||||
print("No active task, exitting gracefully")
|
||||
exit(0)
|
||||
|
||||
records = records[0]
|
||||
|
||||
|
||||
|
||||
print("Importing PLIP..",end="")
|
||||
|
||||
from plip.basic import config, logger
|
||||
|
||||
from plip.basic.config import __version__
|
||||
from plip.basic.parallel import parallel_fn
|
||||
from plip.basic.remote import VisualizerData
|
||||
from plip.exchange.webservices import fetch_pdb
|
||||
from plip.structure.preparation import create_folder_if_not_exists, extract_pdbid
|
||||
from plip.structure.preparation import tilde_expansion, PDBComplex
|
||||
|
||||
print(".Done")
|
||||
|
||||
def download_structure(inputpdbid):
|
||||
"""Given a PDB ID, downloads the corresponding PDB structure.
|
||||
Checks for validity of ID and handles error while downloading.
|
||||
Returns the path of the downloaded file."""
|
||||
try:
|
||||
if len(inputpdbid) != 4 or extract_pdbid(inputpdbid.lower()) == 'UnknownProtein':
|
||||
logger.error(f'invalid PDB-ID (wrong format): {inputpdbid}')
|
||||
sys.exit(1)
|
||||
pdbfile, pdbid = fetch_pdb(inputpdbid.lower())
|
||||
pdbpath = tilde_expansion('%s/%s.pdb' % (config.BASEPATH.rstrip('/'), pdbid))
|
||||
create_folder_if_not_exists(config.BASEPATH)
|
||||
with open(pdbpath, 'w') as g:
|
||||
g.write(pdbfile)
|
||||
return pdbpath, pdbid
|
||||
except ValueError: # Invalid PDB ID, cannot fetch from RCBS server
|
||||
logger.error(f'PDB-ID does not exist: {inputpdbid}')
|
||||
sys.exit(1)
|
||||
|
||||
def bounding_box(receptor, residues):
|
||||
try:
|
||||
import pymol2
|
||||
except ImportError:
|
||||
raise ImportError("Failed to import PyMOL")
|
||||
|
||||
session = pymol2.PyMOL()
|
||||
session.start()
|
||||
|
||||
cmd = session.cmd
|
||||
cmd.load(pdbpath,"target")
|
||||
cmd.select("box",(selectionResidues))
|
||||
|
||||
extent = 5
|
||||
|
||||
([minX, minY, minZ],[maxX, maxY, maxZ]) = cmd.get_extent("box")
|
||||
|
||||
minX = minX - float(extent)
|
||||
minY = minY - float(extent)
|
||||
minZ = minZ - float(extent)
|
||||
maxX = maxX + float(extent)
|
||||
maxY = maxY + float(extent)
|
||||
maxZ = maxZ + float(extent)
|
||||
|
||||
SizeX = maxX - minX
|
||||
SizeY = maxY - minY
|
||||
SizeZ = maxZ - minZ
|
||||
CenterX = (maxX + minX)/2
|
||||
CenterY = (maxY + minY)/2
|
||||
CenterZ = (maxZ + minZ)/2
|
||||
|
||||
session.stop()
|
||||
|
||||
return {"size_x": SizeX, "size_y": SizeY, "size_z": SizeZ, "center_x": CenterX, "center_y": CenterY, "center_z": CenterZ}
|
||||
|
||||
def removeWater(pdbpath):
|
||||
import pymol2
|
||||
session = pymol2.PyMOL()
|
||||
session.start()
|
||||
cmd = session.cmd
|
||||
cmd.load(pdbpath,"target")
|
||||
cmd.remove('resn HOH')
|
||||
cmd.save(pdbpath,"target")
|
||||
session.stop()
|
||||
|
||||
def getResidues(pdbpath):
|
||||
mol = PDBComplex()
|
||||
mol.load_pdb(pdbpath)
|
||||
for ligand in mol.ligands:
|
||||
mol.characterize_complex(ligand)
|
||||
|
||||
residues = []
|
||||
|
||||
for x in range(len(mol.interaction_sets)):
|
||||
if len(mol.interaction_sets[list(mol.interaction_sets.keys())[x]].interacting_res) != 0:
|
||||
residues.append(mol.interaction_sets[list(mol.interaction_sets.keys())[x]].interacting_res)
|
||||
|
||||
print(residues)
|
||||
return residues
|
||||
|
||||
def get_select_command(residues,allResidues=False):
|
||||
residues.sort(key=len,reverse=True)
|
||||
selectionResidues = ""
|
||||
allRes = []
|
||||
if len(residues) == 0:
|
||||
#print("what the frick, no interacting ligands???")
|
||||
print("We could not find any binding sites within the structure.")
|
||||
else:
|
||||
for x in residues:
|
||||
selectionResidues = ""
|
||||
for y in x:
|
||||
selectionResidues += 'resi ' + y.replace("A","") + ' + '
|
||||
allRes.append(selectionResidues.strip()[:-1].strip())
|
||||
|
||||
if allResidues == False:
|
||||
return allRes[0]
|
||||
return allRes
|
||||
|
||||
def convert_pdb_pdbqt(pdbpath):
|
||||
import oddt
|
||||
from oddt.docking.AutodockVina import write_vina_pdbqt
|
||||
print(pdbpath)
|
||||
try:
|
||||
receptor = next(oddt.toolkit.readfile("pdb",pdbpath.split("./")[1]))
|
||||
"""
|
||||
# remove zero order bonds from metals
|
||||
for atom in receptor:
|
||||
if atom.atomicnum == 30: # Atomic num of treated metals
|
||||
for bond in atom.bonds:
|
||||
print("del")
|
||||
receptor.OBMol.DeleteBond(bond.OBBond)
|
||||
"""
|
||||
receptor.calccharges()
|
||||
except Exception:
|
||||
print("Molecule failed to charge, falling back to RDKit")
|
||||
receptor = next(oddt.toolkits.rdk.readfile("pdb",pdbpath.split("./")[1]))
|
||||
receptor.calccharges()
|
||||
|
||||
path = write_vina_pdbqt(receptor,'./',flexible=False)
|
||||
return path
|
||||
|
||||
|
||||
def email(zipArchive):
|
||||
import smtplib
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.base import MIMEBase
|
||||
from email import encoders
|
||||
|
||||
fromaddr = "navanchauhan@gmail.com"
|
||||
|
||||
msg = MIMEMultipart()
|
||||
msg['From'] = fromaddr
|
||||
msg['To'] = toaddr
|
||||
msg['Subject'] = "Curie Web Results for Job ID " + str(jobID)
|
||||
body = "Attached Zip contains the docked files, PLIP report and PyMOL Visualisations. If the ZIP file does not contain these files, please report this issue by replying to this email. Job was submitted on {} with the description {}".format(date, description)
|
||||
|
||||
msg.attach(MIMEText(body, 'plain'))
|
||||
filename = "Curie_Web_Results_Job_ID_" + str(jobID) + ".zip"
|
||||
p = MIMEBase('application', 'octet-stream')
|
||||
with open((str(zipArchive) + ".zip"), "rb") as attachment:
|
||||
p.set_payload((attachment).read())
|
||||
encoders.encode_base64(p)
|
||||
p.add_header('Content-Disposition', "attachment; filename= %s" % filename)
|
||||
msg.attach(p)
|
||||
|
||||
s = smtplib.SMTP('smtp.gmail.com', 587)
|
||||
s.starttls()
|
||||
s.login(fromaddr, 'okrs shoc ahtk idui')
|
||||
text = msg.as_string()
|
||||
|
||||
s.sendmail(fromaddr, toaddr, text)
|
||||
s.quit()
|
||||
|
||||
|
||||
inPDB = records[2]
|
||||
jobID = records[0]
|
||||
toaddr = records[1]
|
||||
description = records[5]
|
||||
date = records[6]
|
||||
|
||||
#pdb_file_name = pdbpath.split('/')[-1]
|
||||
#pdbpath="./6lu7.pdb"
|
||||
|
||||
import os
|
||||
cd = os.getcwd()
|
||||
f = os.path.join(cd,"static/uploads")
|
||||
#t = os.path.join(f,"receptor",target)
|
||||
#r = os.path.join(f,"ligands",ligand)
|
||||
#c = os.path.join(f,"configs",config)
|
||||
import tempfile
|
||||
from shutil import make_archive
|
||||
import time
|
||||
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
print('The created temporary directory is %s' % directory)
|
||||
os.chdir(directory)
|
||||
pdbpath, pdbid = download_structure(inPDB)
|
||||
residues = getResidues(pdbpath)
|
||||
selectionResidues = get_select_command(residues,allResidues=False)
|
||||
#print(selectionResidues)
|
||||
removeWater(pdbpath)
|
||||
config = bounding_box(pdbpath,selectionResidues)
|
||||
print("Configuration:",config)
|
||||
pdbqt = convert_pdb_pdbqt(pdbpath)
|
||||
configuration = "size_x={}\nsize_y={}\nsize_z={}\ncenter_x={}\ncenter_y={}\ncenter_z={}".format(config["size_x"],config["size_y"],config["size_z"],config["center_x"],config["center_y"],config["center_z"])
|
||||
with open("config.txt","w") as file:
|
||||
file.write(configuration)
|
||||
os.system('obabel -:"%s" --gen3d -opdbqt -O%s.pdbqt' % (records[3],records[4]))
|
||||
os.system("docker run --rm -v ${PWD}:/results -w /results -u $(id -u ${USER}):$(id -g ${USER}) navanchauhan/curie-cli -r %s -l %s -c config.txt -dpi" % (pdbqt,str(records[4]+".pdbqt")))
|
||||
z = "Curie_Web_Result_"+str(jobID)
|
||||
zi = os.path.join(f,z)
|
||||
make_archive(zi, 'zip', directory)
|
||||
#copy(("Curie_Web_Result_"+str(jobID)),f)
|
||||
email(zi)
|
||||
#print((str(zi) + ".zip"))
|
||||
mycursor.execute('UPDATE curieweb set done=1 where id="%s"' % (jobID))
|
||||
mycon.commit()
|
|
@ -34,6 +34,13 @@ class curieForm(FlaskForm):
|
|||
class statusForm(FlaskForm):
|
||||
jobID = StringField('Job ID',validators=[DataRequired()])
|
||||
|
||||
class dockSingleForm(FlaskForm):
|
||||
description = StringField('Description',default="Curie Web Task")
|
||||
pdbID = StringField('PDB ID',validators=[DataRequired()])
|
||||
smiles = StringField('SMILES',validators=[DataRequired()])
|
||||
name = StringField('Ligand Name',validators=[DataRequired()])
|
||||
email = StringField('Email', validators=[DataRequired(), Email()])
|
||||
|
||||
class generateSMILES(FlaskForm):
|
||||
n = IntegerField('Number of Molecules to Generate',default=1,validators=[DataRequired()])
|
||||
#modelSelection = SelectField('Model',choices=[("alpha","Alpha"),("beta","Beta")])
|
||||
|
|
|
@ -68,15 +68,16 @@
|
|||
<div class="col-sm-6 col-md-3 item">
|
||||
<h3>Features</h3>
|
||||
<ul>
|
||||
<li><a href="#">Dock and Report</a></li>
|
||||
<li><a href="#">PubMed Search</a></li>
|
||||
<li><a href="{{ url_for('dock_upload') }}">Dock and Report (Manual)</a></li>
|
||||
<li><a href="{{ url_for('dock_upload_single') }}">Dock and Report (Automatic)</a></li>
|
||||
<li><a href="{{ url_for('pubmed') }}">PubMed Search</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3 item">
|
||||
<h3>Beta Features</h3>
|
||||
<ul>
|
||||
<li><a href="#">LSTM Generator</a></li>
|
||||
<li><a href="#">Visualiser</a></li>
|
||||
<li><a href="{{ url_for('generate') }}">LSTM Generator</a></li>
|
||||
<li><a href="{{ url_for('visualise') }}">Visualiser</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-6 item text">
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block main %}
|
||||
<h2>Enter Your Configuration</h2>
|
||||
<form action="{{ url_for('dock_upload_single') }}" method="post" enctype="multipart/form-data">
|
||||
{% include 'flash_messages.html' %}
|
||||
{{ form.csrf_token }}
|
||||
<div class="form-group">
|
||||
{{ form.description.label }}
|
||||
{{ form.description(class="form-control") }}
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="col">
|
||||
{{ form.pdbID.label }}
|
||||
{{ form.pdbID(class="form-control")}}
|
||||
</div>
|
||||
<div class="col">
|
||||
{{ form.smiles.label }}
|
||||
{{ form.smiles(class="form-control")}}
|
||||
</div>
|
||||
<div class="col">
|
||||
{{ form.name.label }}
|
||||
{{ form.name(class="form-control")}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{ form.email.label }}
|
||||
{{ form.email(class="form-control") }}
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary">Upload</button>
|
||||
</form>
|
||||
<br>
|
||||
<section>
|
||||
<style>
|
||||
#discovery{
|
||||
height: 40vh;
|
||||
}
|
||||
</style>
|
||||
<div id="discovery"></div>
|
||||
<script src="{{url_for('static',filename='js/discovery.js')}}"></script>
|
||||
</section>
|
||||
{% endblock %}
|
|
@ -34,7 +34,7 @@
|
|||
<script>
|
||||
SmilesDrawer.parse('{{result[0]}}', function(tree) {
|
||||
smilesDrawer.draw(tree, "canvas-{{x}}", "dark", false);
|
||||
console.log(smilesDrawer.draw(tree, "canvas-{{x}}", "light", false))
|
||||
console.log(smilesDrawer.draw(tree, "canvas-{{x}}", "dark", false))
|
||||
});
|
||||
</script>
|
||||
{% endfor %}
|
||||
|
|
|
@ -2,12 +2,29 @@
|
|||
|
||||
{% block main %}
|
||||
<h2>Curie Web Demo</h2>
|
||||
<p>Dock and Report performs molecular docking using AutoDock Vina, generates visualisations using PyMOL and then finds protein-ligand interactions using PLIP. It then compiles all of this into a PDF report and emails it to you.</p>
|
||||
<p>Curie-Web is a part of The Curie Project which aims to make the process of Computer-Aided Drug Design as fast as possible.</p>
|
||||
<p>The following are the currently active modules</p>
|
||||
<h3>Docking</h3>
|
||||
<ul>
|
||||
<li><a href="{{ url_for('dock_upload') }}">Dock and Report</a></li>
|
||||
<li><a href="{{ url_for('status')}}">Job Status</a></li>
|
||||
<li><a href="{{ url_for('generate') }}">Generate</a></li>
|
||||
<li><a href="{{ url_for('dock_upload') }}">Dock and Report (Manual)</a> - You can enter your AutoDock Vina configuration, upload the PDBQT files and it will perform the molecular docking and generate a PDF with proper visualisations and protein-interaction profillings (Using PLIP) </li>
|
||||
<li><a href="{{ url_for('dock_upload_single') }}">Dock and Report (Automatic)</a> - You just enter in the PDB Code, target compound's SMILES structure and name, it will automatically find a binding location and then perform docking and report generation</li>
|
||||
</ul>
|
||||
|
||||
<h3>Drug Designing</h3>
|
||||
<ul>
|
||||
<li><a href="{{ url_for('generate') }}">Generate</a> - You can use this to generate completely new compounds</li>
|
||||
</ul>
|
||||
|
||||
<h3>Researching</h3>
|
||||
<ul>
|
||||
<li><a href="{{ url_for('pubmed') }}">PubMed Search</a> - Handy PubMed search with direct download links</li>
|
||||
<li>Qrious App - You can enter a question for a set of papers (e.g. ChemRxiv preprints) and it uses AI to answer it for each individual paper based on their abstract</li>
|
||||
</ul>
|
||||
|
||||
<h3>Misc.</h3>
|
||||
<ul>
|
||||
<li><a href="{{ url_for('status')}}">Job Status</a> - Check the job status </li>
|
||||
<li><a href="{{ url_for('visualise')}}">Visualise</a> - Molecular Viewer </li>
|
||||
</ul>
|
||||
<img src="{{url_for('static',filename='assets/workingInALaboratory.svg')}}" />
|
||||
{% endblock %}
|
41
app/views.py
41
app/views.py
|
@ -12,10 +12,11 @@ from string import digits, ascii_lowercase
|
|||
from pymed import PubMed
|
||||
from datetime import datetime
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
# Note: that when using Flask-WTF we need to import the Form Class that we created
|
||||
# in forms.py
|
||||
from .forms import MyForm, curieForm, statusForm, generateSMILES, PyMedSearch
|
||||
from .forms import MyForm, curieForm, statusForm, generateSMILES, PyMedSearch, dockSingleForm
|
||||
|
||||
def gen_word(N, min_N_dig, min_N_low):
|
||||
choose_from = [digits]*min_N_dig + [ascii_lowercase]*min_N_low
|
||||
|
@ -30,7 +31,6 @@ def convertToBinaryData(filename):
|
|||
binaryData = file.read()
|
||||
return binaryData
|
||||
|
||||
|
||||
###
|
||||
# Routing for your application.
|
||||
###
|
||||
|
@ -99,7 +99,6 @@ def status():
|
|||
flash_errors(taskStatusForm)
|
||||
return render_template('job_status_form.html',form=taskStatusForm)
|
||||
|
||||
|
||||
@app.route('/basic-form', methods=['GET', 'POST'])
|
||||
def basic_form():
|
||||
if request.method == 'POST':
|
||||
|
@ -114,7 +113,6 @@ def basic_form():
|
|||
|
||||
return render_template('form.html')
|
||||
|
||||
|
||||
@app.route('/wtform', methods=['GET', 'POST'])
|
||||
def wtform():
|
||||
myform = MyForm()
|
||||
|
@ -133,7 +131,7 @@ def wtform():
|
|||
flash_errors(myform)
|
||||
return render_template('wtform.html', form=myform)
|
||||
|
||||
tfWorking = -1
|
||||
tfWorking = 0
|
||||
|
||||
if tfWorking == -1:
|
||||
try:
|
||||
|
@ -218,6 +216,39 @@ def dock_upload():
|
|||
flash_errors(form)
|
||||
return render_template('dock_upload.html', form=form)
|
||||
|
||||
@app.route('/Dock-Single', methods=['GET', 'POST'])
|
||||
def dock_upload_single():
|
||||
form = dockSingleForm()
|
||||
|
||||
if request.method == 'POST' and form.validate_on_submit():
|
||||
print("Recieved task: ",form.description.data)
|
||||
description = form.description.data
|
||||
pdb = form.pdbID.data
|
||||
smile = form.smiles.data
|
||||
name = form.name.data
|
||||
email = form.email.data
|
||||
|
||||
import mysql.connector as con
|
||||
mycon = con.connect(host=app.config['DB_HOST'],user=app.config['DB_USER'],password=app.config['DB_PASSWORD'],port=app.config['DB_PORT'],database=app.config['DB_NAME'])
|
||||
mycursor = mycon.cursor()
|
||||
|
||||
sqlQuery = "insert into curieweb (id, email, pdb, ligand_smile, ligand_name, date, description) values (%s,%s,%s,%s,%s,CURDATE(),%s) "
|
||||
jobID = gen_word(16, 1, 1)
|
||||
|
||||
insert_tuple = (jobID,email,pdb,smile,name,description)
|
||||
mycursor.execute(sqlQuery,insert_tuple)
|
||||
mycon.commit()
|
||||
|
||||
print("Description",description)
|
||||
|
||||
cwd = os.path.join(os.getcwd(),"app")
|
||||
subprocess.Popen(['python3', 'dock-single.py'],cwd=cwd)
|
||||
|
||||
return render_template('display_result.html', filename="OwO", description=description,job=jobID)
|
||||
|
||||
flash_errors(form)
|
||||
return render_template('dock_upload_single.html', form=form)
|
||||
|
||||
###
|
||||
# The functions below should be applicable to all Flask apps.
|
||||
###
|
||||
|
|
Loading…
Reference in New Issue