Skip to content

Commit 3bc48a5

Browse files
Hackathon (#269)
* digestor * add calculator * fix emoji and add src file * min max size * renamed mass calculator * replace with spinbox * fix minor bugs * fix page name * rename of isotope generator * add formula and peptide pattern generator * add oligos * just RNA * fragmentation added * better emojis * more doc * docs * proper support for encodings * fix * slimmer * complete m/z calculator * fix sequence format * max charge * add length column * add start and end position columns to peptide digest output * add coloring * fix * fix lint * add first prefix ions --------- Co-authored-by: Tom David Mueller <tom.mueller@beachouse.de>
1 parent 3c76e97 commit 3bc48a5

10 files changed

Lines changed: 2620 additions & 1 deletion

app.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
st.Page(Path("content", "quickstart.py"), title="Quickstart", icon="👋"),
1515
st.Page(Path("content", "documentation.py"), title="Documentation", icon="📖"),
1616
],
17+
"pyOpenMS Toolbox": [
18+
st.Page(Path("content", "digest.py"), title="In Silico Digest", icon="✂️"),
19+
st.Page(Path("content", "peptide_mz_calculator.py"), title="m/z Calculator", icon="⚖️"),
20+
st.Page(Path("content", "isotope_pattern_generator.py"), title="Isotopic Pattern Calculator", icon="📶"),
21+
st.Page(Path("content", "fragmentation.py"), title="Fragment Ion Generation", icon="💥"),
22+
],
1723
"TOPP Workflow Framework": [
1824
st.Page(Path("content", "topp_workflow_file_upload.py"), title="File Upload", icon="📁"),
1925
st.Page(Path("content", "topp_workflow_parameter.py"), title="Configure", icon="⚙️"),
@@ -33,4 +39,4 @@
3339
}
3440

3541
pg = st.navigation(pages)
36-
pg.run()
42+
pg.run()

content/digest.py

Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
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

Comments
 (0)