Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions src/WorkflowTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import pandas as pd
import plotly.express as px
from streamlit_plotly_events import plotly_events
from pyopenms import IdXMLFile
from scipy.stats import ttest_ind
import numpy as np
import mygene
Expand All @@ -14,7 +13,7 @@
from src.workflow.WorkflowManager import WorkflowManager
from src.common.common import page_setup
from src.common.results_helpers import get_abundance_data
from src.common.results_helpers import parse_idxml, build_spectra_cache
from src.common.results_helpers import parse_idxml, build_spectra_cache, load_idxml
from openms_insight import Table, Heatmap, LinePlot, SequenceView

# params = page_setup()
Expand Down Expand Up @@ -1636,9 +1635,7 @@ def results(self) -> None:
selected_file = st.selectbox("📁 Select Identification result file", comet_files)

def idxml_to_df(idxml_file):
proteins = []
peptides = []
IdXMLFile().load(str(idxml_file), proteins, peptides)
proteins, peptides = load_idxml(idxml_file)

records = []
for pep in peptides:
Expand Down
29 changes: 23 additions & 6 deletions src/common/results_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,37 @@
import streamlit as st
from pathlib import Path
from pyopenms import IdXMLFile, MSExperiment, MzMLFile
try: # pyOpenMS >= 3.5.0
from pyopenms import PeptideIdentificationList
except ImportError: # pyOpenMS <= 3.4.x
PeptideIdentificationList = None
from src.workflow.ParameterManager import ParameterManager

def get_workflow_dir(workspace):
"""Get the workflow directory path."""
return Path(workspace, "topp-workflow")


def idxml_to_df(idxml_file):
"""Parse idXML file and return DataFrame with peptide hits."""
def load_idxml(idxml_file):
"""Load an idXML file, returning ``(protein_ids, peptide_ids)`` as plain lists.

pyOpenMS 3.5.0 changed the third parameter of ``IdXMLFile.load()`` from a
``libcpp_vector[PeptideIdentification]``, which accepted an ordinary Python
list, to a dedicated ``PeptideIdentificationList`` container. A list no longer
matches any overload there and pyOpenMS raises
``Exception: can not handle type of (<path>, [], [])``. Feature-detect the
container so the app runs on 3.5.0 as well as earlier releases, and hand
callers back an ordinary list either way.
"""
proteins = []
peptides = []
peptides = PeptideIdentificationList() if PeptideIdentificationList else []
IdXMLFile().load(str(idxml_file), proteins, peptides)
return proteins, list(peptides)


def idxml_to_df(idxml_file):
"""Parse idXML file and return DataFrame with peptide hits."""
proteins, peptides = load_idxml(idxml_file)

records = []
for pep in peptides:
Expand Down Expand Up @@ -99,9 +118,7 @@ def parse_idxml(idxml_path: Path) -> tuple[pl.DataFrame, list[str]]:
Returns:
Tuple of (id_df, spectra_data list of source filenames)
"""
proteins = []
peptides = []
IdXMLFile().load(str(idxml_path), proteins, peptides)
proteins, peptides = load_idxml(idxml_path)

# Derive mzML filename from idXML filename (e.g., 02COVID_filter.idXML -> 02COVID.mzML)
spectra_data = [extract_filename_from_idxml(idxml_path)]
Expand Down
73 changes: 73 additions & 0 deletions tests/test_results_helpers.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from pathlib import Path

import pyopenms as poms

from src.common.results_helpers import (
extract_filename_from_idxml,
extract_scan_from_ref,
extract_scan_number,
get_workflow_dir,
load_idxml,
)


Expand All @@ -25,3 +28,73 @@ def test_extract_filename_from_idxml_strips_suffixes():
assert extract_filename_from_idxml(Path("02COVID_filter.idXML")) == "02COVID.mzML"
assert extract_filename_from_idxml(Path("sample_comet.idXML")) == "sample.mzML"
assert extract_filename_from_idxml(Path("run_per.idXML")) == "run.mzML"


def _write_idxml(path):
"""Write a small idXML with one protein and two peptide hits."""
prot = poms.ProteinIdentification()
prot.setIdentifier("SEARCH_1")
hit = poms.ProteinHit()
hit.setAccession("sp|P12345|TEST")
prot.setHits([hit])

peptides = []
for i, seq in enumerate(("PEPTIDEK", "ELVISLIVESR")):
pep = poms.PeptideIdentification()
pep.setIdentifier("SEARCH_1")
pep.setRT(100.0 + i)
pep.setMZ(500.0 + i)
pep.setMetaValue("spectrum_reference", f"scan={i + 1}")
pep_hit = poms.PeptideHit()
pep_hit.setSequence(poms.AASequence.fromString(seq))
pep_hit.setCharge(2)
evidence = poms.PeptideEvidence()
evidence.setProteinAccession("sp|P12345|TEST")
pep_hit.setPeptideEvidences([evidence])
pep.setHits([pep_hit])
peptides.append(pep)

# pyOpenMS >= 3.5.0 wants the dedicated container here too.
if hasattr(poms, "PeptideIdentificationList"):
container = poms.PeptideIdentificationList()
for pep in peptides:
container.push_back(pep)
peptides = container

poms.IdXMLFile().store(str(path), [prot], peptides)


def test_load_idxml_reads_identifications(tmp_path):
"""Regression test for pyOpenMS 3.5.0.

3.5.0 changed the third parameter of ``IdXMLFile.load()`` from a plain
``libcpp_vector[PeptideIdentification]`` to a ``PeptideIdentificationList``,
so passing ``[]`` raised ``can not handle type of (<path>, [], [])``.
"""
idxml = tmp_path / "sample_comet.idXML"
_write_idxml(idxml)

proteins, peptides = load_idxml(idxml)

# A plain list either way, so callers can index and len() it.
assert isinstance(peptides, list)
assert isinstance(proteins, list)
assert len(proteins) == 1
assert len(peptides) == 2

sequences = [
hit.getSequence().toString()
for pep in peptides
for hit in pep.getHits()
]
assert sequences == ["PEPTIDEK", "ELVISLIVESR"]
assert peptides[0].getRT() == 100.0
assert peptides[0].getHits()[0].getCharge() == 2


def test_load_idxml_accepts_str_path(tmp_path):
"""Callers pass both Path and str; load_idxml must handle either."""
idxml = tmp_path / "run_per.idXML"
_write_idxml(idxml)

assert len(load_idxml(str(idxml))[1]) == 2
Loading