Source code for biopax2cadbiom.classes

# MIT License
#
# Copyright (c) 2017 IRISA, Jean Coquet, Pierre Vignet, Mateo Boudet
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Contributor(s): Jean Coquet, Pierre Vignet, Mateo Boudet

"""This module describes the classes that wrap the BioPAX formalism."""
from __future__ import unicode_literals
from __future__ import print_function
import pprint

from biopax2cadbiom import commons as cm

LOGGER = cm.logger()


[docs]class GenericBioPAXEntity(object): """Generic class for BioPAX entities which brings basic common functions""" # pylint: disable=too-few-public-methods @property def short_uri(self): """Return the URI without the prefix of the host. Example: - uri: http://pathwaycommons.org/pc2/#Protein_XXX - short_uri: Protein_XXX .. note:: This attribute short_uri is read-only. """ try: return self.uri.rsplit("#", 1)[1] except IndexError: return self.uri.rsplit("/", 1)[1] def __hash__(self): """Define object's unicity""" return hash(self.uri) def __repr__(self): """String represesentation of the object""" # Replace uri by short_uri for readability return ( "'" + self.short_uri + "',\n" + pprint.pformat( { attr: self.__dict__[attr] for attr in sorted(self.__dict__.iterkeys()) if attr not in ("uri", "pathways", "reactions", "evidences") }, 2, ) + "\n\n" )
[docs]class GenericBioPAXInteraction(object): """Generic class for BioPAX interactions which brings basic common functions""" @property def interactionType(self): """Get interaction type""" return self._interactionType @interactionType.setter def interactionType(self, value): """Take a biopax URI and return only the type of the entity. Example: >>> entity = Control( ... "uri", "Catalysis", "controlType", ... "reaction_uri", ... "controller") >>> entity.interactionType "Catalysis" Example: >>> entity = Reaction( ... "uri", "name", "location_uri", ... "http://www.biopax.org/release/biopax-level3.owl#BiochemicalReaction", ... "entityRef") >>> entity.entityType "BiochemicalReaction" """ try: self._interactionType = value.rsplit("#", 1)[1] except IndexError: self._interactionType = value.rsplit("/", 1)[1]
[docs]class PhysicalEntity(GenericBioPAXEntity): """Class for PhysicalEntity Attributes: :param uri: Uri of the entity. :param name: Name of the entity (`displayName` biopax attribute). :param location_uri: Uri of the location of the entity. :param entityType: Type of the entity (Protein, Complex, etc.). :param entityRef: Uri of the entity reference. :type uri: <str> :type name: <str> :type location_uri: <str> :type entityRef: <str> Optional: :param synonyms: All alternative names of an entity (`name` biopax attribute). :param components_uris: Uris of the components of a Complex. :param members: Uris of the members of a generic entity (class). :param location: Location object related to the entity. This attribute is set by :meth:`biopax2cadbiom.biopax_converter.add_locations_to_entities`. :param xrefs: UnificationXref with dbnames as keys and sets of terms as values :param reactions: Set of reactions where the entity is involved. This attribute is set by :meth:`biopax2cadbiom.biopax_converter.add_reactions_and_controllers_to_entities`. :param membersUsed: Set of members of a class involved in at least one reaction. Empty if the entity does not have members. This attribute is set by :meth:`biopax2cadbiom.biopax_converter.detect_members_used`. :param cadbiom_name: Unique name of the entity in a Cadbiom model. This attribute is set by :meth:`add_unique_cadbiom_name_to_entities`. :param modificationFeatures: Dictionary of modification features; modifications as keys; number of modifications as values. This attribute is set by :meth:`add_modifications_features_to_entities`. :param flat_components: Possible components of a complex. If classes are in `components_uris`, the length of `flat_components` is > 1. The unique Cadbiom name of a flat component is available at the same index in `cadbiom_names`. This attribute is set by :meth:`develop_complexes`. :param flat_components_primitives: All primitive objects in `flat_components`. Only intermediate complexes are replaced by their components. Classes and nested classes are not decompiled and are kept as they are. This attribute is used to rebuild a flat_component when we remove the genericity during the duplication of the reactions. This attribute is set by :meth:`develop_complexes`. :param cadbiom_names: Possible names of the entity. Based on the flat components for complexes, members for classes and cadbiom_name for simple entities. This attribute is set by :meth:`add_cadbiom_names_to_entities`. :type synonyms: <set> :type components_uris: <set> :type members: <set> :type location: <Location> :type xrefs: <dict <str>:<set>> :type reactions: <set> :type membersUsed: <set> :type cadbiom_name: <set> :type modificationFeatures: <Counter <str>:<int>> :type flat_components: <list> :type flat_components_primitives: <list> :type cadbiom_names: <list> """ # pylint: disable=too-many-instance-attributes def __init__(self, uri, name, location_uri, entityType, entityRef): self.uri = uri self.name = name self.location_uri = location_uri self.location = None self.entityType = entityType self.entityRef = entityRef self.synonyms = set() self.components_uris = set() self.members = set() self.xrefs = dict() self.reactions = set() self.membersUsed = set() self.cadbiom_name = set() self.modificationFeatures = dict() self.flat_components = list() self.flat_components_primitives = list() self.cadbiom_names = list() @property def entityType(self): """Get entity type""" return self._entityType @entityType.setter def entityType(self, value): """Take a biopax URI and return only the type of the entity. Example: >>> entity = PhysicalEntity( ... "uri", "name", "location_uri", ... "http://www.biopax.org/release/biopax-level3.owl#Protein", ... "entityRef") >>> entity.entityType "Protein" """ try: self._entityType = value.rsplit("#", 1)[1] except IndexError: self._entityType = value.rsplit("/", 1)[1] @property def modificationFeatures(self): """Get modification features :return: Dictionary of modification features; modifications as keys; number of modifications as values :rtype: <Counter <str>:<int>> """ return self._modificationFeatures @modificationFeatures.setter def modificationFeatures(self, value): """Check that modificationFeatures is a dict and not a Counter""" assert isinstance(value, dict) self._modificationFeatures = value @property def is_class(self): """Return True if the object is a class, False otherwise""" return True if self.membersUsed else False @property def is_complex(self): """Return True if the object is a complex, False otherwise""" return self.entityType == "Complex" # return True if self.flat_components else False
[docs] def add_xref(self, dbref, idref): """Add xref to the existant xrefs :param dbref: Name of external database. :param idref: Identifier in the given database. :type dbref: <str> :type idref: <str> """ if dbref in self.xrefs: self.xrefs[dbref].add(idref) else: # Same database, multiple ids self.xrefs[dbref] = {idref}
[docs]class Reaction(GenericBioPAXEntity, GenericBioPAXInteraction): """Class for reaction Attributes: :param uri: Uri of the reaction. :param name: Name of the reaction. :param interactionType: Type of the interaction. Subclass of the BioPAX Interaction class. :param productComponent: :param participantComponent: :type uri: <str> :type name: <str> :type interactionType: <str> :type productComponent: <str> :type participantComponent: <str> Optional: :param pathways: Set of pathways containing the reaction. :param leftComponents: Set of uris of reagents. :param rightComponents: Set of uris of products. :param controllers: Control entities that control the reaction :param cadbiomSympyCond: Sympy condition. :param event: Name of the event (Cadbiom notation) :param complexes: Used during duplication of reactions to replace generic complexes by their unique flat_component. :type pathways: <set> :type leftComponents: <set> :type rightComponents: <set> :type controllers: <set> :type cadbiomSympyCond: <sympy.core.symbol.Symbol> :type event: <str> :type complexes: <dict> TODO: Handle conversionDirection attr. """ # pylint: disable=too-many-instance-attributes def __init__( self, uri, name, interactionType, productComponent, participantComponent ): self.uri = uri self.name = name self.interactionType = interactionType self.productComponent = productComponent self.participantComponent = participantComponent self.pathways = set() self.leftComponents = set() self.rightComponents = set() self.controllers = set() self.cadbiomSympyCond = None self.event = None self.complexes = dict()
[docs]class Location(GenericBioPAXEntity): """Class for Location Attributes: :param uri: Uri of the location. :param locationTerm: :type uri: <str> :type locationTerm: <str> Optional: :param xrefs: UnificationXref with dbnames as keys and sets of terms as values :param cadbiom_name: :type xrefs: <dict <str>:<set>> :type cadbiom_name: <str> """ def __init__(self, uri, locationTerm): self.uri = uri self.name = locationTerm self.xrefs = dict() self.cadbiom_name = ""
[docs] def add_xref(self, dbref, idref): """Add xref to the existant xrefs""" if dbref in self.xrefs: self.xrefs[dbref].add(idref) else: # Same database, multiple ids self.xrefs[dbref] = {idref}
[docs]class Control(GenericBioPAXEntity, GenericBioPAXInteraction): """Class for Control Attributes: :param interactionType: Subclass of Control :param controlType: type of control (`ACTIVATION` or `INHIBITION`). See warning below. :param controlled: Control/Reaction that is controlled. (supposed to be a subclass of Interaction; can be None) :param controller: Entity that controls the reaction :param controllers: Entities that control the reaction. It happens in some borderline cases (KEGG). .. TODO:: Not currently supported! :type interactionType: <str> :type controlType: <str> :type controlled: <str> :type controller: <str> :type controllers: <set> Optional: :param evidences: set of evidences uris (identify controllers of the same reaction) :type evidences: <set> """ def __init__(self, uri, interactionType, controlType, reaction_uri, controller): self.uri = uri self.interactionType = interactionType self.controlType = controlType self.controlled = reaction_uri self.controllers = {controller} self.evidences = set() @property def controlType(self): """Get control type""" return self._controlType @controlType.setter def controlType(self, value): """Set controlType attribute Some `Catalysis` objects have no controlType and by default we set it to "ACTIVATION", because in theory no other type is allowed for this class. In the near future, an exception will be raised for other objects like Modulation with a controlType which is None. .. TODO:: Avoid the creation of the object with a None controlType if interactionType != Catalysis. => This will allow lighter code in biopax_converter by avoiding tests on this value. These kind of Controls will not be used... """ if value is None and self.interactionType == "Catalysis": # Default value for Catalysis that have no controlType self._controlType = "ACTIVATION" return if value is None: # Here the Control is not a Catalysis, we don't accept a None value here LOGGER.error( "controlType is None for <%s>. This is not allowed - This Control will NOT BE used.", self.uri, ) self._controlType = None return assert value in ("ACTIVATION", "INHIBITION"), "<%s> value found" % value self._controlType = value