Mercurial > repos > itaxotools > mold
view api.py @ 0:4e8e2f836d0f draft default tip
planemo upload commit 232ce39054ce38be27c436a4cabec2800e14f988-dirty
author | itaxotools |
---|---|
date | Sun, 29 Jan 2023 16:25:48 +0000 |
parents | |
children |
line wrap: on
line source
#!/usr/bin/env python # -*- coding: utf-8 -*- from flask import Flask, make_response, request, current_app, send_file, url_for from flask_restful import Resource, Api, reqparse, abort from flask.json import jsonify from flasgger import Swagger from flask_cors import CORS, cross_origin from flask_restful.utils import cors import logging import base64 import uuid import tempfile import os import html2text # for emails import smtplib from email.message import EmailMessage from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.image import MIMEImage from celery import Celery from celery.schedules import crontab import json #from MolD_sDNC import SortedDisplayDict #from MolD_sDNC import Step1 #from MolD_sDNC import C_VP_PP #from MolD_sDNC import random_position #from MolD_sDNC import step_reduction_complist #from MolD_sDNC import ConditionD #from MolD_sDNC import RemoveRedundantPositions #from MolD_sDNC import Diagnostic_combinations #from MolD_sDNC import IndependentKey #from MolD_sDNC import random_sequence2 #from MolD_sDNC import GenerateBarcode2 #from MolD_sDNC import Screwed_dataset31 from MolD_sDNCFASTA import mainprocessing logging.basicConfig(format='%(levelname)s: %(asctime)s - %(message)s', level=logging.DEBUG, datefmt='%d.%m.%Y %I:%M:%S %p') app = Flask(__name__) app.config.from_envvar('APPSETTINGS') API_VERSION = app.config.get('API_VERSION', 1) cors = CORS(app, resources={r"/*": {"origins": "*"}}, methods=['GET', 'POST', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']) api = Api(app, prefix=f"/api/v{API_VERSION}") gunicorn_logger = logging.getLogger('gunicorn.error') app.logger.handlers = gunicorn_logger.handlers app.logger.setLevel(gunicorn_logger.level) REDIS_HOST = app.config.get('REDIS_HOST', 'localhost') REDIS_PORT = app.config.get('REDIS_PORT', 6379) REDIS_DB = app.config.get('REDIS_DB', 0) MAILUSER = app.config.get('MAILUSER') MAILPASS = app.config.get('MAILPASS') app.config['SWAGGER'] = { 'uiversion': 3 } swtemplate = { "info": { "title": "MoID API", "description": "MoID API is the online version of a tree independent algorithm to retrieve diagnostic nucleotide characters from monolocus datasets. BioRxiv. DOI: 10.1101/838151", "version": f"{API_VERSION}", }, "schemes": [ "https" ] } swagger_config = { "headers": [ ], "specs": [ { "endpoint": 'apidescr', "route": '/apidescr.json', "rule_filter": lambda rule: True, "model_filter": lambda tag: True, } ], "static_url_path": "/flasgger_static", "swagger_ui": True, "specs_route": "/docs/", 'uiversion': 3 } swagger = Swagger(app, config=swagger_config, template=swtemplate) SEND_EMAILS = True app.config['CELERY_BROKER_URL'] = 'redis://{}:{}/{}'.format(REDIS_HOST, REDIS_PORT, REDIS_DB) app.config['CELERY_RESULT_BACKEND'] = 'redis://{}:{}/{}'.format(REDIS_HOST, REDIS_PORT, REDIS_DB) def make_celery(app): celery = Celery(app.import_name, broker=app.config['CELERY_BROKER_URL']) celery.conf.update(app.config) TaskBase = celery.Task class ContextTask(TaskBase): abstract = True def __call__(self, *args, **kwargs): with app.app_context(): return TaskBase.__call__(self, *args, **kwargs) celery.Task = ContextTask return celery celery = make_celery(app) #@celery.on_after_configure.connect #def setup_periodic_tasks(sender, **kwargs): # sender.add_periodic_task( # crontab(minute=0, hour='*/3'), # check_pending_notifications.s(), # ) @celery.task def send_email_notification(email, status, parameters, taxalist, orig_filename): print("Sending email") sender = MAILUSER taxa = ", ".join(taxalist) print(taxalist) taxa1 = "-".join([t.replace(" ", "_") for t in taxalist]) msg = MIMEMultipart('related') msg['Subject'] = f'MolD results: {taxa}' msg['From'] = sender msg['To'] = email # TODO: add three txt files: # email_body = """\ <html> <head></head> <body> Results: {} *************** <p>Citation: <b>Fedosov A.E.</b>, Achaz G., Puillandre N. 2019. Revisiting use of DNA characters in taxonomy with MolD – a tree independent algorithm to retrieve diagnostic nucleotide characters from monolocus datasets. BioRxiv, published online on 11.11.2019. DOI: 10.1101/838151 </p> </body> </html> """.format(status) plain_text_results = html2text.html2text(status) message_text = MIMEText(email_body, 'html') msg.attach(message_text) text_attachment = f""" Results: {plain_text_results} *************** Citation: Fedosov A.E., Achaz G., Puillandre N. 2019. Revisiting use of DNA characters in taxonomy with MolD – a tree independent algorithm to retrieve diagnostic nucleotide characters from monolocus datasets. BioRxiv, published online on 11.11.2019. DOI: 10.1101/838151 """ mail_attachment = MIMEText(text_attachment) fname = f"{orig_filename}.results.txt" mail_attachment.add_header('Content-Disposition', 'attachment', filename=fname) msg.attach(mail_attachment) print("mail ready to be sent") s = smtplib.SMTP('smtp.gmail.com', 587) s.ehlo() s.starttls() s.login(MAILUSER, MAILPASS) s.sendmail(sender, email, msg.as_string()) s.quit() print("mail sent") @celery.task def process_data(email, gapsaschars, taxalist, taxonrank, cutoff, numnucl, numiter, maxlenraw, maxlenrefined, pdiff, nmax, thresh, tmpfname, orig_fname): print("Processing data") results, qclades = mainprocessing(gapsaschars, taxalist, taxonrank, cutoff, numnucl, numiter, maxlenraw, maxlenrefined, pdiff, nmax, thresh, tmpfname, orig_fname) parameters = f""" List of focus taxa: {taxalist} Taxon rank: {taxonrank} Cutoff: {cutoff} NNNNN...: {numnucl} Num iterations: {numiter} Max length for the raw mDNCs: {maxlenraw} Max length for the refined mDNCs: {maxlenrefined} Pdiff: {pdiff} NMaxSeq: {nmax} Threshold of rDNC rating: {thresh} """ send_email_notification.delay(email, results, parameters, qclades, orig_fname) @app.errorhandler(404) def not_found(error): return make_response(jsonify({'error': 'Not found'}), 404) class DataAPI(Resource): def __init__(self): self.method_decorators = [] def options(self, *args, **kwargs): return jsonify([]) #@token_required @cross_origin() def post(self): """ POST data to analyse --- parameters: - in: formData name: file type: file required: true description: Data file - in: formData name: tags type: array required: false description: A list of image tags to link with the image items: type: string description: Tag text - in: formData name: width type: integer required: false description: Image width - in: formData name: height type: integer required: false description: Image height responses: 200: description: Data processing 400: description: Bad request """ app.logger.debug(request.files) app.logger.debug(request.values) email = request.values.get('email', None) gapsaschars = request.values.get('gapsaschars', "no") taxalist = request.values.get('taxalist', "ALL") taxalist = taxalist.replace(" ", '') taxonrank = int(request.values.get('taxonrank', 2)) cutoff = request.values.get('cutoff', ">0") numnucl = int(request.values.get('numnucl', 5)) numiter = int(request.values.get('numites', 10000)) maxlenraw = int(request.values.get('maxlenraw', 12)) maxlenrefined = int(request.values.get('maxlenrefined', 7)) pdiff = int(request.values.get('pdiff', 1)) #prseq = float(request.values.get('prseq', 0.1)) nmax = int(request.values.get('nmax', 20)) thresh = int(request.values.get('thresh', 85)) if not all([email, gapsaschars, taxalist, taxonrank, cutoff, numnucl, numiter, maxlenraw, maxlenrefined, pdiff, nmax, thresh]): return make_response(jsonify({'error': 'Parameter missing'}), 400) if not request.files: return make_response(jsonify({'error': 'No file provided'}), 400) data_file = [request.files.get(f) for f in request.files][-1] thumb_height = request.values.get('height', None) tmpdname = "/tmp" tmpfname = str(uuid.uuid4()) tmp_upl_file = os.path.join(tmpdname, tmpfname) data_file.save(tmp_upl_file) app.logger.debug(tmp_upl_file) process_data.delay(email, gapsaschars, taxalist, taxonrank, cutoff, numnucl, numiter, maxlenraw, maxlenrefined, pdiff, nmax, thresh, tmp_upl_file, data_file.filename) return jsonify("OK"), 200 api.add_resource(DataAPI, '/data', endpoint='processdata') if __name__ == '__main__': app.debug = True app.run(host='0.0.0.0')