1+ """
2+ In Silico Protein Digest Page
3+
4+ This module provides functionality for performing in silico protein digestion
5+ using pyOpenMS. Users can input protein sequences in FASTA format and get
6+ peptide lists with mass calculations.
7+ """
8+
9+ import streamlit as st
10+ import sys
11+ from pathlib import Path
12+
13+ # Add utils to path
14+ sys .path .append (str (Path (__file__ ).parent .parent ))
15+
16+ from utils .fasta import validate_fasta_input
17+ from utils .digest import perform_digest , get_digest_statistics , get_available_enzymes , filter_peptides_by_length , calculate_protein_coverage , generate_coverage_html
18+
19+ # Default values
20+ DEFAULT_ENZYME = "Trypsin"
21+ DEFAULT_MISSED_CLEAVAGES = 0 # Changed from 2 to 0
22+ DEFAULT_MAX_CHARGES = 5
23+ DEFAULT_MIN_PEPTIDE_LENGTH = 6
24+ DEFAULT_MAX_PEPTIDE_LENGTH = 50
25+
26+
27+ def main ():
28+ """Main function for the digest page."""
29+ st .title ("✂️ In Silico Protein Digest" )
30+
31+ st .markdown ("""
32+ **Simulate enzymatic protein digestion computationally** to predict peptides for mass spectrometry analysis.
33+
34+ This tool uses pyOpenMS to perform theoretical protein digestion with various proteases, helping you:
35+ - **Plan MS experiments** by predicting which peptides will be generated
36+ - **Optimize digestion conditions** by testing different enzymes and parameters
37+ - **Analyze protein coverage** and identify potential issues before experimental work
38+ - **Generate theoretical peptide lists** with accurate mass-to-charge ratios
39+ """ )
40+
41+ with st .expander ("📚 **How In Silico Digestion Works**" ):
42+ st .markdown ("""
43+ **Enzymatic Cleavage Simulation:**
44+ - Enzymes cut proteins at specific amino acid sequences (cleavage sites)
45+ - **Trypsin** cuts after K (lysine) and R (arginine), except when followed by P (proline)
46+ - **Pepsin** cuts preferentially at F, L, W, Y amino acids under acidic conditions
47+ - **Chymotrypsin** cuts after F, W, Y, L amino acids
48+
49+ **Missed Cleavages:**
50+ - Real digestion is not 100% efficient - some cleavage sites are missed
51+ - Allows prediction of longer peptides that contain uncleaved sites
52+ - Important for comprehensive coverage analysis
53+
54+ **Applications:**
55+ - **Bottom-up proteomics** experiment planning
56+ - **Peptide mapping** for protein characterization
57+ - **Method development** for LC-MS/MS workflows
58+ - **Quality control** for digestion efficiency assessment
59+ """ )
60+
61+ # Input form section
62+ with st .form ("digest_form" ):
63+ st .subheader ("Input Parameters" )
64+
65+ # FASTA input
66+ default_sequence = """>sp|Q9UPY3|DICER_HUMAN Endoribonuclease Dicer OS=Homo sapiens OX=9606 GN=DICER1 PE=1 SV=3
67+ MKSPALQPLSMAGLQLMTPASSPMGPFFGLPWQQEAIHDNIYTPRKYQVELLEAALDHNT
68+ IVCLNTGSGKTFIAVLLTKELSYQIRGDFSRNGKRTVFLVNSANQVAQQVSAVRTHSDLK
69+ VGEYSNLEVNASWTKERWNQEFTKHQVLIMTCYVALNVLKNGYLSLSDINLLVFDECHLA
70+ ILDHPYREIMKLCENCPSCPRILGLTASILNGKCDPEELEEKIQKLEKILKSNAETATDL
71+ VVLDRYTSQPCEIVVDCGPFTDRSGLYERLLMELEEALNFINDCNISVHSKERDSTLISK
72+ QILSDCRAVLVVLGPWCADKVAGMMVRELQKYIKHEQEELHRKFLLFTDTFLRKIHALCE
73+ EHFSPASLDLKFVTPKVIKLLEILRKYKPYERQQFESVEWYNNRNQDNYVSWSDSEDDDE
74+ DEEIEEKEKPETNFPSPFTNILCGIIFVERRYTAVVLNRLIKEAGKQDPELAYISSNFIT
75+ GHGIGKNQPRNKQMEAEFRKQEEVLRKFRAHETNLLIATSIVEEGVDIPKCNLVVRFDLP
76+ TEYRSYVQSKGRARAPISNYIMLADTDKIKSFEEDLKTYKAIEKILRNKCSKSVDTGETD
77+ IDPVMDDDDVFPPYVLRPDDGGPRVTINTAIGHINRYCARLPSDPFTHLAPKCRTRELPD
78+ GTFYSTLYLPINSPLRASIVGPPMSCVRLAERVVALICCEKLHKIGELDDHLMPVGKETV
79+ KYEEELDLHDEEETSVPGRPGSTKRRQCYPKAIPECLRDSYPRPDQPCYLYVIGMVLTTP
80+ LPDELNFRRRKLYPPEDTTRCFGILTAKPIPQIPHFPVYTRSGEVTISIELKKSGFMLSL
81+ QMLELITRLHQYIFSHILRLEKPALEFKPTDADSAYCVLPLNVVNDSSTLDIDFKFMEDI
82+ EKSEARIGIPSTKYTKETPFVFKLEDYQDAVIIPRYRNFDQPHRFYVADVYTDLTPLSKF
83+ PSPEYETFAEYYKTKYNLDLTNLNQPLLDVDHTSSRLNLLTPRHLNQKGKALPLSSAEKR
84+ KAKWESLQNKQILVPELCAIHPIPASLWRKAVCLPSILYRLHCLLTAEELRAQTASDAGV
85+ GVRSLPADFRYPNLDFGWKKSIDSKSFISISNSSSAENDNYCKHSTIVPENAAHQGANRT
86+ SSLENHDQMSVNCRTLLSESPGKLHVEVSADLTAINGLSYNQNLANGSYDLANRDFCQGN
87+ QLNYYKQEIPVQPTTSYSIQNLYSYENQPQPSDECTLLSNKYLDGNANKSTSDGSPVMAV
88+ MPGTTDTIQVLKGRMDSEQSPSIGYSSRTLGPNPGLILQALTLSNASDGFNLERLEMLGD
89+ SFLKHAITTYLFCTYPDAHEGRLSYMRSKKVSNCNLYRLGKKKGLPSRMVVSIFDPPVNW
90+ LPPGYVVNQDKSNTDKWEKDEMTKDCMLANGKLDEDYEEEDEEEESLMWRAPKEEADYED
91+ DFLEYDQEHIRFIDNMLMGSGAFVKKISLSPFSTTDSAYEWKMPKKSSLGSMPFSSFDED
92+ FDYSSWDAMCYLDPSKAVEEDDFVVGFWNPSEENCGVDTGKQSISYDLHTEQCIADKSIA
93+ DCVEALLGCYLTSCGERAAQLFLCSLGLKVLPVIKRTDREKALCPTRENFNSQQKNLSVS
94+ CAAASVASSRSSVLKDSEYGCLKIPPRCMFDHPDADKTLNHLISGFENFEKKINYRFKNK
95+ AYLLQAFTHASYHYNTITDCYQRLEFLGDAILDYLITKHLYEDPRQHSPGVLTDLRSALV
96+ NNTIFASLAVKYDYHKYFKAVSPELFHVIDDFVQFQLEKNEMQGMDSELRRSEEDEEKEE
97+ DIEVPKAMGDIFESLAGAIYMDSGMSLETVWQVYYPMMRPLIEKFSANVPRSPVRELLEM
98+ EPETAKFSPAERTYDGKVRVTVEVVGKGKFKGVGRSYRIAKSAAARRALRSLKANQPQVP
99+ NS"""
100+
101+ fasta_input = st .text_area (
102+ "Paste protein sequences in FASTA format" ,
103+ value = default_sequence ,
104+ height = 200 ,
105+ help = "Default sequence: DICER_HUMAN protein for demonstration"
106+ )
107+
108+ # Get available enzymes
109+ try :
110+ available_enzymes = get_available_enzymes ()
111+ # convert bytes to str if necessary
112+ available_enzymes = [enzyme .decode () if isinstance (enzyme , bytes ) else enzyme for enzyme in available_enzymes ]
113+
114+ except Exception as e :
115+ st .error (f"❌ Cannot load enzyme database: { e } " )
116+ st .error ("Please ensure pyOpenMS is properly configured before using the digest functionality." )
117+ st .stop ()
118+
119+ # Enzyme selection
120+ enzyme_index = 0
121+ if DEFAULT_ENZYME in available_enzymes :
122+ enzyme_index = available_enzymes .index (DEFAULT_ENZYME )
123+
124+ enzyme = st .selectbox (
125+ "Enzyme" ,
126+ options = available_enzymes ,
127+ index = enzyme_index ,
128+ help = "Select the enzyme for protein digestion"
129+ )
130+
131+ # Parameters
132+ col1 , col2 = st .columns (2 )
133+
134+ with col1 :
135+ missed_cleavages = st .number_input (
136+ "Max missed cleavages" ,
137+ min_value = 0 ,
138+ max_value = 10 ,
139+ value = DEFAULT_MISSED_CLEAVAGES ,
140+ help = "Maximum number of missed cleavages allowed"
141+ )
142+
143+ with col2 :
144+ max_charges = st .number_input (
145+ "Max charge state (N)" ,
146+ min_value = 1 ,
147+ max_value = 10 ,
148+ value = DEFAULT_MAX_CHARGES ,
149+ help = "Maximum charge state to calculate [M + nH]"
150+ )
151+
152+ # Peptide length filtering
153+ st .subheader ("Peptide Length Filtering" )
154+ col3 , col4 = st .columns (2 )
155+
156+ with col3 :
157+ min_peptide_length = st .number_input (
158+ "Min peptide length (AA)" ,
159+ min_value = 1 ,
160+ max_value = 100 ,
161+ value = DEFAULT_MIN_PEPTIDE_LENGTH ,
162+ help = "Minimum peptide length in amino acids"
163+ )
164+
165+ with col4 :
166+ max_peptide_length = st .number_input (
167+ "Max peptide length (AA)" ,
168+ min_value = 1 ,
169+ max_value = 200 ,
170+ value = DEFAULT_MAX_PEPTIDE_LENGTH ,
171+ help = "Maximum peptide length in amino acids"
172+ )
173+
174+ # Submit button
175+ submit = st .form_submit_button ("🧬 Digest Proteins" , type = "primary" )
176+
177+ # Process form submission
178+ if submit :
179+ if not fasta_input .strip ():
180+ st .error ("❌ Please provide FASTA sequences to digest." )
181+ return
182+
183+ # Show progress
184+ with st .spinner ("🔬 Performing in silico digest..." ):
185+ # Validate FASTA input
186+ is_valid , error_message , sequences = validate_fasta_input (fasta_input )
187+
188+ if not is_valid :
189+ st .error (f"❌ FASTA validation failed: { error_message } " )
190+ return
191+
192+ if not sequences :
193+ st .error ("❌ No valid sequences found in the input." )
194+ return
195+
196+ # Show input summary
197+ st .success (f"✅ Successfully parsed { len (sequences )} protein sequence(s)" )
198+
199+ # Progress bar
200+ progress_bar = st .progress (0 , text = "Initializing digest..." )
201+
202+ try :
203+ # Perform digest
204+ progress_bar .progress (30 , text = "Performing enzymatic digest..." )
205+
206+ df_results = perform_digest (
207+ sequences = sequences ,
208+ enzyme = enzyme ,
209+ missed_cleavages = missed_cleavages ,
210+ max_charges = max_charges
211+ )
212+
213+ progress_bar .progress (60 , text = "Applying peptide length filters..." )
214+
215+ # Apply peptide length filtering
216+ df_results = filter_peptides_by_length (
217+ df_results ,
218+ min_length = min_peptide_length ,
219+ max_length = max_peptide_length
220+ )
221+
222+ progress_bar .progress (80 , text = "Processing results..." )
223+
224+ if df_results .empty :
225+ st .warning ("⚠️ No peptides were generated from the digest or all peptides were filtered out. Try adjusting the parameters or check your input sequences." )
226+ progress_bar .empty ()
227+ return
228+
229+ progress_bar .progress (100 , text = "Complete!" )
230+ progress_bar .empty ()
231+
232+ # Display results
233+ st .subheader ("📊 Digest Results" )
234+
235+ # Summary statistics
236+ stats = get_digest_statistics (df_results )
237+
238+ col1 , col2 , col3 , col4 = st .columns (4 )
239+ with col1 :
240+ st .metric ("Total Peptides" , f"{ stats ['total_peptides' ]:,} " )
241+ with col2 :
242+ st .metric ("Unique Proteins" , stats ['unique_proteins' ])
243+ with col3 :
244+ st .metric ("Avg Length" , f"{ stats ['avg_peptide_length' ]:.1f} AA" )
245+ with col4 :
246+ st .metric ("Mass Range" , f"{ stats ['mass_range' ][0 ]:.0f} -{ stats ['mass_range' ][1 ]:.0f} Da" )
247+
248+ # Results table
249+ st .dataframe (
250+ df_results ,
251+ use_container_width = True ,
252+ hide_index = True ,
253+ column_config = {
254+ "Accession" : st .column_config .TextColumn ("Accession" , width = "small" ),
255+ "Description" : st .column_config .TextColumn ("Description" , width = "large" ),
256+ "Peptide Sequence" : st .column_config .TextColumn ("Peptide Sequence" , width = "medium" ),
257+ "Length" : st .column_config .NumberColumn ("Length" , help = "Peptide length in amino acids" ),
258+ "Start" : st .column_config .TextColumn ("Start" , width = "small" , help = "1-based start position(s) in protein sequence" ),
259+ "End" : st .column_config .TextColumn ("End" , width = "small" , help = "1-based end position(s) in protein sequence" ),
260+ "[M]" : st .column_config .NumberColumn ("[M]" , format = "%.4f" ),
261+ }
262+ )
263+
264+ # Protein Coverage Visualization
265+ st .subheader ("🎨 Protein Coverage Visualization" )
266+ st .markdown ("""
267+ **Sequence Coverage Analysis:** Each amino acid is colored based on how many peptides cover that position.
268+ Hover over amino acids to see exact coverage counts.
269+ """ )
270+
271+ # Calculate coverage for each protein
272+ coverage_data = calculate_protein_coverage (df_results , sequences )
273+
274+ # Display coverage for each protein
275+ for accession , coverage_info in coverage_data .items ():
276+ coverage_html = generate_coverage_html (accession , coverage_info )
277+ st .markdown (coverage_html , unsafe_allow_html = True )
278+
279+ # Download section
280+ st .subheader ("⬇️ Download Results" )
281+
282+ # Generate TSV
283+ tsv_data = df_results .to_csv (sep = "\t " , index = False )
284+
285+ col1 , col2 = st .columns (2 )
286+ with col1 :
287+ st .download_button (
288+ label = "📄 Download as TSV" ,
289+ data = tsv_data ,
290+ file_name = f"digest_results_{ enzyme } _{ missed_cleavages } mc.tsv" ,
291+ mime = "text/tab-separated-values" ,
292+ help = "Download results as tab-separated values file"
293+ )
294+
295+ with col2 :
296+ csv_data = df_results .to_csv (index = False )
297+ st .download_button (
298+ label = "📄 Download as CSV" ,
299+ data = csv_data ,
300+ file_name = f"digest_results_{ enzyme } _{ missed_cleavages } mc.csv" ,
301+ mime = "text/csv" ,
302+ help = "Download results as comma-separated values file"
303+ )
304+
305+ # Additional information
306+ with st .expander ("ℹ️ Digest Parameters Used" ):
307+ st .write (f"**Enzyme:** { enzyme } " )
308+ st .write (f"**Max missed cleavages:** { missed_cleavages } " )
309+ st .write (f"**Max charge states:** { max_charges } " )
310+ st .write (f"**Input sequences:** { len (sequences )} " )
311+
312+ except Exception as e :
313+ progress_bar .empty ()
314+ st .exception (f"❌ An error occurred during digest: { str (e )} " )
315+ st .error ("Please check your input and try again. If the problem persists, try with a simpler enzyme like Trypsin." )
316+
317+
318+ if __name__ == "__main__" :
319+
320+ main ()
321+ main ()
0 commit comments