def test_balance_balanced(self):
        '''Tests get_elem_comp method for balanced reaction.'''
        balanced = [('C5H7O4', -1, -1.0, 'C5H7O4'),
                    ('H', 1, 1.0, 'H'),
                    ('C3H3O3', -1, 2.0, 'C3H3O3'),
                    ('CO2', 0, -1.0, 'CO2')]
        is_balanced, was_balanced, balanced_def = chm_util.balance(balanced)

        self.assertTrue(is_balanced)
        self.assertTrue(was_balanced)
        self.assertEqual(sorted(balanced), sorted(balanced_def))
    def test_balance_problem(self):
        '''Tests get_elem_comp method for problematic reaction.'''
        problem = [('C144H238N2O57P2', -2.0, -1.0, 'C144H238N2O57P2'),
                   ('C86H142O9P', -1.0, -1.0, 'C86H142O9P'),
                   ('H', 1.0, 1.0, 'H'),
                   ('C150H248N2O62P2', -2.0, 1.0, 'C150H248N2O62P2'),
                   ('C80H131O4P', -2.0, 1.0, 'C80H131O4P')]

        is_balanced, was_balanced, balanced_def = chm_util.balance(
            problem, optional_comp=[])

        self.assertTrue(is_balanced)
        self.assertTrue(was_balanced)
        self.assertEqual(sorted(problem), sorted(balanced_def))
    def __add_reac_nodes(self, reac_data):
        '''Get reaction nodes from data.'''
        reac_id_def = {}

        for properties in reac_data.values():
            reac_def = []
            mnx_id = properties.pop('id')

            # Remove equation and description (may be inconsistent with
            # balanced reaction):
            if 'description' in properties:
                properties.pop('description')

            for prt in properties.pop('reac_defn'):
                chem_id, _ = self.__chem_man.add_chemical({'mnx': prt[0]})

                reac_def.append([self.__chem_man.get_prop(prt[0], 'formula'),
                                 self.__chem_man.get_prop(prt[0],
                                                          'charge:int', 0),
                                 prt[1],
                                 chem_id])

            if all([values[0] is not None for values in reac_def]):
                balanced, _, balanced_def = chem_utils.balance(reac_def)
                properties['balance'] = balanced
            else:
                properties['balance'] = 'unknown'
                balanced_def = reac_def

            reac_id = self.__reac_man.add_reaction('mnx', mnx_id,
                                                   properties)
            reac_id_def[reac_id] = balanced_def

        chem_id_mass = self.__chem_man.get_props('monoisotopic_mass:float',
                                                 float('NaN'))
        cofactors = [chem_id
                     for chem_id, mass in chem_id_mass.iteritems()
                     if mass > 0 and mass < 44]  # Assume mass < CO2 = cofactor

        cofactor_pairs = _calc_cofactors(reac_id_def.values(), cofactors)
        rels = []

        for reac_id, defn in reac_id_def.iteritems():
            reactants = [term[3] for term in defn if term[2] < 0]
            products = [term[3] for term in defn if term[2] > 0]
            reac_cofactors = []

            # Set metabolites as cofactors:
            for met in [term[3] for term in defn]:
                if met in cofactors:
                    reac_cofactors.append(met)

            # Set pairs as cofactors:
            for pair in itertools.product(reactants, products):
                if tuple(sorted(pair)) in cofactor_pairs:
                    reac_cofactors.extend(pair)

            for term in defn:
                rels.append([reac_id,
                             'has_cofactor' if term[3] in reac_cofactors
                             else 'has_reactant',
                             term[3],
                             {'stoichiometry:float': term[2]}])

        return rels