Commit 4292ad33 authored by Elad Noor's avatar Elad Noor

moving to pytest and tox

parent 6d9ef874
{}
\ No newline at end of file
[
"tests/test_unit/test_api.py::test_atp_hydrolysis",
"tests/test_unit/test_api.py::test_reaction_balancing",
"tests/test_unit/test_api.py::test_gibbs_energy",
"tests/test_unit/test_api.py::test_reduction_potential",
"tests/test_unit/test_api.py::test_mdf",
"tests/test_unit/test_api.py::test_reaction_matcher"
]
\ No newline at end of file
.. highlight:: shell
============
Contributing
============
Contributions are welcome, and they are greatly appreciated! Every little bit
helps, and credit will always be given.
You can contribute in many ways:
Types of Contributions
======================
Report Bugs
-----------
Report bugs at https://gitlab.com/elad.noor/equilibrator-api/issues.
If you are reporting a bug, please follow the presented issue template since it
is designed to ultimately make helping you easier and thus faster.
Write Documentation
-------------------
As any open source project, equilibrator-api could always use more and
better documentation, whether as part of the official docs, in docstrings, or
even on the web in blog posts, articles.
Submit Feedback
---------------
The best way to send feedback is to file an issue at
https://gitlab.com/elad.noor/equilibrator-api/issues.
If you are proposing a feature:
* Explain in detail how it would work.
* Keep the scope as narrow as possible, to make it easier to implement.
* Remember that this is a volunteer-driven project, and that contributions
are welcome ;)
Get Started!
------------
Ready to contribute? Here's how to set up ``equilibrator-api`` for local
development.
1. Install `git-lfs <https://git-lfs.github.com/>`__. On many linux
distributions this can be installed from official package managers. 2. Fork
the ``equilibrator-api`` repo on GitLab. 3. Clone your fork locally::
git clone git@gitlab.com:<your_name_here>/equilibrator-api.git
4. Create a branch for local development::
git checkout -b name-of-your-bugfix-or-feature-branch
Now you can make your changes locally.
5. When you're done making changes, check that your changes pass the quality
control::
tox
To get tox, just pip install it.
6. Commit your changes using `semantic commit messages
<https://seesparkbox.com/foundry/semantic_commit_messages>`__ and push your
branch to GitHub::
git add . git commit -m "feat: your detailed description of your changes"
git push origin name-of-your-bugfix-or-feature
7. Submit a pull request to this repository through the GitHub website.
The MIT License (MIT)
Copyright (c) 2013 Weizmann Institute of Science
Copyright (c) 2018 Institute for Molecular Systems Biology,
ETH Zurich
Copyright (c) 2018 Novo Nordisk Foundation Center for Biosustainability,
Technical University of Denmark
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.
recursive-include equilibrator_api/data *.csv *.npz *.json *.tsv
include versioneer.py
include component_contribution/_version.py
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Thu Aug 10 17:22:11 2017
@author: noore
"""
import unittest
import os
import warnings
import inspect
from equilibrator_api import Reaction, ComponentContribution, ReactionMatcher
from equilibrator_api import Pathway
TEST_DIR = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
class TestReactionParsing(unittest.TestCase):
def test_atp_hydrolysis(self):
warnings.simplefilter('ignore', ResourceWarning)
formula = ' C00002 + C00001 <= C00008 + C00009'
kegg_ids = set(('C00002', 'C00001', 'C00008', 'C00009'))
try:
reaction = Reaction.parse_formula(formula)
except ValueError as e:
self.fail('unable to parse the formula\n' + str(e))
self.assertSetEqual(set(reaction.kegg_ids), kegg_ids)
for kegg_id in kegg_ids:
self.assertIsNotNone(reaction.get_compound(kegg_id))
self.assertNotEqual(reaction.get_coeff(kegg_id), 0)
self.assertIsNone(reaction.get_compound('C00003'))
self.assertEqual(reaction.get_coeff('C00003'), 0)
def test_reaction_balancing(self):
warnings.simplefilter('ignore', ResourceWarning)
kegg_id_to_coeff = {'C00011' : -1, 'C00001' : -1, 'C01353' : 1}
reaction = Reaction(kegg_id_to_coeff)
self.assertTrue(reaction.check_full_reaction_balancing())
kegg_id_to_coeff = {'C00036' : -1, 'C00149' : 1} # oxaloacetate = malate
reaction = Reaction(kegg_id_to_coeff)
self.assertAlmostEqual(reaction.check_half_reaction_balancing(), 2.0)
kegg_id_to_coeff = {'C00031' : -1, 'C00469' : 2} # missing two CO2
reaction = Reaction(kegg_id_to_coeff)
self.assertDictEqual(reaction._get_reaction_atom_balance(),
{'O': -4, 'C': -2, 'e-': -44})
def test_gibbs_energy(self):
warnings.simplefilter('ignore', ResourceWarning)
kegg_id_to_coeff = {'C00002' : -1, 'C00001' : -1,
'C00008' : 1, 'C00009' : 1} # ATP + H2O = ADP + Pi
kegg_id_to_conc = {'C00002' : 1e-3,
'C00009' : 1e-4}
reaction = Reaction(kegg_id_to_coeff)
cc = ComponentContribution(pH=7.0, ionic_strength=0.1)
dG0_prime, dG0_uncertainty = cc.dG0_prime(reaction)
self.assertAlmostEqual(dG0_prime, -26.4, 1)
self.assertAlmostEqual(dG0_uncertainty, 0.6, 1)
dG_prime, _ = cc.dG_prime(reaction, kegg_id_to_conc)
self.assertAlmostEqual(dG_prime, -32.1, 1)
dGm_prime, _ = cc.dGm_prime(reaction)
self.assertAlmostEqual(dGm_prime, -43.5, 1)
def test_reduction_potential(self):
warnings.simplefilter('ignore', ResourceWarning)
kegg_id_to_coeff = {'C00036' : -1, 'C00149' : 1} # oxaloacetate = malate
reaction = Reaction(kegg_id_to_coeff)
cc = ComponentContribution(pH=7.0, ionic_strength=0.1)
E0_prime_mV, E0_uncertainty = cc.E0_prime(reaction)
self.assertAlmostEqual(E0_prime_mV, -175.2, 1)
self.assertAlmostEqual(E0_uncertainty, 5.3, 1)
def test_mdf(self):
warnings.simplefilter('ignore', ResourceWarning)
sbtab_fname = os.path.join(TEST_DIR, 'pathway_ethanol_SBtab.tsv')
pp = Pathway.from_sbtab(sbtab_fname)
mdf_res = pp.calc_mdf().mdf_result
self.assertAlmostEqual(mdf_res.mdf, 1.69, 2)
self.assertAlmostEqual(mdf_res.max_total_dG, -159.05, 2)
self.assertAlmostEqual(mdf_res.min_total_dG, -181.05, 2)
self.assertAlmostEqual(mdf_res.reaction_prices[0, 0], 0.0, 2)
self.assertAlmostEqual(mdf_res.reaction_prices[3, 0], 0.25, 2)
self.assertAlmostEqual(mdf_res.reaction_prices[4, 0], 0.25, 2)
self.assertAlmostEqual(mdf_res.reaction_prices[5, 0], 0.50, 2)
self.assertAlmostEqual(mdf_res.compound_prices[1, 0], 0.0, 2)
self.assertAlmostEqual(mdf_res.compound_prices[2, 0], 1.24, 2)
self.assertAlmostEqual(mdf_res.compound_prices[3, 0], -1.24, 2)
def test_reaction_matcher(self):
warnings.simplefilter('ignore', ResourceWarning)
formulas = [('ATP + H2O <=> ADP + Phosphate',
{'C00002': -1, 'C00001': -1, 'C00008': 1, 'C00009': 1}),
('O2 + 2 NADH <=> 2 NAD+ + 2 H2O',
{'C00007': -1, 'C00004': -2, 'C00003': 2, 'C00001': 2}),
('O2 + 2 NADH <=> 2 NAD+ + 2 H2O',
{'C00007': -1, 'C00004': -2, 'C00003': 2, 'C00001': 2}),
('ATP + D-arbino-hexulose <=> ADP + D-Fructose-1-phophte', # with some typos
{'C00002': -1, 'C00095': -1, 'C00008': 1, 'C01094': 1}),
]
rm = ReactionMatcher()
for plaintext, kegg_id_to_coeff in formulas:
rxn = rm.match(plaintext)
if rxn is None:
self.fail('unable to parse the plaintext formula\n' + plaintext)
else:
self.assertDictEqual(rxn.kegg_id_to_coeff, kegg_id_to_coeff)
if __name__ == '__main__':
unittest.main()
[build-system]
requires = [
"setuptools>=30.3.0",
"wheel"
]
numpy>=1.15.2
scipy>=1.1.0
optlang>=1.4.3
pandas>=0.23.4
nltk>=3.2.5
pyparsing>=2.2.0
sbtab>=0.9.62
matplotlib>=3.0.0
[metadata]
name = equilibrator-api
url = https://gitlab.com/elad.noor/equilibrator-api
author = Elad Noor
author_email = noor@imsb.biol.ethz.ch
maintainer = Elad Noor
maintainer_email = noor@imsb.biol.ethz.ch
classifiers =
Development Status :: 4 - Beta
Intended Audience :: Science/Research
Topic :: Scientific/Engineering :: Chemistry
License :: OSI Approved :: MIT License
Natural Language :: English
Programming Language :: Python :: 3
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
license = MIT License
description = Standard reaction Gibbs energy estimation for biochemical reactions.
long_description = file: README.rst
keywords =
Gibbs energy
biochemical reaction
equilibrator
[options]
zip_safe = True
install_requires =
importlib_resources
numpy>=1.15.2
scipy>=1.1.0
optlang>=1.4.3
pandas>=0.23.4
nltk>=3.2.5
pyparsing>=2.2.0
sbtab>=0.9.49
matplotlib>=3.0.0
requests
python_requires = >=3.5
tests_require =
tox
packages = find:
package_dir =
= src
[options.packages.find]
where = src
[options.package_data]
equilibrator_api.data =
*.csv
*.tsv
*.json
*.npz
[wheel]
universal = 1
[bdist_wheel]
universal = 1
[flake8]
max-line-length = 80
exclude =
__init__.py
docs
[pydocstyle]
match_dir = equilibrator_api
[tool:unittest]
testpaths = tests
[isort]
line_length = 80
indent = 4
multi_line_output = 4
lines_after_imports = 2
known_first_party = equilibrator_api
known_third_party =
numpy
pandas
pybel
unittest
scipy
optlang
nltk
pyparsing
sbtab
matplotlib
# See the docstring in versioneer.py for instructions. Note that you must
# re-run 'versioneer.py setup' after changing this section, and commit the
# resulting files.
[versioneer]
VCS = git
style = pep440
versionfile_source = src/equilibrator_api/_version.py
versionfile_build = equilibrator_api/_version.py
tag_prefix =
parentdir_prefix = equilibrator_api-
#!/usr/bin/python3
#!/usr/bin/env python
__doc__ = "eQuilibrator API - A command-line API with minimal dependencies for calculation of standard thermodynamic potentials of biochemical reactions using the data found on eQuilibrator. Does not require any network connections."
__version__ = '0.1.8'
import os
import versioneer
from setuptools import setup
try:
import setuptools
except Exception as ex:
print(ex)
os.sys.exit(-1)
mydata_files = ['cc_compounds.json',
'cc_preprocess.npz',
'cofactors.csv',
'iJO1366_reactions.csv',
'kegg_compound_names.tsv',
'kegg_compound_renaming.tsv']
data_files = [('data',
[os.path.join('equilibrator_api/data', f) for f in mydata_files])]
# Most arguments are set in the `setup.cfg`.
setup(
version=versioneer.get_version(),
cmdclass=versioneer.get_cmdclass(),
)
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name='equilibrator_api',
version=__version__,
description=__doc__,
long_description=long_description,
url='https://gitlab.com/elad.noor/equilibrator-api',
author='Elad Noor',
author_email='noor@imsb.biol.ethz.ch',
license='MIT',
packages=['equilibrator_api'],
install_requires=[
'numpy>=1.15.2',
'scipy>=1.1.0',
'optlang>=1.4.3',
'pandas>=0.23.4',
'nltk>=3.2.5',
'pyparsing>=2.2.0',
'sbtab>=0.9.49',
'matplotlib>=3.0.0',
],
include_package_data=True,
data_files = data_files
)
This diff is collapsed.
import pkg_resources
import logging
import csv
from numpy import ones, log
import logging
from copy import deepcopy
from equilibrator_api.concs import ConcentrationConverter
import pkg_resources
from numpy import log, ones
from equilibrator_api import settings
from equilibrator_api.concs import ConcentrationConverter
class InvalidBounds(Exception):
pass
......
import pkg_resources
import logging
from numpy import array, load, zeros, sqrt, log
import pkg_resources
from numpy import array, load, log, sqrt, zeros
from equilibrator_api import settings
PREPROCESS_FNAME = pkg_resources.resource_filename(
'equilibrator_api', 'data/cc_preprocess.npz')
......@@ -234,4 +237,3 @@ class ComponentContribution(object):
sum_w_gc = sum(abs(weights_gc).flat)
logging.info('sum(w_gc) = %.2g' % sum_w_gc)
return sum_w_gc > 1e-5
......@@ -8,9 +8,11 @@ Created on Sun Oct 1 13:01:52 2017
import logging
import re
from numpy import sqrt
from numpy import array, logaddexp, sqrt, zeros
from equilibrator_api import settings
from numpy import array, zeros, logaddexp
class Species(object):
......
......@@ -7,6 +7,7 @@ Created on Sun Oct 1 13:28:00 2017
"""
import numpy as np
class ReactionMDFData(object):
def __init__(self, reaction, flux, dG0, dGm, dGr, shadow_price):
......
......@@ -7,15 +7,18 @@ Created on Sun Oct 1 13:26:31 2017
"""
import csv
import logging
from numpy import array, eye, log, zeros
from scipy import linalg
from sbtab import SBtab
from equilibrator_api.reaction import Reaction
from scipy import linalg
from equilibrator_api.bounds import DEFAULT_BOUNDS, Bounds
from equilibrator_api.component_contribution import ComponentContribution
from equilibrator_api.max_min_driving_force import PathwayMDFData
from equilibrator_api.reaction import Reaction
from equilibrator_api.settings import DEFAULT_IONIC_STRENGTH, DEFAULT_PH, RT
from equilibrator_api.thermo_models import PathwayThermoModel
from equilibrator_api.component_contribution import ComponentContribution
from equilibrator_api.settings import RT, DEFAULT_PH, DEFAULT_IONIC_STRENGTH
class PathwayParseError(Exception):
pass
......
import logging
import pyparsing
import re
import numpy
import pyparsing
POSSIBLE_REACTION_ARROWS = ('=', '=>', '<=>', '->',
'<->', u'\u2192', u'\u21CC')
......@@ -137,4 +139,3 @@ class QueryParser(object):
except pyparsing.ParseException as msg:
logging.error('Failed to parse query %s', query)
raise ParseError(msg)
import pkg_resources
import json
import re
import logging
import re
import pandas as pd
from numpy import array, zeros, log, nan, inf
import pkg_resources
from numpy import array, inf, log, nan, zeros
from numpy.linalg import inv
from equilibrator_api import settings
......@@ -434,4 +435,3 @@ if __name__ == '__main__':
r = Reaction.get_oxidation_reaction('C04619')
print(r.write_formula())
print('standard oxidation energy of (3R)-3-Hydroxydecanoyl-[acyl-carrier protein]: %s kJ/mol' % r.dG0_prime())
import pkg_resources
from nltk.metrics import edit_distance
from collections import defaultdict
import logging
import itertools
import logging
from collections import defaultdict
import pandas as pd
from equilibrator_api import Reaction, Compound
from equilibrator_api.query_parser import QueryParser, ParseError
import pkg_resources
from nltk.metrics import edit_distance
from equilibrator_api import Compound, Reaction
from equilibrator_api.query_parser import ParseError, QueryParser
COMPOUND_NAME_FILE = pkg_resources.resource_filename(
'equilibrator_api', 'data/kegg_compound_names.tsv')
......
from numpy import log
POSSIBLE_REACTION_ARROWS = ['<=>', '<=', '=>', '->', '<->', '=']
R = 8.31e-3 # kJ/(K*mol)
......@@ -17,4 +18,4 @@ MG_FORMATION_ENERGY = -455.3 # kJ/mol, formation energy of Mg2+
FARADAY = 96.485 # kC/mol
DEFAULT_CONC_LB = 1e-6
DEFAULT_CONC_UB = 1e-2
\ No newline at end of file
DEFAULT_CONC_UB = 1e-2
import logging
from optlang.glpk_interface import Variable, Model, Constraint, Objective
from numpy import array, zeros, ones, sign, diag, log, isnan, nonzero, \
vstack, hstack, sqrt, eye, exp, nan
from numpy import (
array, diag, exp, eye, hstack, isnan, log, nan, nonzero, ones, sign, sqrt,
vstack, zeros)
from optlang.glpk_interface import Constraint, Model, Objective, Variable
from equilibrator_api import settings
from equilibrator_api.bounds import Bounds
class MDFResult(object):
def __init__(self, model, mdf,
......
python3 -m unittest discover equilibrator_api/tests
# The MIT License (MIT)
#
# Copyright (c) 2013 The Weizmann Institute of Science.
# Copyright (c) 2018 Novo Nordisk Foundation Center for Biosustainability,
# Technical University of Denmark.
# Copyright (c) 2018 Institute for Molecular Systems Biology, ETH Zurich,
#
# 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.
import inspect
import os
import warnings
import pytest
from equilibrator_api import (
ComponentContribution, Pathway, Reaction, ReactionMatcher)
TEST_DIR = os.path.dirname(
os.path.abspath(inspect.getfile(inspect.currentframe())))
def test_atp_hydrolysis():
warnings.simplefilter('ignore', ResourceWarning)
formula = ' C00002 + C00001 <= C00008 + C00009'
kegg_ids = set(('C00002', 'C00001', 'C00008', 'C00009'))
reaction = Reaction.parse_formula(formula)
assert set(reaction.kegg_ids) == kegg_ids
for kegg_id in kegg_ids:
assert reaction.get_compound(kegg_id) is not None
assert reaction.get_coeff(kegg_id) != 0
assert reaction.get_compound('C00003') is None
assert reaction.get_coeff('C00003') == 0
def test_reaction_balancing():
warnings.simplefilter('ignore', ResourceWarning)
kegg_id_to_coeff = {'C00011': -1, 'C00001': -1, 'C01353': 1}
reaction = Reaction(kegg_id_to_coeff)
assert reaction.check_full_reaction_balancing()
kegg_id_to_coeff = {'C00036': -1, 'C00149': 1} # oxaloacetate = malate
reaction = Reaction(kegg_id_to_coeff)
assert reaction.check_half_reaction_balancing() == \
pytest.approx(2.0, rel=1e-3)
kegg_id_to_coeff = {'C00031': -1, 'C00469': 2} # missing two CO2
reaction = Reaction(kegg_id_to_coeff)
assert reaction._get_reaction_atom_balance() == \
{'O': -4, 'C': -2, 'e-': -44}
def test_gibbs_energy():
warnings.simplefilter('ignore', ResourceWarning)
kegg_id_to_coeff = {'C00002': -1, 'C00001': -1,
'C00008': 1, 'C00009': 1} # ATP + H2O = ADP + Pi
kegg_id_to_conc = {'C00002': 1e-3, 'C00009': 1e-4}
reaction = Reaction(kegg_id_to_coeff)
cc = ComponentContribution(pH=7.0, ionic_strength=0.1)
dG0_prime, dG0_uncertainty = cc.dG0_prime(reaction)
assert dG0_prime == pytest.approx(-26.4, abs=1e-1)
assert dG0_uncertainty == pytest.approx(0.6, abs=1e-1)
dG_prime, _ = cc.dG_prime(reaction, kegg_id_to_conc)
assert dG_prime == pytest.approx(-32.1, abs=1e-1)
dGm_prime, _ = cc.dGm_prime(reaction)
assert dGm_prime == pytest.approx(-43.5, abs=1e-1)
def test_reduction_potential():
warnings.simplefilter('ignore', ResourceWarning)
kegg_id_to_coeff = {'C00036': -1, 'C00149': 1} # oxaloacetate = malate
reaction = Reaction(kegg_id_to_coeff)