Source code for pygtop.ligands

"""Contains ligand-specific objects and functions."""

from collections import Counter
from . import gtop
from . import pdb
from .interactions import Interaction, get_interaction_by_id
from .exceptions import NoSuchLigandError
from .shared import DatabaseLink, strip_html

[docs]def get_ligand_by_id(ligand_id): """Returns a Ligand object of the ligand with the given ID. :param int ligand_id: The GtoP ID of the Ligand desired. :rtype: :py:class:`Ligand` :raises: :class:`.NoSuchLigandError` if no such ligand exists in the database""" if not isinstance(ligand_id, int): raise TypeError("ligand_id must be int, not '%s'" % str(ligand_id)) json_data = gtop.get_json_from_gtop("ligands/%i" % ligand_id) if json_data: return Ligand(json_data) else: raise NoSuchLigandError("There is no ligand with ID %i" % ligand_id)
[docs]def get_all_ligands(): """Returns a list of all ligands in the Guide to PHARMACOLOGY database. This can take a few seconds. :returns: list of :py:class:`Ligand` objects""" json_data = gtop.get_json_from_gtop("ligands") return [Ligand(l) for l in json_data]
[docs]def get_ligands_by(criteria): """Get all ligands which specify the criteria dictionary. :param dict criteria: A dictionary of `field=value` pairs. See the\ `GtoP ligand web services page <http://www.guidetopharmacology.org/\ webServices.jsp#ligands>`_ for key/value pairs which can be supplied. :returns: list of :py:class:`Ligand` objects.""" if not isinstance(criteria, dict): raise TypeError("criteria must be dict, not '%s'" % str(criteria)) search_string = "&".join(["%s=%s" % (key, criteria[key]) for key in criteria]) json_data = gtop.get_json_from_gtop("ligands?%s" % search_string) if json_data: return [Ligand(l) for l in json_data] else: return []
[docs]def get_ligand_by_name(name): """Returns the ligand which matches the name given. :param str name: The name of the ligand to search for. Note that synonyms \ will not be searched. :rtype: :py:class:`Ligand` :raises: :class:`.NoSuchLigandError` if no such ligand exists in the database.""" if not isinstance(name, str): raise TypeError("name must be str, not '%s'" % str(name)) ligands = get_ligands_by({"name": name}) if ligands: return ligands[0] else: raise NoSuchLigandError("There is no ligand with name %s" % name)
[docs]def get_ligands_by_smiles(smiles, search_type="exact", cutoff=0.8): """Search for ligands by SMILES string. :param str smiles: The SMILES string to search with. :param str search_type: The type of search. Viable options are ``"exact"``, \ ``"substructure"`` or ``"similarity"``. :param float cutoff: If performing a similarity search, this is the cutoff \ used for similarity. The default is 0.8 and the maximum is 1. :returns: list of :py:class:`Ligand` objects.""" if not isinstance(smiles, str): raise TypeError("smiles must be str, not '%s'" % str(smiles)) if not isinstance(search_type, str): raise TypeError("search_type must be str, not '%s'" % str(search_type)) if search_type not in ["exact", "substructure", "similarity"]: raise ValueError("'%s' is not a valud search type" % search_type) if not isinstance(cutoff, int) and not isinstance(cutoff, float): raise TypeError("cutoff must be numeric, not '%s'" % str(cutoff)) if not 0 <= cutoff <= 1: raise ValueError("cutoff must be between 0 and 1, not %s" % (str(cutoff))) query = "ligands/%s?smiles=%s%s" % ( search_type, smiles, ("&similarityGt=%i" % (cutoff * 100)) if search_type == "similarity" else "" ) json_data = gtop.get_json_from_gtop(query) if json_data: return [Ligand(l) for l in json_data] else: return []
[docs]class Ligand: """A Guide to PHARMACOLOGY ligand object. :param json_data: A dictionary obtained from the web services.""" def __init__(self, json_data): self.json_data = json_data self._ligand_id = json_data["ligandId"] self._name = json_data["name"] self._abbreviation = json_data["abbreviation"] if json_data["abbreviation"] else None self._inn = json_data["inn"] self._ligand_type = json_data["type"] self._species = json_data["species"] self._radioactive = json_data["radioactive"] self._labelled = json_data["labelled"] self._approved = json_data["approved"] self._withdrawn = json_data["withdrawn"] self._approval_source = json_data["approvalSource"] self._subunit_ids = json_data["subunitIds"] self._complex_ids = json_data["complexIds"] self._prodrug_ids = json_data["prodrugIds"] self._active_drug_ids = json_data["activeDrugIds"] def __repr__(self): return "<Ligand %i (%s)>" % (self._ligand_id, self._name)
[docs] def ligand_id(self): """Returns the ligand's GtoP ID. :rtype: int""" return self._ligand_id
@strip_html def name(self): """Returns the ligand's name. :param bool strip_html: If ``True``, the name will have HTML entities stripped. :rtype: str""" return self._name @strip_html def abbreviation(self): """Returns the ligand's abbreviated name. :param bool strip_html: If ``True``, the abbreviation will have HTML entities stripped. :rtype: str""" return self._abbreviation @strip_html def inn(self): """Returns the ligand's INN name. :param bool strip_html: If ``True``, the name will have HTML entities stripped. :rtype: str""" return self._inn
[docs] def ligand_type(self): """Returns the ligand's type. :rtype: str""" return self._ligand_type
[docs] def species(self): """Returns the ligand's species, where appropriate. :rtype: str""" return self._species
[docs] def radioactive(self): """Returns True if the ligand is radioactive. :rtype: bool""" return self._radioactive
[docs] def labelled(self): """Returns True if the ligand is labelled. :rtype: bool""" return self._labelled
[docs] def approved(self): """Returns True if the ligand is approved. :rtype: bool""" return self._approved
[docs] def withdrawn(self): """Returns True if the ligand has been withdrawn. :rtype: bool""" return self._withdrawn
@strip_html def approval_source(self): """Returns the regulatory body that approved the ligand, where appropriate. :param bool strip_html: If ``True``, the name will have HTML entities stripped. :rtype: str""" return self._approval_source
[docs] def subunit_ids(self): """Returns the the ligand IDs of all ligands which are subunits of this target. :returns: list of ``int``""" return self._subunit_ids
[docs] def subunits(self): """Returns a list of all ligands which are subunits of this ligand. :returns: list of :py:class:`Ligand` objects""" return [get_ligand_by_id(id_) for id_ in self._subunit_ids]
[docs] def complex_ids(self): """Returns the the ligand IDs of all ligands of which this target is a subunit. :returns: list of ``int``""" return self._complex_ids
[docs] def complexes(self): """Returns a list of all ligands of which this ligand is a subunit. :returns: list of :py:class:`Ligand` objects""" return [get_ligand_by_id(id_) for id_ in self._complex_ids]
[docs] def prodrug_ids(self): """Returns the the ligand IDs of all ligands which are prodrugs of this ligand. :returns: list of ``int``""" return self._prodrug_ids
[docs] def prodrugs(self): """Returns a list of all ligands which are prodrugs of this ligand. :returns: list of :py:class:`Ligand` objects""" return [get_ligand_by_id(id_) for id_ in self._prodrug_ids]
[docs] def active_drug_ids(self): """Returns the the ligand IDs of all ligands which are active equivalents of this ligand. :returns: list of ``int``""" return self._active_drug_ids
[docs] def active_drugs(self): """Returns a list of all ligands which are active equivalents of this ligand. :returns: list of :py:class:`Ligand` objects""" return [get_ligand_by_id(id_) for id_ in self._active_drug_ids]
[docs] def iupac_name(self): """Returns the ligand's IUPAC name. :rtype: str""" return self._get_structure_json().get("iupacName")
[docs] def smiles(self): """Returns the ligand's SMILES string. :rtype: str""" return self._get_structure_json().get("smiles")
[docs] def inchi(self): """Returns the ligand's InChI string. :rtype: str""" return self._get_structure_json().get("inchi")
[docs] def inchi_key(self): """Returns the ligand's InChI key. :rtype: str""" return self._get_structure_json().get("inchiKey")
[docs] def one_letter_sequence(self): """Returns the ligand's single letter amino acid sequence where appropriate. :rtype: str""" return self._get_structure_json().get("oneLetterSeq")
[docs] def three_letter_sequence(self): """Returns the ligand's three letter amino acid sequence where appropriate. :rtype: str""" return self._get_structure_json().get("threeLetterSeq")
[docs] def post_translational_modifications(self): """Returns any post-translational modifications. :rtype: str""" return self._get_structure_json().get("postTranslationalModifications")
[docs] def chemical_modifications(self): """Returns any chemical modifications. :rtype: str""" return self._get_structure_json().get("chemicalModifications")
[docs] def hydrogen_bond_acceptors(self): """Returns the number of hydrogen bond accepting atoms. :rtype: int""" return self._get_molecular_json().get("hydrogenBondAcceptors")
[docs] def hydrogen_bond_donors(self): """Returns the number of hydrogen bond donor atoms. :rtype: int""" return self._get_molecular_json().get("hydrogenBondDonors")
[docs] def rotatable_bonds(self): """Returns the number of rotatable bonds in the ligand. :rtype: int""" return self._get_molecular_json().get("rotatableBonds")
[docs] def topological_polar_surface_area(self): """Returns the polar surface area of the ligand in Angstroms. :rtype: float""" return self._get_molecular_json().get("topologicalPolarSurfaceArea")
[docs] def molecular_weight(self): """Returns the ligand's mass in Daltons. :rtype: float""" return self._get_molecular_json().get("molecularWeight")
[docs] def log_p(self): """Returns the logP value of the ligand. :rtype: int""" return self._get_molecular_json().get("logP")
[docs] def lipinski_rules_broken(self): """Returns the number of Lipinski's Rules the ligand breaks. :rtype: int""" return self._get_molecular_json().get("lipinskisRuleOfFive")
@strip_html def synonyms(self): """Returns the number ligand's synonyms :returns: list of ``str``""" return [synonym["name"] for synonym in self._get_synonym_json()]
[docs] def general_comments(self): """Returns general comments pertaining to the ligand. :rtype: str""" return self._get_comments_json().get("comments")
[docs] def bioactivity_comments(self): """Returns comments pertaining to bioactivity. :rtype: str""" return self._get_molecular_json().get("bioactivityComments")
[docs] def clinical_use_comments(self): """Returns comments pertaining to clinical use. :rtype: str""" return self._get_molecular_json().get("clinicalUse")
[docs] def mechanism_of_action_comments(self): """Returns comments pertaining to mechanism. :rtype: str""" return self._get_molecular_json().get("mechanismOfAction")
[docs] def absorption_and_distribution_comments(self): """Returns comments pertaining to absorption and distribution. :rtype: str""" return self._get_molecular_json().get("absorptionAndDistribution")
[docs] def metabolism_comments(self): """Returns comments pertaining to metabolism. :rtype: str""" return self._get_molecular_json().get("metabolism")
[docs] def elimination_comments(self): """Returns comments pertaining to elimination from the body. :rtype: str""" return self._get_molecular_json().get("elimination")
[docs] def population_pharmacokinetics_comments(self): """Returns comments pertaining to population pharmacokinetics. :rtype: str""" return self._get_molecular_json().get("populationPharmacokinetics")
[docs] def organ_function_impairments_comments(self): """Returns comments pertaining to organ function impairment. :rtype: str""" return self._get_molecular_json().get("organFunctionImpairment")
[docs] def mutations_and_pathophysiology_comments(self): """Returns comments pertaining to mutations and pathophysiology. :rtype: str""" return self._get_molecular_json().get("mutationsAndPathophysiology")
[docs] def interactions(self): """Returns a list of interactions for this ligand. :rtype: list of :py:class:`.Interaction`""" return [Interaction(interaction_json) for interaction_json in self._get_interactions_json()]
get_interaction_by_id = get_interaction_by_id """Returns an Interaction object of a given ID belonging to the ligand. :param int interaction_id: The interactions's ID. :rtype: :py:class:`.Interaction` :raises: :class:`.NoSuchInteractionError`: if no such interaction exists in the database."""
[docs] def targets(self): """Returns a list of all targets which this ligand interacts with. :returns: list of :py:class:`.Target` objects""" targets = [] for interaction in self.interactions(): target = interaction.target() if target not in targets: targets.append(target) return targets
@pdb.ask_about_molecupy def gtop_pdbs(self): """Returns a list of PDBs which the Guide to PHARMACOLOGY says contain this ligand. :param bool as_molecupy: Returns the PDBs as \ `molecuPy <http://molecupy.readthedocs.io>`_ PDB objects. :returns: list of ``str`` PDB codes""" pdbs = [] for interaction in self.interactions(): for pdb in interaction.gtop_pdbs(): if pdb not in pdbs: pdbs.append(pdb) return pdbs @pdb.ask_about_molecupy def smiles_pdbs(self, search_type="exact"): """Queries the RSCB PDB database with the ligand's SMILES string. :param str search_type: The type of search to run - whether exact matches\ only should be returned. :param bool as_molecupy: Returns the PDBs as \ `molecuPy <http://molecupy.readthedocs.io>`_ PDB objects. :returns: list of ``str`` PDB codes""" if self.smiles(): xml = pdb.query_rcsb("smilesQuery", { "smiles": self.smiles(), "search_type": search_type }) if xml: ligand_elements = list(xml[0]) return [element.attrib["structureId"] for element in ligand_elements] else: return [] else: return [] @pdb.ask_about_molecupy def inchi_pdbs(self): """Queries the RSCB PDB database with the ligand's InChI string. :param bool as_molecupy: Returns the PDBs as \ `molecuPy <http://molecupy.readthedocs.io>`_ PDB objects. :returns: list of ``str`` PDB codes""" if self.inchi(): results = pdb.query_rcsb_advanced("ChemCompDescriptorQuery", { "descriptor": self.inchi(), "descriptorType": "InChI" }) return results if results else [] else: return [] @pdb.ask_about_molecupy def name_pdbs(self, comparator="equals"): """Queries the RSCB PDB database with the ligand's name. :param str comparator: The type of search to run - whether exact matches\ only should be returned, or substrings etc. :param bool as_molecupy: Returns the PDBs as \ `molecuPy <http://molecupy.readthedocs.io>`_ PDB objects. :returns: list of ``str`` PDB codes""" results = pdb.query_rcsb_advanced("ChemCompNameQuery", { "comparator": comparator.title(), "name": self.name(), "polymericType": "Any" }) return results if results else [] @pdb.ask_about_molecupy def sequence_pdbs(self): """Queries the RSCB PDB database with the ligand's amino acid sequence,\ if that ligand is a peptide. :param bool as_molecupy: Returns the PDBs as \ `molecuPy <http://molecupy.readthedocs.io>`_ PDB objects. :returns: list of ``str`` PDB codes""" if self.one_letter_sequence(): results = pdb.query_rcsb_advanced("SequenceQuery", { "sequence": self.one_letter_sequence(), "eCutOff": "0.01", "searchTool": "blast", "sequenceIdentityCutoff": "100" }) return results if results else [] else: return [] @pdb.ask_about_molecupy def het_pdbs(self): """Queries the RSCB PDB database with the ligand's amino acid sequence,\ if that ligand is a peptide. :param bool as_molecupy: Returns the PDBs as \ `molecuPy <http://molecupy.readthedocs.io>`_ PDB objects. :returns: list of ``str`` PDB codes""" het = [h for h in self.database_links() if "PDB" in h.database()] if het: results = pdb.query_rcsb_advanced("ChemCompIdQuery", { "chemCompId": het[0].accession(), }) return results if results else [] else: return [] @pdb.ask_about_molecupy def all_external_pdbs(self): """Queries the RSCB PDB database by all parameters. :param bool as_molecupy: Returns the PDBs as \ `molecuPy <http://molecupy.readthedocs.io>`_ PDB objects. :returns: list of ``str`` PDB codes""" return list(set( self.smiles_pdbs() + self.inchi_pdbs() + self.name_pdbs() + self.sequence_pdbs() + self.het_pdbs() )) @pdb.ask_about_molecupy def all_pdbs(self): """Get a list of PDB codes using all means available - annotated and external. :param bool as_molecupy: Returns the PDBs as \ `molecuPy <http://molecupy.readthedocs.io>`_ PDB objects. :returns: list of ``str`` PDB codes""" return list(set( self.gtop_pdbs() + self.all_external_pdbs() ))
[docs] def find_in_pdb_by_smiles(self, molecupy_pdb): """Searches for the ligand in a `molecuPy <http://molecupy.readthedocs.io>`_ PDB object by SMILES string and returns the small molecule it finds. :param molecupy_pdb: The molecuPy PDB object. :rtype: ``SmallMolecule``""" if self.smiles(): formula = Counter([char.upper() for char in self.smiles() if char.isalpha() and char.upper() != "H"]) for molecule in molecupy_pdb.model().small_molecules(): if molecule.formula() == formula: return molecule
[docs] def find_in_pdb_by_name(self, molecupy_pdb): """Searches for the ligand in a `molecuPy <http://molecupy.readthedocs.io>`_ PDB object by ligand name and returns the small molecule it finds. :param molecupy_pdb: The molecuPy PDB object. :rtype: ``SmallMolecule``""" if self.name(): for molecule in molecupy_pdb.model().small_molecules(): molecule_name = molecupy_pdb.data_file().het_names().get(molecule.molecule_name()) if molecule_name and self.name().lower() == molecule_name.lower(): return molecule
[docs] def find_in_pdb_by_mass(self, molecupy_pdb): """Searches for the ligand in a `molecuPy <http://molecupy.readthedocs.io>`_ PDB object by ligand mass and returns the small molecule it finds. :param molecupy_pdb: The molecuPy PDB object. :rtype: ``SmallMolecule``""" if self.molecular_weight(): molecules = sorted( list(molecupy_pdb.model().small_molecules()), key=lambda k: abs(k.mass() - self.molecular_weight()) ) if molecules and -40 < (molecules[0].mass() - self.molecular_weight()) < 40: return molecules[0]
[docs] def find_in_pdb_by_peptide_string(self, molecupy_pdb): """Searches for the ligand in a `molecuPy <http://molecupy.readthedocs.io>`_ PDB object by peptide sequence and returns the chain it finds. :param molecupy_pdb: The molecuPy PDB object. :rtype: ``Chain``""" if self.one_letter_sequence(): for chain in molecupy_pdb.model().chains(): if self.one_letter_sequence() in chain.sequence_string() and 0.9 <= ( len(self.one_letter_sequence()) / len(chain.sequence_string()) ) <= 1: return chain
def _get_structure_json(self): json_object = gtop.get_json_from_gtop( "ligands/%i/structure" % self._ligand_id ) return json_object if json_object else {} def _get_molecular_json(self): json_object = gtop.get_json_from_gtop( "ligands/%i/molecularProperties" % self._ligand_id ) return json_object if json_object else {} def _get_synonym_json(self): json_object = gtop.get_json_from_gtop( "ligands/%i/synonyms" % self._ligand_id ) return json_object if json_object else [] def _get_comments_json(self): json_object = gtop.get_json_from_gtop( "ligands/%i/comments" % self._ligand_id ) return json_object if json_object else {} def _get_database_json(self): json_object = gtop.get_json_from_gtop( "ligands/%i/databaseLinks" % self._ligand_id ) return json_object if json_object else [] def _get_interactions_json(self): json_object = gtop.get_json_from_gtop( "ligands/%i/interactions" % self._ligand_id ) return json_object if json_object else []