diff --git a/README.md b/README.md
index 81f5494..6c3b97c 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,11 @@
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![CI-CD](https://github.com/openCONTRABASS/CONTRABASS-webservice/actions/workflows/main.yml/badge.svg)](https://github.com/openCONTRABASS/CONTRABASS-webservice/actions/workflows/main.yml)
+[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=openCONTRABASS_CONTRABASS-webservice&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=openCONTRABASS_CONTRABASS-webservice)
+[![codecov](https://codecov.io/gh/openCONTRABASS/CONTRABASS-webservice/branch/main/graph/badge.svg?token=T1S7E99XJJ)](https://codecov.io/gh/openCONTRABASS/CONTRABASS-webservice)
+[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
+
+```CONTRABASS RESTful API``` is a python-based asynchronous-tasks RESTful API intended for the computation and analysis of vulnerabilities in genome-scale metabolic models. These webservices are just a backed for [CONTRABASS python tool](https://github.com/openCONTRABASS/CONTRABASS).
## Table of Contents
- [License](#license)
diff --git a/src/restapi/app.py b/src/restapi/app.py
index c062dfc..3a4ab71 100644
--- a/src/restapi/app.py
+++ b/src/restapi/app.py
@@ -16,6 +16,7 @@
along with this program. If not, see .
"""
import eventlet
+
eventlet.monkey_patch()
import os
@@ -45,13 +46,13 @@
from .exception_handler import init_exception_handler
basedir = os.path.abspath(os.path.dirname(__file__))
-load_dotenv(os.path.join(basedir, '../../.env'))
+load_dotenv(os.path.join(basedir, "../../.env"))
LOGGER = logging.getLogger(__name__)
app = Flask(__name__)
-app.config.from_object(os.environ['APP_SETTINGS'])
+app.config.from_object(os.environ["APP_SETTINGS"])
# Logger config
logging.config.dictConfig(app.config["DICT_LOGGER"])
@@ -59,7 +60,7 @@
# Database config
# MONGODB
-'''
+"""
from .database import db
app.config['MONGODB_SETTINGS'] = {
@@ -69,25 +70,22 @@
'alias': ''
}
db.init_app(app)
-'''
+"""
# MYSQL
db.init_app(app)
LOGGER.info("Successfully init db connection")
# Swagger config
-app.config['SWAGGER'] = {
- 'title': 'CONTRABASS API',
- 'uiversion': 3
-}
-Swagger(app, template_file='swagger.yml')
+app.config["SWAGGER"] = {"title": "CONTRABASS API", "uiversion": 3}
+Swagger(app, template_file="swagger.yml")
LOGGER.info("Successfully init Swagger")
# Register blueprints
-app.register_blueprint(submit_bp, url_prefix='/')
-app.register_blueprint(models_bp, url_prefix='/')
-app.register_blueprint(results_bp, url_prefix='/')
-app.register_blueprint(websockets_bp, url_prefix='/')
+app.register_blueprint(submit_bp, url_prefix="/")
+app.register_blueprint(models_bp, url_prefix="/")
+app.register_blueprint(results_bp, url_prefix="/")
+app.register_blueprint(websockets_bp, url_prefix="/")
LOGGER.info("Successfully init blueprints")
# Register exception handlers
@@ -98,32 +96,38 @@
CORS(app)
# Init websockets
-socketio = SocketIO(app, \
- cors_allowed_origins='*', \
- logger=True, \
- engineio_logger=True, \
- message_queue=app.config['REDIS_BROKER_URL'])
+socketio = SocketIO(
+ app,
+ cors_allowed_origins="*",
+ logger=True,
+ engineio_logger=True,
+ message_queue=app.config["REDIS_BROKER_URL"],
+)
@app.errorhandler(404)
def page_not_found(e):
- #return render_template('404.html'), 404
- response = jsonify({'message': "Not found"})
+ # return render_template('404.html'), 404
+ response = jsonify({"message": "Not found"})
response.status_code = 404
return response
-@app.route('/hola/')
+
+@app.route("/hola/")
def hello_name(name):
return "Hello {}!".format(name)
-@socketio.on('connect')
+
+@socketio.on("connect")
def test_connect():
LOGGER.info("Client connected via websocket")
-@socketio.on('join')
+
+@socketio.on("join")
def on_join(room_name):
LOGGER.info(f"socket.io/join {room_name}")
join_room(room_name)
-if __name__ == '__main__':
+
+if __name__ == "__main__":
socketio.run(app)
diff --git a/src/restapi/beans/Chokepoint.py b/src/restapi/beans/Chokepoint.py
index fa36e32..49b940e 100644
--- a/src/restapi/beans/Chokepoint.py
+++ b/src/restapi/beans/Chokepoint.py
@@ -1,7 +1,4 @@
-
-
class Chokepoint:
-
def __init__(self, reaction, metabolite):
self.__reaction = reaction
self.__metabolite = metabolite
diff --git a/src/restapi/beans/ConfigChokepoints.py b/src/restapi/beans/ConfigChokepoints.py
index d8f23bd..9b7e147 100644
--- a/src/restapi/beans/ConfigChokepoints.py
+++ b/src/restapi/beans/ConfigChokepoints.py
@@ -1,6 +1,4 @@
-
class ConfigReactionsSets:
-
def __init__(self):
self.__objective = None
self.__fraction_of_optimum = None
diff --git a/src/restapi/beans/ConfigReactionsSets.py b/src/restapi/beans/ConfigReactionsSets.py
index 3e36243..6451d5d 100644
--- a/src/restapi/beans/ConfigReactionsSets.py
+++ b/src/restapi/beans/ConfigReactionsSets.py
@@ -1,8 +1,8 @@
from .OptimizationEnum import *
from .MediumEnum import *
-class ConfigReactionsSets:
+class ConfigReactionsSets:
def __init__(self):
self.__objective = None
self.__fraction_of_optimum = None
diff --git a/src/restapi/beans/MediumEnum.py b/src/restapi/beans/MediumEnum.py
index 8f9aab2..6091520 100644
--- a/src/restapi/beans/MediumEnum.py
+++ b/src/restapi/beans/MediumEnum.py
@@ -1,5 +1,6 @@
from enum import Enum
+
class MediumEnum(Enum):
DEFAULT = 1
- COMPLETE = 2
\ No newline at end of file
+ COMPLETE = 2
diff --git a/src/restapi/beans/ModelId.py b/src/restapi/beans/ModelId.py
index f712e6d..7382b27 100644
--- a/src/restapi/beans/ModelId.py
+++ b/src/restapi/beans/ModelId.py
@@ -1,6 +1,4 @@
-
class ModelId:
-
def __init__(self, model, metabolites, reactions, genes):
self.model_uuid = model
self.metabolites = metabolites
diff --git a/src/restapi/beans/OptimizationEnum.py b/src/restapi/beans/OptimizationEnum.py
index e6e5f05..a007a29 100644
--- a/src/restapi/beans/OptimizationEnum.py
+++ b/src/restapi/beans/OptimizationEnum.py
@@ -1,5 +1,6 @@
from enum import Enum
+
class OptimizationEnum(Enum):
FBA = 1
- pFBA = 2
\ No newline at end of file
+ pFBA = 2
diff --git a/src/restapi/beans/ResponseChannel.py b/src/restapi/beans/ResponseChannel.py
index 90b79b7..812379a 100644
--- a/src/restapi/beans/ResponseChannel.py
+++ b/src/restapi/beans/ResponseChannel.py
@@ -2,7 +2,6 @@
class ResponseChannel:
-
def __init__(self, channel=None):
self.channel = channel
@@ -14,6 +13,7 @@ def channel_attr(self):
def channel_attr(self, channel):
self.channel = channel
+
class ResponseChannelEncoder(JSONEncoder):
def default(self, o):
- return o.__dict__
\ No newline at end of file
+ return o.__dict__
diff --git a/src/restapi/beans/ResponseChokepoints.py b/src/restapi/beans/ResponseChokepoints.py
index 824bd32..7db9270 100644
--- a/src/restapi/beans/ResponseChokepoints.py
+++ b/src/restapi/beans/ResponseChokepoints.py
@@ -2,7 +2,6 @@
class ResponseChokepoints:
-
def __init__(self, status=None, finished=None, result=None, pending_length=None):
self.__status = status
self.__finished = finished
@@ -41,6 +40,7 @@ def pending_length(self):
def pending_length(self, pending_length):
self.__pending_length = pending_length
+
class ResponseChokepointsEncoder(JSONEncoder):
def default(self, o):
- return o.__dict__
\ No newline at end of file
+ return o.__dict__
diff --git a/src/restapi/beans/ResponseEndpoint.py b/src/restapi/beans/ResponseEndpoint.py
index 5c9dc5b..73b6c66 100644
--- a/src/restapi/beans/ResponseEndpoint.py
+++ b/src/restapi/beans/ResponseEndpoint.py
@@ -2,7 +2,6 @@
class ResponseEndpoint:
-
def __init__(self, endpoint=None):
self.endpoint = endpoint
@@ -14,6 +13,7 @@ def endpoint_attr(self):
def endpoint_attr(self, endpoint):
self.endpoint = endpoint
+
class ResponseEndpointEncoder(JSONEncoder):
def default(self, o):
- return o.__dict__
\ No newline at end of file
+ return o.__dict__
diff --git a/src/restapi/beans/ResponseReport.py b/src/restapi/beans/ResponseReport.py
index bb1d6c8..b75153a 100644
--- a/src/restapi/beans/ResponseReport.py
+++ b/src/restapi/beans/ResponseReport.py
@@ -2,8 +2,14 @@
class ResponseReport:
-
- def __init__(self, status=None, finished=None, file_spreadsheet=None, file_html=None, pending_length=None):
+ def __init__(
+ self,
+ status=None,
+ finished=None,
+ file_spreadsheet=None,
+ file_html=None,
+ pending_length=None,
+ ):
self.status = status
self.finished = finished
self.file_spreadsheet = file_spreadsheet
@@ -49,4 +55,3 @@ def pending_length_attr(self):
@pending_length_attr.setter
def pending_length_attr(self, pending_length):
self.pending_length = pending_length
-
diff --git a/src/restapi/beans/TaskFormCriticalReactions.py b/src/restapi/beans/TaskFormCriticalReactions.py
index 18820ac..85485ee 100644
--- a/src/restapi/beans/TaskFormCriticalReactions.py
+++ b/src/restapi/beans/TaskFormCriticalReactions.py
@@ -1,8 +1,18 @@
from wtforms import Form, StringField, DecimalField, validators
+
class TaskFormCriticalReactions(Form):
- objective = StringField('objective',
- [validators.optional(), validators.Length(min=1, max=256)])
- fraction_of_optimum = DecimalField('fraction_of_optimum',
- [validators.optional(),
- validators.NumberRange(min=0.0, max=1.0, message='Fraction of optimum must be in the range [0, 1]')])
+ objective = StringField(
+ "objective", [validators.optional(), validators.Length(min=1, max=256)]
+ )
+ fraction_of_optimum = DecimalField(
+ "fraction_of_optimum",
+ [
+ validators.optional(),
+ validators.NumberRange(
+ min=0.0,
+ max=1.0,
+ message="Fraction of optimum must be in the range [0, 1]",
+ ),
+ ],
+ )
diff --git a/src/restapi/beans/TaskFormReactionsSets.py b/src/restapi/beans/TaskFormReactionsSets.py
index c1267dc..19f266f 100644
--- a/src/restapi/beans/TaskFormReactionsSets.py
+++ b/src/restapi/beans/TaskFormReactionsSets.py
@@ -1,19 +1,40 @@
-from wtforms import Form, StringField, DecimalField, SelectField, BooleanField, validators
+from wtforms import (
+ Form,
+ StringField,
+ DecimalField,
+ SelectField,
+ BooleanField,
+ validators,
+)
from .OptimizationEnum import *
from .MediumEnum import *
+
class TaskFormReactionsSets(Form):
- objective = StringField('objective',
- [validators.optional(), validators.Length(min=1, max=256)])
- fraction_of_optimum = DecimalField('fraction_of_optimum',
- [validators.optional(),
- validators.NumberRange(min=0.0, max=1.0, message='Fraction of optimum must be in the range [0, 1]')])
- medium = SelectField(u'Growth medium',
- [validators.optional()],
- choices=[name for name, member in MediumEnum.__members__.items()])
- optimization = SelectField(u'Growth Optimization',
- [validators.optional()],
- choices=[name for name, member in OptimizationEnum.__members__.items()])
- skip_knockout = BooleanField(u'Skip knock-out computation',
- [validators.optional()],
- default=True)
+ objective = StringField(
+ "objective", [validators.optional(), validators.Length(min=1, max=256)]
+ )
+ fraction_of_optimum = DecimalField(
+ "fraction_of_optimum",
+ [
+ validators.optional(),
+ validators.NumberRange(
+ min=0.0,
+ max=1.0,
+ message="Fraction of optimum must be in the range [0, 1]",
+ ),
+ ],
+ )
+ medium = SelectField(
+ u"Growth medium",
+ [validators.optional()],
+ choices=[name for name, member in MediumEnum.__members__.items()],
+ )
+ optimization = SelectField(
+ u"Growth Optimization",
+ [validators.optional()],
+ choices=[name for name, member in OptimizationEnum.__members__.items()],
+ )
+ skip_knockout = BooleanField(
+ u"Skip knock-out computation", [validators.optional()], default=True
+ )
diff --git a/src/restapi/beans/TaskInit.py b/src/restapi/beans/TaskInit.py
index 04ff848..e626043 100644
--- a/src/restapi/beans/TaskInit.py
+++ b/src/restapi/beans/TaskInit.py
@@ -1,7 +1,4 @@
-
-
class TaskInit:
-
def __init__(self, task_id, pending_length=None):
self.task_id = task_id
self.pending_length = pending_length
@@ -20,4 +17,4 @@ def pending_length_attr(self):
@pending_length_attr.setter
def pending_length_attr(self, pending_length):
- self.pending_length = pending_length
\ No newline at end of file
+ self.pending_length = pending_length
diff --git a/src/restapi/beans/WebsocketEvent.py b/src/restapi/beans/WebsocketEvent.py
index 353a30a..5dd067c 100644
--- a/src/restapi/beans/WebsocketEvent.py
+++ b/src/restapi/beans/WebsocketEvent.py
@@ -1,7 +1,7 @@
from json import JSONEncoder
-class WebsocketEvent:
+class WebsocketEvent:
def __init__(self, event, message):
self.event = event
self.message = message
@@ -22,6 +22,7 @@ def message_attr(self):
def message_attr(self, message):
self.message = message
+
class ResponseWebsocketEventEncoder(JSONEncoder):
def default(self, o):
- return o.__dict__
\ No newline at end of file
+ return o.__dict__
diff --git a/src/restapi/blueprints/models/models_bp.py b/src/restapi/blueprints/models/models_bp.py
index c29c528..279677c 100644
--- a/src/restapi/blueprints/models/models_bp.py
+++ b/src/restapi/blueprints/models/models_bp.py
@@ -21,8 +21,11 @@
from flask import Blueprint, jsonify, current_app, request
-from src.restapi.tasks.tasks import compute_chokepoints_task, task_compute_critical_reactions, \
- task_compute_growth_dependent_reactions
+from src.restapi.tasks.tasks import (
+ compute_chokepoints_task,
+ task_compute_critical_reactions,
+ task_compute_growth_dependent_reactions,
+)
from src.restapi.celery_app import celery_app, get_pending_tasks_length
from src.restapi.service.ModelService import ModelService
@@ -35,17 +38,20 @@
from src.restapi.validation import sanitize_string, empty_string
-models_bp = Blueprint('models_bp', __name__,
- template_folder='templates',
- static_folder='static',
- static_url_path='assets')
+models_bp = Blueprint(
+ "models_bp",
+ __name__,
+ template_folder="templates",
+ static_folder="static",
+ static_url_path="assets",
+)
LOGGER = logging.getLogger(__name__)
model_service = ModelService()
# Temporarily disabled
-#@models_bp.route('/models//chokepoints', methods=['POST'])
+# @models_bp.route('/models//chokepoints', methods=['POST'])
def compute_chokepoints(uuid):
LOGGER.info(f"POST /models/{uuid}/chokepoints")
@@ -53,7 +59,7 @@ def compute_chokepoints(uuid):
sanitize_string(str(uuid))
filename = model_service.query_by_uuid(uuid).url
- path = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
+ path = os.path.join(current_app.config["UPLOAD_FOLDER"], filename)
task = compute_chokepoints_task.delay(path)
task_uuid = task.id
@@ -63,7 +69,7 @@ def compute_chokepoints(uuid):
return response
-@models_bp.route('/models//critical_reactions', methods=['POST'])
+@models_bp.route("/models//critical_reactions", methods=["POST"])
def compute_critical_reactions(uuid):
request_data = request.form
@@ -86,17 +92,17 @@ def compute_critical_reactions(uuid):
fraction_of_optimum = float(form.fraction_of_optimum.data)
filename = model_service.query_by_uuid(uuid).url
- pathname = current_app.config['UPLOAD_FOLDER']
+ pathname = current_app.config["UPLOAD_FOLDER"]
path = os.path.join(pathname, filename)
- output_dir = current_app.config['STATIC_FOLDER']
- output_filename = str(uuid) + current_app.config['OUTPUT_FILE_EXTENSION']
+ output_dir = current_app.config["STATIC_FOLDER"]
+ output_filename = str(uuid) + current_app.config["OUTPUT_FILE_EXTENSION"]
task = task_compute_critical_reactions.delay(
path, # model path
output_dir + "/" + output_filename, # output path
objective=objective,
fraction_of_optimum=fraction_of_optimum,
- model_uuid=str(uuid)
+ model_uuid=str(uuid),
)
task_uuid = task.id
@@ -104,7 +110,8 @@ def compute_critical_reactions(uuid):
LOGGER.info(f"Response: {response}")
return response
-@models_bp.route('/models//growth_dependent_reactions', methods=['POST'])
+
+@models_bp.route("/models//growth_dependent_reactions", methods=["POST"])
def compute_growth_dependent_reactions(uuid):
request_data = request.form
@@ -123,24 +130,23 @@ def compute_growth_dependent_reactions(uuid):
objective = str(form.objective.data)
filename = model_service.query_by_uuid(uuid).url
- pathname = current_app.config['UPLOAD_FOLDER']
+ pathname = current_app.config["UPLOAD_FOLDER"]
path = os.path.join(pathname, filename)
- output_dir = current_app.config['STATIC_FOLDER']
- output_filename = str(uuid) + current_app.config['OUTPUT_FILE_EXTENSION']
+ output_dir = current_app.config["STATIC_FOLDER"]
+ output_filename = str(uuid) + current_app.config["OUTPUT_FILE_EXTENSION"]
task = task_compute_growth_dependent_reactions.delay(
- path,
- output_dir + "/" + output_filename, # output path
- objective,
- str(uuid))
+ path, output_dir + "/" + output_filename, objective, str(uuid) # output path
+ )
task_uuid = task.id
response = jsonify(vars(TaskInit(task_uuid, get_pending_tasks_length())))
LOGGER.info(f"Response: {response}")
return response
+
# Temporarily disabled
-#@models_bp.route('/models//report_reactions', methods=['POST'])
+# @models_bp.route('/models//report_reactions', methods=['POST'])
def compute_report_reactions(uuid):
request_data = request.form
@@ -171,10 +177,10 @@ def compute_report_reactions(uuid):
skip_knockout = bool(form.skip_knockout.data)
filename = model_service.query_by_uuid(uuid).url
- pathname = current_app.config['UPLOAD_FOLDER']
+ pathname = current_app.config["UPLOAD_FOLDER"]
path = os.path.join(pathname, filename)
- output_dir = current_app.config['STATIC_FOLDER']
- output_filename = str(uuid) + current_app.config['OUTPUT_FILE_EXTENSION']
+ output_dir = current_app.config["STATIC_FOLDER"]
+ output_filename = str(uuid) + current_app.config["OUTPUT_FILE_EXTENSION"]
config = ConfigReactionsSets()
config.fraction_of_optimum = fraction_of_optimum
@@ -183,7 +189,9 @@ def compute_report_reactions(uuid):
config.optimization = optimization
config.skip_knockout_computation = skip_knockout
- task = compute_sets_report_task.delay(path, output_dir, output_filename, str(uuid), config)
+ task = compute_sets_report_task.delay(
+ path, output_dir, output_filename, str(uuid), config
+ )
task_uuid = task.id
response = jsonify(vars(TaskInit(task_uuid, get_pending_tasks_length())))
diff --git a/src/restapi/blueprints/results/results_bp.py b/src/restapi/blueprints/results/results_bp.py
index 1a8eed6..0524a3a 100644
--- a/src/restapi/blueprints/results/results_bp.py
+++ b/src/restapi/blueprints/results/results_bp.py
@@ -23,7 +23,11 @@
from flask import Blueprint, jsonify
from celery.result import AsyncResult
-from src.restapi.celery_app import celery_app, get_pending_tasks_length, get_task_pending_position
+from src.restapi.celery_app import (
+ celery_app,
+ get_pending_tasks_length,
+ get_task_pending_position,
+)
from src.restapi.service.ModelService import ModelService
from src.restapi.beans.ResponseChokepoints import *
@@ -33,17 +37,20 @@
from src.restapi.constants import RESPONSE_TASK_NONE
-results_bp = Blueprint('results_bp', __name__,
- template_folder='templates',
- static_folder='static',
- static_url_path='assets')
+results_bp = Blueprint(
+ "results_bp",
+ __name__,
+ template_folder="templates",
+ static_folder="static",
+ static_url_path="assets",
+)
LOGGER = logging.getLogger(__name__)
model_service = ModelService()
# Temporarily disabled
-#@results_bp.route('/results//chokepoints', methods=['GET'])
+# @results_bp.route('/results//chokepoints', methods=['GET'])
def get_chokepoints(uuid):
LOGGER.info(f"GET /results/{uuid}/chokepoints")
@@ -68,7 +75,8 @@ def get_chokepoints(uuid):
LOGGER.info(f"Response: {response}")
return response
-@results_bp.route('/results//critical_reactions', methods=['GET'])
+
+@results_bp.route("/results//critical_reactions", methods=["GET"])
def get_critical_reactions(uuid):
LOGGER.info(f"GET /results/{uuid}/critical_reactions")
@@ -96,7 +104,8 @@ def get_critical_reactions(uuid):
LOGGER.info(f"Response: {response}")
return response
-@results_bp.route('/results//growth_dependent_reactions', methods=['GET'])
+
+@results_bp.route("/results//growth_dependent_reactions", methods=["GET"])
def get_growth_dependent_reactions(uuid):
LOGGER.info(f"GET /results/{uuid}/growth_dependent_reactions")
@@ -124,8 +133,9 @@ def get_growth_dependent_reactions(uuid):
LOGGER.info(f"Response: {response}")
return response
+
# Temporarily disabled
-#@results_bp.route('/models//report_reactions', methods=['GET'])
+# @results_bp.route('/models//report_reactions', methods=['GET'])
def get_report_reactions(uuid):
LOGGER.info(f"GET /models/{uuid}/report_reactions")
@@ -150,8 +160,9 @@ def get_report_reactions(uuid):
LOGGER.info(f"Response: {response}")
return response
+
# Terminate a running task
-@results_bp.route('/results//terminate', methods=['POST'])
+@results_bp.route("/results//terminate", methods=["POST"])
def terminate_task(uuid):
LOGGER.info(f"POST /results/{uuid}/terminate ")
@@ -160,8 +171,7 @@ def terminate_task(uuid):
result = ResponseReport()
async_result = AsyncResult(id=str(uuid), app=celery_app)
- async_result.revoke(signal='SIGKILL')
+ async_result.revoke(signal="SIGKILL")
LOGGER.info(f"Response: 'success':True 200")
- return json.dumps({'success': True}), 200, {'ContentType': 'application/json'}
-
+ return json.dumps({"success": True}), 200, {"ContentType": "application/json"}
diff --git a/src/restapi/blueprints/submit/submit_bp.py b/src/restapi/blueprints/submit/submit_bp.py
index 7ee13ad..1335d44 100644
--- a/src/restapi/blueprints/submit/submit_bp.py
+++ b/src/restapi/blueprints/submit/submit_bp.py
@@ -32,35 +32,39 @@
from werkzeug.utils import secure_filename
-submit_bp = Blueprint('submit_bp', __name__,
- template_folder='templates',
- static_folder='static',
- static_url_path='assets')
+submit_bp = Blueprint(
+ "submit_bp",
+ __name__,
+ template_folder="templates",
+ static_folder="static",
+ static_url_path="assets",
+)
LOGGER = logging.getLogger(__name__)
model_service = ModelService()
-@submit_bp.route('/submit', methods=['POST'])
+
+@submit_bp.route("/submit", methods=["POST"])
def submit_model():
LOGGER.info("POST /submit")
- if 'file' not in request.files:
+ if "file" not in request.files:
raise ValidationException("No file part")
- file = request.files['file']
- if file.filename == '':
+ file = request.files["file"]
+ if file.filename == "":
raise ValidationException("No selected file")
- if not (file and allowed_file(file.filename)) :
+ if not (file and allowed_file(file.filename)):
raise ValidationException("Invalid file extension")
filename, file_extension = os.path.splitext(secure_filename(file.filename))
model_uuid = str(uuid.uuid4())
filename = f"{model_uuid}{file_extension}"
- path = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
+ path = os.path.join(current_app.config["UPLOAD_FOLDER"], filename)
LOGGER.info(f"Saving model to: {filename}")
file.save(path)
@@ -75,6 +79,11 @@ def submit_model():
model_service.insert(model_uuid, filename)
- response = ModelId(model_uuid, len(cobra_model.metabolites), len(cobra_model.reactions), len(cobra_model.genes)).__dict__
+ response = ModelId(
+ model_uuid,
+ len(cobra_model.metabolites),
+ len(cobra_model.reactions),
+ len(cobra_model.genes),
+ ).__dict__
LOGGER.info(f"Response: {response}")
return jsonify(response)
diff --git a/src/restapi/blueprints/websockets/websockets_bp.py b/src/restapi/blueprints/websockets/websockets_bp.py
index d267656..203af9e 100644
--- a/src/restapi/blueprints/websockets/websockets_bp.py
+++ b/src/restapi/blueprints/websockets/websockets_bp.py
@@ -28,27 +28,32 @@
from src.restapi.constants import *
from src.restapi.validation import *
-websockets_bp = Blueprint('websockets_bp', __name__,
- template_folder='templates',
- static_folder='static',
- static_url_path='assets')
+websockets_bp = Blueprint(
+ "websockets_bp",
+ __name__,
+ template_folder="templates",
+ static_folder="static",
+ static_url_path="assets",
+)
LOGGER = logging.getLogger(__name__)
model_service = ModelService()
-@websockets_bp.route('/websockets/get_endpoint', methods=['GET'])
+
+@websockets_bp.route("/websockets/get_endpoint", methods=["GET"])
def get_endpoint():
LOGGER.info(f"GET /websockets/get_endpoint")
- result = ResponseEndpoint(current_app.config['WEBSOCKETS_URL'])
+ result = ResponseEndpoint(current_app.config["WEBSOCKETS_URL"])
response = jsonify(vars(result))
LOGGER.info(f"Response: {response}")
return response
-@websockets_bp.route('/websockets/notification_channel/', methods=['GET'])
+
+@websockets_bp.route("/websockets/notification_channel/", methods=["GET"])
def get_notification_channel(uuid):
LOGGER.info(f"GET /websockets/notification_channel/{uuid}")
@@ -65,7 +70,8 @@ def get_notification_channel(uuid):
LOGGER.info(f"Response: {response}")
return response
-@websockets_bp.route('/websockets/example_event_join', methods=['GET'])
+
+@websockets_bp.route("/websockets/example_event_join", methods=["GET"])
def get_example_join_event():
LOGGER.info(f"GET /websockets/example_event_join")
@@ -76,12 +82,15 @@ def get_example_join_event():
LOGGER.info(f"Response: {response}")
return response
-@websockets_bp.route('/websockets/example_event_message', methods=['GET'])
+
+@websockets_bp.route("/websockets/example_event_message", methods=["GET"])
def get_example_event_message():
LOGGER.info(f"GET /websockets/example_event_message")
- result = WebsocketEvent("00000000-0000-0000-0000-000000000000", "<>")
+ result = WebsocketEvent(
+ "00000000-0000-0000-0000-000000000000", "<>"
+ )
response = jsonify(vars(result))
LOGGER.info(f"Response: {response}")
diff --git a/src/restapi/celery_app.py b/src/restapi/celery_app.py
index 52b95be..4ffdab2 100644
--- a/src/restapi/celery_app.py
+++ b/src/restapi/celery_app.py
@@ -22,14 +22,14 @@
from dotenv import load_dotenv
basedir = os.path.abspath(os.path.dirname(__file__))
-load_dotenv(os.path.join(basedir, '../../.env'))
+load_dotenv(os.path.join(basedir, "../../.env"))
from celery import Celery
celery_app = Celery(
- broker=os.environ.get('CELERY_BROKER_URL'),
- backend=os.environ.get('CELERY_RESULT_BACKEND'),
+ broker=os.environ.get("CELERY_BROKER_URL"),
+ backend=os.environ.get("CELERY_RESULT_BACKEND"),
)
celery_app.conf.update(
@@ -40,12 +40,13 @@
result_expires=7200, # 2 hours
# Always restart workers after finishing.
worker_max_tasks_per_child=1,
- task_serializer='pickle',
- result_serializer='pickle',
- accept_content=['pickle'],
- imports = (os.environ.get('CELERY_IMPORTS'),)
+ task_serializer="pickle",
+ result_serializer="pickle",
+ accept_content=["pickle"],
+ imports=(os.environ.get("CELERY_IMPORTS"),),
)
+
def get_pending_tasks_length():
i = celery_app.control.inspect()
if i.reserved() is None:
@@ -54,6 +55,7 @@ def get_pending_tasks_length():
len_tasks = len(list(itertools.chain.from_iterable(i.reserved().values())))
return len_tasks
+
def get_task_pending_position(task_uuid):
i = celery_app.control.inspect()
if i.reserved() is None:
@@ -63,4 +65,4 @@ def get_task_pending_position(task_uuid):
g = [i for i, e in enumerate(v) if e["id"] == task_uuid]
if len(g) != 0:
return g[0]
- return 0 # Default value
\ No newline at end of file
+ return 0 # Default value
diff --git a/src/restapi/config.py b/src/restapi/config.py
index d81fa13..15b628e 100644
--- a/src/restapi/config.py
+++ b/src/restapi/config.py
@@ -20,7 +20,7 @@
from dotenv import load_dotenv
basedir = os.path.abspath(os.path.dirname(__file__))
-load_dotenv(os.path.join(basedir, '.env'))
+load_dotenv(os.path.join(basedir, ".env"))
class Config(object):
@@ -28,20 +28,18 @@ class Config(object):
TESTING = False
CSRF_ENABLED = True
- SECRET_KEY = os.environ.get('SECRET_KEY')
+ SECRET_KEY = os.environ.get("SECRET_KEY")
OUTPUT_FILE_EXTENSION = ".xls"
MAX_CONTENT_LENGTH = 1024 * 1024 * 30 # 30MB max file size
DICT_LOGGER = {
- 'version': 1,
- 'disable_existing_loggers': False,
- 'formatters': {
- 'simple': {
- 'format': (
- "%(asctime)s [%(levelname)s] [%(name)s] | %(message)s"
- )
+ "version": 1,
+ "disable_existing_loggers": False,
+ "formatters": {
+ "simple": {
+ "format": ("%(asctime)s [%(levelname)s] [%(name)s] | %(message)s")
},
},
}
@@ -50,43 +48,43 @@ class Config(object):
class ProductionConfig(Config):
DEBUG = False
- SQLALCHEMY_DATABASE_URI = f"mysql+pymysql:" \
- + f"//{os.environ.get('MYSQL_USER')}:" \
- + f"{os.environ.get('MYSQL_PASSWORD')}" \
- + f"@{os.environ.get('MYSQL_HOST')}" \
- f"/{os.environ.get('MYSQL_DATABASE')}"
+ SQLALCHEMY_DATABASE_URI = (
+ f"mysql+pymysql:"
+ + f"//{os.environ.get('MYSQL_USER')}:"
+ + f"{os.environ.get('MYSQL_PASSWORD')}"
+ + f"@{os.environ.get('MYSQL_HOST')}"
+ f"/{os.environ.get('MYSQL_DATABASE')}"
+ )
SQLALCHEMY_POOL_SIZE = 1
- CELERY_IMPORTS = ("app")
- CELERY_BROKER_URL = os.environ.get('CELERY_BROKER_URL')
- CELERY_RESULT_BACKEND = os.environ.get('CELERY_RESULT_BACKEND')
- REDIS_BROKER_URL = os.environ.get('REDIS_BROKER_URL')
- WEBSOCKETS_URL = os.environ.get('WEBSOCKETS_URL')
+ CELERY_IMPORTS = "app"
+ CELERY_BROKER_URL = os.environ.get("CELERY_BROKER_URL")
+ CELERY_RESULT_BACKEND = os.environ.get("CELERY_RESULT_BACKEND")
+ REDIS_BROKER_URL = os.environ.get("REDIS_BROKER_URL")
+ WEBSOCKETS_URL = os.environ.get("WEBSOCKETS_URL")
- UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER')
- STATIC_FOLDER = os.environ.get('STATIC_FOLDER')
+ UPLOAD_FOLDER = os.environ.get("UPLOAD_FOLDER")
+ STATIC_FOLDER = os.environ.get("STATIC_FOLDER")
DICT_LOGGER = {
- 'version': 1,
- 'disable_existing_loggers': False,
- 'formatters': {
- 'simple': {
- 'format': (
- "%(asctime)s [%(levelname)s] [%(name)s] | %(message)s"
- )
+ "version": 1,
+ "disable_existing_loggers": False,
+ "formatters": {
+ "simple": {
+ "format": ("%(asctime)s [%(levelname)s] [%(name)s] | %(message)s")
},
},
- 'handlers': {
- 'console': {
- 'level': 'DEBUG',
- 'class': 'logging.StreamHandler',
- 'formatter': 'simple'
+ "handlers": {
+ "console": {
+ "level": "DEBUG",
+ "class": "logging.StreamHandler",
+ "formatter": "simple",
},
},
- 'root': {
- 'level': 'INFO',
- 'handlers': ['console'],
- 'propagate': True,
+ "root": {
+ "level": "INFO",
+ "handlers": ["console"],
+ "propagate": True,
},
}
@@ -95,64 +93,66 @@ class StagingConfig(Config):
DEVELOPMENT = True
DEBUG = True
- SQLALCHEMY_DATABASE_URI = f"mysql+pymysql:" \
- + f"//{os.environ.get('MYSQL_USER')}:" \
- + f"{os.environ.get('MYSQL_PASSWORD')}" \
- + f"@{os.environ.get('MYSQL_HOST')}" \
- f"/{os.environ.get('MYSQL_DATABASE')}"
+ SQLALCHEMY_DATABASE_URI = (
+ f"mysql+pymysql:"
+ + f"//{os.environ.get('MYSQL_USER')}:"
+ + f"{os.environ.get('MYSQL_PASSWORD')}"
+ + f"@{os.environ.get('MYSQL_HOST')}"
+ f"/{os.environ.get('MYSQL_DATABASE')}"
+ )
SQLALCHEMY_POOL_SIZE = 1
- CELERY_IMPORTS = ("app")
+ CELERY_IMPORTS = "app"
CELERY_BROKER_URL = "redis://localhost:6379"
CELERY_RESULT_BACKEND = "redis://localhost:6379"
REDIS_BROKER_URL = "redis://localhost:6379"
WEBSOCKETS_URL = "localhost:5000"
- UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER')
- STATIC_FOLDER = os.environ.get('STATIC_FOLDER')
+ UPLOAD_FOLDER = os.environ.get("UPLOAD_FOLDER")
+ STATIC_FOLDER = os.environ.get("STATIC_FOLDER")
class DevelopmentConfig(Config):
DEVELOPMENT = True
DEBUG = True
- SQLALCHEMY_DATABASE_URI = f"mysql+pymysql:" \
- + f"//{os.environ.get('MYSQL_USER')}:" \
- + f"{os.environ.get('MYSQL_PASSWORD')}" \
- + f"@{os.environ.get('MYSQL_HOST')}" \
- f"/{os.environ.get('MYSQL_DATABASE')}"
+ SQLALCHEMY_DATABASE_URI = (
+ f"mysql+pymysql:"
+ + f"//{os.environ.get('MYSQL_USER')}:"
+ + f"{os.environ.get('MYSQL_PASSWORD')}"
+ + f"@{os.environ.get('MYSQL_HOST')}"
+ f"/{os.environ.get('MYSQL_DATABASE')}"
+ )
SQLALCHEMY_POOL_SIZE = 1
- CELERY_IMPORTS = ("app")
+ CELERY_IMPORTS = "app"
CELERY_BROKER_URL = "redis://localhost:6379"
CELERY_RESULT_BACKEND = "redis://localhost:6379"
REDIS_BROKER_URL = "redis://localhost:6379"
WEBSOCKETS_URL = "localhost:5000"
- UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER')
- STATIC_FOLDER = os.environ.get('STATIC_FOLDER')
+ UPLOAD_FOLDER = os.environ.get("UPLOAD_FOLDER")
+ STATIC_FOLDER = os.environ.get("STATIC_FOLDER")
class TestingConfig(Config):
TESTING = True
- '''
+ """
If DEBUG is set to True it causes an AssertionError. See:
- https://github.com/ga4gh/ga4gh-server/issues/791
- https://github.com/alexmclarty/mirror/issues/6
- '''
+ """
DEBUG = False
SQLALCHEMY_DATABASE_URI = "sqlite://"
SQLALCHEMY_POOL_SIZE = None
SQLALCHEMY_POOL_TIMEOUT = None
- CELERY_IMPORTS = ("app")
+ CELERY_IMPORTS = "app"
CELERY_BROKER_URL = "redis://"
CELERY_RESULT_BACKEND = "redis://"
REDIS_BROKER_URL = "redis://"
WEBSOCKETS_URL = "localhost:5000"
- UPLOAD_FOLDER = '/tmp'
- STATIC_FOLDER = '/tmp'
-
-
+ UPLOAD_FOLDER = "/tmp"
+ STATIC_FOLDER = "/tmp"
diff --git a/src/restapi/constants.py b/src/restapi/constants.py
index c77c1ce..a5a55f2 100644
--- a/src/restapi/constants.py
+++ b/src/restapi/constants.py
@@ -21,7 +21,7 @@
RESPONSE_TASK_NONE = "none"
MONGODB_DATABASE = "MONGODB"
-MYSQL_DATABASE = "MYSQL"
+MYSQL_DATABASE = "MYSQL"
AVAILABLE_DATABASES = [MONGODB_DATABASE, MYSQL_DATABASE]
-SESSION_ROOM_ID_KEY = "session_room"
\ No newline at end of file
+SESSION_ROOM_ID_KEY = "session_room"
diff --git a/src/restapi/exception_handler.py b/src/restapi/exception_handler.py
index baaa517..ca488cf 100644
--- a/src/restapi/exception_handler.py
+++ b/src/restapi/exception_handler.py
@@ -32,37 +32,42 @@ def init_exception_handler(app):
app.register_error_handler(InvalidException, handle_invalid_exception)
app.register_error_handler(Exception, handle_exception)
+
def handle_duplicate_exception(error):
LOGGER.info(error)
LOGGER.info("Response : 409")
- response = jsonify({'message': "Value already exists"})
+ response = jsonify({"message": "Value already exists"})
response.status_code = 409
return response
+
def handle_validation_exception(error):
LOGGER.info(error)
LOGGER.info("Response : 400")
- response = jsonify({'message': str(error)})
+ response = jsonify({"message": str(error)})
response.status_code = 400
return response
+
def handle_not_found_exception(error):
LOGGER.info(error)
LOGGER.info("Response : 404")
- response = jsonify({'message': "Not found"})
+ response = jsonify({"message": "Not found"})
response.status_code = 404
return response
+
def handle_exception(error):
LOGGER.error(traceback.format_exc())
LOGGER.info("Response : 500")
- response = jsonify({'message': "Internal server error"})
+ response = jsonify({"message": "Internal server error"})
response.status_code = 500
return response
+
def handle_invalid_exception(error):
LOGGER.info(error)
LOGGER.info("Response : 401")
- response = jsonify({'message': str(error)})
+ response = jsonify({"message": str(error)})
response.status_code = 401
- return response
\ No newline at end of file
+ return response
diff --git a/src/restapi/exceptions.py b/src/restapi/exceptions.py
index 9392e4f..ad43b2b 100644
--- a/src/restapi/exceptions.py
+++ b/src/restapi/exceptions.py
@@ -16,18 +16,22 @@
along with this program. If not, see .
"""
+
class ValidationException(Exception):
def __init__(self, message):
super().__init__(message)
+
class DuplicateException(Exception):
def __init__(self):
super().__init__()
+
class NotFoundException(Exception):
def __init__(self):
super().__init__()
+
class InvalidException(Exception):
def __init__(self, message):
- super().__init__(message)
\ No newline at end of file
+ super().__init__(message)
diff --git a/src/restapi/models/ModelBean.py b/src/restapi/models/ModelBean.py
index 76aa20f..8cd2f74 100644
--- a/src/restapi/models/ModelBean.py
+++ b/src/restapi/models/ModelBean.py
@@ -1,7 +1,4 @@
-
-
class Model:
-
def __init__(self, uuid=None, url=None):
self.__uuid = uuid
self.__url = url
@@ -21,4 +18,3 @@ def url(self):
@url.setter
def url(self, url):
self.__url = url
-
diff --git a/src/restapi/models/ModelMongo.py b/src/restapi/models/ModelMongo.py
index 0ce9aef..966f9ef 100644
--- a/src/restapi/models/ModelMongo.py
+++ b/src/restapi/models/ModelMongo.py
@@ -1,9 +1,9 @@
from src.restapi.database import db
-db.connect('models')
+db.connect("models")
+
class Model(db.Document):
uuid = db.StringField(unique=True, required=True)
- url = db.StringField(required=True)
-
+ url = db.StringField(required=True)
diff --git a/src/restapi/models/ModelMysql.py b/src/restapi/models/ModelMysql.py
index da96467..054f64d 100644
--- a/src/restapi/models/ModelMysql.py
+++ b/src/restapi/models/ModelMysql.py
@@ -1,6 +1,7 @@
from src.restapi.database import db
+
class Models(db.Model):
- __tablename__ = 'MODELS'
+ __tablename__ = "MODELS"
uuid = db.Column(db.String(100), unique=True, nullable=False, primary_key=True)
url = db.Column(db.Text, unique=True, nullable=False)
diff --git a/src/restapi/repository/ModelRepositoryMongo.py b/src/restapi/repository/ModelRepositoryMongo.py
index 19e57a3..ebcaea6 100644
--- a/src/restapi/repository/ModelRepositoryMongo.py
+++ b/src/restapi/repository/ModelRepositoryMongo.py
@@ -10,7 +10,6 @@
class ModelRepository:
-
def insert(self, uuid, url):
try:
LOGGER.info(f"Saving Model(uuid={uuid}, url={url})")
@@ -18,7 +17,6 @@ def insert(self, uuid, url):
except (NotUniqueError, DuplicateKeyError) as err:
raise DuplicateException()
-
def query_by_uuid(self, uuid):
LOGGER.info(f"Quering Model(uuid={uuid})")
value = Model.objects(uuid=str(uuid)).first()
@@ -28,4 +26,4 @@ def query_by_uuid(self, uuid):
return value
def query(self):
- return Model.objects.all()
\ No newline at end of file
+ return Model.objects.all()
diff --git a/src/restapi/repository/ModelRepositoryMysql.py b/src/restapi/repository/ModelRepositoryMysql.py
index 683fea6..a4adf6a 100644
--- a/src/restapi/repository/ModelRepositoryMysql.py
+++ b/src/restapi/repository/ModelRepositoryMysql.py
@@ -8,39 +8,44 @@
LOGGER = logging.getLogger(__name__)
-INSERT_MODEL = 'INSERT INTO MODELS (UUID, URL) VALUES (% s, % s)'
-QUERY_MODEL = 'SELECT * FROM MODELS WHERE uuid = % s'
+INSERT_MODEL = "INSERT INTO MODELS (UUID, URL) VALUES (% s, % s)"
+QUERY_MODEL = "SELECT * FROM MODELS WHERE uuid = % s"
-class ModelRepository:
+class ModelRepository:
def log_insert(self, query, args):
query_log = query
for par in list(args):
- query_log = query_log.replace('% s', par, 1)
+ query_log = query_log.replace("% s", par, 1)
LOGGER.info(query_log)
with connection.cursor() as cursor:
print("Done cursor")
- cursor.execute(query,
- args)
+ cursor.execute(query, args)
print("Done execute")
# Disabled to avoid: pymysql.err.Error: Already closed
- #connection.commit()
+ # connection.commit()
def log_query_one(self, query, args):
query_log = query
for par in list(args):
- query_log = query_log.replace('% s', par, 1)
+ query_log = query_log.replace("% s", par, 1)
LOGGER.info(query_log)
with connection.cursor() as cursor:
cursor.execute(query, args)
result = cursor.fetchone()
# Disabled to avoid: pymysql.err.Error: Already closed
- #cursor.close()
+ # cursor.close()
return result
return None
def insert(self, uuid, url):
- self.log_insert(INSERT_MODEL, (uuid, url,))
+ self.log_insert(
+ INSERT_MODEL,
+ (
+ uuid,
+ url,
+ ),
+ )
def query_by_uuid(self, uuid):
uuid = str(uuid)
@@ -51,5 +56,5 @@ def query_by_uuid(self, uuid):
model.url = result["URL"]
return model
- #def query(self):
- # return Model.objects.all()
\ No newline at end of file
+ # def query(self):
+ # return Model.objects.all()
diff --git a/src/restapi/repository/ModelRepositoryMysqlAlchemy.py b/src/restapi/repository/ModelRepositoryMysqlAlchemy.py
index 040dae4..34c02a8 100644
--- a/src/restapi/repository/ModelRepositoryMysqlAlchemy.py
+++ b/src/restapi/repository/ModelRepositoryMysqlAlchemy.py
@@ -26,7 +26,6 @@
class ModelRepository:
-
def insert(self, uuid, url):
LOGGER.info(f"Saving Models(uuid={uuid}, url={url})")
models = Models(uuid=uuid, url=url)
diff --git a/src/restapi/service/ModelService.py b/src/restapi/service/ModelService.py
index 5c5a6fa..fb1edbe 100644
--- a/src/restapi/service/ModelService.py
+++ b/src/restapi/service/ModelService.py
@@ -16,9 +16,10 @@
along with this program. If not, see .
"""
-#from src.restapi.repository.ModelRepositoryMongo import ModelRepository
+# from src.restapi.repository.ModelRepositoryMongo import ModelRepository
from src.restapi.repository.ModelRepositoryMysqlAlchemy import ModelRepository
+
class ModelService:
model_repository = ModelRepository()
diff --git a/src/restapi/socket_util.py b/src/restapi/socket_util.py
index e0b05ff..97f680e 100644
--- a/src/restapi/socket_util.py
+++ b/src/restapi/socket_util.py
@@ -3,8 +3,8 @@
LOGGER = get_task_logger(__name__)
-socketio = SocketIO(message_queue='redis://')
+socketio = SocketIO(message_queue="redis://")
def send_message_client(room_name, message):
- socketio.emit(room_name, {'data': message}, to=room_name, room=room_name)
+ socketio.emit(room_name, {"data": message}, to=room_name, room=room_name)
diff --git a/src/restapi/tasks/tasks.py b/src/restapi/tasks/tasks.py
index 5f96154..9ee887e 100644
--- a/src/restapi/tasks/tasks.py
+++ b/src/restapi/tasks/tasks.py
@@ -27,15 +27,30 @@
def compute_chokepoints_task(model_path, exclude_dead_reactions=True):
return compute_chokepoints(model_path, exclude_dead_reactions)
+
@celery_app.task
-def compute_sets_report_task(model_path, output_path, output_filename, model_uuid, config):
- generate_sets_report(model_path, output_path + "/" + output_filename, model_uuid, config)
+def compute_sets_report_task(
+ model_path, output_path, output_filename, model_uuid, config
+):
+ generate_sets_report(
+ model_path, output_path + "/" + output_filename, model_uuid, config
+ )
return output_filename
+
@celery_app.task
-def task_compute_critical_reactions(model_path, output_path, objective=None, fraction_of_optimum=None, model_uuid=None):
- return compute_critical_reactions(model_path, output_path, objective, fraction_of_optimum, model_uuid)
+def task_compute_critical_reactions(
+ model_path, output_path, objective=None, fraction_of_optimum=None, model_uuid=None
+):
+ return compute_critical_reactions(
+ model_path, output_path, objective, fraction_of_optimum, model_uuid
+ )
+
@celery_app.task
-def task_compute_growth_dependent_reactions(model_path, output_path, objective=None, model_uuid=None):
- return compute_growth_dependent_reactions(model_path, output_path, objective, model_uuid)
+def task_compute_growth_dependent_reactions(
+ model_path, output_path, objective=None, model_uuid=None
+):
+ return compute_growth_dependent_reactions(
+ model_path, output_path, objective, model_uuid
+ )
diff --git a/src/restapi/util/model_utils.py b/src/restapi/util/model_utils.py
index 6644a22..95ead5a 100644
--- a/src/restapi/util/model_utils.py
+++ b/src/restapi/util/model_utils.py
@@ -26,7 +26,7 @@
def read_model(path):
try:
# check if file exists
- open(path, 'r')
+ open(path, "r")
# read submit
if path[-4:] == ".xml":
cobra_model = cobra.io.read_sbml_model(path)
@@ -41,13 +41,14 @@ def read_model(path):
return cobra_model
-def compute_chokepoints(mdoel_path, exclude_dead_reations = True):
+
+def compute_chokepoints(mdoel_path, exclude_dead_reations=True):
cpmodel = CobraMetabolicModel(mdoel_path)
cpmodel.find_chokepoints(exclude_dead_reactions=exclude_dead_reations)
return [(r.id, m.id) for r, m in cpmodel.chokepoints()]
-def in_range_zero(up , low):
+def in_range_zero(up, low):
return up + CONST_EPSILON >= 0 and low - CONST_EPSILON <= 0
@@ -74,7 +75,10 @@ def alive_reactions(cobra_model, fva_solution):
def reaction_reversible(reaction):
- return reaction.upper_bound > CONST_EPSILON and abs(reaction.lower_bound) > CONST_EPSILON
+ return (
+ reaction.upper_bound > CONST_EPSILON
+ and abs(reaction.lower_bound) > CONST_EPSILON
+ )
def reversible_alive_flux(cobra_model, cobra_summary):
@@ -112,4 +116,4 @@ def fva_fluxes(cobra_model, fva_solution):
i = i + 1
- return result
\ No newline at end of file
+ return result
diff --git a/src/restapi/util/report_sets_utils.py b/src/restapi/util/report_sets_utils.py
index 83561e7..8ba9942 100644
--- a/src/restapi/util/report_sets_utils.py
+++ b/src/restapi/util/report_sets_utils.py
@@ -52,13 +52,13 @@ def read_config_model(model_path, config):
return model
-def generate_sets_report(model_path, output_path, model_uuid, config):
+def generate_sets_report(model_path, output_path, model_uuid, config):
def verbose_f(text, args1=None, args2=None):
- '''
+ """
args1 = room name
args2 = None
- '''
+ """
LOGGER.info(text)
send_message_client(args1, text)
@@ -67,7 +67,7 @@ def verbose_f(text, args1=None, args2=None):
if config.fraction_of_optimum is not None:
fraction_of_optimum = config.fraction_of_optimum
- MODEL = model_path
+ MODEL = model_path
# compute essential reactions
verbose_f("Computing essential reactions", model_uuid)
@@ -109,10 +109,13 @@ def verbose_f(text, args1=None, args2=None):
book = s.get_workbook()
info = book.add_sheet("submit info")
s.spreadsheet_write_reactions(state, "reactions", ordered=True)
- s.spreadsheet_write_metabolites(state, "metabolites", ordered=True, print_reactions=True)
-
+ s.spreadsheet_write_metabolites(
+ state, "metabolites", ordered=True, print_reactions=True
+ )
- chokepoints = set([r.reaction.id for r in cpmodel.find_chokepoints(exclude_dead_reactions=True)])
+ chokepoints = set(
+ [r.reaction.id for r in cpmodel.find_chokepoints(exclude_dead_reactions=True)]
+ )
model = cpmodel.model()
# FBA
@@ -154,12 +157,17 @@ def verbose_f(text, args1=None, args2=None):
MGR = alive_flux(model, sol_fba)
if config.optimization == OptimizationEnum.FBA:
verbose_f("Computing Flux Variability Analysis", model_uuid)
- fba_fva = flux_variability_analysis(model, processes=PROCESSES,
- fraction_of_optimum=fraction_of_optimum)
+ fba_fva = flux_variability_analysis(
+ model, processes=PROCESSES, fraction_of_optimum=fraction_of_optimum
+ )
elif config.optimization == OptimizationEnum.pFBA:
verbose_f("Computing parsimonious Flux Variability Analysis", model_uuid)
- fba_fva = flux_variability_analysis(model, pfba_factor = 1.0, processes=PROCESSES,
- fraction_of_optimum=fraction_of_optimum)
+ fba_fva = flux_variability_analysis(
+ model,
+ pfba_factor=1.0,
+ processes=PROCESSES,
+ fraction_of_optimum=fraction_of_optimum,
+ )
else:
pass
@@ -168,27 +176,26 @@ def verbose_f(text, args1=None, args2=None):
verbose_f("Computing suboptimal Flux Variability Analysis", model_uuid)
cpmodel = read_config_model(MODEL, config)
- cpmodel.fva(update_flux = True, threshold=0.0)
+ cpmodel.fva(update_flux=True, threshold=0.0)
DR_0 = set([r.id for r in cpmodel.dead_reactions()])
cpmodel.find_chokepoints(exclude_dead_reactions=True)
CP_0 = set([r.id for r, m in cpmodel.chokepoints()])
verbose_f("Computing optimal Flux Variability Analysis", model_uuid)
cpmodel = read_config_model(MODEL, config)
- cpmodel.fva(update_flux = True, threshold=1.0)
+ cpmodel.fva(update_flux=True, threshold=1.0)
DR_1 = set([r.id for r in cpmodel.dead_reactions()])
cpmodel.find_chokepoints(exclude_dead_reactions=True)
CP_1 = set([r.id for r, m in cpmodel.chokepoints()])
verbose_f("Computing suboptimal Flux Variability Analysis", model_uuid)
cpmodel = read_config_model(MODEL, config)
- cpmodel.fva(update_flux = True, threshold=0.9)
+ cpmodel.fva(update_flux=True, threshold=0.9)
DR_09 = set([r.id for r in cpmodel.dead_reactions()])
cpmodel = read_config_model(MODEL, config)
model = cpmodel.model()
-
info.write(0, 0, "MODEL ID")
info.write(0, 1, model.id)
info.write(1, 0, "REACTIONS")
@@ -235,7 +242,9 @@ def verbose_f(text, args1=None, args2=None):
knock_reaction.upper_bound = float(0.0)
knock_reaction.lower_bound = float(0.0)
- verbose_f("Computing results for reaction: " + knock_reaction.id, model_uuid)
+ verbose_f(
+ "Computing results for reaction: " + knock_reaction.id, model_uuid
+ )
aux_sol = 0
if config.optimization == OptimizationEnum.FBA:
@@ -249,19 +258,27 @@ def verbose_f(text, args1=None, args2=None):
if not config.skip_knockout_computation:
if config.optimization == OptimizationEnum.FBA:
- prima_fva_sol = flux_variability_analysis(model, processes=PROCESSES,
- fraction_of_optimum=fraction_of_optimum)
+ prima_fva_sol = flux_variability_analysis(
+ model,
+ processes=PROCESSES,
+ fraction_of_optimum=fraction_of_optimum,
+ )
elif config.optimization == OptimizationEnum.pFBA:
- prima_fva_sol = flux_variability_analysis(model, pfba_factor = 1.0, processes=PROCESSES,
- fraction_of_optimum=fraction_of_optimum)
+ prima_fva_sol = flux_variability_analysis(
+ model,
+ pfba_factor=1.0,
+ processes=PROCESSES,
+ fraction_of_optimum=fraction_of_optimum,
+ )
else:
pass
NR_PLUS_RR_prima = alive_reactions(model, prima_fva_sol)
- DR_prima = set([r.id for r in model.reactions]).difference(set(NR_PLUS_RR_prima))
+ DR_prima = set([r.id for r in model.reactions]).difference(
+ set(NR_PLUS_RR_prima)
+ )
Y = DR_prima.difference(DR_1)
-
if r in chokepoints:
is_choke = "TRUE"
else:
@@ -316,7 +333,10 @@ def verbose_f(text, args1=None, args2=None):
i = counters[sheet]
if not config.skip_knockout_computation:
- cell_X = "{}: {}".format(len(set(MGR_prima).intersection(DR_1)), str(set(MGR_prima).intersection(DR_1)))
+ cell_X = "{}: {}".format(
+ len(set(MGR_prima).intersection(DR_1)),
+ str(set(MGR_prima).intersection(DR_1)),
+ )
cell_Y = "{}: {}".format(len(Y), str(Y))
else:
cell_X = " "
diff --git a/src/restapi/util/report_utils.py b/src/restapi/util/report_utils.py
index de6056f..7c29db8 100644
--- a/src/restapi/util/report_utils.py
+++ b/src/restapi/util/report_utils.py
@@ -29,13 +29,15 @@
LOGGER = get_task_logger(__name__)
-def compute_critical_reactions(model_path, output_path, objective=None, fraction_of_optimum=None, model_uuid=None):
+def compute_critical_reactions(
+ model_path, output_path, objective=None, fraction_of_optimum=None, model_uuid=None
+):
def verbose_f(text, args1=None, args2=None):
- '''
+ """
args1 = ws room name
args2 = None
- '''
+ """
LOGGER.info(text)
send_message_client(args1, text)
@@ -46,7 +48,7 @@ def verbose_f(text, args1=None, args2=None):
config.args2 = None
config.output_path_spreadsheet = output_path
if output_path is not None:
- config.output_path_html = output_path[:output_path.rfind('.')] + '.html'
+ config.output_path_html = output_path[: output_path.rfind(".")] + ".html"
config.objective = objective
config.fraction = fraction_of_optimum
config.processes = 1
@@ -60,13 +62,15 @@ def verbose_f(text, args1=None, args2=None):
_, filename2 = ntpath.split(config.output_path_spreadsheet)
return (filename1, filename2)
-def compute_growth_dependent_reactions(model_path, output_path, objective=None, model_uuid=None):
+def compute_growth_dependent_reactions(
+ model_path, output_path, objective=None, model_uuid=None
+):
def verbose_f(text, args1=None, args2=None):
- '''
+ """
args1 = ws room name
args2 = None
- '''
+ """
LOGGER.info(text)
send_message_client(args1, text)
@@ -77,7 +81,7 @@ def verbose_f(text, args1=None, args2=None):
config.args2 = None
config.output_path_spreadsheet = output_path
if output_path is not None:
- config.output_path_html = output_path[:output_path.rfind('.')] + '.html'
+ config.output_path_html = output_path[: output_path.rfind(".")] + ".html"
config.objective = objective
config.processes = 1
@@ -87,4 +91,3 @@ def verbose_f(text, args1=None, args2=None):
_, filename1 = ntpath.split(config.output_path_html)
_, filename2 = ntpath.split(config.output_path_spreadsheet)
return (filename1, filename2)
-
diff --git a/src/restapi/validation.py b/src/restapi/validation.py
index 276df91..43daa11 100644
--- a/src/restapi/validation.py
+++ b/src/restapi/validation.py
@@ -20,9 +20,9 @@
from .exceptions import *
import re
+
def allowed_file(filename):
- return '.' in filename and \
- filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
+ return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
def sanitize_string(string):
@@ -32,4 +32,4 @@ def sanitize_string(string):
def empty_string(string):
- return string is None or string == ''
+ return string is None or string == ""
diff --git a/tests/conftest.py b/tests/conftest.py
index cc03184..b047492 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,11 +1,10 @@
-
import os, sys
import pytest
import os
from os.path import join, dirname
from dotenv import load_dotenv
-DOTENV_FILENAME = '.test.env'
+DOTENV_FILENAME = ".test.env"
sys.path.append("..")
@@ -18,9 +17,10 @@
from src.restapi.app import app as _app
from src.restapi.database import db as _db
+
@pytest.fixture
def client():
- _app.config.from_object('src.restapi.config.TestingConfig')
+ _app.config.from_object("src.restapi.config.TestingConfig")
with _app.test_client() as client:
_db.init_app(_app)
@@ -30,4 +30,3 @@ def client():
with _app.app_context():
_db.session.remove()
_db.drop_all()
-
diff --git a/tests/test_ops.py b/tests/test_ops.py
index 63983b3..7b20284 100644
--- a/tests/test_ops.py
+++ b/tests/test_ops.py
@@ -7,25 +7,22 @@
def __read_file(path):
- with open(path, 'r') as file:
- data = file.read().replace('\n', '')
+ with open(path, "r") as file:
+ data = file.read().replace("\n", "")
return data
def test_websocket_endpoints(client):
- assert client.get('/websockets/get_endpoint').status_code == 200
- assert client.get('/websockets/example_event_join').status_code == 200
- assert client.get('/websockets/example_event_message').status_code == 200
+ assert client.get("/websockets/get_endpoint").status_code == 200
+ assert client.get("/websockets/example_event_join").status_code == 200
+ assert client.get("/websockets/example_event_message").status_code == 200
@pytest.mark.parametrize("filename", [DATA_MODEL_FILE])
def test_submit(client, filename):
- data = {'file': (io.BytesIO(__read_file(filename).encode()), "model.xml")}
+ data = {"file": (io.BytesIO(__read_file(filename).encode()), "model.xml")}
result = client.post(
- '/submit',
- data=data,
- follow_redirects=True,
- content_type='multipart/form-data'
+ "/submit", data=data, follow_redirects=True, content_type="multipart/form-data"
)
assert result.status_code == 200
@@ -35,32 +32,32 @@ def test_submit(client, filename):
@pytest.mark.parametrize("filename", [DATA_MODEL_FILE])
@pytest.mark.skip(reason="Error running celery on github action")
def test_critical_reactions(client, filename):
- data = {'file': (io.BytesIO(__read_file(filename).encode()), "model.xml")}
+ data = {"file": (io.BytesIO(__read_file(filename).encode()), "model.xml")}
result = client.post(
- '/submit',
- data=data,
- follow_redirects=True,
- content_type='multipart/form-data'
+ "/submit", data=data, follow_redirects=True, content_type="multipart/form-data"
)
assert result.status_code == 200
data = json.loads(result.get_data(as_text=True))
- assert client.get(f"/results/{data['model_uuid']}/critical_reactions").status_code == 200
+ assert (
+ client.get(f"/results/{data['model_uuid']}/critical_reactions").status_code
+ == 200
+ )
+
# TODO: fix celery execution on github action
@pytest.mark.parametrize("filename", [DATA_MODEL_FILE])
@pytest.mark.skip(reason="Error running celery on github action")
def test_growth_dependent(client, filename):
- data = {'file': (io.BytesIO(__read_file(filename).encode()), "model.xml")}
+ data = {"file": (io.BytesIO(__read_file(filename).encode()), "model.xml")}
result = client.post(
- '/submit',
- data=data,
- follow_redirects=True,
- content_type='multipart/form-data'
+ "/submit", data=data, follow_redirects=True, content_type="multipart/form-data"
)
assert result.status_code == 200
data = json.loads(result.get_data(as_text=True))
- assert client.get(f"/results/{data['model_uuid']}/critical_reactions").status_code == 200
+ assert (
+ client.get(f"/results/{data['model_uuid']}/critical_reactions").status_code
+ == 200
+ )
print(client.get(f"/results/{data['model_uuid']}/critical_reactions").get_data())
-
diff --git a/tests/test_tasks.py b/tests/test_tasks.py
index 2c74b98..e779ad6 100644
--- a/tests/test_tasks.py
+++ b/tests/test_tasks.py
@@ -4,10 +4,14 @@
from flask import g, session
sys.path.append("..")
-from src.restapi.tasks.tasks import task_compute_critical_reactions, task_compute_growth_dependent_reactions
+from src.restapi.tasks.tasks import (
+ task_compute_critical_reactions,
+ task_compute_growth_dependent_reactions,
+)
DATA_MODEL_FILE = "tests/data/MODEL1507180056_url.xml"
+
@pytest.mark.parametrize("filename", [DATA_MODEL_FILE])
def test_compute_critical_reactions(client, filename):
task_compute_critical_reactions(
@@ -15,13 +19,12 @@ def test_compute_critical_reactions(client, filename):
"output_path.xls",
objective=None,
fraction_of_optimum=None,
- model_uuid=None)
+ model_uuid=None,
+ )
@pytest.mark.parametrize("filename", [DATA_MODEL_FILE])
def test_compute_growth_dependent_reactions(client, filename):
task_compute_growth_dependent_reactions(
- filename,
- "output_path.xls",
- objective=None,
- model_uuid=None)
\ No newline at end of file
+ filename, "output_path.xls", objective=None, model_uuid=None
+ )