diff --git a/.travis.yml b/.travis.yml index ed98b8337..8a6347137 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,6 @@ addons: - pandoc - openbabel before_install: -#- if [ ! -d "cplex/python/$TRAVIS_PYTHON_VERSION/x86-64_linux/" ]; then curl -L SECRET_CPLEX_LINK -o cplex.tar.gz && tar xvf cplex.tar.gz; fi - pip install pip --upgrade - pip install codecov - curl -L $SECRET_CPLEX_LINK -o cplex.tar.gz @@ -37,7 +36,7 @@ install: - pip install flake8 - pip install .[swiglpk,test,parallel] before_script: -- "flake8 ." +- flake8 . script: nosetests after_success: - codecov @@ -45,17 +44,28 @@ notifications: slack: biosustain:UGm09rOjMCgXko3rS3wUivoX before_deploy: - pip install twine -deploy: - provider: pypi - user: Nikolaus.Sonnenschein - password: - secure: nxjszXtUzQfnLlfg0cmFjd9gRekXDog6dkkN1rMc7CIWH2gZ1gAX4sNETVChnuSmu9egzhuIkviHstRrdyGoEZ7ZkHlTXmpVAs9AY96eMSejnwHHODhYno0jB7DjGcfejodLF+lo6lWz7S7mXXwML6YLM3xxG+AOjLHlHbPTaKc= - distributions: sdist bdist_wheel - on: - branch: master - tags: true - repo: biosustain/cameo - docs_dir: docs/_build/html + - python setup.py sdist bdist_wheel env: global: secure: QgrOXEgpcH6xgToVfWIX6j6CPvycKMPtNnoYAxPrZjkMzd2aCHHeokv0FZkCn3uePO0I8W8TkKBxilGZbWYoseDq+Snds18sBTG9u2NHvYHnDQb4Oki7+NoxhlnGIOj/8ADONOpc0n7PyFDPK8zmKVZvv9p78OHZO5CmV/ktOeg= +deploy: + - provider: releases + api_key: + secure: VsKdkwYvp7lf65S/pzLWbrk8PaRAzBVClB57s4jYepx+BbJdPJi5Zwz4zmu0Ifa1K7K2Jh0rITV9GZAyC+0Eq2ffXtZsBOsC5+2yKaWV5WiU7kNdUVhD9EFkUaNknT8+B2/sjPsl+GP8DTzCqstgdGo5EAAnvpV53qIAwwF9n0U= + file_glob: true + file: dist/cameo*.whl + skip_cleanup: true + on: + branch: master + tags: true + repo: biosustain/cameo + - provider: pypi + user: Nikolaus.Sonnenschein + password: + secure: nxjszXtUzQfnLlfg0cmFjd9gRekXDog6dkkN1rMc7CIWH2gZ1gAX4sNETVChnuSmu9egzhuIkviHstRrdyGoEZ7ZkHlTXmpVAs9AY96eMSejnwHHODhYno0jB7DjGcfejodLF+lo6lWz7S7mXXwML6YLM3xxG+AOjLHlHbPTaKc= + distributions: sdist bdist_wheel + on: + branch: master + tags: true + repo: biosustain/cameo + docs_dir: docs/_build/html diff --git a/cameo/core/solver_based_model.py b/cameo/core/solver_based_model.py index e994b4966..bc33e4687 100644 --- a/cameo/core/solver_based_model.py +++ b/cameo/core/solver_based_model.py @@ -307,13 +307,22 @@ def remove_reactions(self, the_reactions, delete=True, remove_orphans=False): super(SolverBasedModel, self).remove_reactions(the_reactions, delete=delete, remove_orphans=remove_orphans) def add_demand(self, metabolite, prefix="DM_", time_machine=None): - """Add a demand reaction for a metabolite (metabolite --> Ø) + from warnings import warn + warn('"add_demand" function is replaced with "add_exchange".', PendingDeprecationWarning) + return self.add_exchange(metabolite, prefix=prefix, time_machine=time_machine) + + def add_exchange(self, metabolite, demand=True, prefix='DM_', bound=1000.0, time_machine=None): + """Add an exchange reaction for a metabolite (demand=TRUE: metabolite --> Ø or demand=False: 0 --> metabolite ) Parameters ---------- metabolite : Metabolite + demand : bool, optional + True for sink type exchange, False for uptake type exchange prefix : str, optional A prefix that will be added to the metabolite ID to be used as the demand reaction's ID (defaults to 'DM_'). + bound : float, optional + Upper bound for sink reaction / lower bound for uptake (multiplied by -1) time_machine : TimeMachine, optional A TimeMachine instance that enables undoing. @@ -323,22 +332,30 @@ def add_demand(self, metabolite, prefix="DM_", time_machine=None): The created demand reaction. """ id = str(prefix + metabolite.id) - name = "Exchange %s" % metabolite.name if prefix == "EX_" else "Demand %s" % metabolite.name + name = "Exchange %s" % metabolite.name if prefix != "DM_" else "Demand %s" % metabolite.name if id in self.reactions: raise ValueError("The metabolite already has a demand reaction.") - demand_reaction = Reaction() - demand_reaction.id = id - demand_reaction.name = name - demand_reaction.add_metabolites({metabolite: -1}) - demand_reaction.lower_bound = 0 - demand_reaction.upper_bound = 1000 + reaction = Reaction() + reaction.id = id + reaction.name = name + + reaction.add_metabolites({metabolite: -1}) + if demand: + reaction.upper_bound = bound + reaction.lower_bound = 0 + else: + reaction.upper_bound = 0 + reaction.lower_bound = -bound + + + if time_machine is not None: - time_machine(do=partial(self.add_reactions, [demand_reaction]), - undo=partial(self.remove_reactions, [demand_reaction], delete=False)) + time_machine(do=partial(self.add_reactions, [reaction]), + undo=partial(self.remove_reactions, [reaction], delete=False)) else: - self.add_reactions([demand_reaction]) - return demand_reaction + self.add_reactions([reaction]) + return reaction def fix_objective_as_constraint(self, time_machine=None, fraction=1): """Fix current objective as an additional constraint (e.g., ..math`c^T v >= max c^T v`). diff --git a/docs/Makefile b/docs/Makefile index e567ae428..4ce568f65 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -47,13 +47,17 @@ help: @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " ipy2rst to convert ipython notebooks in to rst files" @echo " apidoc to autogenerate API " + @echo " updatenb to update the notebooks git submodule" + +updatenb: + git submodule update --remote --merge ipy2rst: for i in notebooks/*.ipynb; do if [ $$i != "notebooks/index.ipynb" ] ; then jupyter nbconvert $$i --to rst; fi done sed -i '' 's/``/`/g' *.rst apidoc: - sphinx-apidoc --no-toc -f -o apidoc_output/ ../cameo ../cameo/strain_design/heuristic/multiprocess ../cameo/stuff ../cameo/strain_design/heuristic/evolutionary + sphinx-apidoc --no-toc -f -o apidoc_output/ ../cameo ../cameo/strain_design/heuristic/multiprocess ../cameo/stuff ../cameo/strain_design/heuristic/evolutionary ../cameo/visualization/plotting clean: rm -rf $(BUILDDIR)/* diff --git a/docs/README.md b/docs/README.md index 79409300b..396c1c6f0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,6 +4,10 @@ Cameo tutorials come in the form of jupyter notebooks (./notebooks) that can be ```make ipy2rst``` +To update to the latest notebooks run + +```git submodule update --remote --merge``` + The notebooks folder is a git submodule pointing to [http://github.com/biosustain/cameo-notebooks](http://github.com/biosustain/cameo-notebooks). To regenerate the API docs run diff --git a/docs/_static/cameo_logo.png b/docs/_static/cameo_logo.png new file mode 100644 index 000000000..cf86d51ff Binary files /dev/null and b/docs/_static/cameo_logo.png differ diff --git a/docs/_static/cameo_touch_icon.png b/docs/_static/cameo_touch_icon.png new file mode 100644 index 000000000..45544685a Binary files /dev/null and b/docs/_static/cameo_touch_icon.png differ diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 000000000..c5fc8fe9f Binary files /dev/null and b/docs/_static/favicon.ico differ diff --git a/docs/apidoc_output/cameo.core.rst b/docs/apidoc_output/cameo.core.rst index 0174d2824..635329bdc 100644 --- a/docs/apidoc_output/cameo.core.rst +++ b/docs/apidoc_output/cameo.core.rst @@ -4,6 +4,22 @@ cameo.core package Submodules ---------- +cameo.core.gene module +---------------------- + +.. automodule:: cameo.core.gene + :members: + :undoc-members: + :show-inheritance: + +cameo.core.metabolite module +---------------------------- + +.. automodule:: cameo.core.metabolite + :members: + :undoc-members: + :show-inheritance: + cameo.core.pathway module ------------------------- diff --git a/docs/apidoc_output/cameo.flux_analysis.rst b/docs/apidoc_output/cameo.flux_analysis.rst index c7e1f7968..d53510297 100644 --- a/docs/apidoc_output/cameo.flux_analysis.rst +++ b/docs/apidoc_output/cameo.flux_analysis.rst @@ -20,6 +20,14 @@ cameo.flux_analysis.simulation module :undoc-members: :show-inheritance: +cameo.flux_analysis.structural module +------------------------------------- + +.. automodule:: cameo.flux_analysis.structural + :members: + :undoc-members: + :show-inheritance: + cameo.flux_analysis.util module ------------------------------- diff --git a/docs/apidoc_output/cameo.visualization.rst b/docs/apidoc_output/cameo.visualization.rst index dab4f282b..9ffc80211 100644 --- a/docs/apidoc_output/cameo.visualization.rst +++ b/docs/apidoc_output/cameo.visualization.rst @@ -12,10 +12,18 @@ cameo.visualization.escher_ext module :undoc-members: :show-inheritance: -cameo.visualization.plotting module ------------------------------------ +cameo.visualization.palette module +---------------------------------- -.. automodule:: cameo.visualization.plotting +.. automodule:: cameo.visualization.palette + :members: + :undoc-members: + :show-inheritance: + +cameo.visualization.plotting_old module +--------------------------------------- + +.. automodule:: cameo.visualization.plotting_old :members: :undoc-members: :show-inheritance: diff --git a/docs/conf.py b/docs/conf.py index 7f3afedc4..17dc342ac 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -176,7 +176,7 @@ # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -# html_favicon = None +html_favicon = 'favicon.ico' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, diff --git a/docs/development.rst b/docs/development.rst index 97d1304ad..3e568560f 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -114,7 +114,7 @@ The LP/MILP solver can be changed in the following way. model.solver = 'cplex' -Currently ``cplex`` and ``glpk`` are supported. +Currently `cplex` and `glpk` are supported. Manipulating the solver object ------------------------------ diff --git a/docs/index.rst b/docs/index.rst index a7bed2ea5..80086f8aa 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -2,7 +2,7 @@ Welcome to cameo! ================= -|Build Status| |Coverage Status| |DOI| +|PyPI| |License| |Build Status| |Coverage Status| |DOI| **Cameo** is a high-level python library developed to aid the strain design process in metabolic engineering projects. The library provides a @@ -61,9 +61,13 @@ Indices and tables * :ref:`search` +.. |PyPI| image:: https://img.shields.io/pypi/v/cameo.svg + :target: https://pypi.python.org/pypi/cameo +.. |License| image:: http://img.shields.io/badge/license-APACHE2-blue.svg + :target: http://img.shields.io/badge/license-APACHE2-blue.svg .. |Build Status| image:: https://travis-ci.org/biosustain/cameo.svg?branch=master :target: https://travis-ci.org/biosustain/cameo -.. |Coverage Status| image:: https://codecov.io/github/biosustain/cameo/coverage.svg?branch=devel - :target: https://codecov.io/github/biosustain/cameo?branch=devel -.. |DOI| image:: https://zenodo.org/badge/doi/10.5281/zenodo.19827.svg - :target: http://dx.doi.org/10.5281/zenodo.19827 +.. |Coverage Status| image:: https://coveralls.io/repos/biosustain/cameo/badge.svg?branch=devel + :target: https://coveralls.io/r/biosustain/cameo?branch=devel +.. |DOI| image:: https://zenodo.org/badge/5031/biosustain/cameo.svg + :target: https://zenodo.org/badge/latestdoi/5031/biosustain/cameo diff --git a/docs/notebooks b/docs/notebooks index b04ac19fe..af26f59f4 160000 --- a/docs/notebooks +++ b/docs/notebooks @@ -1 +1 @@ -Subproject commit b04ac19fed51c95f9558b9187c7c2064049dde64 +Subproject commit af26f59f4b1fb043b22b878a971bf0efda1ba60f diff --git a/tests/test_solver_based_model.py b/tests/test_solver_based_model.py index 9c0d15f84..fc5671b96 100644 --- a/tests/test_solver_based_model.py +++ b/tests/test_solver_based_model.py @@ -714,25 +714,27 @@ def test_remove_reactions(self): for reaction in reactions_to_remove: self.assertIn(reaction, self.model.reactions) - def test_add_demand(self): - for metabolite in self.model.metabolites: - demand_reaction = self.model.add_demand(metabolite, prefix="DemandReaction_") - self.assertEqual(self.model.reactions.get_by_id(demand_reaction.id), demand_reaction) - self.assertEqual(demand_reaction.reactants, [metabolite]) - self.assertTrue(self.model.solver.constraints[metabolite.id].expression.has( - self.model.solver.variables["DemandReaction_" + metabolite.id])) - - def test_add_demand_time_machine(self): - with TimeMachine() as tm: + def test_add_exchange(self): + for demand, prefix in {True: 'DemandReaction_', False: 'SupplyReaction_'}.items(): for metabolite in self.model.metabolites: - demand_reaction = self.model.add_demand(metabolite, time_machine=tm) + demand_reaction = self.model.add_exchange(metabolite, demand=demand, prefix=prefix) self.assertEqual(self.model.reactions.get_by_id(demand_reaction.id), demand_reaction) self.assertEqual(demand_reaction.reactants, [metabolite]) - self.assertTrue(-self.model.solver.constraints[metabolite.id].expression.has( - self.model.solver.variables["DM_" + metabolite.id])) - for metabolite in self.model.metabolites: - self.assertNotIn("DM_" + metabolite.id, self.model.reactions) - self.assertNotIn("DM_" + metabolite.id, self.model.solver.variables.keys()) + self.assertTrue(self.model.solver.constraints[metabolite.id].expression.has( + self.model.solver.variables[prefix + metabolite.id])) + + def test_add_exchange_time_machine(self): + for demand, prefix in {True: 'DemandReaction_', False: 'SupplyReaction_'}.items(): + with TimeMachine() as tm: + for metabolite in self.model.metabolites: + demand_reaction = self.model.add_exchange(metabolite, demand=demand, prefix=prefix, time_machine=tm) + self.assertEqual(self.model.reactions.get_by_id(demand_reaction.id), demand_reaction) + self.assertEqual(demand_reaction.reactants, [metabolite]) + self.assertTrue(-self.model.solver.constraints[metabolite.id].expression.has( + self.model.solver.variables[prefix + metabolite.id])) + for metabolite in self.model.metabolites: + self.assertNotIn(prefix + metabolite.id, self.model.reactions) + self.assertNotIn(prefix + metabolite.id, self.model.solver.variables.keys()) def test_objective(self): obj = self.model.objective