# This file is part of bioservices software
#
# Copyright (c) 2013-2014 - EBI-EMBL
#
# Distributed under the GPLv3 License.
# See accompanying file LICENSE.txt or copy at
# http://www.gnu.org/licenses/gpl-3.0.html
#
# website: https://github.com/cokelaer/bioservices
# documentation: http://packages.python.org/bioservices
#
##############################################################################
"""This module provides a class :class:`~BioModels` to access to BioModels WS.
.. topic:: What is BioModels ?
:URL: http://www.ebi.ac.uk/biomodels/
:Service: http://www.ebi.ac.uk/biomodels
:Citations: please visit https://www.ebi.ac.uk/biomodels/citation for details
.. highlights::
"BioModels is a repository of mathematical models of biological and biomedical
systems. It hosts a vast selection of existing literature-based physiologically
and pharmaceutically relevant mechanistic models in standard formats. Our
mission is to provide the systems modelling community with reproducible,
high-quality, freely-accessible models published in the scientific literature."
-- From BioModels website, March 2020
"""
import os
import copy
import webbrowser
from functools import wraps
from bioservices import logger
logger.name = __name__
from bioservices.services import REST
try:
# python 3
from urllib.request import urlopen
except:
from urllib2 import urlopen
__all__ = ["BioModels"]
[docs]class BioModels(REST):
"""Interface to the `BioModels <http://www.ebi.ac.uk/biomodels>`_ service
::
from bioservices import BioModels
bm = BioModels()
model = bm.get_model('BIOMD0000000299')
Previous API had several functions such as *getAuthorsByModelId*. This is
easy to mimic with the new API::
bm = BioModels()
models = bm.get_all_models()
[x['submitter'] for x in res if x[] == "MODEL1204280003"][0]
This is also true for *getDateLastModifByModelId* and *getModelNameById* if
one use the field *lastModified* or *name*. There was the ability to
search for models based on their CHEBI identifiers, which is not
supported anymore; this concerns functions
*getModelsIdByChEBI*, *getModelsIdByChEBIId*, *getSimpleModelsByChEBIIds*,
*getSimpleModelsRelatedWithChEBI*. For other searches related to Reactome,
Uniprot identifiers or GO terms, the :meth:`search` method should work::
bm.search("P10113")
bm.search("REACT_33")
bm.search("GO:0006919")
"""
_url = "https://www.ebi.ac.uk/biomodels"
_id_example = "BIOMD0000000100"
def __init__(self, verbose=True):
""".. rubric:: constructor
:param bool verbose:
"""
super(BioModels, self).__init__(name="BioModels", url=BioModels._url, verbose=verbose)
def _check_format(self, frmt, supported=["json", "xml", "html"]):
if frmt not in supported:
raise ValueError("Supported format for this function are {}. You provided {}".format(supported, frmt))
[docs] def get_all_models(self, chunk=100):
"""Return all models"""
models = []
offset = 0
res = self.search("*.*", numResults=chunk)
while len(res["facets"]):
models.extend(res["models"])
offset += chunk
res = self.search("*.*", offset=offset, numResults=chunk)
return models
[docs] def get_model(self, model_id, frmt="json"):
"""Fetch information about a given model at a particular revision."""
self._check_format(frmt)
res = self.http_get(model_id, frmt=frmt, params={"format": frmt})
return res
[docs] def get_model_files(self, model_id, frmt="json"):
"""Extract metadata information of model files of a particular model
:param model_id: a valid BioModels identifier
:param frmt: format of the output (json, xml)
"""
self._check_format(frmt, ["xml", "json"])
res = self.http_get("model/files/{}".format(model_id), frmt=frmt, params={"format": frmt})
return res
[docs] def get_model_download(self, model_id, filename=None, output_filename=None):
"""Download a particular file associated with a given model or all its
files as a COMBINE archive.
:param model_id: a valid BioModels identifier
:param str filename: this is the requested filename to be found in the
model
:param str output_filename: if you request a different output filename,
use this parameter
:param frmt: format of the output (json, xml, html)
:return: nothing. This function save the model into a ZIP file called
after the model identifier. If parameter *filename* is specified,
then the output file is the requested filename (if found)
::
bm.get_model_download("BIOMD0000000100", filename="BIOMD0000000100.png")
bm.get_model_download("BIOMD0000000100")
This function can retrieve all files in a ZIP archive or a single image.
In the example below, we retrieve the PNG and plot it using matplotlib.
Using your favorite image viewver, you should get a better resolution.
Or just download the SVG version of the model.
.. plot::
:include-source:
from bioservices import BioModels
bm = BioModels()
from easydev import TempFile
with TempFile(suffix=".png") as fout:
bm.get_model_download("BIOMD0000000100",
filename="BIOMD0000000100.png",
output_filename=fout.name)
from pylab import imshow, imread
imshow(imread(fout.name), aspect="auto")
"""
params = {}
if filename:
params["filename"] = filename
res = self.http_get("model/download/{}".format(model_id), params=params)
if filename:
self.logging.info("Saving {}".format(filename))
if output_filename is None:
output_filename = filename
with open(output_filename, "wb") as fout:
fout.write(res.content)
else:
self.logging.info("Saving file {}.zip".format(model_id))
if output_filename is None:
output_filename = "{}.zip".format(model_id)
with open(output_filename, "wb") as fout:
fout.write(res.content)
[docs] def search(self, query, offset=None, numResults=None, sort=None, frmt="json"):
"""Search models of interest via keywords.
Examples: PUBMED:"27869123" to search models associated with the PubMed
record identified by 27869123.
:param str query: search query. colon character must be escaped
:param int offset: number of items to skip before starting to collect the
result set
:param int numResults: number of items to return
:param str sort: sort criteria in {id-asc, relevance-asc, relevance-desc,
first_author-asc, first_author, name-asc, name-desc,
publication_year-asc, publication_year-desc}
:param str frmt: format of the output (json, xml)
"""
self._check_format(frmt, ["xml", "json", "html"])
params = {"query": query, "format": frmt}
if offset:
params["offset"] = offset
if numResults:
params["numResults"] = numResults
if sort:
params["sort"] = sort
sort_options = [
"id-asc",
"relevance-asc",
"relevance-desc",
"first_author-asc",
"first_author",
"name-asc",
"name-desc",
"publication_year-asc",
"publication_year-desc",
]
if sort and sort not in sort_options:
raise ValueError("sort must be in {}. You provided {}".format(sort_options, sort))
res = self.http_get("search", params=params)
return res
[docs] def search_download(self, models, output_filename="models.zip", force=False):
"""Returns models (XML) corresponding to a list of model identifiers.
:param str models: list of model identifiers using comma to separate
them. Could be a list of string (e.g 'BIOMD1,BIOMD2' or ['BIOMD1',
'BIOMD2']
:param str output_filename: file used to save the models. This is a
zipped file. If the file exists, you must use the *force** parameter
.. todo:: if no models are found (typos), an error message is printed.
if one model is not found, there is no warning or errors. Could be
nice to have a warning by introspecting the number of models in the
output file
"""
if isinstance(models, list):
models = ",".join(models)
res = self.http_get("search/download", params={"models": models})
if res == 404:
self.logging.error("One of your model ID was probably incorrect")
return
self.logging.info(output_filename)
if os.path.exists(output_filename) and force is False:
raise IOError(
"{} exists already. Set force to True or change the output_filename argument".format(output_filename)
)
with open(output_filename, "wb") as fout:
fout.write(res.content)
[docs] def search_parameter(self, query, start=0, size=10, sort=None, frmt="json"):
"""Search for parameters of a model
**Details** BioModels Parameters is a resource that facilitates easy
search and retrieval of parameter values used in the SBML models stored in the
BioModels repository. Users can search for a model entity (e.g. a protein or
drug) to retrieve the rate equations describing it; the associated parameter
values and the initial concentration from the SBML models in BioModels. Although
these data are directly extracted from the curated SBML models, they are not
individually curated or validated; rather presented as such in the table below.
Hence BioModels Parameters table will only provide a quick overview of available
parameter values for guidance and original model should be referred to
understand the complete context of the parameter usage.
:param str query: A query to search against the model parameter values.
:param int start: if is the offset of the result set (default 0)
:param int size: number of items to display per page
:param str sort: model or entity
:param str frmt: the format of the result (xml, csv, json)
::
bm.search_parameter("MAPK", size=100, sort="entity")
"""
self._check_format(frmt, ["xml", "json", "csv"])
params = {"format": frmt, "size": size, "start": start, "query": query}
sort_options = ["model", "entity", None]
if sort not in sort_options:
raise ValueError("sort must be in {}. You provided {}".format(sort_options, sort))
if sort:
params["sort"] = sort
res = self.http_get("parameterSearch/search", params=params)
return res
[docs] def get_p2m_missing(self, frmt="json"):
"""Retrieve all models in Path2Models that are now only available indirectly,
through the representative model for the corresponding genus
:param str frmt: the format of the result (xml, csv, json)
:return: list of model identifiers
"""
self._check_format(frmt)
res = self.http_get("p2m/missing", params={"format": frmt})
res = res["missing"]
self.logging.info("Found {} missing model".format(len(res)))
return res
[docs] def get_p2m_representative(self, model, frmt="json"):
"""Retrieve a representative model in Path2Models
Get the representative model identifier for a given missing model in Path2Models.
This endpoint accepts as parameters a mandatory model identifier and an
optional response format
:param str model: The identifier of a model of interest
:param str frmt: the format of the result (xml, csv, json)
"""
self._check_format(frmt)
res = self.http_get("p2m/representative", params={"format": frmt, "model": model})
return res
[docs] def get_p2m_representatives(self, models, frmt="json"):
"""Find the replacement accessions for a set of Path2Models entries
Get the representative model identifiers of a set of given missing
models in Path2Models. This end point expects a comma-separated list of model
identifiers (without any surrounding whitespace) and an optional response
format. Examples: BMID000000112902, BMID000000009880, BMID000000027397.
:param str model: The model identifiers separated by commas, or as a
list.
:param str frmt: the format of the result (xml, csv, json)
.. doctest::
from bioservices import BioModels
bm = BioModels()
bm.get_p2m_representatives("BMID000000112902, BMID000000009880, BMID000000027397")
"""
if isinstance(models, list):
models = ",".join(models)
else:
# Get rid of possible spaces
models = ",".join([x.strip() for x in models.split(",")])
self._check_format(frmt)
res = self.http_get("p2m/representatives", params={"format": frmt, "modelIds": models})
return res
[docs] def get_pdgsmm_missing(self, frmt="json"):
"""Retrieve the identifiers of all PDGSMM entries that are no longer directly accessible
:param str frmt: the format of the result (xml, csv, json)
:return: list of model identifiers
"""
self._check_format(frmt)
res = self.http_get("pdgsmm/missing", params={"format": frmt})
res = res["missing"]
self.logging.info("Found {} missing model".format(len(res)))
return res
[docs] def get_pdgsmm_representative(self, model, frmt="json"):
"""Retrieve a representative model in PDGSMM
Get the representative model identifier for a given missing model in
PDGSMM. This endpoint accepts as parameters a mandatory model identifier and an
optional response format.
:param str model: The identifier of a model of interest
:param str frmt: the format of the result (xml, csv, json)
"""
self._check_format(frmt)
res = self.http_get("pdgsmm/representative", params={"format": frmt, "model": model})
return res
[docs] def get_pdgsmm_representatives(self, models, frmt="json"):
"""Find the replacement accessions for a set of PDFSSM
Get the representative model identifiers of a set of given missing
models in PDGSMM. This end point expects a comma-separated list of model
identifiers (without any surrounding whitespace) and an optional response
format. Examples: MODEL1707110145,MODEL1707112456,MODEL1707115900.
:param str model: The model identifiers separated by commas, or as a
list.
:param str frmt: the format of the result (xml, csv, json)
"""
if isinstance(models, list):
models = ",".join(models)
else:
# Get rid of possible spaces
models = ",".join([x.strip() for x in models.split(",")])
self._check_format(frmt)
res = self.http_get("pdgsmm/representatives", params={"format": frmt, "modelIds": models})
return res