@@ -62,8 +62,11 @@ def configure(self) -> None:
6262 if self .params .get ("generate-decoys" , True ):
6363 st .info ("""
6464 **Decoy Database Settings:**
65- * **decoy_string**: Prefix/suffix for decoy protein accessions
66- * **method**: Method for generating decoys (reverse, shuffle)
65+ * **method**: How decoy sequences are generated from target protein sequences.
66+ *Reverse* creates decoys by reversing each sequence, while *shuffle* randomly
67+ rearranges the amino acids. Both methods preserve the amino acid composition
68+ of the original protein, ensuring decoys have similar properties to real sequences
69+ for accurate false discovery rate (FDR) estimation.
6770 """ )
6871 self .ui .input_TOPP (
6972 "DecoyDatabase" ,
@@ -72,7 +75,7 @@ def configure(self) -> None:
7275 "decoy_string_position" : "prefix" ,
7376 "method" : "reverse" ,
7477 },
75- include_parameters = ["decoy_string" , " method" ],
78+ include_parameters = ["method" ],
7679 )
7780
7881 comet_info = """
@@ -234,7 +237,7 @@ def configure(self) -> None:
234237 # Store in session_state for results section compatibility
235238 st .session_state ["mzML_groups" ] = group_map
236239
237- def execution (self ) -> None :
240+ def execution (self ) -> bool :
238241 """
239242 Refactored TOPP workflow execution:
240243 - Per-sample: CometAdapter -> PercolatorAdapter -> IDFilter
@@ -245,37 +248,44 @@ def execution(self) -> None:
245248 # ================================
246249 if not self .params .get ("mzML-files" ):
247250 st .error ("No mzML files selected." )
248- return
251+ return False
249252
250253 if not self .params .get ("fasta-file" ):
251254 st .error ("No FASTA file selected." )
252- return
255+ return False
253256
254257 in_mzML = self .file_manager .get_files (self .params ["mzML-files" ])
255258 fasta_file = self .file_manager .get_files ([self .params ["fasta-file" ]])[0 ]
256259
257260 if len (in_mzML ) < 1 :
258261 st .error ("At least one mzML file is required." )
259- return
262+ return False
260263
261264 fasta_path = Path (fasta_file )
262265
266+ self .logger .log (f"📂 Loaded { len (in_mzML )} sample(s)" )
267+
263268 if self .params .get ("generate-decoys" , True ):
264269 decoy_fasta = fasta_path .with_suffix (".decoy.fasta" )
265270 # Get decoy_string from DecoyDatabase params
266271 decoy_string = self .params .get ("DecoyDatabase" , {}).get ("decoy_string" , "rev_" )
267272
268273 if not decoy_fasta .exists ():
274+ self .logger .log ("🧬 Generating decoy database..." )
269275 st .info ("Generating decoy FASTA database..." )
270- self .executor .run_topp (
276+ if not self .executor .run_topp (
271277 "DecoyDatabase" ,
272278 {"in" : [str (fasta_path )], "out" : [str (decoy_fasta )]},
273- )
279+ ):
280+ self .logger .log ("Workflow stopped due to error" )
281+ return False
282+ self .logger .log ("✅ Decoy database ready" )
274283 st .success (f"Using decoy FASTA: { decoy_fasta .name } " )
275284 database_fasta = decoy_fasta
276285 else :
277286 # Get decoy_string from CometAdapter params
278287 decoy_string = self .params .get ("CometAdapter" , {}).get ("PeptideIndexing:decoy_string" , "rev_" )
288+ self .logger .log ("📄 Using existing FASTA database" )
279289 st .info (f"Using original FASTA: { fasta_path .name } " )
280290 database_fasta = fasta_path
281291
@@ -293,6 +303,8 @@ def execution(self) -> None:
293303 for d in [comet_dir , perc_dir , filter_dir , quant_dir ]:
294304 d .mkdir (parents = True , exist_ok = True )
295305
306+ self .logger .log ("📁 Output directories created" )
307+
296308 # ================================
297309 # 2️⃣ File path definitions (per sample)
298310 # ================================
@@ -313,50 +325,64 @@ def execution(self) -> None:
313325 stem = Path (mz ).stem
314326 st .info (f"Processing sample: { stem } " )
315327
328+ self .logger .log ("🔬 Starting per-sample processing..." )
329+
316330 # --- CometAdapter ---
331+ self .logger .log ("🔎 Running peptide search..." )
317332 with st .spinner (f"CometAdapter ({ stem } )" ):
318333 comet_extra_params = {"database" : str (database_fasta )}
319334 if self .params .get ("generate-decoys" , True ):
320335 # Propagate decoy_string from DecoyDatabase
321336 comet_extra_params ["PeptideIndexing:decoy_string" ] = decoy_string
322337
323- self .executor .run_topp (
338+ if not self .executor .run_topp (
324339 "CometAdapter" ,
325340 {
326341 "in" : in_mzML ,
327342 "out" : comet_results ,
328343 },
329344 comet_extra_params ,
330- )
345+ ):
346+ self .logger .log ("Workflow stopped due to error" )
347+ return False
348+ self .logger .log ("✅ Peptide search complete" )
331349
332350 # if not Path(comet_results).exists():
333351 # st.error(f"CometAdapter failed for {stem}")
334352 # st.stop()
335353
336354 # --- PercolatorAdapter ---
355+ self .logger .log ("📊 Running rescoring..." )
337356 with st .spinner (f"PercolatorAdapter ({ stem } )" ):
338- self .executor .run_topp (
357+ if not self .executor .run_topp (
339358 "PercolatorAdapter" ,
340359 {
341360 "in" : comet_results ,
342361 "out" : percolator_results ,
343362 },
344363 {"decoy_pattern" : decoy_string }, # Always propagated from upstream
345- )
346-
364+ ):
365+ self .logger .log ("Workflow stopped due to error" )
366+ return False
367+ self .logger .log ("✅ Rescoring complete" )
368+
347369 # if not Path(percolator_results[i]).exists():
348370 # st.error(f"PercolatorAdapter failed for {stem}")
349371 # st.stop()
350372
351373 # --- IDFilter ---
374+ self .logger .log ("🔧 Filtering identifications..." )
352375 with st .spinner (f"IDFilter ({ stem } )" ):
353- self .executor .run_topp (
376+ if not self .executor .run_topp (
354377 "IDFilter" ,
355378 {
356379 "in" : percolator_results ,
357380 "out" : filter_results ,
358381 },
359- )
382+ ):
383+ self .logger .log ("Workflow stopped due to error" )
384+ return False
385+ self .logger .log ("✅ Filtering complete" )
360386
361387 # if not Path(filter_results[i]).exists():
362388 # st.error(f"IDFilter failed for {stem}")
@@ -367,6 +393,7 @@ def execution(self) -> None:
367393 # # ================================
368394 # # 4️⃣ ProteomicsLFQ (cross-sample)
369395 # # ================================
396+ self .logger .log ("📈 Running cross-sample quantification..." )
370397 st .info ("Running ProteomicsLFQ (cross-sample quantification)" )
371398
372399 quant_mztab = str (quant_dir / "openms_quant.mzTab" )
@@ -389,7 +416,7 @@ def execution(self) -> None:
389416 st .write ("**combined_ids:**" , combined_ids )
390417 st .write ("**combined_ids type:**" , type (combined_ids ).__name__ )
391418
392- self .executor .run_topp (
419+ if not self .executor .run_topp (
393420 "ProteomicsLFQ" ,
394421 {
395422 "in" : [in_mzML ],
@@ -407,7 +434,10 @@ def execution(self) -> None:
407434 "PeptideQuantification:extract:IM_window" : "0.0" ,
408435 "PeptideQuantification:faims:merge_features" : "false" ,
409436 }
410- )
437+ ):
438+ self .logger .log ("Workflow stopped due to error" )
439+ return False
440+ self .logger .log ("✅ Quantification complete" )
411441
412442 # if not Path(quant_mztab).exists():
413443 # st.error("ProteomicsLFQ failed: mzTab not created")
@@ -427,6 +457,8 @@ def execution(self) -> None:
427457 st .write (f"- consensusXML: { quant_cxml } " )
428458 st .write (f"- MSstats CSV: { quant_msstats } " )
429459
460+ return True
461+
430462 @st .fragment
431463 def results (self ) -> None :
432464
0 commit comments