|
from Bio.Blast import NCBIWWW, NCBIXML |
|
from Bio.Seq import Seq |
|
from Bio import SeqIO |
|
from Bio.SeqUtils import GC |
|
import time |
|
import warnings |
|
import streamlit as st |
|
from PIL import Image |
|
from io import StringIO |
|
|
|
img = Image.open("image.png") |
|
|
|
st.image(img, use_column_width=True) |
|
|
|
st.write(""" ## **Your results will display here :** |
|
""") |
|
def set_button_style(): |
|
button_style = """ |
|
<style> |
|
.stButton button { |
|
background-color: #FF6F61; |
|
color: white; |
|
border-radius: 100px; |
|
border: 2px solid #FFFFFF; |
|
box-shadow: 2px 2px 5px #000000; |
|
width: 160px; |
|
} |
|
</style> |
|
""" |
|
st.markdown(button_style, unsafe_allow_html=True) |
|
|
|
|
|
st.sidebar.title('Protein alignment with BLAST') |
|
|
|
up = st.sidebar.file_uploader("Choose an existing fasta file", accept_multiple_files=False) |
|
if up : |
|
fasta = up.read() |
|
|
|
|
|
seq = st.sidebar.text_area('Or enter a sequence to analyze : ') |
|
|
|
b1 = st.sidebar.button("Calculate GC") |
|
b2 = st.sidebar.button("Replicate") |
|
b3 = st.sidebar.button("Transcribe") |
|
b4 = st.sidebar.button("Back-Transcribe") |
|
b5 = st.sidebar.button("Translate") |
|
|
|
type = st.sidebar.text_input('Enter BLAST type : ',placeholder='exemple : blastp, blastn ...') |
|
data = st.sidebar.text_input('Enter the database : ', placeholder='exemple : nt, nr ...') |
|
detailed = st.sidebar.text_input('Enter detailed search : ', placeholder='Homo sapiens') |
|
set_button_style() |
|
b = st.sidebar.button("Run BLAST") |
|
s = st.sidebar.button("Run detailed search") |
|
|
|
|
|
|
|
def blast(seq, type, data): |
|
|
|
st.write("""Starting BLAST search...""") |
|
start_time = time.time() |
|
result_handle2 = NCBIWWW.qblast(type, data, seq) |
|
end_time = time.time() |
|
|
|
st.write("""BLAST search completed.""") |
|
st.write(f"Time taken: {end_time - start_time:.2f} seconds.") |
|
|
|
with open("output.xml", "w") as out_handle: |
|
out_handle.write(result_handle2.read()) |
|
st.write("Results saved to output.xml") |
|
st.write("===================================") |
|
result_handle = open("output.xml") |
|
blast_record = NCBIXML.read(result_handle) |
|
st.write(""" **Results :** """) |
|
count = 0 |
|
for alignment in blast_record.alignments: |
|
st.write("**"+str(alignment.title)+"**") |
|
for hsp in alignment.hsps: |
|
st.write(str(hsp.query[0:50])) |
|
st.write(str(hsp.query[0:50])) |
|
st.write(str(hsp.query[0:50])) |
|
percentage_result = (hsp.query_end - hsp.query_start + 1) / hsp.align_length * 100 |
|
st.write(str(hsp)) |
|
if percentage_result >= 50: |
|
st.write(f'<p style="color:green;">{percentage_result:.2f}</p>', unsafe_allow_html=True) |
|
else: |
|
st.write(f'<p style="color:red;">{percentage_result:.2f}</p>', unsafe_allow_html=True) |
|
count += 1 |
|
if count == 10: |
|
break |
|
|
|
def detailed_search(detailed): |
|
|
|
result_handle = open("output.xml") |
|
blast_record = NCBIXML.read(result_handle) |
|
|
|
st.write(" Detailed search on : " + "**" + str(detailed) + "**") |
|
for alignment in blast_record.alignments: |
|
if detailed in alignment.title: |
|
st.write(str(alignment.title)) |
|
for hsp in alignment.hsps: |
|
st.write(str(hsp.query[0:50])) |
|
st.write(str(hsp.match[0:50])) |
|
st.write(str(hsp.sbjct[0:50])) |
|
st.write(str(hsp.positives) + str(hsp.score) + str(hsp.expect) + str(hsp.bits) + str(hsp.gaps)) |
|
|
|
def save_seq(seq): |
|
with open("sequenceinput.fsa", "w") as file: |
|
file.write(str(seq)) |
|
st.write("sequence saved to sequenceinput.fsa") |
|
|
|
if seq : |
|
save_seq(seq) |
|
|
|
if (seq and type and data and b) : |
|
blast(seq, type, data) |
|
elif (up and type and data and b) : |
|
blast(fasta, type, data) |
|
|
|
if detailed and s : |
|
detailed_search(detailed) |
|
|
|
|
|
def calculate(seq): |
|
with warnings.catch_warnings(): |
|
warnings.simplefilter("ignore") |
|
gc_content = GC(seq) |
|
st.write(f"### **GC :** {gc_content}") |
|
|
|
if seq and b1 : |
|
calculate(seq) |
|
elif up and b1 : |
|
calculate(fasta.decode()) |
|
|
|
def reverse(seq): |
|
sequence = Seq(seq) |
|
r_seq = sequence.reverse_complement() |
|
chunked_r_seq = [r_seq[i:i+50] for i in range(0, len(r_seq), 50)] |
|
|
|
st.write("### **Reversed sequence :**") |
|
for chunk in chunked_r_seq: |
|
st.write("#### " + str(chunk)) |
|
|
|
if seq and b2 : |
|
reverse(seq) |
|
elif up and b2 : |
|
reverse(fasta) |
|
|
|
def transcribe_seq(seq): |
|
sequence = Seq(seq) |
|
r_seq = sequence.transcribe() |
|
chunked_r_seq = [r_seq[i:i+50] for i in range(0, len(r_seq), 50)] |
|
|
|
st.write("### **Transcribed sequence :** ") |
|
for chunk in chunked_r_seq: |
|
st.write("#### " + str(chunk)) |
|
|
|
if seq and b3 : |
|
transcribe_seq(seq) |
|
elif up and b3 : |
|
transcribe_seq(fasta) |
|
|
|
def back_transcribe_seq(seq): |
|
sequence = Seq(seq) |
|
r_seq = sequence.back_transcribe() |
|
chunked_r_seq = [r_seq[i:i+50] for i in range(0, len(r_seq), 50)] |
|
|
|
st.write("### **Back-Transcribed sequence :** ") |
|
for chunk in chunked_r_seq: |
|
st.write("#### " + str(chunk)) |
|
|
|
if seq and b4 : |
|
back_transcribe_seq(seq) |
|
elif up and b4 : |
|
back_transcribe_seq(fasta) |
|
|
|
def translate_seq(seq): |
|
try : |
|
sequence = Seq(seq) |
|
r_seq = sequence.translate() |
|
chunked_r_seq = [r_seq[i:i+50] for i in range(0, len(r_seq), 50)] |
|
|
|
st.write("### **Translated sequence :** ") |
|
for chunk in chunked_r_seq: |
|
st.write("#### " + str(chunk)) |
|
except Exception as e: |
|
st.write(f"#### Unable to translate this sequence! {e}") |
|
|
|
if seq and b5 : |
|
translate_seq(seq) |
|
elif up and b5 : |
|
transcribe_seq(fasta) |
|
|