aboutsummaryrefslogtreecommitdiff
path: root/plip/exchange
diff options
context:
space:
mode:
Diffstat (limited to 'plip/exchange')
-rw-r--r--plip/exchange/__init__.py0
-rw-r--r--plip/exchange/json.py1
-rw-r--r--plip/exchange/report.py839
-rw-r--r--plip/exchange/webservices.py54
-rw-r--r--plip/exchange/xml.py285
5 files changed, 0 insertions, 1179 deletions
diff --git a/plip/exchange/__init__.py b/plip/exchange/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/plip/exchange/__init__.py
+++ /dev/null
diff --git a/plip/exchange/json.py b/plip/exchange/json.py
deleted file mode 100644
index 5c7ad5c..0000000
--- a/plip/exchange/json.py
+++ /dev/null
@@ -1 +0,0 @@
-# place holder for module to add Json support \ No newline at end of file
diff --git a/plip/exchange/report.py b/plip/exchange/report.py
deleted file mode 100644
index 8ea2130..0000000
--- a/plip/exchange/report.py
+++ /dev/null
@@ -1,839 +0,0 @@
-import time
-from operator import itemgetter
-
-import lxml.etree as et
-
-from plip.basic import config
-from plip.basic.config import __version__
-from plip.structure.preparation import PDBComplex
-
-
-class StructureReport:
- """Creates reports (xml or txt) for one structure/"""
-
- def __init__(self, mol: PDBComplex, outputprefix: str = "report"):
- self.mol = mol
- self.excluded = self.mol.excluded
- self.xmlreport = self.construct_xml_tree()
- self.txtreport = self.construct_txt_file()
- self.get_bindingsite_data()
- self.outpath = mol.output_path
- self.outputprefix = outputprefix
-
- def construct_xml_tree(self):
- """Construct the basic XML tree"""
- report = et.Element("report")
- plipversion = et.SubElement(report, "plipversion")
- plipversion.text = __version__
- date_of_creation = et.SubElement(report, "date_of_creation")
- date_of_creation.text = time.strftime("%Y/%m/%d")
- citation_information = et.SubElement(report, "citation_information")
- citation_information.text = (
- "Salentin,S. et al. PLIP: fully automated protein-ligand interaction profiler. "
- "Nucl. Acids Res. (1 July 2015) 43 (W1): W443-W447. doi: 10.1093/nar/gkv315"
- )
-
- maintainer_information = et.SubElement(report, "maintainer_information")
- maintainer_information.text = config.__maintainer__
- mode = et.SubElement(report, "mode")
- if config.DNARECEPTOR:
- mode.text = "dna_receptor"
- else:
- mode.text = "default"
- pdbid = et.SubElement(report, "pdbid")
- pdbid.text = self.mol.pymol_name.upper()
- filetype = et.SubElement(report, "filetype")
- filetype.text = self.mol.filetype.upper()
- pdbfile = et.SubElement(report, "pdbfile")
- pdbfile.text = self.mol.sourcefiles["pdbcomplex"]
- pdbfixes = et.SubElement(report, "pdbfixes")
- pdbfixes.text = str(self.mol.information["pdbfixes"])
- filename = et.SubElement(report, "filename")
- filename.text = str(self.mol.sourcefiles.get("filename") or None)
- exligs = et.SubElement(report, "excluded_ligands")
- for i, exlig in enumerate(self.excluded):
- e = et.SubElement(exligs, "excluded_ligand", id=str(i + 1))
- e.text = exlig
- covalent = et.SubElement(report, "covlinkages")
- for i, covlinkage in enumerate(self.mol.covalent):
- e = et.SubElement(covalent, "covlinkage", id=str(i + 1))
- f1 = et.SubElement(e, "res1")
- f2 = et.SubElement(e, "res2")
- f1.text = ":".join(
- [covlinkage.id1, covlinkage.chain1, str(covlinkage.pos1)]
- )
- f2.text = ":".join(
- [covlinkage.id2, covlinkage.chain2, str(covlinkage.pos2)]
- )
- return report
-
- def construct_txt_file(self):
- """Construct the header of the txt file"""
- textlines = [
- "Prediction of noncovalent interactions for PDB structure %s"
- % self.mol.pymol_name.upper(),
- ]
- textlines.append("=" * len(textlines[0]))
- textlines.append(
- "Created on %s using PLIP v%s\n" % (time.strftime("%Y/%m/%d"), __version__)
- )
- textlines.append("If you are using PLIP in your work, please cite:")
- textlines.append(
- "Salentin,S. et al. PLIP: fully automated protein-ligand interaction profiler."
- )
- textlines.append(
- "Nucl. Acids Res. (1 July 2015) 43 (W1): W443-W447. doi: 10.1093/nar/gkv315\n"
- )
- 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")
- return textlines
-
- def get_bindingsite_data(self):
- """Get the additional data for the binding sites"""
- for i, site in enumerate(sorted(self.mol.interaction_sets)):
- s = self.mol.interaction_sets[site]
- bindingsite = BindingSiteReport(s).generate_xml()
- bindingsite.set("id", str(i + 1))
- bindingsite.set("has_interactions", "False")
- self.xmlreport.insert(i + 1, bindingsite)
- for itype in BindingSiteReport(s).generate_txt():
- self.txtreport.append(itype)
- if not s.no_interactions:
- bindingsite.set("has_interactions", "True")
- else:
- self.txtreport.append("No interactions detected.")
-
- def write_xml(self, as_string=False):
- """Write the XML report"""
- if not as_string:
- et.ElementTree(self.xmlreport).write(
- "{}/{}.xml".format(self.outpath, self.outputprefix),
- pretty_print=True,
- xml_declaration=True,
- )
- else:
- output = et.tostring(self.xmlreport, pretty_print=True)
- print(output.decode("utf8"))
-
- def write_txt(self, as_string=False):
- """Write the TXT report"""
- if not as_string:
- with open("{}/{}.txt".format(self.outpath, self.outputprefix), "w") as f:
- [f.write(textline + "\n") for textline in self.txtreport]
- else:
- output = "\n".join(self.txtreport)
- print(output)
-
-
-class BindingSiteReport:
- """Gather report data and generate reports for one binding site in different formats."""
-
- def __init__(self, plcomplex):
-
- ################
- # GENERAL DATA #
- ################
-
- self.complex = plcomplex
- self.ligand = self.complex.ligand
- self.bindingsite = self.complex.bindingsite
- self.output_path = self.complex.output_path
- self.bsid = ":".join(
- [self.ligand.hetid, self.ligand.chain, str(self.ligand.position)]
- )
- self.longname = self.ligand.longname
- self.ligtype = self.ligand.type
- self.bs_res = self.bindingsite.bs_res
- self.min_dist = self.bindingsite.min_dist
- self.bs_res_interacting = self.complex.interacting_res
- self.pdbid = self.complex.pdbid.upper()
- self.lig_members = self.complex.lig_members
- self.interacting_chains = self.complex.interacting_chains
-
- ############################
- # HYDROPHOBIC INTERACTIONS #
- ############################
-
- self.hydrophobic_features = (
- "RESNR",
- "RESTYPE",
- "RESCHAIN",
- "RESNR_LIG",
- "RESTYPE_LIG",
- "RESCHAIN_LIG",
- "DIST",
- "LIGCARBONIDX",
- "PROTCARBONIDX",
- "LIGCOO",
- "PROTCOO",
- )
- self.hydrophobic_info = []
- for hydroph in self.complex.hydrophobic_contacts:
- self.hydrophobic_info.append(
- (
- hydroph.resnr,
- hydroph.restype,
- hydroph.reschain,
- hydroph.resnr_l,
- hydroph.restype_l,
- hydroph.reschain_l,
- "%.2f" % hydroph.distance,
- hydroph.ligatom_orig_idx,
- hydroph.bsatom_orig_idx,
- hydroph.ligatom.coords,
- hydroph.bsatom.coords,
- )
- )
-
- ##################
- # HYDROGEN BONDS #
- ##################
-
- self.hbond_features = (
- "RESNR",
- "RESTYPE",
- "RESCHAIN",
- "RESNR_LIG",
- "RESTYPE_LIG",
- "RESCHAIN_LIG",
- "SIDECHAIN",
- "DIST_H-A",
- "DIST_D-A",
- "DON_ANGLE",
- "PROTISDON",
- "DONORIDX",
- "DONORTYPE",
- "ACCEPTORIDX",
- "ACCEPTORTYPE",
- "LIGCOO",
- "PROTCOO",
- )
- self.hbond_info = []
- for hbond in self.complex.hbonds_pdon + self.complex.hbonds_ldon:
- ligatom, protatom = (
- (hbond.a, hbond.d) if hbond.protisdon else (hbond.d, hbond.a)
- )
- self.hbond_info.append(
- (
- hbond.resnr,
- hbond.restype,
- hbond.reschain,
- hbond.resnr_l,
- hbond.restype_l,
- hbond.reschain_l,
- hbond.sidechain,
- "%.2f" % hbond.distance_ah,
- "%.2f" % hbond.distance_ad,
- "%.2f" % hbond.angle,
- hbond.protisdon,
- hbond.d_orig_idx,
- hbond.dtype,
- hbond.a_orig_idx,
- hbond.atype,
- ligatom.coords,
- protatom.coords,
- )
- )
-
- #################
- # WATER-BRIDGES #
- #################
-
- self.waterbridge_features = (
- "RESNR",
- "RESTYPE",
- "RESCHAIN",
- "RESNR_LIG",
- "RESTYPE_LIG",
- "RESCHAIN_LIG",
- "DIST_A-W",
- "DIST_D-W",
- "DON_ANGLE",
- "WATER_ANGLE",
- "PROTISDON",
- "DONOR_IDX",
- "DONORTYPE",
- "ACCEPTOR_IDX",
- "ACCEPTORTYPE",
- "WATER_IDX",
- "LIGCOO",
- "PROTCOO",
- "WATERCOO",
- )
- # The coordinate format is an exception here, since the interaction is not only between ligand and protein
- self.waterbridge_info = []
- for wbridge in self.complex.water_bridges:
- lig, prot = (
- (wbridge.a, wbridge.d) if wbridge.protisdon else (wbridge.d, wbridge.a)
- )
- self.waterbridge_info.append(
- (
- wbridge.resnr,
- wbridge.restype,
- wbridge.reschain,
- wbridge.resnr_l,
- wbridge.restype_l,
- wbridge.reschain_l,
- "%.2f" % wbridge.distance_aw,
- "%.2f" % wbridge.distance_dw,
- "%.2f" % wbridge.d_angle,
- "%.2f" % wbridge.w_angle,
- wbridge.protisdon,
- wbridge.d_orig_idx,
- wbridge.dtype,
- wbridge.a_orig_idx,
- wbridge.atype,
- wbridge.water_orig_idx,
- lig.coords,
- prot.coords,
- wbridge.water.coords,
- )
- )
-
- ################
- # SALT BRIDGES #
- ################
-
- self.saltbridge_features = (
- "RESNR",
- "RESTYPE",
- "RESCHAIN",
- "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,
- "%.2f" % sb.distance,
- sb.protispos,
- group.capitalize(),
- ",".join(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,
- "%.2f" % sb.distance,
- sb.protispos,
- group.capitalize(),
- ",".join(ids),
- tuple(sb.positive.center),
- tuple(sb.negative.center),
- )
- )
-
- ###############
- # PI-STACKING #
- ###############
-
- self.pistacking_features = (
- "RESNR",
- "RESTYPE",
- "RESCHAIN",
- "RESNR_LIG",
- "RESTYPE_LIG",
- "RESCHAIN_LIG",
- "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]
- 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),
- tuple(stack.ligandring.center),
- tuple(stack.proteinring.center),
- )
- )
-
- ##########################
- # PI-CATION INTERACTIONS #
- ##########################
-
- self.pication_features = (
- "RESNR",
- "RESTYPE",
- "RESCHAIN",
- "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]
- group = "Aromatic"
- self.pication_info.append(
- (
- picat.resnr,
- picat.restype,
- picat.reschain,
- picat.resnr_l,
- picat.restype_l,
- picat.reschain_l,
- "%.2f" % picat.distance,
- "%.2f" % picat.offset,
- picat.protcharged,
- group,
- ",".join(ids),
- tuple(picat.ring.center),
- tuple(picat.charge.center),
- )
- )
- else:
- ids = [str(x) for x in picat.charge.atoms_orig_idx]
- group = picat.charge.fgroup
- self.pication_info.append(
- (
- picat.resnr,
- picat.restype,
- picat.reschain,
- picat.resnr_l,
- picat.restype_l,
- picat.reschain_l,
- "%.2f" % picat.distance,
- "%.2f" % picat.offset,
- picat.protcharged,
- group,
- ",".join(ids),
- tuple(picat.charge.center),
- tuple(picat.ring.center),
- )
- )
-
- #################
- # HALOGEN BONDS #
- #################
-
- self.halogen_features = (
- "RESNR",
- "RESTYPE",
- "RESCHAIN",
- "RESNR_LIG",
- "RESTYPE_LIG",
- "RESCHAIN_LIG",
- "SIDECHAIN",
- "DIST",
- "DON_ANGLE",
- "ACC_ANGLE",
- "DON_IDX",
- "DONORTYPE",
- "ACC_IDX",
- "ACCEPTORTYPE",
- "LIGCOO",
- "PROTCOO",
- )
- self.halogen_info = []
- for halogen in self.complex.halogen_bonds:
- self.halogen_info.append(
- (
- halogen.resnr,
- halogen.restype,
- halogen.reschain,
- halogen.resnr_l,
- halogen.restype_l,
- halogen.reschain_l,
- halogen.sidechain,
- "%.2f" % halogen.distance,
- "%.2f" % halogen.don_angle,
- "%.2f" % halogen.acc_angle,
- halogen.don_orig_idx,
- halogen.donortype,
- halogen.acc_orig_idx,
- halogen.acctype,
- halogen.acc.o.coords,
- halogen.don.x.coords,
- )
- )
-
- ###################
- # METAL COMPLEXES #
- ###################
-
- self.metal_features = (
- "RESNR",
- "RESTYPE",
- "RESCHAIN",
- "RESNR_LIG",
- "RESTYPE_LIG",
- "RESCHAIN_LIG",
- "METAL_IDX",
- "METAL_TYPE",
- "TARGET_IDX",
- "TARGET_TYPE",
- "COORDINATION",
- "DIST",
- "LOCATION",
- "RMS",
- "GEOMETRY",
- "COMPLEXNUM",
- "METALCOO",
- "TARGETCOO",
- )
- self.metal_info = []
- # Coordinate format here is non-standard since the interaction partner can be either ligand or protein
- for m in self.complex.metal_complexes:
- self.metal_info.append(
- (
- m.resnr,
- m.restype,
- m.reschain,
- m.resnr_l,
- m.restype_l,
- m.reschain_l,
- m.metal_orig_idx,
- m.metal_type,
- m.target_orig_idx,
- m.target_type,
- m.coordination_num,
- "%.2f" % m.distance,
- m.location,
- "%.2f" % m.rms,
- m.geometry,
- str(m.complexnum),
- m.metal.coords,
- m.target.atom.coords,
- )
- )
-
- def write_section(self, name, features, info, f):
- """Provides formatting for one section (e.g. hydrogen bonds)"""
- if not len(info) == 0:
- f.write("\n\n### %s ###\n" % name)
- f.write("%s\n" % "\t".join(features))
- for line in info:
- f.write("%s\n" % "\t".join(map(str, line)))
-
- def rst_table(self, array):
- """Given an array, the function formats and returns and table in rST format."""
- # Determine cell width for each column
- cell_dict = {}
- for i, row in enumerate(array):
- for j, val in enumerate(row):
- if j not in cell_dict:
- cell_dict[j] = []
- cell_dict[j].append(val)
- for item in cell_dict:
- cell_dict[item] = (
- max([len(x) for x in cell_dict[item]]) + 1
- ) # Contains adapted width for each column
-
- # Format top line
- num_cols = len(array[0])
- form = "+"
- for col in range(num_cols):
- form += (cell_dict[col] + 1) * "-"
- form += "+"
- form += "\n"
-
- # Format values
- for i, row in enumerate(array):
- form += "| "
- for j, val in enumerate(row):
- cell_width = cell_dict[j]
- form += str(val) + (cell_width - len(val)) * " " + "| "
- form.rstrip()
- form += "\n"
-
- # Seperation lines
- form += "+"
- if i == 0:
- sign = "="
- else:
- sign = "-"
- for col in range(num_cols):
- form += (cell_dict[col] + 1) * sign
- form += "+"
- form += "\n"
- return form
-
- def generate_txt(self):
- """Generates an flat text report for a single binding site"""
-
- txt = []
- titletext = "%s (%s) - %s" % (self.bsid, self.longname, self.ligtype)
- txt.append(titletext)
- for i, member in enumerate(self.lig_members[1:]):
- txt.append(" + %s" % ":".join(str(element) for element in member))
- txt.append("-" * len(titletext))
- txt.append(
- "Interacting chain(s): %s\n"
- % ",".join([chain for chain in self.interacting_chains])
- )
- for section in [
- [
- "Hydrophobic Interactions",
- self.hydrophobic_features,
- self.hydrophobic_info,
- ],
- ["Hydrogen Bonds", self.hbond_features, self.hbond_info],
- ["Water Bridges", self.waterbridge_features, self.waterbridge_info],
- ["Salt Bridges", self.saltbridge_features, self.saltbridge_info],
- ["pi-Stacking", self.pistacking_features, self.pistacking_info],
- ["pi-Cation Interactions", self.pication_features, self.pication_info],
- ["Halogen Bonds", self.halogen_features, self.halogen_info],
- ["Metal Complexes", self.metal_features, self.metal_info],
- ]:
- iname, features, interaction_information = section
- # Sort results first by res number, then by distance and finally ligand coordinates to get a unique order
- interaction_information = sorted(
- interaction_information, key=itemgetter(0, 2, -2)
- )
- if not len(interaction_information) == 0:
-
- txt.append("\n**%s**" % iname)
- table = [
- features,
- ]
- for single_contact in interaction_information:
- values = []
- for x in single_contact:
- if type(x) == str:
- values.append(x)
- elif type(x) == tuple and len(x) == 3: # Coordinates
- values.append("%.3f, %.3f, %.3f" % x)
- else:
- values.append(str(x))
- table.append(values)
- txt.append(self.rst_table(table))
- txt.append("\n")
- return txt
-
- def generate_xml(self):
- """Generates an XML-formatted report for a single binding site"""
- report = et.Element("bindingsite")
- identifiers = et.SubElement(report, "identifiers")
- longname = et.SubElement(identifiers, "longname")
- ligtype = et.SubElement(identifiers, "ligtype")
- hetid = et.SubElement(identifiers, "hetid")
- chain = et.SubElement(identifiers, "chain")
- position = et.SubElement(identifiers, "position")
- composite = et.SubElement(identifiers, "composite")
- members = et.SubElement(identifiers, "members")
- smiles = et.SubElement(identifiers, "smiles")
- inchikey = et.SubElement(identifiers, "inchikey")
-
- # Ligand properties. Number of (unpaired) functional atoms and rings.
- lig_properties = et.SubElement(report, "lig_properties")
- num_heavy_atoms = et.SubElement(lig_properties, "num_heavy_atoms")
- num_hbd = et.SubElement(lig_properties, "num_hbd")
- num_hbd.text = str(self.ligand.num_hbd)
- num_unpaired_hbd = et.SubElement(lig_properties, "num_unpaired_hbd")
- num_unpaired_hbd.text = str(self.complex.num_unpaired_hbd)
- num_hba = et.SubElement(lig_properties, "num_hba")
- num_hba.text = str(self.ligand.num_hba)
- num_unpaired_hba = et.SubElement(lig_properties, "num_unpaired_hba")
- num_unpaired_hba.text = str(self.complex.num_unpaired_hba)
- num_hal = et.SubElement(lig_properties, "num_hal")
- num_hal.text = str(self.ligand.num_hal)
- num_unpaired_hal = et.SubElement(lig_properties, "num_unpaired_hal")
- num_unpaired_hal.text = str(self.complex.num_unpaired_hal)
- num_aromatic_rings = et.SubElement(lig_properties, "num_aromatic_rings")
- num_aromatic_rings.text = str(self.ligand.num_rings)
- num_rot_bonds = et.SubElement(lig_properties, "num_rotatable_bonds")
- num_rot_bonds.text = str(self.ligand.num_rot_bonds)
- molweight = et.SubElement(lig_properties, "molweight")
- molweight.text = str(self.ligand.molweight)
- logp = et.SubElement(lig_properties, "logp")
- logp.text = str(self.ligand.logp)
-
- ichains = et.SubElement(report, "interacting_chains")
- bsresidues = et.SubElement(report, "bs_residues")
- for i, ichain in enumerate(self.interacting_chains):
- c = et.SubElement(ichains, "interacting_chain", id=str(i + 1))
- c.text = ichain
- for i, bsres in enumerate(self.bs_res):
- contact = "True" if bsres in self.bs_res_interacting else "False"
- distance = "%.1f" % self.min_dist[bsres][0]
- aatype = self.min_dist[bsres][1]
- c = et.SubElement(
- bsresidues,
- "bs_residue",
- id=str(i + 1),
- contact=contact,
- min_dist=distance,
- aa=aatype,
- )
- c.text = bsres
- hetid.text, chain.text, position.text = (
- self.ligand.hetid,
- self.ligand.chain,
- str(self.ligand.position),
- )
- composite.text = "True" if len(self.lig_members) > 1 else "False"
- longname.text = self.longname
- ligtype.text = self.ligtype
- smiles.text = self.ligand.smiles
- inchikey.text = self.ligand.inchikey
- num_heavy_atoms.text = str(
- self.ligand.heavy_atoms
- ) # Number of heavy atoms in ligand
- for i, member in enumerate(self.lig_members):
- bsid = ":".join(str(element) for element in member)
- m = et.SubElement(members, "member", id=str(i + 1))
- m.text = bsid
- interactions = et.SubElement(report, "interactions")
-
- def format_interactions(element_name, features, interaction_information):
- """Returns a formatted element with interaction information."""
- interaction = et.Element(element_name)
- # Sort results first by res number, then by distance and finally ligand coordinates to get a unique order
- interaction_information = sorted(
- interaction_information, key=itemgetter(0, 2, -2)
- )
- for j, single_contact in enumerate(interaction_information):
- if not element_name == "metal_complexes":
- new_contact = et.SubElement(
- interaction, element_name[:-1], id=str(j + 1)
- )
- else: # Metal Complex[es]
- 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":
- feat = et.SubElement(new_contact, features[i].lower())
- for k, atm_idx in enumerate(feature.split(",")):
- idx = et.SubElement(feat, "idx", id=str(k + 1))
- idx.text = str(atm_idx)
- elif features[i].endswith("COO"):
- feat = et.SubElement(new_contact, features[i].lower())
- xc, yc, zc = feature
- xcoo = et.SubElement(feat, "x")
- xcoo.text = "%.3f" % xc
- ycoo = et.SubElement(feat, "y")
- ycoo.text = "%.3f" % yc
- zcoo = et.SubElement(feat, "z")
- zcoo.text = "%.3f" % zc
- else:
- feat = et.SubElement(new_contact, features[i].lower())
- feat.text = str(feature)
- return interaction
-
- interactions.append(
- format_interactions(
- "hydrophobic_interactions",
- self.hydrophobic_features,
- self.hydrophobic_info,
- )
- )
- interactions.append(
- format_interactions("hydrogen_bonds", self.hbond_features, self.hbond_info)
- )
- interactions.append(
- format_interactions(
- "water_bridges", self.waterbridge_features, self.waterbridge_info
- )
- )
- interactions.append(
- format_interactions(
- "salt_bridges", self.saltbridge_features, self.saltbridge_info
- )
- )
- interactions.append(
- format_interactions(
- "pi_stacks", self.pistacking_features, self.pistacking_info
- )
- )
- interactions.append(
- format_interactions(
- "pi_cation_interactions", self.pication_features, self.pication_info
- )
- )
- interactions.append(
- format_interactions(
- "halogen_bonds", self.halogen_features, self.halogen_info
- )
- )
- interactions.append(
- format_interactions("metal_complexes", self.metal_features, self.metal_info)
- )
-
- # Mappings
- mappings = et.SubElement(report, "mappings")
- smiles_to_pdb = et.SubElement(
- mappings, "smiles_to_pdb"
- ) # SMILES numbering to PDB file numbering (atoms)
- bsid = ":".join(
- [self.ligand.hetid, self.ligand.chain, str(self.ligand.position)]
- )
- if self.ligand.atomorder is not None:
- smiles_to_pdb_map = [
- (
- key,
- self.ligand.Mapper.mapid(
- self.ligand.can_to_pdb[key], mtype="protein", bsid=bsid
- ),
- )
- for key in self.ligand.can_to_pdb
- ]
- smiles_to_pdb.text = ",".join(
- [
- str(mapping[0]) + ":" + str(mapping[1])
- for mapping in smiles_to_pdb_map
- ]
- )
- else:
- smiles_to_pdb.text = ""
-
- return report
diff --git a/plip/exchange/webservices.py b/plip/exchange/webservices.py
deleted file mode 100644
index 0c8cd3e..0000000
--- a/plip/exchange/webservices.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import sys
-from urllib.error import HTTPError
-from urllib.request import urlopen
-
-import lxml.etree as et
-
-from plip.basic import logger
-
-logger = logger.get_logger()
-
-
-def check_pdb_status(pdbid):
- """Returns the status and up-to-date entry in the PDB for a given PDB ID"""
- url = 'http://www.rcsb.org/pdb/rest/idStatus?structureId=%s' % pdbid
- xmlf = urlopen(url)
- xml = et.parse(xmlf)
- xmlf.close()
- status = None
- current_pdbid = pdbid
- for df in xml.xpath('//record'):
- status = df.attrib['status'] # Status of an entry can be either 'UNKWOWN', 'OBSOLETE', or 'CURRENT'
- if status == 'OBSOLETE':
- current_pdbid = df.attrib['replacedBy'] # Contains the up-to-date PDB ID for obsolete entries
- return [status, current_pdbid.lower()]
-
-
-def fetch_pdb(pdbid):
- """Get the newest entry from the RCSB server for the given PDB ID. Exits with '1' if PDB ID is invalid."""
- pdbid = pdbid.lower()
- logger.info(f'checking status of PDB-ID {pdbid}')
- state, current_entry = check_pdb_status(pdbid) # Get state and current PDB ID
-
- if state == 'OBSOLETE':
- logger.info(f'entry is obsolete, getting {current_entry} instead')
- elif state == 'CURRENT':
- logger.info('entry is up-to-date')
- elif state == 'UNKNOWN':
- logger.error('invalid PDB-ID (entry does not exist on PDB server)')
- sys.exit(1)
- logger.info('downloading file from PDB')
- # get URL for current entry
- # @todo needs update to react properly on response codes of RCSB servers
- pdburl = f'http://www.rcsb.org/pdb/files/{current_entry}.pdb'
- try:
- pdbfile = urlopen(pdburl).read().decode()
- # If no PDB file is available, a text is now shown with "We're sorry, but ..."
- # Could previously be distinguished by an HTTP error
- if 'sorry' in pdbfile:
- logger.error('no file in PDB format available from wwPDB for the given PDB ID.')
- sys.exit(1)
- except HTTPError:
- logger.error('no file in PDB format available from wwPDB for the given PDB ID')
- sys.exit(1)
- return [pdbfile, current_entry]
diff --git a/plip/exchange/xml.py b/plip/exchange/xml.py
deleted file mode 100644
index a5eee21..0000000
--- a/plip/exchange/xml.py
+++ /dev/null
@@ -1,285 +0,0 @@
-from lxml import etree
-
-from urllib.request import urlopen
-
-
-class XMLStorage:
- """Generic class for storing XML data from PLIP XML files."""
-
- def getdata(self, tree, location, force_string=False):
- """Gets XML data from a specific element and handles types."""
- found = tree.xpath('%s/text()' % location)
- if not found:
- return None
- else:
- data = found[0]
- if force_string:
- return data
- if data == 'True':
- return True
- elif data == 'False':
- return False
- else:
- try:
- return int(data)
- except ValueError:
- try:
- return float(data)
- except ValueError:
- # It's a string
- return data
-
- def getcoordinates(self, tree, location):
- """Gets coordinates from a specific element in PLIP XML"""
- return tuple(float(x) for x in tree.xpath('.//%s/*/text()' % location))
-
-
-class Interaction(XMLStorage):
- """Stores information on a specific interaction type"""
-
- def __init__(self, interaction_part):
- self.id = interaction_part.get('id')
- self.resnr = self.getdata(interaction_part, 'resnr')
- self.restype = self.getdata(interaction_part, 'restype', force_string=True)
- self.reschain = self.getdata(interaction_part, 'reschain', force_string=True)
- self.resnr_lig = self.getdata(interaction_part, 'resnr_lig')
- self.restype_lig = self.getdata(interaction_part, 'restype_lig', force_string=True)
- self.reschain_lig = self.getdata(interaction_part, 'reschain_lig', force_string=True)
- self.ligcoo = self.getcoordinates(interaction_part, 'ligcoo')
- self.protcoo = self.getcoordinates(interaction_part, 'protcoo')
-
-
-class HydrophobicInteraction(Interaction):
- """Stores information on a hydrophobic interaction"""
-
- def __init__(self, hydrophobic_part):
- Interaction.__init__(self, hydrophobic_part)
- self.dist = self.getdata(hydrophobic_part, 'dist')
- self.ligcarbonidx = self.getdata(hydrophobic_part, 'ligcarbonidx')
- self.protcarbonidx = self.getdata(hydrophobic_part, 'protcarbonidx')
-
-
-class HydrogenBond(Interaction):
- """Stores information on a hydrogen bond interaction"""
-
- def __init__(self, hbond_part):
- Interaction.__init__(self, hbond_part)
- self.sidechain = self.getdata(hbond_part, 'sidechain')
- self.dist_h_a = self.getdata(hbond_part, 'dist_h-a')
- self.dist_d_a = self.getdata(hbond_part, 'dist_d-a')
- self.dist = self.dist_d_a
-
- self.don_angle = self.getdata(hbond_part, 'don_angle')
- self.protisdon = self.getdata(hbond_part, 'protisdon')
- self.donoridx = self.getdata(hbond_part, 'donoridx')
- self.acceptoridx = self.getdata(hbond_part, 'acceptoridx')
- self.donortype = self.getdata(hbond_part, 'donortype', force_string=True)
- self.acceptortype = self.getdata(hbond_part, 'acceptortype', force_string=True)
-
-
-class WaterBridge(Interaction):
- """Stores information on a water bridge interaction"""
-
- def __init__(self, wbridge_part):
- Interaction.__init__(self, wbridge_part)
- self.dist_a_w = self.getdata(wbridge_part, 'dist_a-w')
- self.dist_d_w = self.getdata(wbridge_part, 'dist_d-w')
- self.don_angle = self.getdata(wbridge_part, 'don_angle')
- self.water_angle = self.getdata(wbridge_part, 'water_angle')
- self.protisdon = self.getdata(wbridge_part, 'protisdon')
- self.dist = self.dist_a_w if self.protisdon else self.dist_d_w
-
- self.donor_idx = self.getdata(wbridge_part, 'donor_idx')
- self.acceptor_idx = self.getdata(wbridge_part, 'acceptor_idx')
- self.donortype = self.getdata(wbridge_part, 'donortype', force_string=True)
- self.acceptortype = self.getdata(wbridge_part, 'acceptortype', force_string=True)
- self.water_idx = self.getdata(wbridge_part, 'water_idx')
- self.watercoo = self.getcoordinates(wbridge_part, 'watercoo')
-
-
-class SaltBridge(Interaction):
- """Stores information on a salt bridge interaction"""
-
- def __init__(self, sbridge_part):
- Interaction.__init__(self, sbridge_part)
- self.dist = self.getdata(sbridge_part, 'dist')
- self.protispos = self.getdata(sbridge_part, 'protispos')
- 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')]
-
-
-class PiStacking(Interaction):
- """Stores information on a pi stacking interaction"""
-
- def __init__(self, pistack_part):
- Interaction.__init__(self, pistack_part)
- self.centdist = self.getdata(pistack_part, 'centdist')
- self.dist = self.centdist
- self.angle = self.getdata(pistack_part, 'angle')
- self.offset = self.getdata(pistack_part, 'offset')
- self.type = self.getdata(pistack_part, 'type')
- self.lig_idx_list = [int(tagpart.text) for tagpart in
- pistack_part.xpath('lig_idx_list/idx')]
-
-
-class PiCation(Interaction):
- """Stores information on a pi cation interaction"""
-
- def __init__(self, pication_part):
- Interaction.__init__(self, pication_part)
- self.dist = self.getdata(pication_part, 'dist')
- self.offset = self.getdata(pication_part, 'offset')
- self.protcharged = self.getdata(pication_part, 'protcharged')
- self.lig_group = self.getdata(pication_part, 'lig_group')
- self.lig_idx_list = [int(tag.text) for tag in pication_part.xpath('.//lig_idx_list/idx')]
-
-
-class HalogenBond(Interaction):
- """Stores information on a halogen bond interaction"""
-
- def __init__(self, halogen_part):
- Interaction.__init__(self, halogen_part)
- self.dist = self.getdata(halogen_part, 'dist')
- self.don_angle = self.getdata(halogen_part, 'don_angle')
- self.acc_angle = self.getdata(halogen_part, 'acc_angle')
- self.donortype = self.getdata(halogen_part, 'donortype', force_string=True)
- self.acceptortype = self.getdata(halogen_part, 'acceptortype', force_string=True)
- self.don_idx = self.getdata(halogen_part, 'don_idx')
- self.acc_idx = self.getdata(halogen_part, 'acc_idx')
- self.sidechain = self.getdata(halogen_part, 'sidechain')
-
-
-class MetalComplex(Interaction):
- """Stores information on a metal complexe interaction"""
-
- def __init__(self, metalcomplex_part):
- Interaction.__init__(self, metalcomplex_part)
- self.metal_idx = self.getdata(metalcomplex_part, 'metal_idx')
- self.metal_type = self.getdata(metalcomplex_part, 'metal_type', force_string=True)
- self.target_idx = self.getdata(metalcomplex_part, 'target_idx')
- self.target_type = self.getdata(metalcomplex_part, 'target_type', force_string=True)
- self.coordination = self.getdata(metalcomplex_part, 'coordination')
- self.dist = self.getdata(metalcomplex_part, 'dist')
- self.location = self.getdata(metalcomplex_part, 'location', force_string=True)
- self.rms = self.getdata(metalcomplex_part, 'rms')
- self.geometry = self.getdata(metalcomplex_part, 'geometry', force_string=True)
- self.complexnum = self.getdata(metalcomplex_part, 'complexnum')
- self.targetcoo = self.getcoordinates(metalcomplex_part, 'targetcoo')
- self.metalcoo = self.getcoordinates(metalcomplex_part, 'metalcoo')
-
-
-class BSite(XMLStorage):
- """Stores all information about an specific binding site."""
-
- def __init__(self, bindingsite, pdbid):
- self.bindingsite = bindingsite
- self.pdbid = pdbid
- self.bsid = ":".join(bindingsite.xpath('identifiers/*/text()')[2:5])
- self.uniqueid = ":".join([self.pdbid, self.bsid])
- self.hetid = self.getdata(bindingsite, 'identifiers/hetid', force_string=True)
- self.longname = self.getdata(bindingsite, 'identifiers/longname', force_string=True)
- self.ligtype = self.getdata(bindingsite, 'identifiers/ligtype', force_string=True)
- self.smiles = self.getdata(bindingsite, 'identifiers/smiles', force_string=True)
- self.inchikey = self.getdata(bindingsite, 'identifiers/inchikey', force_string=True)
- self.position = self.getdata(bindingsite, 'identifiers/position')
- self.chain = self.getdata(bindingsite, 'identifiers/chain', force_string=True)
-
- # Information on binding site members
- self.members = []
- for member in bindingsite.xpath('identifiers/members/member'):
- self.members += member.xpath('text()')
-
- self.composite = self.getdata(bindingsite, 'identifiers/composite')
-
- # Ligand Properties
- self.heavy_atoms = self.getdata(bindingsite, 'lig_properties/num_heavy_atoms')
- self.hbd = self.getdata(bindingsite, 'lig_properties/num_hbd')
- self.unpaired_hbd = self.getdata(bindingsite, 'lig_properties/num_unpaired_hbd')
- self.hba = self.getdata(bindingsite, 'lig_properties/num_hba')
- self.unpaired_hba = self.getdata(bindingsite, 'lig_properties/num_unpaired_hba')
- self.hal = self.getdata(bindingsite, 'lig_properties/num_hal')
- self.unpaired_hal = self.getdata(bindingsite, 'lig_properties/num_unpaired_hal')
- self.molweight = self.getdata(bindingsite, 'lig_properties/molweight')
- self.logp = self.getdata(bindingsite, 'lig_properties/logp')
- self.rotatable_bonds = self.getdata(bindingsite, 'lig_properties/num_rotatable_bonds')
- self.rings = self.getdata(bindingsite, 'lig_properties/num_aromatic_rings')
-
- # Binding Site residues
- self.bs_res = []
- for tagpart in bindingsite.xpath('bs_residues/bs_residue'):
- resnumber, reschain = tagpart.text[:-1], tagpart.text[-1]
- aa, contact, min_dist = tagpart.get('aa'), tagpart.get('contact'), tagpart.get('min_dist')
- new_bs_res = {'resnr': int(resnumber), 'reschain': reschain, 'aa': aa,
- 'contact': True if contact == 'True' else False, 'min_dist': float(min_dist)}
- self.bs_res.append(new_bs_res)
-
- # Interacting chains
- self.interacting_chains = []
- for chain in bindingsite.xpath('interacting_chains/interacting_chain'):
- self.interacting_chains += chain.xpath('text()')
-
- # Interactions
- interactions = bindingsite.xpath('interactions')[0]
- self.hydrophobics = [HydrophobicInteraction(x) for x in
- interactions.xpath('hydrophobic_interactions/hydrophobic_interaction')]
- self.hbonds = [HydrogenBond(x) for x in interactions.xpath('hydrogen_bonds/hydrogen_bond')]
- self.wbridges = [WaterBridge(x) for x in interactions.xpath('water_bridges/water_bridge')]
- self.sbridges = [SaltBridge(x) for x in interactions.xpath('salt_bridges/salt_bridge')]
- self.pi_stacks = [PiStacking(x) for x in interactions.xpath('pi_stacks/pi_stack')]
- self.pi_cations = [PiCation(x) for x in interactions.xpath('pi_cation_interactions/pi_cation_interaction')]
- 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)
- self.has_interactions = self.num_contacts > 0
-
- self.get_atom_mapping()
- self.counts = self.get_counts()
-
- def get_atom_mapping(self):
- """Parses the ligand atom mapping."""
- # Atom mappings
- smiles_to_pdb_mapping = self.bindingsite.xpath('mappings/smiles_to_pdb/text()')
- if not smiles_to_pdb_mapping:
- self.mappings = {'smiles_to_pdb': None, 'pdb_to_smiles': None}
- else:
- smiles_to_pdb_mapping = {int(y[0]): int(y[1]) for y in [x.split(':')
- for x in smiles_to_pdb_mapping[0].split(',')]}
- self.mappings = {'smiles_to_pdb': smiles_to_pdb_mapping}
- self.mappings['pdb_to_smiles'] = {v: k for k, v in self.mappings['smiles_to_pdb'].items()}
-
- def get_counts(self):
- """counts the interaction types and backbone hydrogen bonding in a binding site"""
-
- hbondsback = len([hb for hb in self.hbonds if not hb.sidechain])
- counts = {'hydrophobics': len(self.hydrophobics), 'hbonds': len(self.hbonds),
- 'wbridges': len(self.wbridges), 'sbridges': len(self.sbridges), 'pistacks': len(self.pi_stacks),
- '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']
- return counts
-
-
-class PlipXML(XMLStorage):
- """Parses and stores all information from a PLIP XML file."""
-
- def __init__(self, xmlfile):
- self.load_data(xmlfile)
-
- # Parse general information
- self.version = self.getdata(self.doc, '/report/plipversion/')
- self.pdbid = self.getdata(self.doc, '/report/pdbid', force_string=True)
- self.filetype = self.getdata(self.doc, '/report/filetype')
- self.fixed = self.getdata(self.doc, '/report/pdbfixes/')
- self.filename = self.getdata(self.doc, '/report/filename')
- self.excluded = self.doc.xpath('/report/excluded_ligands/excluded_ligand/text()')
-
- # Parse binding site information
- self.bsites = {BSite(bs, self.pdbid).bsid: BSite(bs, self.pdbid) for bs in self.doc.xpath('//bindingsite')}
- self.num_bsites = len(self.bsites)
-
- 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