[med-svn] [Git][med-team/plip][master] 3 commits: New upstream version 2.2.0+dfsg

Nilesh Patra gitlab at salsa.debian.org
Mon Apr 19 12:44:10 BST 2021



Nilesh Patra pushed to branch master at Debian Med / plip


Commits:
0cc446a0 by Nilesh Patra at 2021-04-19T17:10:36+05:30
New upstream version 2.2.0+dfsg
- - - - -
7f5f6dfb by Nilesh Patra at 2021-04-19T17:10:37+05:30
Update upstream source from tag 'upstream/2.2.0+dfsg'

Update to upstream version '2.2.0+dfsg'
with Debian dir 2031b95d0e697176a5e39f516a580de02cd8a42c
- - - - -
cdf3ace9 by Nilesh Patra at 2021-04-19T17:11:26+05:30
Interim changelog

- - - - -


17 changed files:

- CHANGES.txt
- DOCUMENTATION.md
- README.md
- debian/changelog
- plip/basic/config.py
- plip/exchange/report.py
- plip/exchange/xml.py
- plip/plipcmd.py
- plip/structure/preparation.py
- + plip/test/test_intra.py
- plip/test/test_literature_validated.py
- plip/test/test_metal_coordination.py
- plip/test/test_remote_services.py
- plip/test/test_xml_parser.py
- + plip/test/test_xml_writer.py
- plip/visualization/pymol.py
- plip/visualization/visualize.py


Changes:

=====================================
CHANGES.txt
=====================================
@@ -1,5 +1,20 @@
 Changelog
 ---------
+# 2.2.0
+* new minor release
+* increased detail level of report (individual atoms on protein side)
+* improved handling of multi-model structures
+* minor bug fixes and code optimizations
+
+# 2.1.9
+* fixes issues with DNA receptor handling
+
+# 2.1.8
+* report of individual binding site atoms
+
+# 2.1.7
+* bug fixes
+
 # 2.1.6
 * fetch URL for PDB files updated to avoid issues with RCSB API changes
 


=====================================
DOCUMENTATION.md
=====================================
@@ -115,11 +115,11 @@ $ plip -i 5b2m --intra A -yv
 Please note that detection within a chain takes much longer than detection of protein-ligand interactions, especially for large structures.
 
 ### Interactions of Molecules with DNA/RNA
-PLIP can characterize interactions between ligands and DNA/RNA. A special mode allows to switch from protein to DNA/RNA as the receptor molecule in the structure. To change the receptor mode, start PLIP with the option `--dnareceptor`.
+PLIP can characterize interactions between ligands and DNA/RNA. A special mode allows to switch from treating DNA/RNA molecules as ligands to treating them as part of the receptor in the structure. If a protein is present, too, interactions of the ligand with both, protein and nucleic acids, will be shown. To use this mode, start PLIP with the option `--dnareceptor`.
 
 ## Changing detection thresholds
 The PLIP algorithm uses a rule-based detection to report non-covalent interaction between proteins and their partners. The current settings are based on literature values and have been refined based on extensive testing with independent cases from mainly crystallography journals, covering a broad range of structure resolutions. For most users, it is not recommended to change the standard settings. However, there may be cases where changing detection thresholds is advisable (e.g. sets with multiple very low resolution structures).
- 
+
 PLIP allows you to change the setting permanently or for one run.
 
 ### Permanent change
