aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorNavan Chauhan <navanchauhan@gmail.com>2020-08-29 19:25:45 +0530
committerNavan Chauhan <navanchauhan@gmail.com>2020-08-29 19:25:45 +0530
commit29798c7351ec3e3c6a13f40b4f80e804ec803006 (patch)
tree2b21a0159135a916ed0c40697478ce4315a70441 /app
parentacc43c48185e0f87f74b462bace85e3147578ff1 (diff)
added single dock functionality, added more information
Diffstat (limited to 'app')
-rw-r--r--app/dock-single.py235
-rw-r--r--app/forms.py7
-rw-r--r--app/templates/base.html9
-rw-r--r--app/templates/dock_upload_single.html43
-rw-r--r--app/templates/generate.html2
-rw-r--r--app/templates/home.html25
-rw-r--r--app/views.py41
7 files changed, 348 insertions, 14 deletions
diff --git a/app/dock-single.py b/app/dock-single.py
new file mode 100644
index 0000000..be623f7
--- /dev/null
+++ b/app/dock-single.py
@@ -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() \ No newline at end of file
diff --git a/app/forms.py b/app/forms.py
index 975b3ad..4a3f880 100644
--- a/app/forms.py
+++ b/app/forms.py
@@ -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")])
diff --git a/app/templates/base.html b/app/templates/base.html
index d116ee3..dcae79c 100644
--- a/app/templates/base.html
+++ b/app/templates/base.html
@@ -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">
diff --git a/app/templates/dock_upload_single.html b/app/templates/dock_upload_single.html
new file mode 100644
index 0000000..882675c
--- /dev/null
+++ b/app/templates/dock_upload_single.html
@@ -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 %} \ No newline at end of file
diff --git a/app/templates/generate.html b/app/templates/generate.html
index 45bbb3b..629a2a3 100644
--- a/app/templates/generate.html
+++ b/app/templates/generate.html
@@ -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 %}
diff --git a/app/templates/home.html b/app/templates/home.html
index 541c313..43a84cc 100644
--- a/app/templates/home.html
+++ b/app/templates/home.html
@@ -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 %} \ No newline at end of file
diff --git a/app/views.py b/app/views.py
index 03e74f4..39db678 100644
--- a/app/views.py
+++ b/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.
###