Esempio n. 1
0
    def _exec_pydesign(func_name, compiled_code):
        """
        :param func_name: function name
        :param compiled_code: python code compiled with `compile`
        :return: function
        """
        global_namespace = dict(__name__='__gsmodutils_design_loader__', )
        # Get the function from the file
        try:
            exec_(compiled_code, global_namespace)
        except Exception as ex:
            print(compiled_code)
            raise DesignError("Code execution error in file for {} {}".format(
                func_name, ex))

        if func_name not in global_namespace:
            raise DesignError(
                "function {} not found in python file".format(func_name))

        func = global_namespace[func_name]

        if not hasattr(func, '__call__'):
            raise DesignError(
                "Design function must be callable, got type {}".format(
                    type(func)))

        return func
Esempio n. 2
0
    def from_pydesign(cls, project, did, func_name, compiled_code):
        """ Load a pydesign function as a proper design """
        func = cls._exec_pydesign(func_name, compiled_code)

        # Set default variables
        func.name = getattr(func, 'name', "")
        func.parent = getattr(func, 'parent', None)
        func.description = getattr(func, 'description', func.__doc__)
        func.base_model = getattr(func, 'base_model', None)
        func.conditions = getattr(func, 'conditions', None)

        # Will throw error if parent is not a valid design
        parent = None
        if func.parent is not None:
            try:
                parent = project.get_design(func.parent)
            except DesignError:
                raise DesignOrphanError(
                    "Design parent appears to be invalid {} --> {}".format(
                        func.parent, did))
            except DesignNotFoundError:
                raise DesignOrphanError(
                    "Design parent not found {} --> {}".format(
                        func.parent, did))

        try:
            base_model = project.load_model(func.base_model)
        except IOError:
            raise DesignError(
                "Base model {} does not appear to be valid".format(
                    func.base_model))

        try:
            tmodel = func(base_model.copy(), project)
        except Exception as ex:
            raise DesignError("Error executing design function {} {}".format(
                did, ex))

        if not isinstance(tmodel, cobra.Model):
            raise DesignError("Design does not return a cobra Model instance")

        # We use the loaded model diff as the remaining patameters for the design
        # This is the only reason the model has to be loaded here
        diff = model_diff(base_model, tmodel)

        this = cls(did=did,
                   name=func.name,
                   description=func.description,
                   project=project,
                   parent=parent,
                   base_model=func.base_model,
                   conditions=func.conditions,
                   is_pydesign=True,
                   design_func=func,
                   **diff)
        return this
Esempio n. 3
0
    def check_parents(self, p_stack=None):
        """ Tests to see if their is a loop in parental inheritance"""
        if p_stack is None:
            p_stack = []

        if self.id in p_stack:
            raise DesignError(
                'Error in design {id} - has a cyclical reference')

        p_stack.append(self.id)

        if self.parent is None:
            return True

        if not isinstance(self.parent, StrainDesign):
            raise TypeError('invalid parent design')

        return self.parent.check_parents(p_stack)
Esempio n. 4
0
    def get_design(self, design):
        """
        Get the StrainDesign object (not resulting model) of a design
        :param design: design identifier
        :return:
        """
        if design not in self.list_designs:
            raise DesignNotFoundError(
                "Design of name {} not found in project".format(design))

        if design in self._json_designs:
            des_path = os.path.join(self._project_path, self.config.design_dir,
                                    '{}.json'.format(design))
            self._designs_store[design] = StrainDesign.from_json(
                design, des_path, self)
        else:
            try:
                self._designs_store[design] = self._load_py_design(design)
            except Exception as exp:
                raise DesignError(str(exp))

        return self._designs_store[design]
Esempio n. 5
0
    def load(self):
        """
        Returns a cobra model containing the parent model with the design applied
        :return:
        """
        if self.project is None:
            raise DesignError("No specified project or model.")

        model = self.project.load_model(self.base_model)

        # Add reactions/genes from design to existing model
        self.add_to_model(model)

        if self.conditions is not None:
            try:
                self.project.load_conditions(self.conditions, model=model)
            except KeyError:
                logger.warning(
                    'Cannot find conditions id {} in project specified by design'
                    .format(self.conditions))

        return model
Esempio n. 6
0
    def save_design(self,
                    model,
                    did,
                    name,
                    description='',
                    conditions=None,
                    base_model=None,
                    parent=None,
                    overwrite=False):
        """
        Creates a design from a diff of model_a and model_b
        
        id should be a string with no spaces (conversion handled)
        
        Returns the saved design diff

        :param model: cobrapy model
        :param did: design identifier
        :param name: name of the design
        :param description: text description of what it does
        :param conditions: conditions that should be applied for the design
        :param base_model: Model that the design should be derived from - specified model included in project
        :param parent: string for parent design that this design is a diff from
        :param overwrite: overwrite and existing design (only applies if the id is already in use)
        """
        # Test, infeasible designs should not be added
        status = model.solver.optimize()
        if status == 'infeasible':
            raise Infeasible('Could not find valid solution')

        if parent is not None:
            if isinstance(parent, string_types):
                parent = self.get_design(parent)
            elif not isinstance(
                    parent,
                    StrainDesign) or parent.id not in self.list_designs:
                raise DesignError(
                    'Parent relate a valid project strain design')

        did = str(did).replace(' ', '_')
        design_save_path = os.path.join(self.design_path,
                                        '{}.json'.format(did))

        if os.path.exists(design_save_path) and not overwrite:
            raise IOError('File {} exists'.format(design_save_path))

        if base_model is None and parent is None:
            base_model = self.config.default_model
        elif parent is not None:
            # If a parent design is specified this model is loaded first
            base_model = parent.base_model
        elif base_model is not None and base_model not in self.config.models:
            raise KeyError('Base model not found should be one of {}'.format(
                " ".join(self.config.models)))

        if parent is None:
            lmodel = self.load_model(base_model)
        else:
            lmodel = parent.load()

        if conditions is not None:
            self.load_conditions(conditions, lmodel)

        # Find all the differences between the models
        diff = model_diff(lmodel, model)

        if parent is not None:
            parent = parent.id

        diff['description'] = description
        diff['id'] = did
        diff['name'] = name
        diff['conditions'] = conditions
        diff['base_model'] = base_model
        diff['parent'] = parent

        des = StrainDesign.from_dict(did, diff, self)
        des.to_json(design_save_path, overwrite=overwrite)

        return des