@@ -147,7 +147,7 @@ PLIP offers further command line options which enables you to switch advanced se
 ## Web Service
 A web service for analysis of protein-ligand complexes using PLIP is available at
 [plip.biotec.tu-dresden.de](http://plip.biotec.tu-dresden.de/)
- 
+
 The web site offers advanced functions to search for specific entries from PDB and lists the interaction results in the browser. Additionally, the service used the BioLiP database to annotate biologically relevant ligands. The option to change threshold, ligand filtering, and batch processing is only available in the command line tool and with the Python modules.
 
 ## Algorithm


=====================================
README.md
=====================================
@@ -18,8 +18,6 @@ Analyze noncovalent protein-ligand interactions in 3D structures with ease.
 | "I love the Linux command line and want to build a workflow around PLIP!" | :x:                | :heavy_check_mark: | :heavy_check_mark: | :yellow_circle:    |
 | "I'm a Python programmer and want to use PLIP in my project!"             | :x:                | :yellow_circle:    | :yellow_circle:    | :heavy_check_mark: |
 
-:exclamation: **Note:** The web server is currently running an outdated version of PLIP, which will be replaced soon. For production use and data analysis we highly recommend to use the current command line release. :exclamation:
-
 ---
 
 ## Quickstart


=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+plip (2.2.0+dfsg-1) UNRELEASED; urgency=medium
+
+  * New upstream version 2.2.0+dfsg
+
+ -- Nilesh Patra <nilesh at debian.org>  Mon, 19 Apr 2021 17:10:53 +0530
+
 plip (2.1.7+dfsg-1) unstable; urgency=medium
 
   * New upstream version


=====================================
plip/basic/config.py
=====================================
@@ -1,5 +1,5 @@
-__version__ = '2.1.6'
-__maintainer__ = 'PharmAI GmbH (2020) - www.pharm.ai - hello at pharm.ai'
+__version__ = '2.2.0'
+__maintainer__ = 'PharmAI GmbH (2020-2021) - www.pharm.ai - hello at pharm.ai'
 
 import logging
 


=====================================
plip/exchange/report.py
=====================================
@@ -40,6 +40,8 @@ class StructureReport:
             mode.text = 'default'
         pdbid = et.SubElement(report, 'pdbid')
         pdbid.text = self.mol.pymol_name.upper()
+        model = et.SubElement(report, 'model')
+        model.text = str(config.MODEL)
         filetype = et.SubElement(report, 'filetype')
         filetype.text = self.mol.filetype.upper()
         pdbfile = et.SubElement(report, 'pdbfile')
@@ -72,7 +74,8 @@ class StructureReport:
         if len(self.excluded) != 0:
             textlines.append('Excluded molecules as ligands: %s\n' % ','.join([lig for lig in self.excluded]))
         if config.DNARECEPTOR:
-            textlines.append('DNA/RNA in structure was chosen as the receptor part.\n')
+            textlines.append('DNA/RNA in structure was chosen to be part of the receptor.')
+        textlines.append(f'Analysis was done on model {config.MODEL}.\n')
         return textlines
 
     def get_bindingsite_data(self):
@@ -191,23 +194,28 @@ class BindingSiteReport:
         ################
 
         self.saltbridge_features = (
-            'RESNR', 'RESTYPE', 'RESCHAIN', 'RESNR_LIG', 'RESTYPE_LIG', 'RESCHAIN_LIG', 'DIST', 'PROTISPOS',
+            'RESNR', 'RESTYPE', 'RESCHAIN', 'PROT_IDX_LIST', 'RESNR_LIG', 'RESTYPE_LIG', 'RESCHAIN_LIG', 'DIST',
+            'PROTISPOS',
             'LIG_GROUP',
             'LIG_IDX_LIST',
             'LIGCOO', 'PROTCOO')
         self.saltbridge_info = []
         for sb in self.complex.saltbridge_lneg + self.complex.saltbridge_pneg:
             if sb.protispos:
-                group, ids = sb.negative.fgroup, [str(x) for x in sb.negative.atoms_orig_idx]
-                self.saltbridge_info.append((sb.resnr, sb.restype, sb.reschain, sb.resnr_l, sb.restype_l, sb.reschain_l,
+                group, ligand_atom_ids = sb.negative.fgroup, [str(x) for x in sb.negative.atoms_orig_idx]
+                protein_atom_ids = [str(x) for x in sb.positive.atoms_orig_idx]
+                self.saltbridge_info.append((sb.resnr, sb.restype, sb.reschain, ",".join(protein_atom_ids), sb.resnr_l,
+                                             sb.restype_l, sb.reschain_l,
                                              '%.2f' % sb.distance, sb.protispos,
-                                             group.capitalize(), ",".join(ids),
+                                             group.capitalize(), ",".join(ligand_atom_ids),
                                              tuple(sb.negative.center), tuple(sb.positive.center)))
             else:
-                group, ids = sb.positive.fgroup, [str(x) for x in sb.positive.atoms_orig_idx]
-                self.saltbridge_info.append((sb.resnr, sb.restype, sb.reschain, sb.resnr_l, sb.restype_l, sb.reschain_l,
+                group, ligand_atom_ids = sb.positive.fgroup, [str(x) for x in sb.positive.atoms_orig_idx]
+                protein_atom_ids = [str(x) for x in sb.negative.atoms_orig_idx]
+                self.saltbridge_info.append((sb.resnr, sb.restype, sb.reschain, ",".join(protein_atom_ids), sb.resnr_l,
+                                             sb.restype_l, sb.reschain_l,
                                              '%.2f' % sb.distance, sb.protispos,
-                                             group.capitalize(), ",".join(ids),
+                                             group.capitalize(), ",".join(ligand_atom_ids),
                                              tuple(sb.positive.center), tuple(sb.negative.center)))
 
         ###############
@@ -215,15 +223,18 @@ class BindingSiteReport:
         ###############
 
         self.pistacking_features = (
-            'RESNR', 'RESTYPE', 'RESCHAIN', 'RESNR_LIG', 'RESTYPE_LIG', 'RESCHAIN_LIG', 'CENTDIST', 'ANGLE', 'OFFSET',
+            'RESNR', 'RESTYPE', 'RESCHAIN', 'RESNR_LIG', 'RESTYPE_LIG', 'RESCHAIN_LIG', 'PROT_IDX_LIST', 'CENTDIST',
+            'ANGLE', 'OFFSET',
             'TYPE',
             'LIG_IDX_LIST', 'LIGCOO', 'PROTCOO')
         self.pistacking_info = []
         for stack in self.complex.pistacking:
-            ids = [str(x) for x in stack.ligandring.atoms_orig_idx]
+            ligand_atom_ids = [str(x) for x in stack.ligandring.atoms_orig_idx]
+            protein_atom_ids = [str(x) for x in stack.proteinring.atoms_orig_idx]
             self.pistacking_info.append((stack.resnr, stack.restype, stack.reschain, stack.resnr_l, stack.restype_l,
-                                         stack.reschain_l, '%.2f' % stack.distance,
-                                         '%.2f' % stack.angle, '%.2f' % stack.offset, stack.type, ",".join(ids),
+                                         stack.reschain_l, ",".join(protein_atom_ids), '%.2f' % stack.distance,
+                                         '%.2f' % stack.angle, '%.2f' % stack.offset, stack.type,
+                                         ",".join(ligand_atom_ids),
                                          tuple(stack.ligandring.center), tuple(stack.proteinring.center)))
 
         ##########################
@@ -231,24 +242,28 @@ class BindingSiteReport:
         ##########################
 
         self.pication_features = (
-            'RESNR', 'RESTYPE', 'RESCHAIN', 'RESNR_LIG', 'RESTYPE_LIG', 'RESCHAIN_LIG', 'DIST', 'OFFSET', 'PROTCHARGED',
-            'LIG_GROUP',
-            'LIG_IDX_LIST', 'LIGCOO', 'PROTCOO')
+            'RESNR', 'RESTYPE', 'RESCHAIN', 'PROT_IDX_LIST', 'RESNR_LIG', 'RESTYPE_LIG', 'RESCHAIN_LIG', 'DIST',
+            'OFFSET',
+            'PROTCHARGED', 'LIG_GROUP', 'LIG_IDX_LIST', 'LIGCOO', 'PROTCOO')
         self.pication_info = []
         for picat in self.complex.pication_laro + self.complex.pication_paro:
             if picat.protcharged:
-                ids = [str(x) for x in picat.ring.atoms_orig_idx]
+                ligand_atom_ids = [str(x) for x in picat.ring.atoms_orig_idx]
+                protein_atom_ids = [str(x) for x in picat.charge.atoms_orig_idx]
                 group = 'Aromatic'
-                self.pication_info.append((picat.resnr, picat.restype, picat.reschain, picat.resnr_l, picat.restype_l,
+                self.pication_info.append((picat.resnr, picat.restype, picat.reschain, ",".join(protein_atom_ids),
+                                           picat.resnr_l, picat.restype_l,
                                            picat.reschain_l, '%.2f' % picat.distance,
-                                           '%.2f' % picat.offset, picat.protcharged, group, ",".join(ids),
+                                           '%.2f' % picat.offset, picat.protcharged, group, ",".join(ligand_atom_ids),
                                            tuple(picat.ring.center), tuple(picat.charge.center)))
             else:
-                ids = [str(x) for x in picat.charge.atoms_orig_idx]
+                ligand_atom_ids = [str(x) for x in picat.charge.atoms_orig_idx]
+                protein_atom_ids = [str(x) for x in picat.ring.atoms_orig_idx]
                 group = picat.charge.fgroup
-                self.pication_info.append((picat.resnr, picat.restype, picat.reschain, picat.resnr_l, picat.restype_l,
+                self.pication_info.append((picat.resnr, picat.restype, picat.reschain, ",".join(protein_atom_ids),
+                                           picat.resnr_l, picat.restype_l,
                                            picat.reschain_l, '%.2f' % picat.distance,
-                                           '%.2f' % picat.offset, picat.protcharged, group, ",".join(ids),
+                                           '%.2f' % picat.offset, picat.protcharged, group, ",".join(ligand_atom_ids),
                                            tuple(picat.charge.center), tuple(picat.ring.center)))
 
         #################
@@ -451,9 +466,15 @@ class BindingSiteReport:
                     new_contact = et.SubElement(interaction, element_name[:-2], id=str(j + 1))
                 for i, feature in enumerate(single_contact):
                     # Just assign the value unless it's an atom list, use subelements in this case
-                    if features[i] == 'LIG_IDX_LIST':
+                    if features[i] == 'LIG_IDX_LIST' or features[i] == 'PROT_IDX_LIST':
                         feat = et.SubElement(new_contact, features[i].lower())
-                        for k, atm_idx in enumerate(feature.split(',')):
+                        split = list()
+                        # check whether multiple atoms are contained in the atom group
+                        if isinstance(feature, str):
+                            split = feature.split(',')
+                        else:
+                            split = [feature]
+                        for k, atm_idx in enumerate(split):
                             idx = et.SubElement(feat, 'idx', id=str(k + 1))
                             idx.text = str(atm_idx)
                     elif features[i].endswith('COO'):


=====================================
plip/exchange/xml.py
=====================================
@@ -1,7 +1,5 @@
 from lxml import etree
 
-from urllib.request import urlopen
-
 
 class XMLStorage:
     """Generic class for storing XML data from PLIP XML files."""
@@ -109,6 +107,8 @@ class SaltBridge(Interaction):
         self.lig_group = self.getdata(sbridge_part, 'lig_group', force_string=True)
         self.lig_idx_list = [int(tagpart.text) for tagpart in
                              sbridge_part.xpath('lig_idx_list/idx')]
+        self.prot_idx_list = [int(tagpart.text) for tagpart in
+                              sbridge_part.xpath('prot_idx_list/idx')]
 
 
 class PiStacking(Interaction):
@@ -123,6 +123,8 @@ class PiStacking(Interaction):
         self.type = self.getdata(pistack_part, 'type')
         self.lig_idx_list = [int(tagpart.text) for tagpart in
                              pistack_part.xpath('lig_idx_list/idx')]
+        self.prot_idx_list = [int(tagpart.text) for tagpart in
+                              pistack_part.xpath('prot_idx_list/idx')]
 
 
 class PiCation(Interaction):
@@ -233,7 +235,7 @@ class BSite(XMLStorage):
         self.halogens = [HalogenBond(x) for x in interactions.xpath('halogen_bonds/halogen_bond')]
         self.metal_complexes = [MetalComplex(x) for x in interactions.xpath('metal_complexes/metal_complex')]
         self.num_contacts = len(self.hydrophobics) + len(self.hbonds) + len(self.wbridges) + len(self.sbridges) + \
-            len(self.pi_stacks) + len(self.pi_cations) + len(self.halogens) + len(self.metal_complexes)
+                            len(self.pi_stacks) + len(self.pi_cations) + len(self.halogens) + len(self.metal_complexes)
         self.has_interactions = self.num_contacts > 0
 
         self.get_atom_mapping()
@@ -260,7 +262,8 @@ class BSite(XMLStorage):
                   'pications': len(self.pi_cations), 'halogens': len(self.halogens), 'metal': len(self.metal_complexes),
                   'hbond_back': hbondsback, 'hbond_nonback': (len(self.hbonds) - hbondsback)}
         counts['total'] = counts['hydrophobics'] + counts['hbonds'] + counts['wbridges'] + \
-            counts['sbridges'] + counts['pistacks'] + counts['pications'] + counts['halogens'] + counts['metal']
+                          counts['sbridges'] + counts['pistacks'] + counts['pications'] + counts['halogens'] + counts[
+                              'metal']
         return counts
 
 
@@ -284,4 +287,4 @@ class PlipXML(XMLStorage):
 
     def load_data(self, xmlfile):
         """Loads/parses an XML file and saves it as a tree if successful."""
-        self.doc = etree.parse(xmlfile)
\ No newline at end of file
+        self.doc = etree.parse(xmlfile)


=====================================
plip/plipcmd.py
=====================================
@@ -210,7 +210,7 @@ def main():
                         help="Turns off calculation of mapping between canonical and PDB atom order for ligands.",
                         action="store_true")
     parser.add_argument("--dnareceptor", dest="dnareceptor", default=False,
-                        help="Uses the DNA instead of the protein as a receptor for interactions.",
+                        help="Treat nucleic acids as part of the receptor structure (together with any present protein) instead of as a ligand.",
                         action="store_true")
     parser.add_argument("--name", dest="outputfilename", default="report",
                         help="Set a filename for the report TXT and XML files. Will only work when processing single structures.")


=====================================
plip/structure/preparation.py
=====================================
@@ -86,6 +86,7 @@ class PDBParser:
                 except KeyError:
                     corrected_pdb = ''.join(model_dict[1])
                     corrected_lines = model_dict[1]
+                    config.MODEL = 1
                     logger.warning('invalid model number specified, using first model instead')
             else:
                 corrected_pdb = self.pdbpath
@@ -1407,7 +1408,9 @@ class PDBComplex:
 
         if config.DNARECEPTOR:
             self.resis = [obres for obres in pybel.ob.OBResidueIter(
-                self.protcomplex.OBMol) if obres.GetName() in config.DNA + config.RNA]
+                self.protcomplex.OBMol) if obres.GetName() in config.DNA + config.RNA
+                ] + [obres for obres in pybel.ob.OBResidueIter(
+                    self.protcomplex.OBMol) if obres.GetResidueProperty(0)]
         else:
             self.resis = [obres for obres in pybel.ob.OBResidueIter(
                 self.protcomplex.OBMol) if obres.GetResidueProperty(0)]


=====================================
plip/test/test_intra.py
=====================================
@@ -0,0 +1,18 @@
+import unittest
+
+from plip.basic import config
+from plip.exchange.report import StructureReport
+from plip.structure.preparation import PDBComplex
+
+
+class IntraTest(unittest.TestCase):
+
+    def test_4day(self):
+        config.PEPTIDES = ['C']
+        pdb_complex = PDBComplex()
+        pdb_complex.load_pdb('./pdb/4day.pdb')
+        for ligand in pdb_complex.ligands:
+            pdb_complex.characterize_complex(ligand)
+            structure_report = StructureReport(pdb_complex, outputprefix="test_")
+            structure_report.write_xml(as_string=True)
+        config.PEPTIDES = []


=====================================
plip/test/test_literature_validated.py
=====================================
@@ -6,6 +6,7 @@ test_literature_validated.py - Unit Tests for literature-validated cases.
 
 
 import unittest
+
 from plip.structure.preparation import PDBComplex
 
 


=====================================
plip/test/test_metal_coordination.py
=====================================
@@ -6,6 +6,7 @@ test_metal_coordination.py - Unit Tests for Metal Coordination.
 
 
 import unittest
+
 from plip.structure.preparation import PDBComplex
 
 


=====================================
plip/test/test_remote_services.py
=====================================
@@ -6,6 +6,7 @@ test_remote_services.py - Unit Tests for remote services.
 
 
 import unittest
+
 from plip.exchange.webservices import check_pdb_status
 
 


=====================================
plip/test/test_xml_parser.py
=====================================
@@ -6,6 +6,7 @@ test_xml_parser.py - Unit Tests for XML Parser.
 
 
 import unittest
+
 from plip.exchange.xml import PlipXML
 
 


=====================================
plip/test/test_xml_writer.py
=====================================
@@ -0,0 +1,24 @@
+import unittest
+
+from plip.exchange.report import StructureReport
+from plip.structure.preparation import PDBComplex
+
+
+class XMLWriterTest(unittest.TestCase):
+    def test_pi_stacking(self):
+        pdb_complex = PDBComplex()
+        pdb_complex.load_pdb('./pdb/4dst_protonated.pdb')
+        for ligand in pdb_complex.ligands:
+            if ':'.join([ligand.hetid, ligand.chain, str(ligand.position)]) == 'GCP:A:202':
+                pdb_complex.characterize_complex(ligand)
+                structure_report = StructureReport(pdb_complex, outputprefix="test_")
+                structure_report.write_xml(as_string=True)
+
+    def test_pication(self):
+        pdb_complex = PDBComplex()
+        pdb_complex.load_pdb('./pdb/6nhb.pdb')
+        for ligand in pdb_complex.ligands:
+            if ':'.join([ligand.hetid, ligand.chain, str(ligand.position)]) == 'H4B:A:802':
+                pdb_complex.characterize_complex(ligand)
+                structure_report = StructureReport(pdb_complex, outputprefix="test_")
+                structure_report.write_xml(as_string=True)
\ No newline at end of file


=====================================
plip/visualization/pymol.py
=====================================
@@ -5,6 +5,8 @@ from time import sleep
 
 from pymol import cmd
 
+from plip.basic import config
+
 
 class PyMOLVisualizer:
 
@@ -340,10 +342,11 @@ class PyMOLVisualizer:
         cmd.viewport(width, height)
         cmd.zoom('visible', 1.5)  # Adapt the zoom to the viewport
         cmd.set('ray_trace_frames', 1)  # Frames are raytraced before saving an image.
-        cmd.mpng(filepath, 1, 1)  # Use batch png mode with 1 frame only
+        cmd.mpng(filepath, config.MODEL, config.MODEL)  # Use batch png mode with 1 frame only
         cmd.mplay()  # cmd.mpng needs the animation to 'run'
         cmd.refresh()
-        originalfile = "".join([filepath, '0001.png'])
+        originalfile = "".join(
+            [filepath, (4 - len(str(config.MODEL))) * '0' + str(config.MODEL) + '.png'])
         newfile = "".join([filepath, '.png'])
 
         #################################################


=====================================
plip/visualization/visualize.py
=====================================
@@ -38,6 +38,7 @@ def visualize_in_pymol(plcomplex):
     vis.set_initial_representations()
 
     cmd.load(plcomplex.sourcefile)
+    cmd.frame(config.MODEL)
     current_name = cmd.get_object_list(selection='(all)')[0]
 
     logger.debug(f'setting current_name to {current_name} and PDB-ID to {pdbid}')



View it on GitLab: https://salsa.debian.org/med-team/plip/-/compare/e8c8aa5dc159972fae62f95ad7da41ea05970804...cdf3ace90672432387f5a06aa0b5d0b86b544d03

-- 
View it on GitLab: https://salsa.debian.org/med-team/plip/-/compare/e8c8aa5dc159972fae62f95ad7da41ea05970804...cdf3ace90672432387f5a06aa0b5d0b86b544d03
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20210419/ea98e4b2/attachment-0001.htm>


More information about the debian-med-commit mailing list