From 70c3b27130f15c6d291978f161fc7937246887d0 Mon Sep 17 00:00:00 2001 From: Chris Brasnett <35073246+csbrasnett@users.noreply.github.com> Date: Wed, 13 Dec 2023 16:18:48 +0100 Subject: [PATCH 01/22] fixed the system error added run_system to annotate_mut_mod to get rid of undesired errors. Have broken the test. --- vermouth/processors/annotate_mut_mod.py | 46 ++++++++++++++++--------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index 52bdee045..64c2d1c2f 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -210,25 +210,30 @@ def annotate_modifications(molecule, modifications, mutations): (mutations, 'mutation', molecule.force_field.blocks)] residue_graph = make_residue_graph(molecule) + residue = {key: residue_graph.nodes[0].get(key) + for key in 'chain resid resname insertion_code'.split()} + chain = residue['chain'] + for mutmod, key, library in associations: for resspec, mod in mutmod: - mod_found = False - for res_idx in residue_graph: - if residue_matches(resspec, residue_graph, res_idx): - mod_found = True - if mod != 'none' and mod not in library: - raise NameError('{} is not known as a {} for ' - 'force field {}' - ''.format(mod, key, molecule.force_field.name)) - res = residue_graph.nodes[res_idx] - LOGGER.debug('Annotating {} with {} {}', - _format_resname(res), key, mod) - for node_idx in res['graph']: - molecule.nodes[node_idx][key] = molecule.nodes[node_idx].get(key, []) + [mod] - if mod_found == False: - LOGGER.warning('Mutation "{}" not found. ' - 'Check target resid!' - ''.format(_format_resname(resspec))) + if resspec.get('chain') == chain: + mod_found = False + for res_idx in residue_graph: + if residue_matches(resspec, residue_graph, res_idx): + mod_found = True + if mod != 'none' and mod not in library: + raise NameError('{} is not known as a {} for ' + 'force field {}' + ''.format(mod, key, molecule.force_field.name)) + res = residue_graph.nodes[res_idx] + LOGGER.debug('Annotating {} with {} {}', + _format_resname(res), key, mod) + for node_idx in res['graph']: + molecule.nodes[node_idx][key] = molecule.nodes[node_idx].get(key, []) + [mod] + if mod_found == False: + LOGGER.warning('Mutation "{}" not found. ' + 'Check target resid!' + ''.format(_format_resname(resspec))) class AnnotateMutMod(Processor): """ @@ -259,3 +264,10 @@ def __init__(self, modifications=None, mutations=None): def run_molecule(self, molecule): annotate_modifications(molecule, self.modifications, self.mutations) return molecule + def run_system(self, system): + print(self.mutations) + mols = [] + for molecule in system.molecules: + mols.append(self.run_molecule(molecule)) + system.molecules = mols + From d4c886ea42aee3f3598a91f40fa66e2fda77f51d Mon Sep 17 00:00:00 2001 From: Chris Brasnett <35073246+csbrasnett@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:02:44 +0000 Subject: [PATCH 02/22] Fixed edge case and tests if generic mutation is given to target all residues then need to check that the resspec actually contains a chain --- vermouth/processors/annotate_mut_mod.py | 21 +++++++++++++++++++-- vermouth/tests/test_annotate_mut_mod.py | 18 +++++++++--------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index 64c2d1c2f..c12c33098 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -210,13 +210,15 @@ def annotate_modifications(molecule, modifications, mutations): (mutations, 'mutation', molecule.force_field.blocks)] residue_graph = make_residue_graph(molecule) + # Get the name of the chain in the molecule that we're looking at residue = {key: residue_graph.nodes[0].get(key) for key in 'chain resid resname insertion_code'.split()} chain = residue['chain'] for mutmod, key, library in associations: for resspec, mod in mutmod: - if resspec.get('chain') == chain: + # Ie. the target residue is chain specific + if (resspec.get('chain') is not None) and (resspec.get('chain') == chain): mod_found = False for res_idx in residue_graph: if residue_matches(resspec, residue_graph, res_idx): @@ -234,6 +236,22 @@ def annotate_modifications(molecule, modifications, mutations): LOGGER.warning('Mutation "{}" not found. ' 'Check target resid!' ''.format(_format_resname(resspec))) + # If instead we're targeting all residues in the chain + elif resspec.get(chain) == None: + for res_idx in residue_graph: + if residue_matches(resspec, residue_graph, res_idx): + mod_found = True + if mod != 'none' and mod not in library: + raise NameError('{} is not known as a {} for ' + 'force field {}' + ''.format(mod, key, molecule.force_field.name)) + res = residue_graph.nodes[res_idx] + LOGGER.debug('Annotating {} with {} {}', + _format_resname(res), key, mod) + for node_idx in res['graph']: + molecule.nodes[node_idx][key] = molecule.nodes[node_idx].get(key, []) + [mod] + + class AnnotateMutMod(Processor): """ @@ -265,7 +283,6 @@ def run_molecule(self, molecule): annotate_modifications(molecule, self.modifications, self.mutations) return molecule def run_system(self, system): - print(self.mutations) mols = [] for molecule in system.molecules: mols.append(self.run_molecule(molecule)) diff --git a/vermouth/tests/test_annotate_mut_mod.py b/vermouth/tests/test_annotate_mut_mod.py index 3608258be..4be85ec40 100644 --- a/vermouth/tests/test_annotate_mut_mod.py +++ b/vermouth/tests/test_annotate_mut_mod.py @@ -305,18 +305,18 @@ def test_nter_cter_modifications(node_data, edge_data, expected): @pytest.mark.parametrize('node_data, edge_data, expected', [ ( [ - {'resname': 'GLY', 'resid': 1}, - {'resname': 'ALA', 'resid': 2}, - {'resname': 'ALA', 'resid': 3} + {'chain': 'A', 'resname': 'GLY', 'resid': 1}, + {'chain': 'A', 'resname': 'ALA', 'resid': 2}, + {'chain': 'A', 'resname': 'ALA', 'resid': 3} ], [(0, 1), (1, 2)], False ), ( [ - {'resname': 'ALA', 'resid': 1}, - {'resname': 'ALA', 'resid': 2}, - {'resname': 'ALA', 'resid': 3} + {'chain': 'A', 'resname': 'ALA', 'resid': 1}, + {'chain': 'A', 'resname': 'ALA', 'resid': 2}, + {'chain': 'A', 'resname': 'ALA', 'resid': 3} ], [(0, 1), (1, 2)], True @@ -328,12 +328,12 @@ def test_mod_resid_not_correct(caplog, node_data, edge_data, expected): mol = Molecule(force_field=ForceField(FF_UNIVERSAL_TEST)) mol.add_nodes_from(enumerate(node_data)) mol.add_edges_from(edge_data) - mutation = [({'resname': 'GLY', 'resid': 1}, 'MET')] + mutation = [({'resname': 'GLY', 'resid': 1, 'chain': 'A'}, 'MET')] caplog.clear() annotate_modifications(mol, [], mutation) - if expected: - assert '"GLY1" not found.' in str(caplog.records[0].getMessage()) + if expected: + assert '"A-GLY1" not found.' in str(caplog.records[0].getMessage()) else: assert caplog.records == [] From 597f64a56916043f250e33b3f4ee2267f44bc03d Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Fri, 5 Apr 2024 15:59:43 +0200 Subject: [PATCH 03/22] moved loop into its own function --- .gitignore | 2 + vermouth/processors/annotate_mut_mod.py | 50 ++++++++++--------------- 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index c3fe7be3d..38f20f8cc 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ htmlcov doc/build doc/source/api doc/source/.doctrees + +.idea/ diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index c12c33098..2c78e8d4f 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -176,6 +176,22 @@ def _format_resname(res): out += res.get('insertion_code', '') return out +def _resiter(mod, residue_graph, resspec, library, key, molecule): + mod_found = False + for res_idx in residue_graph: + if residue_matches(resspec, residue_graph, res_idx): + mod_found = True + if mod != 'none' and mod not in library: + raise NameError('{} is not known as a {} for ' + 'force field {}' + ''.format(mod, key, molecule.force_field.name)) + res = residue_graph.nodes[res_idx] + LOGGER.debug('Annotating {} with {} {}', + _format_resname(res), key, mod) + for node_idx in res['graph']: + molecule.nodes[node_idx][key] = molecule.nodes[node_idx].get(key, []) + [mod] + return mod_found + def annotate_modifications(molecule, modifications, mutations): """ @@ -219,39 +235,14 @@ def annotate_modifications(molecule, modifications, mutations): for resspec, mod in mutmod: # Ie. the target residue is chain specific if (resspec.get('chain') is not None) and (resspec.get('chain') == chain): - mod_found = False - for res_idx in residue_graph: - if residue_matches(resspec, residue_graph, res_idx): - mod_found = True - if mod != 'none' and mod not in library: - raise NameError('{} is not known as a {} for ' - 'force field {}' - ''.format(mod, key, molecule.force_field.name)) - res = residue_graph.nodes[res_idx] - LOGGER.debug('Annotating {} with {} {}', - _format_resname(res), key, mod) - for node_idx in res['graph']: - molecule.nodes[node_idx][key] = molecule.nodes[node_idx].get(key, []) + [mod] + mod_found = _resiter(mod, residue_graph, resspec, library, key, molecule) if mod_found == False: LOGGER.warning('Mutation "{}" not found. ' 'Check target resid!' ''.format(_format_resname(resspec))) # If instead we're targeting all residues in the chain elif resspec.get(chain) == None: - for res_idx in residue_graph: - if residue_matches(resspec, residue_graph, res_idx): - mod_found = True - if mod != 'none' and mod not in library: - raise NameError('{} is not known as a {} for ' - 'force field {}' - ''.format(mod, key, molecule.force_field.name)) - res = residue_graph.nodes[res_idx] - LOGGER.debug('Annotating {} with {} {}', - _format_resname(res), key, mod) - for node_idx in res['graph']: - molecule.nodes[node_idx][key] = molecule.nodes[node_idx].get(key, []) + [mod] - - + _resiter(mod, residue_graph, resspec, library, key, molecule) class AnnotateMutMod(Processor): """ @@ -283,8 +274,5 @@ def run_molecule(self, molecule): annotate_modifications(molecule, self.modifications, self.mutations) return molecule def run_system(self, system): - mols = [] - for molecule in system.molecules: - mols.append(self.run_molecule(molecule)) - system.molecules = mols + super().run_system(system) From 32b2ad962676c8bdd57e1cd22f15d0ec65be652d Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Fri, 5 Apr 2024 16:00:10 +0200 Subject: [PATCH 04/22] found a new quote --- vermouth/data/quotes.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vermouth/data/quotes.txt b/vermouth/data/quotes.txt index 92ac7f39f..42cd87498 100644 --- a/vermouth/data/quotes.txt +++ b/vermouth/data/quotes.txt @@ -35,3 +35,5 @@ Happiness is a dry martini and a good woman... or a bad woman. -- George Burns A classical Martini is made without Vermouth, although it is better with. -- Peter C Kroon A classical Martini is made with up to 2 sizes of olives, although newer variants can contain up to three sizes of olives. -- Peter C Kroon + +So I said 'I must get out of these wet clothes, and into a dry Martini' -- Homer Simpson From f07e2288730feb8a5b42cdb33ac0404e75b074ed Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Fri, 5 Apr 2024 16:07:53 +0200 Subject: [PATCH 05/22] minor formatting changes --- vermouth/processors/annotate_mut_mod.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index 2c78e8d4f..caab7e042 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -236,12 +236,11 @@ def annotate_modifications(molecule, modifications, mutations): # Ie. the target residue is chain specific if (resspec.get('chain') is not None) and (resspec.get('chain') == chain): mod_found = _resiter(mod, residue_graph, resspec, library, key, molecule) - if mod_found == False: - LOGGER.warning('Mutation "{}" not found. ' - 'Check target resid!' - ''.format(_format_resname(resspec))) - # If instead we're targeting all residues in the chain - elif resspec.get(chain) == None: + if not mod_found: + LOGGER.warning('Residue specified by "{}" for mutation "{}" not found. ', + _format_resname(resspec), mod) + # If instead we're targeting residues in any chain + elif resspec.get(chain) is None: _resiter(mod, residue_graph, resspec, library, key, molecule) class AnnotateMutMod(Processor): From 319ede6e8924f37167d424ac94cf9d41d6409224 Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Fri, 5 Apr 2024 17:24:46 +0200 Subject: [PATCH 06/22] made changes and fixed annotation test --- vermouth/processors/annotate_mut_mod.py | 18 ++++++++++++++++++ vermouth/tests/test_annotate_mut_mod.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index caab7e042..e03aab67e 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -177,6 +177,24 @@ def _format_resname(res): return out def _resiter(mod, residue_graph, resspec, library, key, molecule): + """ + Iterate over residues to find a specific modification + + Parameters + ---------- + mod: str + The modification to apply, eg N-ter, C-ter + residue_graph: networkx.Graph + A graph with one node per residue. + resspec: dict + Attributes that must be present in the residue node. 'resname' is + treated specially as described above. + library: dict + dictionary of modifications/mutations from the force field + key: str + from associations + molecule: networkx.Graph + """ mod_found = False for res_idx in residue_graph: if residue_matches(resspec, residue_graph, res_idx): diff --git a/vermouth/tests/test_annotate_mut_mod.py b/vermouth/tests/test_annotate_mut_mod.py index 4be85ec40..04948e5be 100644 --- a/vermouth/tests/test_annotate_mut_mod.py +++ b/vermouth/tests/test_annotate_mut_mod.py @@ -334,6 +334,6 @@ def test_mod_resid_not_correct(caplog, node_data, edge_data, expected): annotate_modifications(mol, [], mutation) if expected: - assert '"A-GLY1" not found.' in str(caplog.records[0].getMessage()) + assert 'Residue specified by "A-GLY1" for mutation "MET" not found.' in str(caplog.records[0].getMessage()) else: assert caplog.records == [] From 84ba9f55d3de6b5d4fee3c0f17cbcb207e705063 Mon Sep 17 00:00:00 2001 From: Peter Kroon Date: Mon, 8 Apr 2024 11:45:01 +0200 Subject: [PATCH 07/22] Update codecov action --- .github/workflows/deploy.yml | 10 +++++++--- .github/workflows/run_tests.yml | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6dea77172..4d169aa8c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -56,9 +56,13 @@ jobs: coverage report --omit='*/bin/pytest' - if: ${{ matrix.WITH_CODECOV }} - name: Report code coverage - run: | - codecov + name: Upload coverage codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage.xml + fail_ci_if_error: true + verbose: true lint: runs-on: ubuntu-latest diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 72e8c9db4..f5dc54266 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -60,7 +60,7 @@ jobs: - if: ${{ matrix.WITH_CODECOV }} name: Upload coverage codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.xml From bc7e724d2fffbf1f5b16fc442ccccad8da716058 Mon Sep 17 00:00:00 2001 From: Peter Kroon Date: Mon, 8 Apr 2024 11:51:09 +0200 Subject: [PATCH 08/22] Don't fail deploy on minor issues --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4d169aa8c..ec1373c40 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -61,7 +61,7 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.xml - fail_ci_if_error: true + fail_ci_if_error: false verbose: true lint: From 2c681be66fff2e68bfd65ef33763694eb08ce5a9 Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Mon, 8 Apr 2024 14:58:19 +0200 Subject: [PATCH 09/22] added and clarified tests --- vermouth/tests/test_annotate_mut_mod.py | 42 ++++++++++++++++++++----- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/vermouth/tests/test_annotate_mut_mod.py b/vermouth/tests/test_annotate_mut_mod.py index 04948e5be..3325e59a4 100644 --- a/vermouth/tests/test_annotate_mut_mod.py +++ b/vermouth/tests/test_annotate_mut_mod.py @@ -302,7 +302,7 @@ def test_nter_cter_modifications(node_data, edge_data, expected): assert found == expected -@pytest.mark.parametrize('node_data, edge_data, expected', [ +@pytest.mark.parametrize('node_data, edge_data, mutation, expected', [ ( [ {'chain': 'A', 'resname': 'GLY', 'resid': 1}, @@ -310,6 +310,7 @@ def test_nter_cter_modifications(node_data, edge_data, expected): {'chain': 'A', 'resname': 'ALA', 'resid': 3} ], [(0, 1), (1, 2)], + [({'resname': 'GLY', 'resid': 1, 'chain': 'A'}, 'MET')], False ), ( @@ -319,21 +320,48 @@ def test_nter_cter_modifications(node_data, edge_data, expected): {'chain': 'A', 'resname': 'ALA', 'resid': 3} ], [(0, 1), (1, 2)], + [({'resname': 'GLY', 'resid': 1, 'chain': 'A'}, 'MET')], True - )]) -def test_mod_resid_not_correct(caplog, node_data, edge_data, expected): + ), + ( + [ + {'chain': 'A', 'resname': 'ALA', 'resid': 1}, + {'chain': 'A', 'resname': 'ALA', 'resid': 2}, + {'chain': 'A', 'resname': 'ALA', 'resid': 3}, + {'chain': 'B', 'resname': 'ALA', 'resid': 1}, + {'chain': 'B', 'resname': 'ALA', 'resid': 2}, + {'chain': 'B', 'resname': 'ALA', 'resid': 3} + ], + [(0, 1), (1, 2), (3, 4), (4, 5)], + [({'resname': 'ALA', 'chain': 'A'}, 'GLY')], + False + ), + ( + [ + {'chain': 'A', 'resname': 'ALA', 'resid': 1}, + {'chain': 'A', 'resname': 'ALA', 'resid': 2}, + {'chain': 'A', 'resname': 'ALA', 'resid': 3}, + {'chain': 'B', 'resname': 'ALA', 'resid': 1}, + {'chain': 'B', 'resname': 'ALA', 'resid': 2}, + {'chain': 'B', 'resname': 'ALA', 'resid': 3} + ], + [(0, 1), (1, 2), (3, 4), (4, 5)], + [({'resname': 'GLY', 'chain': 'A'}, 'ALA')], + True + ) +]) +def test_mod_resid_not_correct(caplog, node_data, edge_data, mutation, expected): """ Tests that the modification is found in the expected residue. """ mol = Molecule(force_field=ForceField(FF_UNIVERSAL_TEST)) mol.add_nodes_from(enumerate(node_data)) mol.add_edges_from(edge_data) - mutation = [({'resname': 'GLY', 'resid': 1, 'chain': 'A'}, 'MET')] - + caplog.clear() annotate_modifications(mol, [], mutation) - + if expected: - assert 'Residue specified by "A-GLY1" for mutation "MET" not found.' in str(caplog.records[0].getMessage()) + assert any(rec.levelname == 'WARNING' for rec in caplog.records) else: assert caplog.records == [] From 2129a4dbe9f7b9896f6407948a209559831afce0 Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Mon, 8 Apr 2024 15:44:11 +0200 Subject: [PATCH 10/22] added resid tests --- vermouth/tests/test_annotate_mut_mod.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/vermouth/tests/test_annotate_mut_mod.py b/vermouth/tests/test_annotate_mut_mod.py index 3325e59a4..9e9bd76de 100644 --- a/vermouth/tests/test_annotate_mut_mod.py +++ b/vermouth/tests/test_annotate_mut_mod.py @@ -323,6 +323,16 @@ def test_nter_cter_modifications(node_data, edge_data, expected): [({'resname': 'GLY', 'resid': 1, 'chain': 'A'}, 'MET')], True ), + ( + [ + {'chain': 'A', 'resname': 'ALA', 'resid': 1}, + {'chain': 'A', 'resname': 'ALA', 'resid': 2}, + {'chain': 'A', 'resname': 'ALA', 'resid': 3} + ], + [(0, 1), (1, 2)], + [({'resname': 'GLY', 'resid': 1}, 'MET')], + False + ), ( [ {'chain': 'A', 'resname': 'ALA', 'resid': 1}, @@ -336,6 +346,19 @@ def test_nter_cter_modifications(node_data, edge_data, expected): [({'resname': 'ALA', 'chain': 'A'}, 'GLY')], False ), + ( + [ + {'chain': 'A', 'resname': 'ALA', 'resid': 1}, + {'chain': 'A', 'resname': 'ALA', 'resid': 2}, + {'chain': 'A', 'resname': 'ALA', 'resid': 3}, + {'chain': 'B', 'resname': 'ALA', 'resid': 1}, + {'chain': 'B', 'resname': 'ALA', 'resid': 2}, + {'chain': 'B', 'resname': 'ALA', 'resid': 3} + ], + [(0, 1), (1, 2), (3, 4), (4, 5)], + [({'resname': 'GLY', 'resid': '1'}, 'ALA')], + False + ), ( [ {'chain': 'A', 'resname': 'ALA', 'resid': 1}, From da16846215185638d9a27cc3e5d159f466593642 Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Mon, 8 Apr 2024 17:03:39 +0200 Subject: [PATCH 11/22] added mut/mod examples to help --- bin/martinize2 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bin/martinize2 b/bin/martinize2 index f6e4f8d3b..fb269959d 100755 --- a/bin/martinize2 +++ b/bin/martinize2 @@ -634,7 +634,9 @@ def entry(): help="Mutate a residue. Desired mutation is " "specified as, e.g. A-PHE45:ALA. The format is " "-:. Elements " - "of the specification can be omitted as required.", + "of the specification can be omitted as required." + "e.g. PHE45:ALA will mutate all PHE with resid 45 to ALA, " + "A-PHE:ALA will mutate all PHE on chain A to ALA", ) prot_group.add_argument( "-modify", @@ -646,7 +648,9 @@ def entry(): "modification is specified as, e.g. A-ASP45:ASP0. " "The format is -:." " Elements of the specification can be omitted as " - "required.", + "required. e.g. ASP45:ASP0 will modify all ASP with " + "resid 45 to ASP0, A-ASP:ASP0 will modify all ASP on " + "chain A to ASP0", ) prot_group.add_argument( "-nter", From 0a523939529e44d93605ce893f38f2c89f58a25a Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Mon, 8 Apr 2024 17:04:13 +0200 Subject: [PATCH 12/22] fixed resid specification and added tests --- vermouth/processors/annotate_mut_mod.py | 7 +++-- vermouth/tests/test_annotate_mut_mod.py | 41 +++++++++++++++++++------ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index e03aab67e..6108675fb 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -210,7 +210,6 @@ def _resiter(mod, residue_graph, resspec, library, key, molecule): molecule.nodes[node_idx][key] = molecule.nodes[node_idx].get(key, []) + [mod] return mod_found - def annotate_modifications(molecule, modifications, mutations): """ Annotate nodes in molecule with the desired modifications and mutations @@ -251,8 +250,10 @@ def annotate_modifications(molecule, modifications, mutations): for mutmod, key, library in associations: for resspec, mod in mutmod: - # Ie. the target residue is chain specific - if (resspec.get('chain') is not None) and (resspec.get('chain') == chain): + # Ie. the target residue is chain or residue specific + condition0 = ((resspec.get('chain') is not None) and (resspec.get('chain') == chain)) + condition1 = ((resspec.get('chain') is None) and (resspec.get('resid') is not None)) + if condition0 or condition1: mod_found = _resiter(mod, residue_graph, resspec, library, key, molecule) if not mod_found: LOGGER.warning('Residue specified by "{}" for mutation "{}" not found. ', diff --git a/vermouth/tests/test_annotate_mut_mod.py b/vermouth/tests/test_annotate_mut_mod.py index 9e9bd76de..f62438be0 100644 --- a/vermouth/tests/test_annotate_mut_mod.py +++ b/vermouth/tests/test_annotate_mut_mod.py @@ -324,14 +324,24 @@ def test_nter_cter_modifications(node_data, edge_data, expected): True ), ( - [ - {'chain': 'A', 'resname': 'ALA', 'resid': 1}, - {'chain': 'A', 'resname': 'ALA', 'resid': 2}, - {'chain': 'A', 'resname': 'ALA', 'resid': 3} - ], - [(0, 1), (1, 2)], - [({'resname': 'GLY', 'resid': 1}, 'MET')], - False + [ + {'chain': 'A', 'resname': 'ALA', 'resid': 1}, + {'chain': 'A', 'resname': 'ALA', 'resid': 2}, + {'chain': 'A', 'resname': 'ALA', 'resid': 3} + ], + [(0, 1), (1, 2)], + [({'resname': 'GLY', 'resid': 1}, 'MET')], + True + ), + ( + [ + {'chain': 'A', 'resname': 'ALA', 'resid': 1}, + {'chain': 'A', 'resname': 'ALA', 'resid': 2}, + {'chain': 'A', 'resname': 'ALA', 'resid': 3} + ], + [(0, 1), (1, 2)], + [({'resname': 'ALA', 'resid': 1}, 'MET')], + False ), ( [ @@ -356,7 +366,20 @@ def test_nter_cter_modifications(node_data, edge_data, expected): {'chain': 'B', 'resname': 'ALA', 'resid': 3} ], [(0, 1), (1, 2), (3, 4), (4, 5)], - [({'resname': 'GLY', 'resid': '1'}, 'ALA')], + [({'resname': 'GLY', 'resid': 1}, 'ALA')], + True + ), + ( + [ + {'chain': 'A', 'resname': 'ALA', 'resid': 1}, + {'chain': 'A', 'resname': 'ALA', 'resid': 2}, + {'chain': 'A', 'resname': 'ALA', 'resid': 3}, + {'chain': 'B', 'resname': 'ALA', 'resid': 1}, + {'chain': 'B', 'resname': 'ALA', 'resid': 2}, + {'chain': 'B', 'resname': 'ALA', 'resid': 3} + ], + [(0, 1), (1, 2), (3, 4), (4, 5)], + [({'resname': 'ALA', 'resid': 1}, 'GLY')], False ), ( From 311704ee1e9fb4ad4fbf4f5cac74f05dbb8917bd Mon Sep 17 00:00:00 2001 From: Peter Kroon Date: Wed, 10 Apr 2024 16:36:16 +0200 Subject: [PATCH 13/22] Adapt mutmod warning test to run on System --- vermouth/tests/test_annotate_mut_mod.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/vermouth/tests/test_annotate_mut_mod.py b/vermouth/tests/test_annotate_mut_mod.py index f62438be0..cc87a7d15 100644 --- a/vermouth/tests/test_annotate_mut_mod.py +++ b/vermouth/tests/test_annotate_mut_mod.py @@ -17,6 +17,7 @@ import networkx as nx import pytest +from vermouth.system import System from vermouth.molecule import Molecule from vermouth.forcefield import ForceField from vermouth.processors.annotate_mut_mod import ( @@ -394,18 +395,39 @@ def test_nter_cter_modifications(node_data, edge_data, expected): [(0, 1), (1, 2), (3, 4), (4, 5)], [({'resname': 'GLY', 'chain': 'A'}, 'ALA')], True + ), + ( + [ + {'chain': 'A', 'resname': 'ALA', 'resid': 1}, + {'chain': 'A', 'resname': 'ALA', 'resid': 2}, + {'chain': 'A', 'resname': 'ALA', 'resid': 3}, + {'chain': 'B', 'resname': 'ASN', 'resid': 1}, + {'chain': 'B', 'resname': 'ASN', 'resid': 2}, + {'chain': 'B', 'resname': 'ASN', 'resid': 3} + ], + [(0, 1), (1, 2), (3, 4), (4, 5)], + [({'resid': 1, 'resname': 'ASN'}, 'ALA')], + False ) ]) def test_mod_resid_not_correct(caplog, node_data, edge_data, mutation, expected): """ Tests that the modification is found in the expected residue. """ + system = System(force_field=ForceField(FF_UNIVERSAL_TEST)) mol = Molecule(force_field=ForceField(FF_UNIVERSAL_TEST)) mol.add_nodes_from(enumerate(node_data)) mol.add_edges_from(edge_data) + mols = nx.connected_components(mol) + for nodes in mols: + system.add_molecule(mol.subgraph(nodes)) + + processor = AnnotateMutMod() + processor.mutations = mutation # Resspecs are already "parsed" + caplog.clear() - annotate_modifications(mol, [], mutation) + processor.run_system(system) if expected: assert any(rec.levelname == 'WARNING' for rec in caplog.records) From 047b98b42f0e2ab293d01f54a7243811b9de9aad Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Thu, 11 Apr 2024 16:32:39 +0200 Subject: [PATCH 14/22] updated mutmod annotating and appropriate tests run the processor as a system, and raise warnings if the mutmod is not found anywhere, or a warning if found in some places but not others one test expected outcome has been changed to reflect this. --- vermouth/processors/annotate_mut_mod.py | 43 ++++++++++++++++++++----- vermouth/tests/test_annotate_mut_mod.py | 10 +++--- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index 6108675fb..3cf5d35d8 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -210,7 +210,7 @@ def _resiter(mod, residue_graph, resspec, library, key, molecule): molecule.nodes[node_idx][key] = molecule.nodes[node_idx].get(key, []) + [mod] return mod_found -def annotate_modifications(molecule, modifications, mutations): +def annotate_modifications(molecule, modifications, mutations, resspec_counts): """ Annotate nodes in molecule with the desired modifications and mutations @@ -247,7 +247,7 @@ def annotate_modifications(molecule, modifications, mutations): residue = {key: residue_graph.nodes[0].get(key) for key in 'chain resid resname insertion_code'.split()} chain = residue['chain'] - + extra = False for mutmod, key, library in associations: for resspec, mod in mutmod: # Ie. the target residue is chain or residue specific @@ -256,11 +256,25 @@ def annotate_modifications(molecule, modifications, mutations): if condition0 or condition1: mod_found = _resiter(mod, residue_graph, resspec, library, key, molecule) if not mod_found: - LOGGER.warning('Residue specified by "{}" for mutation "{}" not found. ', - _format_resname(resspec), mod) - # If instead we're targeting residues in any chain - elif resspec.get(chain) is None: + #if no mod found, return that there's a problem + resspec_counts.append({'status': True, + 'mutmod': _format_resname(resspec), + 'post': mod, + 'current_chain': chain}) + extra = True + # If every residue on a particular chain is being targeted, go for all of them without concern + elif (resspec.get(chain) is None): _resiter(mod, residue_graph, resspec, library, key, molecule) + # if all residues on some chain, but not the current chain + elif (resspec.get(chain) is not None) and (resspec.get(chain) != chain): + resspec_counts.append({'status': True, + 'mutmod': _format_resname(resspec), + 'post': mod, + 'current_chain': chain}) + extra = True + #return that everything's fine by default + if not extra: + resspec_counts.append({'status': False}) class AnnotateMutMod(Processor): """ @@ -277,6 +291,7 @@ class AnnotateMutMod(Processor): :func:`annotate_modifications` """ def __init__(self, modifications=None, mutations=None): + self.resspec_counts = [] if not modifications: modifications = [] if not mutations: @@ -289,8 +304,20 @@ def __init__(self, modifications=None, mutations=None): self.mutations.append((parse_residue_spec(resspec), val)) def run_molecule(self, molecule): - annotate_modifications(molecule, self.modifications, self.mutations) + annotate_modifications(molecule, self.modifications, self.mutations, self.resspec_counts) return molecule def run_system(self, system): super().run_system(system) - + _any = any([i['status'] for i in self.resspec_counts]) + _all = all([i['status'] for i in self.resspec_counts]) + #if the mut/mod hasn't been found anywhere, raise a warning + if _all: + LOGGER.warning('Residue specified by "{}" for mutation "{}" not found anywhere', + self.resspec_counts[0]['mutmod'], self.resspec_counts[0]['post']) + #if it's been found in some places log it with some info + if _any and not _all: + for l in self.resspec_counts: + if l['status']==True: + LOGGER.info('Residue specified by "{}" for mutation "{}" not found on chain {}' + ' but found elsewhere', + l['mutmod'], l['post'], l['current_chain']) diff --git a/vermouth/tests/test_annotate_mut_mod.py b/vermouth/tests/test_annotate_mut_mod.py index cc87a7d15..130fcdbab 100644 --- a/vermouth/tests/test_annotate_mut_mod.py +++ b/vermouth/tests/test_annotate_mut_mod.py @@ -150,7 +150,7 @@ def test_subdict(dict1, dict2, expected): ) ]) def test_annotate_modifications(example_mol, modifications, mutations, expected_mod, expected_mut): - annotate_modifications(example_mol, modifications, mutations) + annotate_modifications(example_mol, modifications, mutations,[]) for node_idx, mods in expected_mod.items(): assert _subdict(mods, example_mol.nodes[node_idx]) for node_idx, mods in expected_mut.items(): @@ -167,7 +167,7 @@ def test_single_residue_mol(): mol.add_edges_from([(0, 1)]) modification = [({'resname': 'A', 'resid': 2}, 'C-ter'),] - annotate_modifications(mol, modification, []) + annotate_modifications(mol, modification, [],[]) assert mol.nodes[0] == {'modification': ['C-ter'], 'resname': 'A', 'resid': 2, 'chain': 'A'} assert mol.nodes[1] == {'modification': ['C-ter'], 'resname': 'A', 'resid': 2, 'chain': 'A'} @@ -179,7 +179,7 @@ def test_single_residue_mol(): ]) def test_annotate_modifications_error(example_mol, modifications, mutations): with pytest.raises(NameError): - annotate_modifications(example_mol, modifications, mutations) + annotate_modifications(example_mol, modifications, mutations,[]) def test_unknown_terminus_match(): @@ -293,7 +293,7 @@ def test_nter_cter_modifications(node_data, edge_data, expected): modification = [({'resname': 'cter'}, 'C-ter'), ({'resname': 'nter'}, 'N-ter')] - annotate_modifications(mol, modification, []) + annotate_modifications(mol, modification, [],[]) found = {} for node_idx in mol: @@ -394,7 +394,7 @@ def test_nter_cter_modifications(node_data, edge_data, expected): ], [(0, 1), (1, 2), (3, 4), (4, 5)], [({'resname': 'GLY', 'chain': 'A'}, 'ALA')], - True + False ), ( [ From 6680cbed15f641efba5790e96f1732fea560a0ca Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Thu, 11 Apr 2024 18:09:27 +0200 Subject: [PATCH 15/22] added more conditions and corrected tests --- vermouth/processors/annotate_mut_mod.py | 28 +++++++++++++++++++++---- vermouth/tests/test_annotate_mut_mod.py | 2 +- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index 3cf5d35d8..ce4373f66 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -258,6 +258,8 @@ def annotate_modifications(molecule, modifications, mutations, resspec_counts): if not mod_found: #if no mod found, return that there's a problem resspec_counts.append({'status': True, + 'condition0': condition0, + 'condition1': condition1, 'mutmod': _format_resname(resspec), 'post': mod, 'current_chain': chain}) @@ -314,10 +316,28 @@ def run_system(self, system): if _all: LOGGER.warning('Residue specified by "{}" for mutation "{}" not found anywhere', self.resspec_counts[0]['mutmod'], self.resspec_counts[0]['post']) - #if it's been found in some places log it with some info + #if it's been found in some places log it appropriately if _any and not _all: for l in self.resspec_counts: if l['status']==True: - LOGGER.info('Residue specified by "{}" for mutation "{}" not found on chain {}' - ' but found elsewhere', - l['mutmod'], l['post'], l['current_chain']) + ''' + if both conditions were met, then a specific target for both chain and resid + has been failed + ''' + if l['condition0'] and l['condition1']: + LOGGER.warning('Residue specified by "{}" for mutation "{}" not found in chain {}', + l['mutmod'], l['post'], l['current_chain']) + ''' + if only condition0 was met, then a chain-wide target has been failed, eg. A-SER:ALA + ''' + elif l['condition0'] and not l['condition1']: + LOGGER.warning('Residue specified by "{}" for mutation "{}" not found in chain {}', + l['mutmod'], l['post'], l['current_chain']) + ''' + if only condition1 was met, something like SER2:ALA hasn't been found on a particular chain + but because _any is True, it has been found elsewhere. + ''' + elif l['condition1'] and not l['condition0']: + LOGGER.info('Residue specified by "{}" for mutation "{}" not found on chain {}' + ' but found elsewhere', + l['mutmod'], l['post'], l['current_chain']) \ No newline at end of file diff --git a/vermouth/tests/test_annotate_mut_mod.py b/vermouth/tests/test_annotate_mut_mod.py index 130fcdbab..dc3f5d6f9 100644 --- a/vermouth/tests/test_annotate_mut_mod.py +++ b/vermouth/tests/test_annotate_mut_mod.py @@ -394,7 +394,7 @@ def test_nter_cter_modifications(node_data, edge_data, expected): ], [(0, 1), (1, 2), (3, 4), (4, 5)], [({'resname': 'GLY', 'chain': 'A'}, 'ALA')], - False + True ), ( [ From 29330796a867c5849fa609c0c9565ef9d3b75b8e Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Thu, 11 Apr 2024 18:17:07 +0200 Subject: [PATCH 16/22] changed comments --- vermouth/processors/annotate_mut_mod.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index ce4373f66..9296c8ccf 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -327,16 +327,12 @@ def run_system(self, system): if l['condition0'] and l['condition1']: LOGGER.warning('Residue specified by "{}" for mutation "{}" not found in chain {}', l['mutmod'], l['post'], l['current_chain']) - ''' - if only condition0 was met, then a chain-wide target has been failed, eg. A-SER:ALA - ''' + #if only condition0 was met, then a chain-wide target has been failed, eg. A-SER:ALA elif l['condition0'] and not l['condition1']: LOGGER.warning('Residue specified by "{}" for mutation "{}" not found in chain {}', l['mutmod'], l['post'], l['current_chain']) - ''' - if only condition1 was met, something like SER2:ALA hasn't been found on a particular chain - but because _any is True, it has been found elsewhere. - ''' + #if only condition1 was met, something like SER2:ALA hasn't been found on a particular chain + #but because _any is True, it has been found elsewhere. elif l['condition1'] and not l['condition0']: LOGGER.info('Residue specified by "{}" for mutation "{}" not found on chain {}' ' but found elsewhere', From 5fb2b3cc2ac8e3424e16b292c2648e3d8aee04a1 Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Thu, 11 Apr 2024 18:30:56 +0200 Subject: [PATCH 17/22] added more tests to increase coverage --- vermouth/tests/test_annotate_mut_mod.py | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/vermouth/tests/test_annotate_mut_mod.py b/vermouth/tests/test_annotate_mut_mod.py index dc3f5d6f9..2b37f575a 100644 --- a/vermouth/tests/test_annotate_mut_mod.py +++ b/vermouth/tests/test_annotate_mut_mod.py @@ -370,6 +370,19 @@ def test_nter_cter_modifications(node_data, edge_data, expected): [({'resname': 'GLY', 'resid': 1}, 'ALA')], True ), + ( + [ + {'chain': 'A', 'resname': 'ALA', 'resid': 1}, + {'chain': 'A', 'resname': 'ALA', 'resid': 2}, + {'chain': 'A', 'resname': 'ALA', 'resid': 3}, + {'chain': 'B', 'resname': 'ALA', 'resid': 1}, + {'chain': 'B', 'resname': 'ALA', 'resid': 2}, + {'chain': 'B', 'resname': 'ALA', 'resid': 3} + ], + [(0, 1), (1, 2), (3, 4), (4, 5)], + [({'resname': 'GLY', 'resid': 1, 'chain': 'A'}, 'ALA')], + True + ), ( [ {'chain': 'A', 'resname': 'ALA', 'resid': 1}, @@ -408,6 +421,19 @@ def test_nter_cter_modifications(node_data, edge_data, expected): [(0, 1), (1, 2), (3, 4), (4, 5)], [({'resid': 1, 'resname': 'ASN'}, 'ALA')], False + ), + ( + [ + {'chain': 'A', 'resname': 'ALA', 'resid': 1}, + {'chain': 'A', 'resname': 'ALA', 'resid': 2}, + {'chain': 'A', 'resname': 'ALA', 'resid': 3}, + {'chain': 'B', 'resname': 'ASN', 'resid': 1}, + {'chain': 'B', 'resname': 'ASN', 'resid': 2}, + {'chain': 'B', 'resname': 'ASN', 'resid': 3} + ], + [(0, 1), (1, 2), (3, 4), (4, 5)], + [({'chain':'B', 'resname': 'ASN'}, 'ALA')], + False ) ]) def test_mod_resid_not_correct(caplog, node_data, edge_data, mutation, expected): From c4c72f07c99188faf1ebb4d3032b122b066dadba Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Thu, 11 Apr 2024 18:37:35 +0200 Subject: [PATCH 18/22] removed a degenerate condition --- vermouth/processors/annotate_mut_mod.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index 9296c8ccf..40b18bd72 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -267,13 +267,6 @@ def annotate_modifications(molecule, modifications, mutations, resspec_counts): # If every residue on a particular chain is being targeted, go for all of them without concern elif (resspec.get(chain) is None): _resiter(mod, residue_graph, resspec, library, key, molecule) - # if all residues on some chain, but not the current chain - elif (resspec.get(chain) is not None) and (resspec.get(chain) != chain): - resspec_counts.append({'status': True, - 'mutmod': _format_resname(resspec), - 'post': mod, - 'current_chain': chain}) - extra = True #return that everything's fine by default if not extra: resspec_counts.append({'status': False}) From 3e1c5c194c31ecf1b1a6af9409259e734bcccc21 Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Tue, 16 Apr 2024 11:56:26 +0200 Subject: [PATCH 19/22] simplified annotation function --- vermouth/processors/annotate_mut_mod.py | 45 +++++-------------------- 1 file changed, 9 insertions(+), 36 deletions(-) diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index 40b18bd72..ec45c08c3 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -253,23 +253,19 @@ def annotate_modifications(molecule, modifications, mutations, resspec_counts): # Ie. the target residue is chain or residue specific condition0 = ((resspec.get('chain') is not None) and (resspec.get('chain') == chain)) condition1 = ((resspec.get('chain') is None) and (resspec.get('resid') is not None)) - if condition0 or condition1: + #if the whole chain is being targeted + condition2 = (resspec.get(chain) == None) + if condition0 or condition1 or condition2: mod_found = _resiter(mod, residue_graph, resspec, library, key, molecule) if not mod_found: #if no mod found, return that there's a problem - resspec_counts.append({'status': True, - 'condition0': condition0, - 'condition1': condition1, + resspec_counts.append({'status': 0, 'mutmod': _format_resname(resspec), - 'post': mod, - 'current_chain': chain}) + 'post': mod,}) extra = True - # If every residue on a particular chain is being targeted, go for all of them without concern - elif (resspec.get(chain) is None): - _resiter(mod, residue_graph, resspec, library, key, molecule) #return that everything's fine by default if not extra: - resspec_counts.append({'status': False}) + resspec_counts.append({'status': 1}) class AnnotateMutMod(Processor): """ @@ -303,30 +299,7 @@ def run_molecule(self, molecule): return molecule def run_system(self, system): super().run_system(system) - _any = any([i['status'] for i in self.resspec_counts]) - _all = all([i['status'] for i in self.resspec_counts]) - #if the mut/mod hasn't been found anywhere, raise a warning - if _all: - LOGGER.warning('Residue specified by "{}" for mutation "{}" not found anywhere', + _exit = sum([i['status'] for i in self.resspec_counts]) + if _exit==0: + LOGGER.warning('Residue specified by "{}" for mutation "{}" not found', self.resspec_counts[0]['mutmod'], self.resspec_counts[0]['post']) - #if it's been found in some places log it appropriately - if _any and not _all: - for l in self.resspec_counts: - if l['status']==True: - ''' - if both conditions were met, then a specific target for both chain and resid - has been failed - ''' - if l['condition0'] and l['condition1']: - LOGGER.warning('Residue specified by "{}" for mutation "{}" not found in chain {}', - l['mutmod'], l['post'], l['current_chain']) - #if only condition0 was met, then a chain-wide target has been failed, eg. A-SER:ALA - elif l['condition0'] and not l['condition1']: - LOGGER.warning('Residue specified by "{}" for mutation "{}" not found in chain {}', - l['mutmod'], l['post'], l['current_chain']) - #if only condition1 was met, something like SER2:ALA hasn't been found on a particular chain - #but because _any is True, it has been found elsewhere. - elif l['condition1'] and not l['condition0']: - LOGGER.info('Residue specified by "{}" for mutation "{}" not found on chain {}' - ' but found elsewhere', - l['mutmod'], l['post'], l['current_chain']) \ No newline at end of file From 643b7e5ca34ba404271a5bb4aaf23b929ab00a61 Mon Sep 17 00:00:00 2001 From: Chris Brasnett <35073246+csbrasnett@users.noreply.github.com> Date: Mon, 22 Apr 2024 12:22:15 +0100 Subject: [PATCH 20/22] addressed comments --- vermouth/processors/annotate_mut_mod.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index ec45c08c3..d56648751 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -259,13 +259,13 @@ def annotate_modifications(molecule, modifications, mutations, resspec_counts): mod_found = _resiter(mod, residue_graph, resspec, library, key, molecule) if not mod_found: #if no mod found, return that there's a problem - resspec_counts.append({'status': 0, + resspec_counts.append({'success': False, 'mutmod': _format_resname(resspec), 'post': mod,}) extra = True #return that everything's fine by default if not extra: - resspec_counts.append({'status': 1}) + resspec_counts.append({'success': True}) class AnnotateMutMod(Processor): """ @@ -299,7 +299,7 @@ def run_molecule(self, molecule): return molecule def run_system(self, system): super().run_system(system) - _exit = sum([i['status'] for i in self.resspec_counts]) - if _exit==0: + _exit = sum([i['success'] for i in self.resspec_counts]) + if _exit == 0: LOGGER.warning('Residue specified by "{}" for mutation "{}" not found', self.resspec_counts[0]['mutmod'], self.resspec_counts[0]['post']) From 72248540082a1f47ec1ca2f9ce6fa5fecd2a3898 Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Tue, 23 Apr 2024 10:20:04 +0200 Subject: [PATCH 21/22] removed unnecessary conditions --- vermouth/processors/annotate_mut_mod.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index d56648751..3606d095d 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -250,19 +250,13 @@ def annotate_modifications(molecule, modifications, mutations, resspec_counts): extra = False for mutmod, key, library in associations: for resspec, mod in mutmod: - # Ie. the target residue is chain or residue specific - condition0 = ((resspec.get('chain') is not None) and (resspec.get('chain') == chain)) - condition1 = ((resspec.get('chain') is None) and (resspec.get('resid') is not None)) - #if the whole chain is being targeted - condition2 = (resspec.get(chain) == None) - if condition0 or condition1 or condition2: - mod_found = _resiter(mod, residue_graph, resspec, library, key, molecule) - if not mod_found: - #if no mod found, return that there's a problem - resspec_counts.append({'success': False, - 'mutmod': _format_resname(resspec), - 'post': mod,}) - extra = True + mod_found = _resiter(mod, residue_graph, resspec, library, key, molecule) + if not mod_found: + #if no mod found, return that there's a problem + resspec_counts.append({'success': False, + 'mutmod': _format_resname(resspec), + 'post': mod,}) + extra = True #return that everything's fine by default if not extra: resspec_counts.append({'success': True}) From 431b322478f2ceede2926641b0fad63d0f22dea9 Mon Sep 17 00:00:00 2001 From: csbrasnett Date: Tue, 23 Apr 2024 13:16:41 +0200 Subject: [PATCH 22/22] added info to docstring --- vermouth/processors/annotate_mut_mod.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vermouth/processors/annotate_mut_mod.py b/vermouth/processors/annotate_mut_mod.py index 3606d095d..8994d56c9 100644 --- a/vermouth/processors/annotate_mut_mod.py +++ b/vermouth/processors/annotate_mut_mod.py @@ -227,6 +227,12 @@ def annotate_modifications(molecule, modifications, mutations, resspec_counts): the attributes a residue has to fulfill. It can contain the elements 'chain', 'resname' and 'resid'. The second element is the mutation that should be applied. + resspec_counts: list[dict] + List modified in place containing information about whether a + modification/mutation has been applied successfully. If the target is + found, the dictionary has one entry, {'success': True}. If not, + 'success' is False and there are additional items to indicate information + about the failure. Raises ------