Esempio n. 7
0
    def add_to_model(self, model, copy=False, add_missing=True):
        """
        Add this design to a given cobra model
        :param model:
        :param copy:
        :param add_missing: add missing metabolites to the model
        :return:
        """
        if not isinstance(model, cobra.Model):
            raise TypeError('Expected cobra model')

        mdl = model
        if copy:
            mdl = model.copy()

        # Load parent design first
        if self.parent is not None:
            self.parent.add_to_model(mdl)

        mdl.design = self

        if self.is_pydesign:
            try:
                mdl = self.design_func(mdl, self.project)
            except Exception as ex:
                raise DesignError("Function execution error {}".format(ex))

            return mdl

        # Add new or changed metabolites to model
        for metabolite in self.metabolites:
            # create new metabolite object if its not in the model already
            if metabolite['id'] in mdl.metabolites:
                metab = mdl.metabolites.get_by_id(metabolite['id'])
            else:
                metab = cobra.Metabolite()

            # Doesn't check any of these properties for differences, just update them
            metab.id = metabolite['id']
            metab.name = metabolite['name']
            metab.charge = metabolite['charge']
            metab.formula = metabolite['formula']
            metab.notes = metabolite['notes']
            metab.annotation = metabolite['annotation']
            metab.compartment = metabolite['compartment']

            if metab.id not in mdl.metabolites:
                mdl.add_metabolites([metab])

        # Add new or changed reactions to model
        for rct in self.reactions:
            if rct['id'] in mdl.reactions:
                reaction = mdl.reactions.get_by_id(rct['id'])
                reaction.remove_from_model()

            reaction = cobra.Reaction()
            reaction.id = rct['id']

            reaction.name = rct['name']
            reaction.lower_bound = rct['lower_bound']
            reaction.upper_bound = rct['upper_bound']

            reaction.gene_reaction_rule = rct['gene_reaction_rule']
            reaction.subsystem = rct['subsystem']
            reaction.name = rct['name']

            mdl.add_reactions([reaction])
            reaction = mdl.reactions.get_by_id(reaction.id)

            metabolites = dict([(str(x), v)
                                for x, v in rct['metabolites'].items()])

            if add_missing:
                for mid in metabolites:
                    try:
                        mdl.metabolites.get_by_id(mid)
                    except KeyError:
                        metab = cobra.Metabolite(id=mid)
                        mdl.add_metabolites(metab)

            reaction.add_metabolites(metabolites)
            reaction.objective_coefficient = rct['objective_coefficient']

        # delete removed metabolites/reactions
        for rtid in self.removed_reactions:
            try:
                reaction = mdl.reactions.get_by_id(rtid)
                reaction.remove_from_model()
            except KeyError:
                pass

        for metid in self.removed_metabolites:
            try:
                met = mdl.metabolites.get_by_id(metid)
                met.remove_from_model()
            except KeyError:
                pass
        mdl.id += "::{}".format(self.id)

        # Add gene annotation
        for gene in self.genes:

            try:
                gobj = model.genes.get_by_id(gene['id'])
            except KeyError:
                # genes should already be contained in the model if they have a reaction relationship
                # However, tolerate bad designs
                continue
            gobj.name = gene['name']
            gobj.functional = gene['functional']
            gobj.annotation = gene['annotation']
            gobj.notes = gene['notes']

        return mdl
Esempio n. 8
0
    def __init__(self,
                 did,
                 name,
                 description,
                 project,
                 parent=None,
                 reactions=None,
                 metabolites=None,
                 genes=None,
                 removed_metabolites=None,
                 removed_reactions=None,
                 removed_genes=None,
                 base_model=None,
                 conditions=None,
                 is_pydesign=False,
                 design_func=None):
        """
        Class for handling strain designs created by the project

        Mainly the useful functionality of this over dicts is validation, relationship to projects, creating the models
        as well as displaying the contents as a pandas dataframe

        :param project: Where project is None, the model will not be able to
        """
        self._as_model = None

        self.project = project
        self.base_model = base_model

        self.id = did
        self.name = name
        self.description = description

        self._reactions = reactions
        if self._reactions is None:
            self._reactions = []

        self._metabolites = metabolites
        if self._metabolites is None:
            self._metabolites = []

        self._genes = genes
        if self._genes is None:
            self._genes = []

        self._removed_metabolites = removed_metabolites
        if self._removed_metabolites is None:
            self._removed_metabolites = []

        self._removed_reactions = removed_reactions
        if self._removed_reactions is None:
            self._removed_reactions = []

        self._removed_genes = removed_genes
        if self._removed_genes is None:
            self._removed_genes = []

        self.conditions = conditions

        self.parent = parent
        self.check_parents()

        self._p_model = None

        self.is_pydesign = is_pydesign
        self.design_func = design_func

        if self.is_pydesign and not hasattr(self.design_func, '__call__'):
            raise DesignError(
                "Python designs require a design function to be passed, got  type {}"
                .format(type(design_func)))