Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for globally-defined terminal modifications #71

Merged
merged 2 commits into from
Mar 18, 2024
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
1 change: 0 additions & 1 deletion psm_utils/io/maxquant.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from psm_utils.io._base_classes import ReaderBase
from psm_utils.peptidoform import Peptidoform
from psm_utils.psm import PSM
from psm_utils.psm_list import PSMList

logger = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion psm_utils/io/tsv.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def __iter__(self):
for row in reader:
try:
yield PSM(**self._parse_entry(row))
except ValidationError as e:
except ValidationError:
logger.warning("Could not parse PSM from row: `{row}`")
continue

Expand Down
29 changes: 21 additions & 8 deletions psm_utils/peptidoform.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from collections import defaultdict
from typing import Iterable, List, Tuple, Union

import numpy as np
Expand All @@ -14,7 +15,7 @@ class Peptidoform:
Peptide sequence, modifications and charge state represented in ProForma notation.
"""

def __init__(self, proforma_sequence: [str, proforma.ProForma]) -> None:
def __init__(self, proforma_sequence: Union[str, proforma.ProForma]) -> None:
"""
Peptide sequence, modifications and charge state represented in ProForma notation.

Expand Down Expand Up @@ -454,7 +455,7 @@ def add_fixed_modifications(

See also
--------
psm_utils.peptidoform.Peptidoform.add_fixed_modifications
psm_utils.peptidoform.Peptidoform.apply_fixed_modifications

Examples
--------
Expand All @@ -463,6 +464,14 @@ def add_fixed_modifications(
>>> peptidoform.proforma
'<[Carbamidomethyl]@C>ATPEILTCNSIGCLK'

Notes
-----
While globally defined terminal modifications are not explicitly supported in ProForma v2,
this function supports adding terminal modifications using the ``N-term`` and ``C-term``
targets in place of an amino acid target. These global modifications are supported in the
:py:meth:`psm_utils.peptidoform.Peptidoform.apply_fixed_modifications` method through a
workaround. See https://github.com/HUPO-PSI/ProForma/issues/6 for discussions on the issue.

"""
if isinstance(modification_rules, dict):
modification_rules = modification_rules.items()
Expand Down Expand Up @@ -497,13 +506,10 @@ def apply_fixed_modifications(self):
"""
if self.properties["fixed_modifications"]:
# Setup target_aa -> modification_list dictionary
rule_dict = {}
rule_dict = defaultdict(list)
for rule in self.properties["fixed_modifications"]:
for target_aa in rule.targets:
try:
rule_dict[target_aa].append(rule.modification_tag)
except KeyError:
rule_dict[target_aa] = [rule.modification_tag]
rule_dict[target_aa].append(rule.modification_tag)

# Apply modifications to sequence
for i, (aa, site_mods) in enumerate(self.parsed_sequence):
Expand All @@ -513,6 +519,14 @@ def apply_fixed_modifications(self):
else:
self.parsed_sequence[i] = (aa, rule_dict[aa])

# Apply terminal modifications
for term, term_name in [("n_term", "N-term"), ("c_term", "C-term")]:
if term_name in rule_dict:
if self.properties[term]:
self.properties[term].extend(rule_dict[term_name])
else:
self.properties[term] = rule_dict[term_name]

# Remove fixed modifications
self.properties["fixed_modifications"] = []

Expand All @@ -526,7 +540,6 @@ def format_number_as_string(num):
return plus + num



class PeptidoformException(PSMUtilsException):
"""Error while handling :py:class:`Peptidoform`."""

Expand Down
14 changes: 14 additions & 0 deletions tests/test_peptidoform.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ def test_rename_modifications(self):
peptidoform.rename_modifications(label_mapping)
assert peptidoform.proforma == expected_out

def test_add_apply_fixed_modifications(self):
test_cases = [
("ACDEK", [("Cmm", ["C"])], "AC[Cmm]DEK"),
("AC[Cmm]DEK", [("SecondMod", ["C"])], "AC[Cmm][SecondMod]DEK"),
("ACDEK", [("TMT6plex", ["K", "N-term"])], "[TMT6plex]-ACDEK[TMT6plex]"),
("ACDEK-[CT]", [("TMT6plex", ["K", "N-term"])], "[TMT6plex]-ACDEK[TMT6plex]-[CT]"),
]

for test_case_in, fixed_modifications, expected_out in test_cases:
peptidoform = Peptidoform(test_case_in)
peptidoform.add_fixed_modifications(fixed_modifications)
peptidoform.apply_fixed_modifications()
assert peptidoform.proforma == expected_out


def test_format_number_as_string():
test_cases = [
Expand Down
Loading