| import streamlit as st |
| from stmol import showmol |
| import py3Dmol |
| import requests |
| import biotite.structure.io as bsio |
| import random |
| import hashlib |
| import urllib3 |
| from Bio.Blast import NCBIWWW, NCBIXML |
| from Bio.Seq import Seq |
| from Bio.SeqRecord import SeqRecord |
| import time |
| import urllib.parse |
|
|
| urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) |
|
|
| |
| st.set_page_config(layout='wide') |
| st.markdown(""" |
| <style> |
| body { |
| color: #fff; |
| background-color: #0e1117; |
| } |
| .stApp { |
| background-color: #0e1117; |
| } |
| .stTextInput > div > div > input { |
| color: #fff; |
| background-color: #262730; |
| } |
| .stNumberInput > div > div > input { |
| color: #fff; |
| background-color: #262730; |
| } |
| .stTextArea > div > div > textarea { |
| color: #fff; |
| background-color: #262730; |
| } |
| .stButton > button { |
| color: #fff; |
| background-color: #0e1117; |
| border: 1px solid #fff; |
| } |
| </style> |
| """, unsafe_allow_html=True) |
|
|
| st.title('🔮 GenPro2 Protein Generator, Structure Predictor, and Analysis Tool') |
| st.write('GenPro2 is an end-to-end protein sequence generator, structure predictor, and analysis tool based [*ESMFold*](https://esmatlas.com/about) and the ESM-2 language model.') |
|
|
| def generate_sequence_from_words(words, length): |
| seed = ' '.join(words).encode('utf-8') |
| random.seed(hashlib.md5(seed).hexdigest()) |
| amino_acids = "ACDEFGHIKLMNPQRSTVWY" |
| return ''.join(random.choice(amino_acids) for _ in range(length)) |
|
|
| def render_mol(pdb): |
| pdbview = py3Dmol.view(width=800, height=500) |
| pdbview.addModel(pdb, 'pdb') |
| pdbview.setStyle({'cartoon': {'color': 'spectrum'}}) |
| pdbview.setBackgroundColor('white') |
| pdbview.zoomTo() |
| pdbview.zoom(2, 800) |
| pdbview.spin(True) |
| showmol(pdbview, height=500, width=800) |
|
|
| def perform_blast_analysis(sequence): |
| st.subheader('Protein Analysis') |
| with st.spinner("Analyzing generated protein... This may take a several minutes. Stay tuned!"): |
| progress_bar = st.progress(0) |
| for i in range(100): |
| progress_bar.progress(i + 1) |
| time.sleep(0.1) |
| |
| try: |
| record = SeqRecord(Seq(sequence), id='random_protein') |
| result_handle = NCBIWWW.qblast("blastp", "swissprot", record.seq) |
| |
| blast_record = NCBIXML.read(result_handle) |
| |
| if blast_record.alignments: |
| alignment = blast_record.alignments[0] |
| hsp = alignment.hsps[0] |
| |
| |
| title_parts = alignment.title.split('|') |
| protein_name = title_parts[-1].strip() |
| organism = title_parts[-2].split('OS=')[-1].split('OX=')[0].strip() |
| |
| |
| identity_percentage = (hsp.identities / alignment.length) * 100 |
| |
| st.write(f"**Top Match:** {protein_name}") |
| st.write(f"**Organism Code:** {organism}") |
| st.write(f"**Sequence Identity:** {identity_percentage:.2f}%") |
| |
| |
| if hasattr(alignment, 'description') and alignment.description: |
| st.write(f"**Potential Function:** {alignment.description}") |
| else: |
| st.write("No significant matches found. This might be a unique protein sequence!") |
| except Exception as e: |
| st.error(f"An error occurred during protein analysis: {str(e)}") |
| st.write("Please try again later or contact support if the issue persists.") |
|
|
| def update(sequence, word1, word2, word3, sequence_length): |
| headers = { |
| 'Content-Type': 'application/x-www-form-urlencoded', |
| } |
| try: |
| response = requests.post('https://api.esmatlas.com/foldSequence/v1/pdb/', |
| headers=headers, |
| data=sequence, |
| verify=False, |
| timeout=300) |
| response.raise_for_status() |
| pdb_string = response.content.decode('utf-8') |
| |
| with open('predicted.pdb', 'w') as f: |
| f.write(pdb_string) |
| |
| struct = bsio.load_structure('predicted.pdb', extra_fields=["b_factor"]) |
| b_value = round(struct.b_factor.mean(), 2) |
| |
| st.session_state.structure_info = { |
| 'pdb_string': pdb_string, |
| 'b_value': b_value, |
| 'word1': word1, |
| 'word2': word2, |
| 'word3': word3, |
| 'sequence_length': sequence_length |
| } |
| |
| st.session_state.show_analyze_button = True |
|
|
| except requests.exceptions.RequestException as e: |
| st.error(f"An error occurred while calling the API: {str(e)}") |
| st.write("Please try again later or contact support if the issue persists.") |
|
|
| def share_on_twitter(word1, word2, word3, length, plddt): |
| tweet_text = f"I just generated a new protein using #GenPro2 from the seed-words '{word1}', '{word2}', and '{word3}' + sequence length of {length}! It's plDDT Score: {plddt}%." |
| tweet_url = f"https://twitter.com/intent/tweet?text={urllib.parse.quote(tweet_text)}" |
| return tweet_url |
|
|
| |
| if 'sequence' not in st.session_state: |
| st.session_state.sequence = None |
| if 'show_analyze_button' not in st.session_state: |
| st.session_state.show_analyze_button = False |
| if 'structure_info' not in st.session_state: |
| st.session_state.structure_info = None |
|
|
| |
| st.subheader("Generate Sequence from Words") |
| col1, col2, col3 = st.columns(3) |
| with col1: |
| word1 = st.text_input("Word 1") |
| with col2: |
| word2 = st.text_input("Word 2") |
| with col3: |
| word3 = st.text_input("Word 3") |
|
|
| sequence_length = st.number_input("Sequence Length", min_value=50, max_value=400, value=100, step=10) |
|
|
| if st.button('Generate and Predict'): |
| if word1 and word2 and word3: |
| sequence = generate_sequence_from_words([word1, word2, word3], sequence_length) |
| st.session_state.sequence = sequence |
| st.text_area("Generated Sequence", sequence, height=100) |
| st.info("Note: The same words and sequence length will always produce the same sequence.") |
| |
| with st.spinner("Predicting protein structure... This may take a few minutes."): |
| update(sequence, word1, word2, word3, sequence_length) |
| else: |
| st.warning("Please enter all three words to generate a sequence.") |
|
|
| |
| if st.session_state.structure_info: |
| info = st.session_state.structure_info |
| st.subheader(f'Predicted protein structure using seed: {info["word1"]}, {info["word2"]}, and {info["word3"]} + length {info["sequence_length"]}') |
| render_mol(info['pdb_string']) |
| |
| st.subheader('plDDT Confidence Score') |
| st.write('plDDT is a benchmark for scoring the confidence level in protein folding predictions based on a scale from 0-100%. 70% or more is good!') |
| plddt_score = int(info["b_value"] * 100) |
| st.info(f'Your plDDT score is: {plddt_score}%') |
| |
| st.subheader("Share your unique protein on X(Twitter)") |
| |
| st.markdown(""" |
| <div style='background-color: #262730; padding: 10px; border-radius: 5px; font-size: 0.8em;'> |
| <ol> |
| <li>Take a screenshot of the protein structure above.</li> |
| <li>Click the 'Share on X' button below to open a pre-filled post with your protein seed-words and score.</li> |
| <li>Be sure to attach the screenshot of your protein before your post!</li> |
| </ol> |
| </div> |
| """, unsafe_allow_html=True) |
| |
| tweet_url = share_on_twitter(info["word1"], info["word2"], info["word3"], info["sequence_length"], plddt_score) |
| st.markdown(f"[Share Results]({tweet_url})") |
|
|
| st.markdown(""" |
| ## What to do next: |
| |
| If you find an interesting protein from the sequence folding, you can explore it even further: |
| |
| 1. Click the 'analyze protein' button to use the [BLAST](https://blast.ncbi.nlm.nih.gov/Blast.cgi?PROGRAM=blastp&PAGE_TYPE=BlastSearch&LINK_LOC=blasthome) tool to see what you protein might do. The sequence identity will show how close of a match your protein is the the best match. *Note this can take several minutes |
| 2. Download your protein data and visit the [Protein Data Bank (PDB)](https://www.rcsb.org/) to match your protein structure against known protein structures. |
| 3. If you think you've discovered a new and useful protein message us! |
| |
| |
| **Remember, this folding is based on randomly generated sequences. Interpret the results with caution. |
| Enjoy exploring the world of protein sequences! |
| """) |
|
|