From 481a55ec4b7cd03ac8f0aeee7970881baf181ed6 Mon Sep 17 00:00:00 2001 From: Andrew Freiburger Date: Sat, 2 Jul 2022 14:49:13 -0400 Subject: [PATCH 1/7] polishing mstemplate --- modelseedpy/core/mstemplate.py | 375 +++++++++++++-------------------- 1 file changed, 146 insertions(+), 229 deletions(-) diff --git a/modelseedpy/core/mstemplate.py b/modelseedpy/core/mstemplate.py index 03c485c1..52cf8a88 100755 --- a/modelseedpy/core/mstemplate.py +++ b/modelseedpy/core/mstemplate.py @@ -1,18 +1,14 @@ import logging +logger = logging.getLogger(__name__) import copy import math from enum import Enum +from modelseedpy.core.msmodel import get_direction_from_constraints, get_reaction_constraints_from_direction, get_cmp_token from cobra.core import Metabolite, Reaction from cobra.core.dictlist import DictList from cobra.util import format_long_string -from modelseedpy.core.msmodel import get_direction_from_constraints, \ - get_reaction_constraints_from_direction, get_cmp_token -from cobra.core.dictlist import DictList #from cobrakbase.kbase_object_info import KBaseObjectInfo -logger = logging.getLogger(__name__) - - class AttrDict(dict): """ Base object to use for subobjects in KBase objects @@ -21,31 +17,18 @@ def __init__(self, *args, **kwargs): super(AttrDict, self).__init__(*args, **kwargs) self.__dict__ = self - class TemplateReactionType(Enum): CONDITIONAL = 'conditional' UNIVERSAL = 'universal' SPONTANEOUS = 'spontaneous' GAPFILLING = 'gapfilling' - class MSTemplateMetabolite: - def __init__(self, cpd_id, formula=None, name='', default_charge=None, - mass=None, delta_g=None, delta_g_error=None, is_cofactor=False, - abbreviation='', aliases=None): - self.id = cpd_id - self.formula = formula - self.name = name - self.abbreviation = abbreviation - self.default_charge = default_charge - self.mass = mass - self.delta_g = delta_g - self.delta_g_error = delta_g_error - self.is_cofactor = is_cofactor - self.aliases = [] - if aliases: - self.aliases = aliases + def __init__(self, cpd_id, formula=None, name='', default_charge=None, mass=None, delta_g=None, delta_g_error=None, is_cofactor=False, abbreviation='', aliases=[]): + self.id = cpd_id; self.formula = formula; self.name = name; self.abbreviation = abbreviation + self.default_charge = default_charge; self.mass = mass; self.delta_g = delta_g + self.delta_g_error = delta_g_error; self.is_cofactor = is_cofactor; self.aliases = aliases self.species = set() self._template = None @@ -97,21 +80,18 @@ def _repr_html_(self): {species} """.format(id=self.id, name=format_long_string(self.name), - formula=self.formula, address='0x0%x' % id(self), + formula=self.formula, n_species=len(self.species), - species=format_long_string( - ', '.join(r.id for r in self.species), 200)) + species=format_long_string(', '.join(r.id for r in self.species), 200)) class MSTemplateSpecies(Metabolite): - def __init__(self, comp_cpd_id: str, charge: int, compartment: str, cpd_id, max_uptake=0, template=None): + def __init__(self, cobra_cpd_id: str, charge: int, compartment: str, cpd_id, max_uptake=0, template=None): self._template_compound = None - super().__init__(comp_cpd_id, '', '', charge, compartment) - self._template = template - self.cpd_id = cpd_id - self.max_uptake = max_uptake + super().__init__(cobra_cpd_id, '', '', charge, compartment) + self._template = template; self.cpd_id = cpd_id; self.max_uptake = max_uptake if self._template: if self.cpd_id in self._template.compounds: self._template_compound = self._template.compounds.get_by_id(self.cpd_id) @@ -122,7 +102,7 @@ def to_metabolite(self, index='0'): :param index: compartment index :return: cobra.core.Metabolite """ - if index is None: + if not index: index = '' cpd_id = f'{self.id}{index}' compartment = f'{self.compartment}{index}' @@ -143,9 +123,9 @@ def name(self): return '' @name.setter - def name(self, value): + def name(self, name): if self._template_compound: - self._template_compound.name = value + self._template_compound.name = name @property def formula(self): @@ -154,9 +134,9 @@ def formula(self): return '' @formula.setter - def formula(self, value): + def formula(self, formula): if self._template_compound: - self._template_compound.formula = value + self._template_compound.formula = formula @staticmethod def from_dict(d, template=None): @@ -183,8 +163,7 @@ class MSTemplateReaction(Reaction): def __init__(self, rxn_id: str, reference_id: str, name='', subsystem='', lower_bound=0.0, upper_bound=None, reaction_type=TemplateReactionType.CONDITIONAL, gapfill_direction='=', - base_cost=1000, reverse_penalty=1000, forward_penalty=1000, - status='OK', reference_reaction_id=None): + base_cost=1000, reverse_penalty=1000, forward_penalty=1000, status='OK'): """ :param rxn_id: @@ -209,8 +188,7 @@ def __init__(self, rxn_id: str, reference_id: str, name='', subsystem='', lower_ self.reverse_penalty = reverse_penalty self.forward_penalty = forward_penalty self.status = status - self.type = reaction_type.value if type(reaction_type) == TemplateReactionType else reaction_type - self.reference_reaction_id = reference_reaction_id # TODO: to be removed + self.type = reaction_type.value if isinstance(reaction_type, TemplateReactionType) else reaction_type self.complexes = DictList() self.templateReactionReagents = {} self._template = None @@ -220,7 +198,7 @@ def gene_reaction_rule(self): return ' or '.join(map(lambda x: x.id, self.complexes)) @gene_reaction_rule.setter - def gene_reaction_rule(self, gpr): + def gene_reaction_rule(self, gpr): # !!! can this function be deleted? pass @property @@ -280,12 +258,12 @@ def add_complexes(self, complex_list): @property def cstoichiometry(self): - return dict(((met.id, met.compartment), coefficient) for (met, coefficient) in self.metabolites.items()) + return {(met.id, met.compartment): coefficient for (met, coefficient) in self.metabolites.items()} - def remove_role(self, role_id): + def remove_role(self, role_id): # !!! can this function be deleted? pass - def remove_complex(self, complex_id): + def remove_complex(self, complex_id): # !!! can this function be deleted? pass def get_roles(self): @@ -295,34 +273,32 @@ def get_roles(self): """ roles = set() for cpx in self.complexes: - for role in cpx.roles: - roles.add(role) + roles.update(cpx.roles) return roles def get_complexes(self): return self.complexes def get_complex_roles(self): - res = {} + roles = {} for complexes in self.data['templatecomplex_refs']: complex_id = complexes.split('/')[-1] - res[complex_id] = set() + roles[complex_id] = set() if self._template: cpx = self._template.get_complex(complex_id) - if cpx: for complex_role in cpx['complexroles']: role_id = complex_role['templaterole_ref'].split('/')[-1] - res[complex_id].add(role_id) + roles[complex_id].add(role_id) else: - print('!!') - return res + print(f'The complex for ID {complex_id} does not exist.') + return roles def get_data(self): - template_reaction_reagents = list(map(lambda x: { - 'coefficient': x[1], - 'templatecompcompound_ref': '~/compcompounds/id/' + x[0].id - }, self.metabolites.items())) + template_reaction_reagents = [{ + 'coefficient': coef, + 'templatecompcompound_ref': '~/compcompounds/id/'+met.id + } for met, coef in self.metabolites.items()] return { 'id': self.id, 'name': self.name, @@ -365,8 +341,7 @@ def get_data(self): class NewModelTemplateRole: - def __init__(self, role_id, name, - features=None, source='', aliases=None): + def __init__(self, role_id, name, features=[], source='', aliases=[]): """ :param role_id: @@ -375,11 +350,7 @@ def __init__(self, role_id, name, :param source: :param aliases: """ - self.id = role_id - self.name = name - self.source = source - self.features = [] if features is None else features - self.aliases = [] if aliases is None else aliases + self.id = role_id; self.name = name; self.source = source; self.features = features; self.aliases = aliases self._complexes = set() self._template = None @@ -435,24 +406,18 @@ def __init__(self, complex_id, name, source='', reference='', confidence=0, temp :param confidence: :param template: """ - self.id = complex_id - self.name = name - self.source = source - self.reference = reference - self.confidence = confidence + + self.id = complex_id; self.name = name; self.source = source; self.reference = reference + self.confidence = confidence; self._template = template self.roles = {} - self._template = template @staticmethod def from_dict(d, template): protein_complex = NewModelTemplateComplex( - d['id'], d['name'], - d['source'], d['reference'], d['confidence'], - template - ) - for o in d['complexroles']: - role = template.roles.get_by_id(o['templaterole_ref'].split('/')[-1]) - protein_complex.add_role(role, o['triggering'] == 1, o['optional_role'] == 1) + d['id'], d['name'], d['source'], d['reference'], d['confidence'], template) + for role in d['complexroles']: + role = template.roles.get_by_id(role['templaterole_ref'].split('/')[-1]) + protein_complex.add_role(role, role['triggering'], role['optional_role']) return protein_complex def add_role(self, role: NewModelTemplateRole, triggering=True, optional=False): @@ -484,16 +449,14 @@ def get_data(self): } def __str__(self): - return " and ".join(map(lambda x: "{}{}{}".format( - x[0].id, - ":trig" if x[1][0] else "", - ":optional" if x[1][1] else ""), self.roles.items())) + return " and ".join( + ["{}{}{}".format(role[0].id, ":trig" if role[1][0] else "", ":optional" if role[1][1] else "") for role in self.roles.items()]) def __repr__(self): return "<%s %s at 0x%x>" % (self.__class__.__name__, self.id, id(self)) def _repr_html_(self): - + return """ @@ -508,21 +471,14 @@ def _repr_html_(self): {complexes}
""".format(id=self.id, name=format_long_string(self.name), - address='0x0%x' % id(self), - n_complexes=len(self.roles), - complexes=format_long_string( - ', '.join("{}:{}:{}:{}".format(r[0].id, r[0].name, r[1][0], r[1][1]) for r in - self.roles.items()), 200)) - + address='0x0%x' % id(self), n_complexes=len(self.roles), + complexes=format_long_string(', '.join("{}:{}:{}:{}".format( + r[0].id, r[0].name, r[1][0], r[1][1]) for r in self.roles.items()), 200)) class MSTemplateCompartment: - - def __init__(self, compartment_id: str, name: str, ph: float, hierarchy=0, aliases=None): - self.id = compartment_id - self.name = name - self.ph = ph - self.hierarchy = hierarchy - self.aliases = [] if aliases is None else list(aliases) + def __init__(self, compartment_id: str, name: str, ph: float, hierarchy=0, aliases:list = []): + self.id = compartment_id; self.name = name; self.ph = ph + self.hierarchy = hierarchy; self.aliases = aliases self._template = None @staticmethod @@ -530,33 +486,17 @@ def from_dict(d): return MSTemplateCompartment(d['id'], d['name'], d['pH'], d['hierarchy'], d['aliases']) def get_data(self): - return { - 'id': self.id, - 'name': self.name, - 'pH': self.ph, - 'aliases': self.aliases, - 'hierarchy': self.hierarchy - } + return {'id': self.id, 'name': self.name, 'pH': self.ph, 'aliases': self.aliases, 'hierarchy': self.hierarchy} class MSTemplate: - - def __init__(self, template_id, name='', domain='', template_type='', version=1, info=None, args=None): - self.id = template_id - self.name = name - self.domain = domain - self.template_type = template_type - self.__VERSION__ = version + def __init__(self, template_id, name='', domain='', template_type='', version=1): + self.id = template_id; self.name = name; self.domain = domain + self.template_type = template_type; self.__VERSION__ = version self.biochemistry_ref = '' - self.compartments = DictList() - self.biomasses = DictList() - self.reactions = DictList() - self.compounds = DictList() - self.compcompounds = DictList() - self.roles = DictList() - self.complexes = DictList() - self.pathways = DictList() - self.subsystems = DictList() + self.compartments, self.biomasses, self.reactions = DictList(), DictList(), DictList() + self.compounds, self.pathways, self.subsystems = DictList(), DictList(), DictList() + self.roles, self.complexes, self.compcompounds = DictList(), DictList(), DictList() def add_compartments(self, compartments: list): """ @@ -564,10 +504,10 @@ def add_compartments(self, compartments: list): :param compartments: :return: """ - duplicates = list(filter(lambda x: x.id in self.compartments, compartments)) + duplicates = list(set(self.compartments).intersection(compartments)) if len(duplicates) > 0: - logger.error("unable to add compartments [%s] already present in the template", duplicates) - return None + logger.error(f"The duplicate compartments {duplicates} cannot be added to the template") + return None #!!! Should the non-duplicate compartments still be added? for x in compartments: x._template = self @@ -579,10 +519,10 @@ def add_roles(self, roles: list): :param roles: :return: """ - duplicates = list(filter(lambda x: x.id in self.roles, roles)) + duplicates = list(set(self.roles).intersection(roles)) if len(duplicates) > 0: - logger.error("unable to add roles [%s] already present in the template", duplicates) - return None + logger.error(f"The duplicate roles {duplicates} cannot be added to the template") + return None # !!! Should the non-duplicate compartments still be added? for x in roles: x._template = self @@ -594,24 +534,24 @@ def add_complexes(self, complexes: list): :param complexes: :return: """ - duplicates = list(filter(lambda x: x.id in self.complexes, complexes)) + duplicates = list(set(self.complexes).intersection(complexes)) if len(duplicates) > 0: - logger.error("unable to add comp compounds [%s] already present in the template", duplicates) - return None + logger.error(f"The duplicate complexes {duplicates} cannot be added to the template") + return None #!!! Should the non-duplicate compartments still be added? roles_to_add = [] - for x in complexes: - x._template = self + for complx in complexes: + complx._template = self roles_rep = {} - for role in x.roles: + for role in complx.roles: r = role if role.id not in self.roles: roles_to_add.append(role) else: r = self.roles.get_by_id(role.id) - roles_rep[r] = x.roles[role] - r._complexes.add(x) - x.roles = roles_rep + roles_rep[r] = complx.roles[role] + r._complexes.add(complx) + complx.roles = roles_rep self.roles += roles_to_add self.complexes += complexes @@ -622,13 +562,13 @@ def add_compounds(self, compounds: list): :param compounds: :return: """ - duplicates = list(filter(lambda x: x.id in self.compounds, compounds)) + duplicates = list(set(self.compounds).intersection(compounds)) if len(duplicates) > 0: - logger.error("unable to add compounds [%s] already present in the template", duplicates) - return None + logger.error(f"The duplicate compounds {duplicates} cannot be added to the template") + return None #!!! Should the non-duplicate compartments still be added? - for x in compounds: - x._template = self + for cpd in compounds: + cpd._template = self self.compounds += compounds def add_comp_compounds(self, comp_compounds: list): @@ -637,16 +577,16 @@ def add_comp_compounds(self, comp_compounds: list): :param comp_compounds: :return: """ - duplicates = list(filter(lambda x: x.id in self.compcompounds, comp_compounds)) + duplicates = list(set(self.compcompounds).intersection(comp_compounds)) if len(duplicates) > 0: - logger.error("unable to add comp compounds [%s] already present in the template", duplicates) - return None - - for x in comp_compounds: - x._template = self - if x.cpd_id in self.compounds: - x._template_compound = self.compounds.get_by_id(x.cpd_id) - x._template_compound.species.add(x) + logger.error(f"The duplicate comp compounds {duplicates} cannot be added to the template") + return None #!!! Should the non-duplicate compartments still be added? + + for comp_cpd in comp_compounds: + comp_cpd._template = self + if comp_cpd.cpd_id in self.compounds: + comp_cpd._template_compound = self.compounds.get_by_id(comp_cpd.cpd_id) + comp_cpd._template_compound.species.add(comp_cpd) self.compcompounds += comp_compounds def add_reactions(self, reaction_list: list): @@ -655,32 +595,31 @@ def add_reactions(self, reaction_list: list): :param reaction_list: :return: """ - duplicates = list(filter(lambda x: x.id in self.reactions, reaction_list)) + duplicates = list(set(self.reactions).intersection(reaction_list)) if len(duplicates) > 0: logger.error("unable to add reactions [%s] already present in the template", duplicates) - return None + return None # !!! Should the non-duplicate compartments still be added? - for x in reaction_list: + for rxn in reaction_list: metabolites_replace = {} complex_replace = set() - x._template = self - for comp_cpd, coefficient in x.metabolites.items(): + rxn._template = self + for comp_cpd, coefficient in rxn.metabolites.items(): if comp_cpd.id not in self.compcompounds: self.add_comp_compounds([comp_cpd]) metabolites_replace[self.compcompounds.get_by_id(comp_cpd.id)] = coefficient - for cpx in x.complexes: + for cpx in rxn.complexes: if cpx.id not in self.complexes: self.add_complexes([cpx]) complex_replace.add(self.complexes.get_by_id(cpx.id)) - x._metabolites = metabolites_replace - x.complexes = complex_replace - + rxn._metabolites = metabolites_replace + rxn.complexes = complex_replace self.reactions += reaction_list - def get_role_sources(self): + def get_role_sources(self): # !!! can this function be deleted? pass - def get_complex_sources(self): + def get_complex_sources(self): # !!! can this function be deleted? pass def get_complex_from_role(self, roles): @@ -690,24 +629,23 @@ def get_complex_from_role(self, roles): return None @staticmethod - def get_last_id_value(object_list, s): + def get_last_id_value(object_list, prefix): last_id = 0 - for o in object_list: - if o.id.startswith(s): - number_part = id[len(s):] + for obj in object_list: + if obj.id.startswith(prefix): + number_part = id[len(prefix):] if len(number_part) == 5: - if int(number_part) > last_id: - last_id = int(number_part) + last_id = max(last_id, int(number_part)) return last_id - def get_complex(self, id): - return self.complexes.get_by_id(id) + def get_complex(self, obj_id): + return self.complexes.get_by_id(obj_id) - def get_reaction(self, id): - return self.reactions.get_by_id(id) + def get_reaction(self, obj_id): + return self.reactions.get_by_id(obj_id) - def get_role(self, id): - return self.roles.get_by_id(id) + def get_role(self, obj_id): + return self.roles.get_by_id(obj_id) # def _to_object(self, key, data): # if key == 'compounds': @@ -748,12 +686,12 @@ def get_data(self): 'domain': self.domain, 'biochemistry_ref': self.biochemistry_ref, 'type': 'Test', - 'compartments': list(map(lambda x: x.get_data(), self.compartments)), - 'compcompounds': list(map(lambda x: x.get_data(), self.compcompounds)), - 'compounds': list(map(lambda x: x.get_data(), self.compounds)), - 'roles': list(map(lambda x: x.get_data(), self.roles)), - 'complexes': list(map(lambda x: x.get_data(), self.complexes)), - 'reactions': list(map(lambda x: x.get_data(), self.reactions)), + 'compartments': list(x.get_data() for x in self.compartments), + 'compcompounds': list(x.get_data() for x in self.compcompounds), + 'compounds': list(x.get_data() for x in self.compounds), + 'roles': list(x.get_data() for x in self.roles), + 'complexes': list(x.get_data() for x in self.complexes), + 'reactions': list(x.get_data() for x in self.reactions), 'biomasses': list(self.biomasses), 'pathways': [], 'subsystems': [], @@ -804,22 +742,12 @@ def _repr_html_(self): class MSTemplateBuilder: - def __init__(self, template_id, name='', domain='', template_type='', version=1, info=None, - biochemistry=None, biomasses=None, pathways=None, subsystems=None): - self.id = template_id - self.version = version - self.name = name - self.domain = domain - self.template_type = template_type - self.compartments = [] - self.biomasses = [] - self.roles = [] - self.complexes = [] - self.compounds = [] - self.compartment_compounds = [] - self.reactions = [] - self.info = info + def __init__(self, template_id, name='', domain='', template_type='', version=1, info=None): + self.id = template_id; self.version = version; self.name = name; self.domain = domain + self.template_type = template_type; self.info = info self.biochemistry_ref = None + self.compartments, self.biomasses, self.roles, self.complexes = [], [], [], [] + self.compounds, self.compartment_compounds, self.reactions = [], [], [] @staticmethod def from_dict(d, info=None, args=None): @@ -830,7 +758,7 @@ def from_dict(d, info=None, args=None): :param args: :return: """ - builder = MSTemplateBuilder(d['id'], d['name'], d['domain'], d['type'], d['__VERSION__'], None) + builder = MSTemplateBuilder(d['id'], d['name'], d['domain'], d['type'], d['__VERSION__'], info) builder.compartments = d['compartments'] builder.roles = d['roles'] builder.complexes = d['complexes'] @@ -843,60 +771,54 @@ def from_dict(d, info=None, args=None): @staticmethod def from_template(template): - b = MSTemplateBuilder() - for o in template.compartments: - b.compartments.append(copy.deepcopy(o)) + builder = MSTemplateBuilder() + for compartment in template.compartments: + builder.compartments.append(copy.deepcopy(compartment)) - return b + return builder - def with_compound_modelseed(self, seed_id, modelseed): + def with_compound_modelseed(self, seed_id, modelseed): # !!! can this function be deleted? pass def with_role(self, template_rxn, role_ids, auto_complex=False): # TODO: copy from template curation complex_roles = template_rxn.get_complex_roles() - role_match = {} - for o in role_ids: - role_match[o] = False + role_match = {role_id:False for role_id in role_ids} for complex_id in complex_roles: - for o in role_match: - if o in complex_roles[complex_id]: - role_match[o] = True + for role in role_match: + if role in complex_roles[complex_id]: + role_match[role] = True all_roles_present = True - for o in role_match: - all_roles_present &= role_match[o] + for role in role_match: + all_roles_present &= role_match[role] if all_roles_present: - logger.debug('ignore %s all present in atleast 1 complex', role_ids) + logger.debug(f'At least one complex does not express all one role of {role_ids}.') return None - complex_id = self.template.get_complex_from_role(role_ids) + complex_id = self.template.get_complex_from_roles(role_ids) if complex_id is None: - logger.warning('unable to find complex for %s', role_ids) + logger.warning(f'A corresponding complex for the roles {role_ids} cannot be found.') if auto_complex: - role_names = set() - for role_id in role_ids: - role = self.template.get_role(role_id) - role_names.add(role['name']) - logger.warning('build complex for %s', role_names) + role_names = set([self.template.get_role(role_id)['name'] for role_id in role_ids]) + logger.warning(f'The complex {role_names} will be added to the template.') complex_id = self.template.add_complex_from_role_names(role_names) else: return None complex_ref = '~/complexes/id/' + complex_id if complex_ref in template_rxn.data['templatecomplex_refs']: - logger.debug('already contains complex %s, role %s', role_ids, complex_ref) + logger.debug(f'The template already contains a complex reference {complex_ref} for complex {complex_id}.') return None return complex_ref - def with_compound(self): + def with_compound(self): # !!! can this function be deleted? pass - def with_compound_compartment(self): + def with_compound_compartment(self): # !!! can this function be deleted? pass def with_compartment(self, cmp_id, name, ph=7, index='0'): - res = list(filter(lambda x: x['id'] == cmp_id, self.compartments)) + res = list(x for x in self.compartments if x['id'] == cmp_id) if len(res) > 0: return res[0] - self.compartments.append({ 'id': cmp_id, 'name': name, @@ -905,20 +827,15 @@ def with_compartment(self, cmp_id, name, ph=7, index='0'): 'index': index, 'pH': ph }) - return self def build(self): template = MSTemplate(self.id, self.name, self.domain, self.template_type, self.version) - template.add_compartments(list(map(lambda x: MSTemplateCompartment.from_dict(x), self.compartments))) - template.add_compounds(list(map(lambda x: MSTemplateMetabolite.from_dict(x), self.compounds))) - template.add_comp_compounds( - list(map(lambda x: MSTemplateSpecies.from_dict(x), self.compartment_compounds))) - template.add_roles(list(map(lambda x: NewModelTemplateRole.from_dict(x), self.roles))) - template.add_complexes( - list(map(lambda x: NewModelTemplateComplex.from_dict(x, template), self.complexes))) - template.add_reactions( - list(map(lambda x: MSTemplateReaction.from_dict(x, template), self.reactions))) - template.biomasses += list(map(lambda x: AttrDict(x), self.biomasses)) # TODO: biomass object - + template.add_compartments([MSTemplateCompartment.from_dict(x) for x in self.compartments]) + template.add_compounds([MSTemplateMetabolite.from_dict(x) for x in self.compounds]) + template.add_comp_compounds([MSTemplateSpecies.from_dict(x) for x in self.compartment_compounds]) + template.add_roles([NewModelTemplateRole.from_dict(x) for x in self.roles]) + template.add_complexes([NewModelTemplateComplex.from_dict(x, template) for x in self.complexes]) + template.add_reactions([MSTemplateReaction.from_dict(x, template) for x in self.reactions]) + template.biomasses += [AttrDict(x) for x in self.biomasses] # TODO: biomass object return template From 73f4391981f02b9d995696990fbed5250f80d1eb Mon Sep 17 00:00:00 2001 From: Andrew Freiburger Date: Sat, 2 Jul 2022 15:10:53 -0400 Subject: [PATCH 2/7] polishing mstemplate --- modelseedpy/core/mstemplate.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/modelseedpy/core/mstemplate.py b/modelseedpy/core/mstemplate.py index 52cf8a88..c95022e1 100755 --- a/modelseedpy/core/mstemplate.py +++ b/modelseedpy/core/mstemplate.py @@ -3,10 +3,10 @@ import copy import math from enum import Enum -from modelseedpy.core.msmodel import get_direction_from_constraints, get_reaction_constraints_from_direction, get_cmp_token from cobra.core import Metabolite, Reaction from cobra.core.dictlist import DictList from cobra.util import format_long_string +from modelseedpy.core.msmodel import get_direction_from_constraints, get_reaction_constraints_from_direction, get_cmp_token #from cobrakbase.kbase_object_info import KBaseObjectInfo class AttrDict(dict): @@ -25,7 +25,8 @@ class TemplateReactionType(Enum): class MSTemplateMetabolite: - def __init__(self, cpd_id, formula=None, name='', default_charge=None, mass=None, delta_g=None, delta_g_error=None, is_cofactor=False, abbreviation='', aliases=[]): + def __init__(self, cpd_id, formula=None, name='', default_charge=None, mass=None, delta_g=None, + delta_g_error=None, is_cofactor=False, abbreviation='', aliases=[]): self.id = cpd_id; self.formula = formula; self.name = name; self.abbreviation = abbreviation self.default_charge = default_charge; self.mass = mass; self.delta_g = delta_g self.delta_g_error = delta_g_error; self.is_cofactor = is_cofactor; self.aliases = aliases @@ -88,9 +89,9 @@ def _repr_html_(self): class MSTemplateSpecies(Metabolite): - def __init__(self, cobra_cpd_id: str, charge: int, compartment: str, cpd_id, max_uptake=0, template=None): + def __init__(self, comp_cpd_id: str, charge: int, compartment: str, cpd_id, max_uptake=0, template=None): self._template_compound = None - super().__init__(cobra_cpd_id, '', '', charge, compartment) + super().__init__(comp_cpd_id, '', '', charge, compartment) self._template = template; self.cpd_id = cpd_id; self.max_uptake = max_uptake if self._template: if self.cpd_id in self._template.compounds: @@ -195,7 +196,7 @@ def __init__(self, rxn_id: str, reference_id: str, name='', subsystem='', lower_ @property def gene_reaction_rule(self): - return ' or '.join(map(lambda x: x.id, self.complexes)) + return ' or '.join([x.id for x in self.complexes]) @gene_reaction_rule.setter def gene_reaction_rule(self, gpr): # !!! can this function be deleted? @@ -258,7 +259,7 @@ def add_complexes(self, complex_list): @property def cstoichiometry(self): - return {(met.id, met.compartment): coefficient for (met, coefficient) in self.metabolites.items()} + return {(met.id, met.compartment):coefficient for (met, coefficient) in self.metabolites.items()} def remove_role(self, role_id): # !!! can this function be deleted? pass @@ -415,9 +416,9 @@ def __init__(self, complex_id, name, source='', reference='', confidence=0, temp def from_dict(d, template): protein_complex = NewModelTemplateComplex( d['id'], d['name'], d['source'], d['reference'], d['confidence'], template) - for role in d['complexroles']: - role = template.roles.get_by_id(role['templaterole_ref'].split('/')[-1]) - protein_complex.add_role(role, role['triggering'], role['optional_role']) + for cplx_role in d['complexroles']: + role = template.roles.get_by_id(cplx_role['templaterole_ref'].split('/')[-1]) + protein_complex.add_role(role, cplx_role['triggering'], cplx_role['optional_role']) return protein_complex def add_role(self, role: NewModelTemplateRole, triggering=True, optional=False): @@ -456,7 +457,7 @@ def __repr__(self): return "<%s %s at 0x%x>" % (self.__class__.__name__, self.id, id(self)) def _repr_html_(self): - + return """ @@ -476,6 +477,7 @@ def _repr_html_(self): r[0].id, r[0].name, r[1][0], r[1][1]) for r in self.roles.items()), 200)) class MSTemplateCompartment: + def __init__(self, compartment_id: str, name: str, ph: float, hierarchy=0, aliases:list = []): self.id = compartment_id; self.name = name; self.ph = ph self.hierarchy = hierarchy; self.aliases = aliases @@ -490,7 +492,8 @@ def get_data(self): class MSTemplate: - def __init__(self, template_id, name='', domain='', template_type='', version=1): + + def __init__(self, template_id, name='', domain='', template_type='', version=1, info=None, args=None): # !!! info and args are never used self.id = template_id; self.name = name; self.domain = domain self.template_type = template_type; self.__VERSION__ = version self.biochemistry_ref = '' @@ -742,7 +745,8 @@ def _repr_html_(self): class MSTemplateBuilder: - def __init__(self, template_id, name='', domain='', template_type='', version=1, info=None): + def __init__(self, template_id, name='', domain='', template_type='', version=1, info=None, + biochemistry=None, biomasses=None, pathways=None, subsystems=None): # !!! biochemistry, biomasses, pathways, and subsystems are nevery used self.id = template_id; self.version = version; self.name = name; self.domain = domain self.template_type = template_type; self.info = info self.biochemistry_ref = None From f0bc07360a456cb1e092a174af77b792630b8b0f Mon Sep 17 00:00:00 2001 From: Andrew Freiburger Date: Sat, 2 Jul 2022 15:21:03 -0400 Subject: [PATCH 3/7] polishing mstemplate --- modelseedpy/core/mstemplate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modelseedpy/core/mstemplate.py b/modelseedpy/core/mstemplate.py index c95022e1..c00a64fc 100755 --- a/modelseedpy/core/mstemplate.py +++ b/modelseedpy/core/mstemplate.py @@ -762,7 +762,7 @@ def from_dict(d, info=None, args=None): :param args: :return: """ - builder = MSTemplateBuilder(d['id'], d['name'], d['domain'], d['type'], d['__VERSION__'], info) + builder = MSTemplateBuilder(d['id'], d['name'], d['domain'], d['type'], d['__VERSION__'], None) # !!! None here should probably be info, which is why info is a function argument builder.compartments = d['compartments'] builder.roles = d['roles'] builder.complexes = d['complexes'] From 2d745661b4cde24e04b72a50eb2f60046cf2f36c Mon Sep 17 00:00:00 2001 From: Andrew Freiburger Date: Fri, 22 Jul 2022 22:16:47 -0400 Subject: [PATCH 4/7] mstemplate polishing --- modelseedpy/core/mstemplate.py | 61 +++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/modelseedpy/core/mstemplate.py b/modelseedpy/core/mstemplate.py index c00a64fc..56c03545 100755 --- a/modelseedpy/core/mstemplate.py +++ b/modelseedpy/core/mstemplate.py @@ -1,5 +1,4 @@ import logging -logger = logging.getLogger(__name__) import copy import math from enum import Enum @@ -9,6 +8,8 @@ from modelseedpy.core.msmodel import get_direction_from_constraints, get_reaction_constraints_from_direction, get_cmp_token #from cobrakbase.kbase_object_info import KBaseObjectInfo +logger = logging.getLogger(__name__) + class AttrDict(dict): """ Base object to use for subobjects in KBase objects @@ -26,10 +27,17 @@ class TemplateReactionType(Enum): class MSTemplateMetabolite: def __init__(self, cpd_id, formula=None, name='', default_charge=None, mass=None, delta_g=None, - delta_g_error=None, is_cofactor=False, abbreviation='', aliases=[]): - self.id = cpd_id; self.formula = formula; self.name = name; self.abbreviation = abbreviation - self.default_charge = default_charge; self.mass = mass; self.delta_g = delta_g - self.delta_g_error = delta_g_error; self.is_cofactor = is_cofactor; self.aliases = aliases + delta_g_error=None, is_cofactor=False, abbreviation='', aliases=None): + self.id = cpd_id + self.formula = formula + self.name = name + self.abbreviation = abbreviation + self.default_charge = default_charge + self.mass = mass + self.delta_g = delta_g + self.delta_g_error = delta_g_error + self.is_cofactor = is_cofactor + self.aliases = aliases or [] self.species = set() self._template = None @@ -103,8 +111,7 @@ def to_metabolite(self, index='0'): :param index: compartment index :return: cobra.core.Metabolite """ - if not index: - index = '' + index = index or '' cpd_id = f'{self.id}{index}' compartment = f'{self.compartment}{index}' name = f'{self.name}' @@ -342,7 +349,7 @@ def get_data(self): class NewModelTemplateRole: - def __init__(self, role_id, name, features=[], source='', aliases=[]): + def __init__(self, role_id, name, features=None, source='', aliases=None): """ :param role_id: @@ -351,7 +358,11 @@ def __init__(self, role_id, name, features=[], source='', aliases=[]): :param source: :param aliases: """ - self.id = role_id; self.name = name; self.source = source; self.features = features; self.aliases = aliases + self.id = role_id + self.name = name + self.source = source + self.features = features or [] + self.aliases = aliases or [] self._complexes = set() self._template = None @@ -407,10 +418,13 @@ def __init__(self, complex_id, name, source='', reference='', confidence=0, temp :param confidence: :param template: """ - - self.id = complex_id; self.name = name; self.source = source; self.reference = reference - self.confidence = confidence; self._template = template + self.id = complex_id + self.name = name + self.source = source + self.reference = reference + self.confidence = confidence self.roles = {} + self._template = template @staticmethod def from_dict(d, template): @@ -478,9 +492,12 @@ def _repr_html_(self): class MSTemplateCompartment: - def __init__(self, compartment_id: str, name: str, ph: float, hierarchy=0, aliases:list = []): - self.id = compartment_id; self.name = name; self.ph = ph - self.hierarchy = hierarchy; self.aliases = aliases + def __init__(self, compartment_id: str, name: str, ph: float, hierarchy=0, aliases:list = None): + self.id = compartment_id + self.name = name + self.ph = ph + self.hierarchy = hierarchy + self.aliases = list(aliases) or [] self._template = None @staticmethod @@ -494,8 +511,11 @@ def get_data(self): class MSTemplate: def __init__(self, template_id, name='', domain='', template_type='', version=1, info=None, args=None): # !!! info and args are never used - self.id = template_id; self.name = name; self.domain = domain - self.template_type = template_type; self.__VERSION__ = version + self.id = template_id + self.name = name + self.domain = domain + self.template_type = template_type + self.__VERSION__ = version self.biochemistry_ref = '' self.compartments, self.biomasses, self.reactions = DictList(), DictList(), DictList() self.compounds, self.pathways, self.subsystems = DictList(), DictList(), DictList() @@ -747,8 +767,11 @@ class MSTemplateBuilder: def __init__(self, template_id, name='', domain='', template_type='', version=1, info=None, biochemistry=None, biomasses=None, pathways=None, subsystems=None): # !!! biochemistry, biomasses, pathways, and subsystems are nevery used - self.id = template_id; self.version = version; self.name = name; self.domain = domain - self.template_type = template_type; self.info = info + self.id = template_id + self.version = version + self.name = name + self.domain = domain + self.template_type = template_type self.biochemistry_ref = None self.compartments, self.biomasses, self.roles, self.complexes = [], [], [], [] self.compounds, self.compartment_compounds, self.reactions = [], [], [] From ceb6842a07797fd7b44b9ecc3511a0c78311f7ea Mon Sep 17 00:00:00 2001 From: Andrew Freiburger Date: Fri, 22 Jul 2022 22:18:16 -0400 Subject: [PATCH 5/7] mstemplate additional edits --- modelseedpy/core/mstemplate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modelseedpy/core/mstemplate.py b/modelseedpy/core/mstemplate.py index 56c03545..e57b3f59 100755 --- a/modelseedpy/core/mstemplate.py +++ b/modelseedpy/core/mstemplate.py @@ -772,9 +772,10 @@ def __init__(self, template_id, name='', domain='', template_type='', version=1, self.name = name self.domain = domain self.template_type = template_type - self.biochemistry_ref = None self.compartments, self.biomasses, self.roles, self.complexes = [], [], [], [] self.compounds, self.compartment_compounds, self.reactions = [], [], [] + self.info = info + self.biochemistry_ref = None @staticmethod def from_dict(d, info=None, args=None): From b014e0ab012ea5c4bb101c3139f7e9be9aeb8590 Mon Sep 17 00:00:00 2001 From: Andrew Freiburger Date: Thu, 15 Sep 2022 15:45:13 -0500 Subject: [PATCH 6/7] final polish --- modelseedpy/core/mstemplate.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modelseedpy/core/mstemplate.py b/modelseedpy/core/mstemplate.py index e57b3f59..b12414d9 100755 --- a/modelseedpy/core/mstemplate.py +++ b/modelseedpy/core/mstemplate.py @@ -100,7 +100,9 @@ class MSTemplateSpecies(Metabolite): def __init__(self, comp_cpd_id: str, charge: int, compartment: str, cpd_id, max_uptake=0, template=None): self._template_compound = None super().__init__(comp_cpd_id, '', '', charge, compartment) - self._template = template; self.cpd_id = cpd_id; self.max_uptake = max_uptake + self._template = template + self.cpd_id = cpd_id + self.max_uptake = max_uptake if self._template: if self.cpd_id in self._template.compounds: self._template_compound = self._template.compounds.get_by_id(self.cpd_id) From 44a96a9b5207c2835cf30fe0587958338c074cb7 Mon Sep 17 00:00:00 2001 From: freiburgermsu Date: Thu, 15 Sep 2022 16:13:55 -0500 Subject: [PATCH 7/7] Update mstemplate.py --- modelseedpy/core/mstemplate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/modelseedpy/core/mstemplate.py b/modelseedpy/core/mstemplate.py index b12414d9..78e22b5b 100755 --- a/modelseedpy/core/mstemplate.py +++ b/modelseedpy/core/mstemplate.py @@ -257,7 +257,6 @@ def from_dict(d, template): d['type'], d['GapfillDirection'], d['base_cost'], d['reverse_penalty'], d['forward_penalty'], d['status'] if 'status' in d else None, - d['reaction_ref'].split('/')[-1] ) reaction.add_metabolites(metabolites) reaction.add_complexes(complexes)