Commit ea464118 authored by David Mendez's avatar David Mendez
Browse files

Remove dependency of flask_restx

parent a7843bb0
......@@ -52,25 +52,6 @@ def create_app():
flask_app.register_blueprint(SWAGGER_BLUEPRINT, url_prefix=f'{base_path}/swagger')
flask_app.register_blueprint(SUBMISSION_BLUEPRINT, url_prefix=f'{base_path}/submit')
# api = Api(
# title='ChEMBL Interface Delayed Jobs',
# version='1.0',
# description='A microservice that runs delayed jobs for the ChEMBL interface. '
# 'For example generating a .csv file from elasticsearch',
# app=blueprint,
# authorizations=authorizations,
# )
#
# flask_app.register_blueprint(blueprint)
#
# for namespace in [job_admin_namespace, job_status_namespace, submit_test_job_namespace,
# submit_similarity_search_namespace, submit_substructure_search_namespace,
# submit_connectivity_search_namespace, submit_blast_search_namespace, record_search_namespace,
# record_download_namespace, submit_download_namespace]:
# api.add_namespace(namespace)
return flask_app
if __name__ == '__main__':
......
"""
Module that describes and handles the requests concerned with the blast search submission
"""
from flask import request
from flask_restx import Namespace, Resource, fields
from app.models import delayed_job_models
from app.namespaces.job_submission.services import job_submission_service
from app.namespaces.job_submission.shared_marshalls import BASE_SUBMISSION_RESPONSE
API = Namespace('submit/blast', description='Namespace to submit a BLAST job')
BLAST_JOB = API.model('BLASTJob', {
'matrix': fields.String(description='(Protein searches) The substitution matrix used for scoring alignments when '
'searching the database.', example='BLOSUM45',
enum=['BLOSUM45', 'BLOSUM50', 'BLOSUM62', 'BLOSUM80', 'BLOSUM90', 'PAM30', 'PAM70',
'PAM250', 'NONE']),
'alignments': fields.Integer(description='Maximum number of match alignments reported in the result output.',
example=50, enum=[5, 10, 20, 50, 100, 150, 200, 500, 750, 1000], min=5, max=1000),
'scores': fields.Integer(description='Maximum number of match score summaries reported in the result output.',
example=50, enum=[5, 10, 20, 50, 100, 150, 200, 500, 750, 1000], min=5, max=1000),
'exp': fields.String(description='Limits the number of scores and alignments reported based on the expectation '
'value. This is the maximum number of times the match is expected to occur by '
'chance.', example='10', enum=['1e-200', '1e-100', '1e-50', '1e-10', '1e-5',
'1e-4', '1e-3', '1e-2', '1e-1', '1.0', '10', '100',
'1000']),
'dropoff': fields.Integer(description='The amount a score can drop before gapped extension of word hits is halted',
example=0, min=0, max=10),
'gapopen': fields.Integer(description='Penalty taken away from the score when a gap is created in sequence. '
'Increasing the gap openning penalty will decrease the number of gaps in the '
'final alignment.',
example=-1, min=-1, max=25),
'gapext': fields.Integer(description='Penalty taken away from the score for each base or residue in the gap. '
'Increasing the gap extension penalty favors short gaps in the final alignment'
', conversly decreasing the gap extension penalty favors long gaps in the '
'final alignment.',
example=-1, min=-1, max=10),
'filter': fields.String(description='Filter regions of low sequence complexity. This can avoid issues with low '
'complexity sequences where matches are found due to composition rather than '
'meaningful sequence similarity. However in some cases filtering also masks '
'regions of interest and so should be used with caution.', example='F',
enum=['F', 'T']),
'seqrange': fields.String(description='Specify a range or section of the input sequence to use in the search. '
'Example: Specifying "34-89" in an input sequence of total length 100, will '
'tell BLAST to only use residues 34 to 89, inclusive.',
example='START-END'),
'gapalign': fields.String(description='Filter regions of low sequence complexity. This can avoid issues with low ',
example='true',
enum=['true', 'false']),
'wordsize': fields.Integer(description='Word size for wordfinder algorithm',
example=6, min=6, max=28),
'taxids': fields.String(description='Specify one or more TaxIDs so that the BLAST search becomes taxonomically '
'aware.', example=''),
'compstats': fields.String(description='se composition-based statistics.',
example='F', enum=['F', 'D', '1', '2', '3']),
'align': fields.Integer(description='Formatting for the alignments',
example=0, min=0, max=12),
'sequence': fields.String(description='The query sequence can be entered directly into this form. The sequence '
'can be in GCG, FASTA, EMBL (Nucleotide only), GenBank, PIR, NBRF, PHYLIP '
'or UniProtKB/Swiss-Prot (Protein only) format. A partially formatted '
'sequence is not accepted. Adding a return to the end of the sequence may '
'help certain applications understand the input. Note that directly using '
'data from word processors may yield unpredictable results as hidden/control '
'characters may be present.',
example='>sp|P35858|ALS_HUMAN Insulin-like growth factor-binding protein complex acid '
'labile subunit OS=Homo sapiens GN=IGFALS PE=1 SV=1\n'
'MALRKGGLALALLLLSWVALGPRSLEGADPGTPGEAEGPACPAACVCSYDDDADELSVFC\n'
'SSRNLTRLPDGVPGGTQALWLDGNNLSSVPPAAFQNLSSLGFLNLQGGQLGSLEPQALLG\n'
'LENLCHLHLERNQLRSLALGTFAHTPALASLGLSNNRLSRLEDGLFEGLGSLWDLNLGWN\n'
'SLAVLPDAAFRGLGSLRELVLAGNRLAYLQPALFSGLAELRELDLSRNALRAIKANVFVQ\n'
'LPRLQKLYLDRNLIAAVAPGAFLGLKALRWLDLSHNRVAGLLEDTFPGLLGLRVLRLSHN\n'
'AIASLRPRTFKDLHFLEELQLGHNRIRQLAERSFEGLGQLEVLTLDHNQLQEVKAGAFLG\n'
'LTNVAVMNLSGNCLRNLPEQVFRGLGKLHSLHLEGSCLGRIRPHTFTGLSGLRRLFLKDN\n'
'GLVGIEEQSLWGLAELLELDLTSNQLTHLPHRLFQGLGKLEYLLLSRNRLAELPADALGP\n'
'LQRAFWLDVSHNRLEALPNSLLAPLGRLRYLSLRNNSLRTFTPQPPGLERLWLEGNPWDC\n'
'GCPLKALRDFALQNPSAVPRFVQAICEGDDCQPPAYTYNNITCASPPEVVGLDLRDLSEA\n'
'HFAPC'),
})
SUBMISSION_RESPONSE = API.inherit('SubmissionResponse', BASE_SUBMISSION_RESPONSE)
@API.route('/')
class SubmitBLASTJob(Resource):
"""
Resource that handles BLAST search job submission requests
"""
job_type = delayed_job_models.JobTypes.BLAST
@API.expect(BLAST_JOB)
@API.doc(body=BLAST_JOB)
@API.marshal_with(BASE_SUBMISSION_RESPONSE)
def post(self): # pylint: disable=no-self-use
"""
Submits a job to the queue.
:return: a json response with the result of the submission
"""
json_data = request.json
job_params = {
**json_data,
'search_type': str(self.job_type),
}
response = job_submission_service.submit_job(self.job_type, job_params)
return response
"""
Module that describes and handles the requests concerned with the connectivity search
"""
from flask import request
from flask_restx import Namespace, Resource, fields
from app.models import delayed_job_models
from app.namespaces.job_submission.services import job_submission_service
from app.namespaces.job_submission.shared_marshalls import BASE_SUBMISSION_RESPONSE
API = Namespace('submit/connectivity', description='Namespace to submit a connectivity job')
CONNECTIVITY_JOB = API.model('ConnectivityJob', {
'structure': fields.String(description='The structure (SMILES) you want to search against',
required=True,
example='CC(=O)Oc1ccccc1C(=O)O'),
})
SUBMISSION_RESPONSE = API.inherit('SubmissionResponse', BASE_SUBMISSION_RESPONSE)
@API.route('/')
class SubmitConnectivityJob(Resource):
"""
Resource that handles connectivity search job submission requests
"""
job_type = delayed_job_models.JobTypes.CONNECTIVITY
@API.expect(CONNECTIVITY_JOB)
@API.doc(body=CONNECTIVITY_JOB)
@API.marshal_with(BASE_SUBMISSION_RESPONSE)
def post(self): # pylint: disable=no-self-use
"""
Submits a job to the queue.
:return: a json response with the result of the submission
"""
json_data = request.json
job_params = {
**json_data,
'search_type': str(self.job_type),
}
response = job_submission_service.submit_job(self.job_type, job_params)
return response
"""
Module that describes and handles the requests concerned with the downloads
"""
from flask import request
from flask_restx import Namespace, Resource, fields
from app.models import delayed_job_models
from app.namespaces.job_submission.services import job_submission_service
from app.namespaces.job_submission.shared_marshalls import BASE_SUBMISSION_RESPONSE
API = Namespace('submit/download', description='Namespace to submit a download job')
DOWNLOAD_JOB = API.model('DownloadJob', {
'index_name': fields.String(description='The index against you want to make the download',
required=True, example='chembl_molecule'),
'query': fields.String(description='Query to execute against the index to get the items',
required=True, example='{"bool":{"must":[{"query_string":'
'{"analyze_wildcard":true,"query":"*"}}],"filter":[[{"bool":'
'{"should":[{"term":{"molecule_type":"Antibody"}}]}}]]}}'),
'format': fields.String(description='Format of the download (SDF is available for the chembl_molecule index only)',
required=True, example='SVG', enum=['CSV', 'TSV', 'SDF']),
'download_columns_group': fields.String(description='Group that summarises and describes the columns that you want '
'to download. See for example: https://www.ebi.ac.uk/chembl/'
'glados_api/shared/properties_configuration/group/'
'chembl_molecule/download/', example='download'),
'context_id': fields.String(description='Id of the results of another job to join to the download. '
'Useful to join structure or sequence search results.'),
})
SUBMISSION_RESPONSE = API.inherit('SubmissionResponse', BASE_SUBMISSION_RESPONSE)
@API.route('/')
class SubmitConnectivityJob(Resource):
"""
Resource that handles download job submission requests
"""
job_type = delayed_job_models.JobTypes.DOWNLOAD
@API.expect(DOWNLOAD_JOB)
@API.doc(body=DOWNLOAD_JOB)
@API.marshal_with(SUBMISSION_RESPONSE)
def post(self): # pylint: disable=no-self-use
"""
Submits a job to the queue.
:return: a json response with the result of the submission
"""
json_data = request.json
response = job_submission_service.submit_job(self.job_type, json_data)
return response
"""
Module that describes and handles the requests concerned with the similarity search
"""
from flask import request
from flask_restx import Namespace, Resource, fields
from app.models import delayed_job_models
from app.namespaces.job_submission.services import job_submission_service
from app.namespaces.job_submission.shared_marshalls import BASE_SUBMISSION_RESPONSE
API = Namespace('submit/similarity', description='Namespace to submit a similarity job')
SIMILARITY_JOB = API.model('SimilarityJob', {
'structure': fields.String(description='The structure (SMILES) you want to search against',
required=True,
example='[H]C1(CCCN1C(=N)N)CC1=NC(=NO1)C1C=CC(=CC=1)NC1=NC(=CS1)C1C=CC(Br)=CC=1'),
'threshold': fields.Integer(description='The threshold for the similarity search',
required=True,
min=70,
max=100,
example=90)
})
SUBMISSION_RESPONSE = API.inherit('SubmissionResponse', BASE_SUBMISSION_RESPONSE)
@API.route('/')
class SubmitSimilarityJob(Resource):
"""
Resource that handles similarity search job submission requests
"""
job_type = delayed_job_models.JobTypes.SIMILARITY
@API.expect(SIMILARITY_JOB)
@API.doc(body=SIMILARITY_JOB)
@API.marshal_with(BASE_SUBMISSION_RESPONSE)
def post(self): # pylint: disable=no-self-use
"""
Submits a job to the queue.
:return: a json response with the result of the submission
"""
json_data = request.json
job_params = {
**json_data,
'search_type': str(self.job_type),
}
response = job_submission_service.submit_job(self.job_type, job_params)
return response
"""
Module that describes and handles the requests concerned with the substructure search
"""
from flask import request
from flask_restx import Namespace, Resource, fields
from app.models import delayed_job_models
from app.namespaces.job_submission.services import job_submission_service
from app.namespaces.job_submission.shared_marshalls import BASE_SUBMISSION_RESPONSE
API = Namespace('submit/substructure', description='Namespace to submit a substructure job')
SUBSTRUCTURE_JOB = API.model('SubstructureJob', {
'structure': fields.String(description='The structure (SMILES) you want to search against',
required=True,
example='CC(=O)Oc1ccccc1C(=O)O'),
})
SUBMISSION_RESPONSE = API.inherit('SubmissionResponse', BASE_SUBMISSION_RESPONSE)
@API.route('/')
class SubmitConnectivityJob(Resource):
"""
Resource that handles substructure search job submission requests
"""
job_type = delayed_job_models.JobTypes.SUBSTRUCTURE
@API.expect(SUBSTRUCTURE_JOB)
@API.doc(body=SUBSTRUCTURE_JOB)
@API.marshal_with(BASE_SUBMISSION_RESPONSE)
def post(self): # pylint: disable=no-self-use
"""
Submits a job to the queue.
:return: a json response with the result of the submission
"""
json_data = request.json
job_params = {
**json_data,
'search_type': str(self.job_type),
}
response = job_submission_service.submit_job(self.job_type, job_params)
return response
"""
Module that describes and handles the requests to submit a test job
"""
import werkzeug
from flask_restx import Resource, Namespace, reqparse
from app.models import delayed_job_models
from app.namespaces.job_submission.services import job_submission_service
from app.namespaces.job_submission.shared_marshalls import BASE_SUBMISSION_RESPONSE
API = Namespace('submit/test_job', description='Namespace to submit a test job')
TEST_JOB_PARSER = reqparse.RequestParser()
TEST_JOB_PARSER.add_argument('input1',
type=werkzeug.datastructures.FileStorage,
location='files',
required=True,
help='Input File 1')
TEST_JOB_PARSER.add_argument('input2',
type=werkzeug.datastructures.FileStorage,
location='files',
required=True,
help='Input File 2')
TEST_JOB_PARSER.add_argument('instruction',
choices=('RUN_NORMALLY', 'FAIL'),
required=True,
help='How do you want the job to behave')
TEST_JOB_PARSER.add_argument('seconds',
choices=tuple(i for i in range(1, 301)),
type=int,
required=True,
help='How many seconds you want the job to run for')
TEST_JOB_PARSER.add_argument('api_url',
required=True,
help='The url on an API to test a call to in the job',
default='https://www.ebi.ac.uk/chembl/api/data/similarity/CN1C(=O)C=C(c2cccc(Cl)c2)c3cc(ccc13)[C@@](N)(c4ccc(Cl)cc4)c5cncn5C/80.json')
SUBMISSION_RESPONSE = API.inherit('SubmissionResponse', BASE_SUBMISSION_RESPONSE)
@API.route('/')
class SubmitTestJob(Resource):
"""
Resource that handles test job submission requests
"""
job_type = delayed_job_models.JobTypes.TEST
docker_image_url = 'docker://dockerhub.ebi.ac.uk/chembl/chembl/delayed-jobs/test-job:latest'
@API.expect(TEST_JOB_PARSER)
# @API.doc(body=TEST_JOB)
@API.marshal_with(BASE_SUBMISSION_RESPONSE)
def post(self): # pylint: disable=no-self-use
"""
Submits a job to the queue.
:return: a json response with the result of the submission
"""
args = TEST_JOB_PARSER.parse_args()
response = job_submission_service.parse_args_and_submit_job(self.job_type, args, self.docker_image_url)
return response
......@@ -10,7 +10,6 @@ import stat
import subprocess
from pathlib import Path
import werkzeug
import yaml
import app.app_logging as app_logging
......
......@@ -138,7 +138,7 @@ class TestJobSubmitter(unittest.TestCase):
job_id_got = params_got.get('job_id')
self.assertEqual(job_id_must_be, job_id_got, msg='The job id was not generated correctly')
status_update_url_must_be = f'http://127.0.0.1:5000/status/{job_id}'
status_update_url_must_be = f'http://0.0.0.0:5000/status/{job_id}'
status_update_url_got = params_got.get('status_update_endpoint').get('url')
self.assertEqual(status_update_url_must_be, status_update_url_got,
msg='The status update url was not set correctly!')
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment