# This file is part of the GEMtractor
# Copyright (C) 2019 Martin Scharm <https://binfalse.de>
#
# The GEMtractor is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# The GEMtractor is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
import logging
import os
import tempfile
import urllib
from django.conf import settings
from django.http import (HttpResponseBadRequest, HttpResponseServerError,
JsonResponse)
from django.shortcuts import redirect, reverse
from django.views.decorators.csrf import csrf_exempt
from libsbml import SBMLWriter
from gemtract.forms import ExportForm
from modules.gemtractor.constants import Constants
from modules.gemtractor.gemtractor import GEMtractor
from modules.gemtractor.utils import Utils
from modules.gemtractor.exceptions import (InvalidBiggId, InvalidBiomodelsId,
InvalidGeneComplexExpression,
InvalidGeneExpression, TooBigForBrowser,
UnableToRetrieveBiomodel)
logging.config.dictConfig(settings.LOGGING)
__logger = logging.getLogger(__name__)
[docs]def get_session_data (request):
"""
get your session data at /api/get_session_data
the returned JSON object will have the following keys:
- status: 'success' if it was successful
- data: dict of the data:
- session: dict on session keys and values
- files: array of string about uploaded files
example:
.. code-block:: json
{
"status": "success",
"data": {
"session": {
"has_session": "yes",
"model_id": "e_coli_core",
"model_name": "e_coli_core",
"model_type": "bigg",
"filter_species": [],
"filter_reactions": [],
"filter_enzymes": [],
"filter_enzyme_complexes": []
},
"files": []
}
}
:param request: the request
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
:return: json object with information about the user's session
:rtype: `django:JsonResponse <https://docs.djangoproject.com/en/2.2/ref/request-response/#jsonresponse-objects>`_
"""
if request.session.session_key is None:
return JsonResponse ({
"status":"success",
"data": {}
})
files = []
if Constants.SESSION_MODEL_TYPE in request.session and request.session[Constants.SESSION_MODEL_TYPE] == Constants.SESSION_MODEL_TYPE_UPLOAD:
files.append (request.session[Constants.SESSION_MODEL_ID] + " (" + Utils.human_readable_bytes (os.path.getsize(Utils.get_model_path (request.session[Constants.SESSION_MODEL_TYPE], request.session[Constants.SESSION_MODEL_ID], request.session.session_key))) + ")")
s = {}
for key, value in request.session.items ():
s[key] = value
return JsonResponse ({
"status":"success",
"data": {
"session": s,
"files": files
}
})
[docs]def clear_data (request):
"""
clear your data at /api/clear_data
will return a JSON object with one key 'status' and the value 'success' if successful
:param request: the request
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
:return: json object with information about the success
:rtype: `django:JsonResponse <https://docs.djangoproject.com/en/2.2/ref/request-response/#jsonresponse-objects>`_
"""
if Constants.SESSION_MODEL_TYPE in request.session and request.session[Constants.SESSION_MODEL_TYPE] == Constants.SESSION_MODEL_TYPE_UPLOAD:
os.remove (Utils.get_model_path (request.session[Constants.SESSION_MODEL_TYPE], request.session[Constants.SESSION_MODEL_ID], request.session.session_key))
Utils.del_session_key (request, None, Constants.SESSION_HAS_SESSION)
Utils.del_session_key (request, None, Constants.SESSION_MODEL_ID)
Utils.del_session_key (request, None, Constants.SESSION_MODEL_NAME)
Utils.del_session_key (request, None, Constants.SESSION_MODEL_TYPE)
Utils.del_session_key (request, None, Constants.SESSION_FILTER_SPECIES)
Utils.del_session_key (request, None, Constants.SESSION_FILTER_REACTION)
Utils.del_session_key (request, None, Constants.SESSION_FILTER_ENZYMES)
Utils.del_session_key (request, None, Constants.SESSION_FILTER_ENZYME_COMPLEXES)
return JsonResponse ({
"status":"success"
})
[docs]def get_network (request):
"""
get the network at /api/get_network
as encoded in the currently selected model
the returned JSON object will look like this (check for 'status' = 'success'):
.. code-block:: json
{
"status":"success",
"network":{
"species":[
{
"id":"EmptySet",
"name":"",
"occ":[ 27, 10 ]
},
{"...": "..."}
],
"reactions":[
{
"id":"Reaction1",
"name":"",
"rev":false,
"cons":[ 30, 10 ],
"prod":[ 17 ],
"enzs":[ 13 ],
"enzc":[ 1 ]
},
{"...": "..."}
],
"enzs":[
{
"id":"b0351",
"reactions":[ 22 ],
"cplx":[ 42 ]
},
{"...": "..."}
],
"enzc":[
{
"id":"b0116 + b0738",
"enzs":[ 12, 28 ],
"reactions":[ 23 ]
},
{"...": "..."}
]
},
"filter":{
"filter_species":[ "..." ],
"filter_reactions":[ "..." ],
"filter_enzymes":[ "..." ],
"filter_enzyme_complexes":[ "..." ]
}
}
special notes:
- network.species[x].occ: in which reactions does the species appear? -> link into the network.reactions array
- network.reactions[x].cons: which species are consumed? -> link into the network.species array
- network.reactions[x].prod: which species are produced? -> link into the snetwork.pecies array
- network.reactions[x].enzs: which enzymes catalyze the reaction? -> link into the network.enzs array
- network.reactions[x].enzc: which enzyme complexes catalyze the reaction? -> link into the network.enzc array
- network.enzs[x].reactions: which reactions are catalyzed by the enzyme? -> link into the network.reactions array
- network.enzs[x].cplx: in which enzyme complexes does the enzyme participate? -> link into the network.enzc array
- network.enzc[x].enzs: which enzymes participate in this complex? -> link into the network.enzs array
- network.enzc[x].reactions: which reactions are catalyzed by this complex? -> link into the network.reactions array
- network.filter.filter_species: list of species identifiers (str)
- network.filter.filter_reactions: list of reaction identifiers (str)
- network.filter.filter_enzymes: list of gene identifiers (str)
- network.filter.filter_enzyme_complexes: list of gene complex identifiers (str)
if the request was not successful, the 'status' key will have a value other than 'success'
and there will be an 'error' key with some information about what went wrong
:param request: the request
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
:return: json object with information about the network
:rtype: `django:JsonResponse <https://docs.djangoproject.com/en/2.2/ref/request-response/#jsonresponse-objects>`_
"""
if request.method == 'POST':
# TODO
return redirect('index:index')
if Constants.SESSION_MODEL_ID in request.session:
try:
__logger.info ("getting sbml")
gemtractor = GEMtractor (Utils.get_model_path (request.session[Constants.SESSION_MODEL_TYPE], request.session[Constants.SESSION_MODEL_ID], request.session.session_key))
# gemtractor.get_sbml ()
network = gemtractor.extract_network_from_sbml ()
if len (network.species) + len (network.reactions) > settings.MAX_ENTITIES_FILTER:
raise TooBigForBrowser ("This model is probably too big for your browser... It contains "+str (len (network.species))+" species and "+str (len (network.reactions))+" reactions. We won't load it for filtering, as you're browser is very likely to die when trying to process that amount of data.. Max is currently set to "+str (settings.MAX_ENTITIES_FILTER)+" entities in total. Please export it w/o filtering or use the API instead.")
__logger.info ("got sbml")
# ~ network.calc_genenet ()
# ~ __logger.info ("got genenet")
filter_species = []
filter_reaction = []
filter_enzymes = []
filter_enzyme_complexes = []
if Constants.SESSION_FILTER_SPECIES in request.session:
filter_species = request.session[Constants.SESSION_FILTER_SPECIES]
if Constants.SESSION_FILTER_REACTION in request.session:
filter_reaction = request.session[Constants.SESSION_FILTER_REACTION]
if Constants.SESSION_FILTER_ENZYMES in request.session:
filter_enzymes = request.session[Constants.SESSION_FILTER_ENZYMES]
if Constants.SESSION_FILTER_ENZYME_COMPLEXES in request.session:
filter_enzyme_complexes = request.session[Constants.SESSION_FILTER_ENZYME_COMPLEXES]
__logger.info ("sending response")
if len (network.species) + len (network.reactions) + len (network.genes) + len (network.gene_complexes) > settings.MAX_ENTITIES_FILTER:
raise TooBigForBrowser ("This model is probably too big for your browser... It contains "+str (len (network.species))+" species, "+str (len (network.reactions))+" reactions, "+str (len (network.genes))+" genes, and "+str (len (network.gene_complexes))+" gene complexes. We won't load it for filtering, as you're browser is very likely to die when trying to process that amount of data.. Max is currently set to "+str (settings.MAX_ENTITIES_FILTER)+" entities in total. Please export it w/o filtering or use the API instead.")
net = network.serialize()
__logger.info ("serialised the network")
return JsonResponse ({
"status":"success",
"network":net,
"filter": {
Constants.SESSION_FILTER_SPECIES: filter_species,
Constants.SESSION_FILTER_REACTION: filter_reaction,
Constants.SESSION_FILTER_ENZYMES: filter_enzymes,
Constants.SESSION_FILTER_ENZYME_COMPLEXES: filter_enzyme_complexes,
}
})
except TooBigForBrowser as e:
__logger.error ("error retrieving network: " + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":getattr(e, 'message', repr(e))})
except IOError as e:
__logger.error ("error retrieving network: " + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":getattr(e, 'message', repr(e))})
except InvalidGeneExpression as e:
__logger.error("InvalidGeneExpression in model " + request.session[Constants.SESSION_MODEL_ID] + ": " + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error": "The model uses an invalid gene expression: " + getattr(e, 'message', repr(e))})
#return JsonResponse ({"nope": "nope"})
# invalid api request
return redirect('index:index')
[docs]def prepare_filter (request):
"""
prepare the session filter setup
produced empty arrays for
- request.session[Constants.SESSION_FILTER_SPECIES]
- request.session[Constants.SESSION_FILTER_REACTION]
- request.session[Constants.SESSION_FILTER_ENZYMES]
- request.session[Constants.SESSION_FILTER_ENZYME_COMPLEXES]
:param request: the request
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
"""
if not Constants.SESSION_FILTER_SPECIES in request.session:
request.session[Constants.SESSION_FILTER_SPECIES] = []
if not Constants.SESSION_FILTER_REACTION in request.session:
request.session[Constants.SESSION_FILTER_REACTION] = []
if not Constants.SESSION_FILTER_ENZYMES in request.session:
request.session[Constants.SESSION_FILTER_ENZYMES] = []
if not Constants.SESSION_FILTER_ENZYME_COMPLEXES in request.session:
request.session[Constants.SESSION_FILTER_ENZYME_COMPLEXES] = []
[docs]def sort_gene_complexes (complexes):
"""
sorts the genes in the gene complex identifiers
splits every identifier it at " + ", sorts the parts, and joins them with " + "
:Example:
- input: ["z + k + y", "a + b"]
- output: ["k + y + z", "a + b"]
:param complexes: the complex identifiers
:type complexes: list of str
:return: the sorted identifiers
:rtype: list of str
"""
c2 = []
for c in complexes:
if " + " not in c:
raise InvalidGeneComplexExpression ("do not understand the following gene complex: " + c)
c2.append (" + ".join (sorted (c.split (" + "))))
return c2
[docs]def store_filter (request):
"""
store the user's filters in the session at /api/store_filter
returns the stored filters in a json object just like this
.. code-block:: json
{
"status":"success",
"filter":{
"filter_species":[
"M_13dpg_c",
"M_2pg_c"
],
"filter_reactions":[
"reaction1"
],
"filter_enzymes":[
],
"filter_enzyme_complexes":[
]
}
}
check for "status" = "success"
if the request was not successful, the 'status' key will have a value other than 'success'
and there will be an 'error' key with some information about what went wrong
:param request: the request
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
:return: json object with information about the filters
:rtype: `django:JsonResponse <https://docs.djangoproject.com/en/2.2/ref/request-response/#jsonresponse-objects>`_
"""
if request.method != 'POST':
# TODO
return redirect('index:index')
prepare_filter (request)
succ, data = parse_json_body (request)
if not succ:
return JsonResponse ({"status":"failed","error":data})
if "species" in data and isinstance(data["species"], list):
request.session[Constants.SESSION_FILTER_SPECIES] = data["species"]
if "reaction" in data and isinstance(data["reaction"], list):
request.session[Constants.SESSION_FILTER_REACTION] = data["reaction"]
if "enzymes" in data and isinstance(data["enzymes"], list):
request.session[Constants.SESSION_FILTER_ENZYMES] = data["enzymes"]
if "enzyme_complexes" in data and isinstance(data["enzyme_complexes"], list):
try:
request.session[Constants.SESSION_FILTER_ENZYME_COMPLEXES] = sort_gene_complexes (data["enzyme_complexes"])
except InvalidGeneComplexExpression as e:
return JsonResponse ({"status":"failed","error":str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e))})
return JsonResponse ({"status":"success",
"filter": {
Constants.SESSION_FILTER_SPECIES: request.session[Constants.SESSION_FILTER_SPECIES],
Constants.SESSION_FILTER_REACTION: request.session[Constants.SESSION_FILTER_REACTION],
Constants.SESSION_FILTER_ENZYMES: request.session[Constants.SESSION_FILTER_ENZYMES],
Constants.SESSION_FILTER_ENZYME_COMPLEXES: request.session[Constants.SESSION_FILTER_ENZYME_COMPLEXES],
}})
[docs]def get_bigg_models (request):
"""
get the list of models from BiGG at /api/get_bigg_models
returns the models in a json object just like this
.. code-block:: json
{
"status":"success",
"results_count":108,
"results":[
{
"reaction_count":95,
"metabolite_count":72,
"organism":"Escherichia coli str. K-12 substr. MG1655",
"bigg_id":"e_coli_core",
"gene_count":137
},
{"...": "..."}
]
}
check for "status" = "success"
if the request was not successful, the 'status' key will have a value other than 'success'
and there will be an 'error' key with some information about what went wrong
:param request: the request
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
:return: json object with information about the models
:rtype: `django:JsonResponse <https://docs.djangoproject.com/en/2.2/ref/request-response/#jsonresponse-objects>`_
"""
try:
models = Utils.get_bigg_models ()
models["status"] = "success"
return JsonResponse (models)
except json.decoder.JSONDecodeError as e:
__logger.error ("error getting bigg models: " + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":"is BiGG models down? "+str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e))})
except urllib.error.HTTPError as e:
__logger.error ("error getting bigg models: " + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e))})
except urllib.error.URLError as e:
if hasattr(e, 'reason'):
__logger.error ("error getting bigg models: " + str (getattr(e, 'reason', repr(e))) + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":str (getattr(e, 'reason', repr(e))) + getattr(e, 'message', repr(e))})
elif hasattr(e, 'code'):
__logger.error ("error getting bigg models: " + str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e))})
[docs]def select_bigg_model (request):
"""
select a certain model from BiGG at /api/select_bigg_model
expects JSON with the key "bigg_id" posted to this endpoint, just like:
.. code-block:: json
{
"bigg_id":"e_coli_core"
}
returns JSON {"status": "success"} if successful
if the request was not successful, the 'status' key will have a value other than 'success'
and there will be an 'error' key with some information about what went wrong
:param request: the request
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
:return: json object with information about the success
:rtype: `django:JsonResponse <https://docs.djangoproject.com/en/2.2/ref/request-response/#jsonresponse-objects>`_
"""
if request.method != 'POST':
# TODO
return redirect('index:index')
succ, data = parse_json_body (request, ["bigg_id"])
if not succ:
return JsonResponse ({"status":"failed","error":data})
try:
Utils.get_bigg_model (data["bigg_id"])
request.session[Constants.SESSION_MODEL_ID] = data["bigg_id"]
request.session[Constants.SESSION_MODEL_NAME] = data["bigg_id"]
request.session[Constants.SESSION_MODEL_TYPE] = Constants.SESSION_MODEL_TYPE_BIGG
Utils.del_session_key (request, {}, Constants.SESSION_FILTER_SPECIES)
Utils.del_session_key (request, {}, Constants.SESSION_FILTER_REACTION)
Utils.del_session_key (request, {}, Constants.SESSION_FILTER_ENZYMES)
Utils.del_session_key (request, {}, Constants.SESSION_FILTER_ENZYME_COMPLEXES)
return JsonResponse ({"status":"success"})
except InvalidBiggId as e:
__logger.error ("error getting bigg model: " + data["bigg_id"] + " -- " + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":getattr(e, 'message', repr(e))})
except urllib.error.HTTPError as e:
__logger.error ("error getting bigg model: " + data["bigg_id"] + " -- " + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e))})
except urllib.error.URLError as e:
if hasattr(e, 'reason'):
__logger.error ("error getting bigg model: " + data["bigg_id"] + " -- " + str (getattr(e, 'reason', repr(e))) + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":str (getattr(e, 'reason', repr(e))) + getattr(e, 'message', repr(e))})
elif hasattr(e, 'code'):
__logger.error ("error getting bigg model: " + data["bigg_id"] + " -- " + str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e))})
[docs]def get_biomodels (request):
"""
get the list of models from biomodels at /api/get_biomodels
returns the models in a json object just like this
.. code-block:: json
{
"status":"success",
"matches":6979,
"models":[
{
"format":"SBML",
"id":"BIOMD0000000239",
"lastModified":"2016-04-07T23:00:00Z",
"name":"Jiang2007 - GSIS system, Pancreatic Beta Cells",
"submissionDate":"2016-04-07T23:00:00Z",
"submitter":"Kieran Smallbone",
"url":"https://www.ebi.ac.uk/biomodels/BIOMD0000000239"
},
{"...": "..."}
]
}
check for "status" = "success"
if the request was not successful, the 'status' key will have a value other than 'success'
and there will be an 'error' key with some information about what went wrong
:param request: the request
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
:return: json object with information about the models
:rtype: `django:JsonResponse <https://docs.djangoproject.com/en/2.2/ref/request-response/#jsonresponse-objects>`_
"""
try:
models = Utils.get_biomodels ()
models["status"] = "success"
return JsonResponse (models)
except json.decoder.JSONDecodeError as e:
__logger.error ("error getting biomodels: " + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":"is biomodels down? "+str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e))})
except urllib.error.HTTPError as e:
__logger.error ("error getting biomodels models: " + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e))})
except urllib.error.URLError as e:
if hasattr(e, 'reason'):
__logger.error ("error getting biomodels models: " + str (getattr(e, 'reason', repr(e))) + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":str (getattr(e, 'reason', repr(e))) + getattr(e, 'message', repr(e))})
elif hasattr(e, 'code'):
__logger.error ("error getting biomodels models: " + str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e))})
[docs]def select_biomodel (request):
"""
select a certain model from biomodels at /api/select_biomodel
expects JSON with the key "biomodels_id" posted to this endpoint, just like:
.. code-block:: json
{
"biomodels_id":"BIOMD0000000496"
}
returns JSON {"status": "success"} if successful
if the request was not successful, the 'status' key will have a value other than 'success'
and there will be an 'error' key with some information about what went wrong
:param request: the request
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
:return: json object with information about the success
:rtype: `django:JsonResponse <https://docs.djangoproject.com/en/2.2/ref/request-response/#jsonresponse-objects>`_
"""
if request.method != 'POST':
# TODO
return redirect('index:index')
succ, data = parse_json_body (request, ["biomodels_id"])
if not succ:
return JsonResponse ({"status":"failed","error":data})
try:
Utils.get_biomodel (data["biomodels_id"])
request.session[Constants.SESSION_MODEL_ID] = data["biomodels_id"]
request.session[Constants.SESSION_MODEL_NAME] = data["biomodels_id"]
request.session[Constants.SESSION_MODEL_TYPE] = Constants.SESSION_MODEL_TYPE_BIOMODELS
Utils.del_session_key (request, {}, Constants.SESSION_FILTER_SPECIES)
Utils.del_session_key (request, {}, Constants.SESSION_FILTER_REACTION)
Utils.del_session_key (request, {}, Constants.SESSION_FILTER_ENZYMES)
Utils.del_session_key (request, {}, Constants.SESSION_FILTER_ENZYME_COMPLEXES)
return JsonResponse ({"status":"success"})
except UnableToRetrieveBiomodel as e:
__logger.error ("error getting biomodels model: " + data["biomodels_id"] + " -- "+ getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":getattr(e, 'message', repr(e))})
except InvalidBiomodelsId as e:
__logger.error ("error getting biomodels model: " + data["biomodels_id"] + " -- " + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":getattr(e, 'message', repr(e))})
except urllib.error.HTTPError as e:
__logger.error ("error getting biomodels model: " + data["biomodels_id"] + " -- " + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error": "Does such a model exist? Can't download from Biomodels: " + str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e))})
except urllib.error.URLError as e:
if hasattr(e, 'reason'):
__logger.error ("error getting biomodels model: " + data["biomodels_id"] + " -- " + str (getattr(e, 'reason', repr(e))) + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":str (getattr(e, 'reason', repr(e))) + getattr(e, 'message', repr(e))})
elif hasattr(e, 'code'):
__logger.error ("error getting biomodels model: " + data["biomodels_id"] + " -- " + str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e)))
return JsonResponse ({"status":"failed","error":str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e))})
[docs]def parse_json_body (request, expected_keys = []):
"""
parse some json payload
loads the json payload from the request and checks if all keys in expected_keys are present
:param request: the request
:param expected_keys: array of keys to expect
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
:type expected_keys: array of str
:return: [true, parsed data] if all expected keys were found, otherwise [false, error message]
:rtype: [bool, dict] or [bool, message]
"""
try:
data=json.loads(request.body)
for k in expected_keys:
if k not in data:
return False, "request is missing key: " + k
return True, data
except json.decoder.JSONDecodeError:
return False, "request is not proper json"
[docs]def serve_file (request, file_name, file_type):
"""
server a file at /api/serve/file_name/file_type
serves the file user-generated file
:param request: the request
:param file_name: the name of the file, will be used in HTTP's Content-Disposition
:param file_type: the mime type of the file, will be used to indicate the mime-type in the HTTP response
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
:type file_name: str
:type file_type: str
:return: HTTP 200 and the file or 404 if no such file
:rtype: `django:HttpResponse <https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects>`_
"""
if request.session.session_key is None:
return HttpResponseBadRequest("bad session")
file_path = Utils.create_generated_file_web (request.session.session_key)
if not os.path.exists(file_path):
return HttpResponseBadRequest("file does not exist")
return Utils.serve_file (file_path, file_name, file_type)
[docs]def export (request):
"""
export the gemtracted file at /api/export
generates the export and returns some JSON data to call /api/serve ... (-> :func:`serve_file`)
expects a job submitted as HTTP POST form data (see :class:`.gemtract.forms.ExportForm`), just like:
.. code-block:: python
network_type: en
network_format: sbml
removing_enzyme_removes_complex: on
returns as JSON object such as:
.. code-block:: python
{
"status": "success",
"name": "BIOMD0000000006-gemtracted-EnzymeNetwork.sbml",
"mime": "application/xml"
}
check for "status" = "success"
if the request was not successful, the 'status' key will have a value other than 'success'
and there will be an 'error' key with some information about what went wrong
:param request: the request
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
:return: json object with information about the exported file
:rtype: `django:JsonResponse <https://docs.djangoproject.com/en/2.2/ref/request-response/#jsonresponse-objects>`_
"""
if request.session.session_key is None or Constants.SESSION_MODEL_NAME not in request.session:
return HttpResponseBadRequest("no such session")
prepare_filter (request)
form = ExportForm(request.POST)
if (form.is_valid()):
file_name = request.session[Constants.SESSION_MODEL_NAME] + "-gemtracted"
try:
gemtractor = GEMtractor (Utils.get_model_path (request.session[Constants.SESSION_MODEL_TYPE], request.session[Constants.SESSION_MODEL_ID], request.session.session_key))
except Exception as e:
return JsonResponse ({"status":"failed","error":"the model has an issue: " + getattr(e, 'message', repr(e))})
sbml = gemtractor.get_sbml (
filter_species = request.session[Constants.SESSION_FILTER_SPECIES],
filter_reactions = request.session[Constants.SESSION_FILTER_REACTION],
filter_genes = request.session[Constants.SESSION_FILTER_ENZYMES],
filter_gene_complexes = request.session[Constants.SESSION_FILTER_ENZYME_COMPLEXES],
remove_reaction_enzymes_removed = form.cleaned_data['remove_reaction_enzymes_removed'],
remove_ghost_species = form.cleaned_data['remove_ghost_species'],
discard_fake_enzymes = form.cleaned_data['discard_fake_enzymes'],
remove_reaction_missing_species = form.cleaned_data['remove_reaction_missing_species'],
removing_enzyme_removes_complex = form.cleaned_data['removing_enzyme_removes_complex'])
if form.cleaned_data['network_type'] == 'en':
file_name = file_name + "-EnzymeNetwork"
net = gemtractor.extract_network_from_sbml ()
net.calc_genenet ()
if form.cleaned_data['network_format'] == 'sbml':
file_name = file_name + ".sbml"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_en_sbml (file_path, gemtractor, request.session[Constants.SESSION_MODEL_ID], request.session[Constants.SESSION_MODEL_NAME],
filter_species = request.session[Constants.SESSION_FILTER_SPECIES],
filter_reactions = request.session[Constants.SESSION_FILTER_REACTION],
filter_genes = request.session[Constants.SESSION_FILTER_ENZYMES],
filter_gene_complexes = request.session[Constants.SESSION_FILTER_ENZYME_COMPLEXES],
remove_reaction_enzymes_removed = form.cleaned_data['remove_reaction_enzymes_removed'],
remove_ghost_species = form.cleaned_data['remove_ghost_species'],
discard_fake_enzymes = form.cleaned_data['discard_fake_enzymes'],
remove_reaction_missing_species = form.cleaned_data['remove_reaction_missing_species'],
removing_enzyme_removes_complex = form.cleaned_data['removing_enzyme_removes_complex'])
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "application/xml"})
else:
return JsonResponse ({"status":"failed","error":"error generating SBML file"})
else:
if form.cleaned_data['network_format'] == 'dot':
file_name = file_name + ".dot"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_en_dot (file_path)
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "application/dot"})
else:
return JsonResponse ({"status":"failed","error":"error generating file"})
elif form.cleaned_data['network_format'] == 'graphml':
file_name = file_name + ".graphml"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_en_graphml (file_path)
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "application/xml"})
else:
return JsonResponse ({"status":"failed","error":"error generating file"})
elif form.cleaned_data['network_format'] == 'gml':
file_name = file_name + ".gml"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_en_gml (file_path)
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "application/gml"})
else:
return JsonResponse ({"status":"failed","error":"error generating file"})
elif form.cleaned_data['network_format'] == 'csv':
file_name = file_name + ".csv"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_en_csv (file_path)
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "text/csv"})
else:
return JsonResponse ({"status":"failed","error":"error generating file"})
else:
return JsonResponse ({"status":"failed","error":"invalid format"})
elif form.cleaned_data['network_type'] == 'rn':
file_name = file_name + "-ReactionNetwork"
net = gemtractor.extract_network_from_sbml ()
net.calc_reaction_net ()
if form.cleaned_data['network_format'] == 'sbml':
file_name = file_name + ".sbml"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_rn_sbml (file_path, gemtractor, request.session[Constants.SESSION_MODEL_ID], request.session[Constants.SESSION_MODEL_NAME],
filter_species = request.session[Constants.SESSION_FILTER_SPECIES],
filter_reactions = request.session[Constants.SESSION_FILTER_REACTION],
filter_genes = request.session[Constants.SESSION_FILTER_ENZYMES],
filter_gene_complexes = request.session[Constants.SESSION_FILTER_ENZYME_COMPLEXES],
remove_reaction_enzymes_removed = form.cleaned_data['remove_reaction_enzymes_removed'],
remove_ghost_species = form.cleaned_data['remove_ghost_species'],
discard_fake_enzymes = form.cleaned_data['discard_fake_enzymes'],
remove_reaction_missing_species = form.cleaned_data['remove_reaction_missing_species'],
removing_enzyme_removes_complex = form.cleaned_data['removing_enzyme_removes_complex'])
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "application/xml"})
else:
return JsonResponse ({"status":"failed","error":"error generating SBML file"})
else:
if form.cleaned_data['network_format'] == 'dot':
file_name = file_name + ".dot"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_rn_dot (file_path)
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "application/dot"})
else:
return JsonResponse ({"status":"failed","error":"error generating file"})
elif form.cleaned_data['network_format'] == 'graphml':
file_name = file_name + ".graphml"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_rn_graphml (file_path)
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "application/xml"})
else:
return JsonResponse ({"status":"failed","error":"error generating file"})
elif form.cleaned_data['network_format'] == 'gml':
file_name = file_name + ".gml"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_rn_gml (file_path)
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "application/gml"})
else:
return JsonResponse ({"status":"failed","error":"error generating file"})
elif form.cleaned_data['network_format'] == 'csv':
file_name = file_name + ".csv"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_rn_csv (file_path)
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "text/csv"})
else:
return JsonResponse ({"status":"failed","error":"error generating file"})
else:
return JsonResponse ({"status":"failed","error":"invalid format"})
elif form.cleaned_data['network_type'] == 'mn':
file_name = file_name + "-MetabolicNetwork"
if form.cleaned_data['network_format'] == 'sbml':
file_name = file_name + ".sbml"
file_path = Utils.create_generated_file_web (request.session.session_key)
SBMLWriter().writeSBML (sbml, file_path)
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "application/xml"})
else:
return JsonResponse ({"status":"failed","error":"error generating file"})
else:
net = gemtractor.extract_network_from_sbml ()
if form.cleaned_data['network_format'] == 'dot':
file_name = file_name + ".dot"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_mn_dot (file_path)
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "application/dot"})
else:
return JsonResponse ({"status":"failed","error":"error generating file"})
elif form.cleaned_data['network_format'] == 'graphml':
file_name = file_name + ".graphml"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_mn_graphml (file_path)
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "application/xml"})
else:
return JsonResponse ({"status":"failed","error":"error generating file"})
elif form.cleaned_data['network_format'] == 'gml':
file_name = file_name + ".gml"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_mn_gml (file_path)
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "application/gml"})
else:
return JsonResponse ({"status":"failed","error":"error generating file"})
elif form.cleaned_data['network_format'] == 'csv':
file_name = file_name + ".csv"
file_path = Utils.create_generated_file_web (request.session.session_key)
net.export_mn_csv (file_path)
if os.path.exists(file_path):
return JsonResponse ({"status":"success", "name": file_name, "mime": "text/csv"})
else:
return JsonResponse ({"status":"failed","error":"error generating file"})
else:
return JsonResponse ({"status":"failed","error":"invalid format"})
else:
return JsonResponse ({"status":"failed","error":"invalid network type"})
return JsonResponse ({"status":"failed","error":"submitted data is invalid"})
[docs]@csrf_exempt
def execute (request):
"""
execute a job at /api/execute
processed the job, that is send as JSON HTTP POST data, such as:
.. code-block:: python
{
"export": {
"network_type":"en",
"network_format":"sbml"
},
"filter": {
"species": ["h2o", "atp"],
"reactions": [],
"enzymes": ["gene_abc"],
"enzyme_complexes": ["a + b + c", "x + Y", "b_098 + r_abc"],
},
"file": model
}
returns HTTP 200 and the generated file, or some other HTTP status and an error message
:param request: the request
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
:return: HTTP 200 and the file or 404 if no such file
:rtype: `django:HttpResponse <https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects>`_
"""
if request.method != 'POST':
return redirect(reverse('index:learn') + '#api')
succ, data = parse_json_body (request, ["file", "export"])
if not succ:
return HttpResponseBadRequest(data)
inputFile = tempfile.NamedTemporaryFile()
with open(inputFile.name, 'w') as f:
f.write (data['file'])
filter_species = []
filter_reactions = []
filter_enzymes = []
filter_enzyme_complexes = []
if "filter" in data:
if "species" in data["filter"]:
filter_species = data["filter"]["species"]
if "reactions" in data["filter"]:
filter_reactions = data["filter"]["reactions"]
if "enzymes" in data["filter"]:
filter_enzymes = data["filter"]["enzymes"]
if "enzyme_complexes" in data["filter"]:
try:
filter_enzyme_complexes = sort_gene_complexes (data["filter"]["enzyme_complexes"])
except InvalidGeneComplexExpression as e:
return HttpResponseBadRequest ("error: " + str (getattr(e, 'code', repr(e))) + getattr(e, 'message', repr(e)))
if not isinstance(filter_species, list):
return HttpResponseBadRequest("filter species needs to be an array")
if not isinstance(filter_reactions, list):
return HttpResponseBadRequest("filter for reactions needs to be an array")
if not isinstance(filter_enzymes, list):
return HttpResponseBadRequest("filter for enzymes needs to be an array")
if not isinstance(filter_enzyme_complexes, list):
return HttpResponseBadRequest("filter for enzyme complexes needs to be an array")
export = data["export"]
if "network_type" not in export:
return HttpResponseBadRequest ("job is missing the desired network_type (en|rn|mn)")
if "network_format" not in export:
return HttpResponseBadRequest ("job is missing the desired network_format (sbml|dot|graphml|gml|csv)")
remove_reaction_enzymes_removed = True
remove_reaction_missing_species = False
remove_ghost_species = False
discard_fake_enzymes = False
removing_enzyme_removes_complex = True
if "remove_reaction_enzymes_removed" in export:
remove_reaction_enzymes_removed = export["remove_reaction_enzymes_removed"]
if "remove_ghost_species" in export:
remove_ghost_species = export["remove_ghost_species"]
if "discard_fake_enzymes" in export:
discard_fake_enzymes = export["discard_fake_enzymes"]
if "remove_reaction_missing_species" in export:
remove_reaction_missing_species = export["remove_reaction_missing_species"]
if "removing_enzyme_removes_complex" in export:
removing_enzyme_removes_complex = export["removing_enzyme_removes_complex"]
try:
gemtractor = GEMtractor (inputFile.name)
sbml = gemtractor.get_sbml (
filter_species = filter_species,
filter_reactions = filter_reactions,
filter_genes = filter_enzymes,
filter_gene_complexes = filter_enzyme_complexes,
remove_reaction_enzymes_removed = remove_reaction_enzymes_removed,
remove_ghost_species = remove_ghost_species,
discard_fake_enzymes = discard_fake_enzymes,
remove_reaction_missing_species = remove_reaction_missing_species,
removing_enzyme_removes_complex = removing_enzyme_removes_complex)
except Exception as e:
return HttpResponseBadRequest ("the model has an issue: " + getattr(e, 'message', repr(e)))
outputFile = tempfile.NamedTemporaryFile()
if export["network_type"] == "en":
net = gemtractor.extract_network_from_sbml ()
# net.calc_genenet ()
if export["network_format"] == "sbml":
net.export_en_sbml (outputFile.name, gemtractor, sbml.getModel ().getId (), sbml.getModel ().getName (),
filter_species = filter_species,
filter_reactions = filter_reactions,
filter_genes = filter_enzymes,
filter_gene_complexes = filter_enzyme_complexes,
remove_reaction_enzymes_removed = remove_reaction_enzymes_removed,
remove_ghost_species = remove_ghost_species,
discard_fake_enzymes = discard_fake_enzymes,
remove_reaction_missing_species = remove_reaction_missing_species,
removing_enzyme_removes_complex = removing_enzyme_removes_complex)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.sbml", "application/xml")
else:
return HttpResponseServerError ("couldn't generate the sbml file")
elif export["network_format"] == "dot":
net.export_en_dot (outputFile.name)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.dot", "application/dot")
else:
return HttpResponseServerError ("couldn't generate the dot file")
elif export["network_format"] == "graphml":
net.export_en_graphml (outputFile.name)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.graphml", "application/xml")
else:
return HttpResponseServerError ("couldn't generate the graphml file")
elif export["network_format"] == "gml":
net.export_en_gml (outputFile.name)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.gml", "application/gml")
else:
return HttpResponseServerError ("couldn't generate the gml file")
elif export["network_format"] == "csv":
net.export_en_csv (outputFile.name)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.csv", "text/csv")
else:
return HttpResponseServerError ("couldn't generate the csv file")
elif export["network_type"] == "rn":
net = gemtractor.extract_network_from_sbml ()
# net.calc_reaction_net ()
if export["network_format"] == "sbml":
net.export_rn_sbml (outputFile.name, gemtractor, sbml.getModel ().getId () + "_RN", sbml.getModel ().getName () + " converted to ReactionNetwork",
filter_species = filter_species,
filter_reactions = filter_reactions,
filter_genes = filter_enzymes,
filter_gene_complexes = filter_enzyme_complexes,
remove_reaction_enzymes_removed = remove_reaction_enzymes_removed,
remove_ghost_species = remove_ghost_species,
discard_fake_enzymes = discard_fake_enzymes,
remove_reaction_missing_species = remove_reaction_missing_species,
removing_enzyme_removes_complex = removing_enzyme_removes_complex)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.sbml", "application/xml")
else:
return HttpResponseServerError ("couldn't generate the sbml file")
elif export["network_format"] == "dot":
net.export_rn_dot (outputFile.name)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.dot", "application/dot")
else:
return HttpResponseServerError ("couldn't generate the dot file")
elif export["network_format"] == "graphml":
net.export_rn_graphml (outputFile.name)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.graphml", "application/xml")
else:
return HttpResponseServerError ("couldn't generate the graphml file")
elif export["network_format"] == "gml":
net.export_rn_gml (outputFile.name)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.gml", "application/gml")
else:
return HttpResponseServerError ("couldn't generate the gml file")
elif export["network_format"] == "csv":
net.export_rn_csv (outputFile.name)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.csv", "text/csv")
else:
return HttpResponseServerError ("couldn't generate the csv file")
elif export["network_type"] == "mn":
if export["network_format"] == "sbml":
SBMLWriter().writeSBML (sbml, outputFile.name)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.sbml", "application/xml")
else:
return HttpResponseServerError ("couldn't generate the sbml file")
else:
net = gemtractor.extract_network_from_sbml ()
if export["network_format"] == "dot":
net.export_mn_dot (outputFile.name)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.dot", "application/dot")
else:
return HttpResponseServerError ("couldn't generate the dot file")
elif export["network_format"] == "graphml":
net.export_mn_graphml (outputFile.name)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.graphml", "application/xml")
else:
return HttpResponseServerError ("couldn't generate the graphml file")
elif export["network_format"] == "gml":
net.export_mn_gml (outputFile.name)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.gml", "application/gml")
else:
return HttpResponseServerError ("couldn't generate the gml file")
elif export["network_format"] == "csv":
net.export_mn_csv (outputFile.name)
if os.path.exists(outputFile.name):
return Utils.serve_file (outputFile.name, "gemtracted-model.csv", "text/csv")
else:
return HttpResponseServerError ("couldn't generate the csv file")
return HttpResponseBadRequest ("job is not well formed, not sure what to do...")
[docs]@csrf_exempt
def status (request):
"""
get the status of this instance
this will clean obsolete files
send the health-secret as JSON POST to get health information about this instance:
.. code-block:: json
{"secret": "XXXX"}
this will then return a json object listing how many files and data we store, such as:
.. code-block:: json
{
"status": "success",
"cache": {
"biomodels": {
"nfiles": 8,
"size": 212254013
},
"bigg": {
"nfiles": 5,
"size": 59429817
}
},
"user": {
"uploaded": {
"nfiles": 0,
"size": 0
},
"generated": {
"nfiles": 0,
"size": 0
}
}
}
:param request: the request
:type request: `django:HttpRequest <https://docs.djangoproject.com/en/2.2/_modules/django/http/request/#HttpRequest>`_
:return: json object with information about the exported file
:rtype: `django:JsonResponse <https://docs.djangoproject.com/en/2.2/ref/request-response/#jsonresponse-objects>`_
"""
Utils.cleanup ()
response = {"status": "success"}
if request.method == 'POST':
succ, data = parse_json_body (request, [])
if not succ:
return HttpResponseBadRequest(data)
if settings.HEALTH_SECRET == "" or ("secret" in data and data["secret"] == settings.HEALTH_SECRET):
Utils.collect_stats (response)
# TODO bit more information
return JsonResponse (response)