File size: 8,902 Bytes
854a59f
3c437e4
f8517eb
 
e8fdfbc
 
 
 
651cc8d
 
 
 
f3feb5a
 
e8fdfbc
 
854a59f
e8fdfbc
854a59f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f8517eb
 
 
 
 
 
 
e8fdfbc
3c437e4
 
 
e8fdfbc
 
 
 
3c437e4
 
651cc8d
5708f34
b2acfb0
651cc8d
 
 
5708f34
651cc8d
 
 
 
 
 
 
 
 
9512b53
651cc8d
9512b53
 
 
 
651cc8d
9512b53
 
651cc8d
9512b53
b2acfb0
9512b53
b2acfb0
9512b53
651cc8d
9512b53
854a59f
9512b53
651cc8d
9512b53
 
af9e04c
e8fdfbc
 
 
 
 
 
 
 
3af0361
 
 
e8fdfbc
 
 
 
 
 
 
 
9512b53
 
 
 
 
 
 
 
e8fdfbc
af9e04c
3af0361
e8fdfbc
 
 
f8517eb
3c437e4
854a59f
3c437e4
 
 
af9e04c
 
 
 
 
9512b53
 
af9e04c
854a59f
 
 
 
 
 
 
 
 
f8517eb
854a59f
f8517eb
854a59f
f8517eb
e8fdfbc
af9e04c
854a59f
 
e8fdfbc
f8517eb
e8fdfbc
f8517eb
854a59f
af9e04c
9512b53
f3feb5a
 
 
8a5ab71
f3feb5a
b2acfb0
854a59f
f3feb5a
854a59f
91d9111
 
f3feb5a
91d9111
854a59f
91d9111
 
fa7774c
 
91d9111
 
 
 
 
 
 
 
fa7774c
 
 
 
 
 
 
b2acfb0
 
91d9111
fa7774c
91d9111
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
 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)

# Set page config and apply dark theme
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)  # Simulate analysis time
        
        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]  # Get the top hit
                hsp = alignment.hsps[0]  # Get the first (best) HSP
                
                # Extract protein name and organism
                title_parts = alignment.title.split('|')
                protein_name = title_parts[-1].strip()
                organism = title_parts[-2].split('OS=')[-1].split('OX=')[0].strip()
                
                # Calculate identity percentage
                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}%")
               
                # Fetch protein function (if available)
                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

# Initialize session state variables
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

# Main layout
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.")

# Display structure information if available
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!
    """)