views.py 24.9 KB
Newer Older
1
from dgenies import app, app_title, app_folder, config_reader, mailer, APP_DATA, MODE
2

Floreal Cabanettes's avatar
Floreal Cabanettes committed
3
import os
4
5
import time
import datetime
6
import shutil
7
import re
8
import threading
Floreal Cabanettes's avatar
Floreal Cabanettes committed
9
from flask import render_template, request, url_for, jsonify, Response, abort, send_file, Markup
10
from pathlib import Path
11
12
13
14
15
from dgenies.lib.paf import Paf
from dgenies.lib.job_manager import JobManager
from dgenies.lib.functions import Functions, ALLOWED_EXTENSIONS
from dgenies.lib.upload_file import UploadFile
from dgenies.lib.fasta import Fasta
Floreal Cabanettes's avatar
Floreal Cabanettes committed
16
17
from markdown import Markdown
from markdown.extensions.toc import TocExtension
18
19
20
if MODE == "webserver":
    from dgenies.database import Session, Gallery
    from peewee import DoesNotExist
21

22

Floreal Cabanettes's avatar
Floreal Cabanettes committed
23
24
25
26
27
28
29
30
@app.context_processor
def global_templates_variables():
    return {
        "title": app_title,
        "mode": MODE
    }


31
32
33
# Main
@app.route("/", methods=['GET'])
def main():
34
35
36
37
38
39
    if MODE == "webserver":
        pict = Gallery.select().order_by("id")
        if len(pict) > 0:
            pict = pict[0].picture
        else:
            pict = None
Floreal Cabanettes's avatar
Floreal Cabanettes committed
40
41
    else:
        pict = None
Floreal Cabanettes's avatar
Floreal Cabanettes committed
42
    return render_template("index.html", menu="index", pict=pict)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
43
44
45
46


@app.route("/run", methods=['GET'])
def run():
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    if MODE == "webserver":
        with Session.connect():
            s_id = Session.new()
    else:
        upload_folder = Functions.random_string(20)
        tmp_dir = config_reader.upload_folder
        upload_folder_path = os.path.join(tmp_dir, upload_folder)
        while os.path.exists(upload_folder_path):
            upload_folder = Functions.random_string(20)
            upload_folder_path = os.path.join(tmp_dir, upload_folder)
        s_id = upload_folder
    id_job = Functions.random_string(5) + "_" + datetime.datetime.fromtimestamp(time.time()).strftime('%Y%m%d%H%M%S')
    if "id_job" in request.args:
        id_job = request.args["id_job"]
    email = ""
    if "email" in request.args:
        email = request.args["email"]
Floreal Cabanettes's avatar
Floreal Cabanettes committed
64
    return render_template("run.html", id_job=id_job, email=email,
65
                           menu="run", allowed_ext=ALLOWED_EXTENSIONS, s_id=s_id,
Floreal Cabanettes's avatar
Floreal Cabanettes committed
66
                           max_upload_file_size=config_reader.max_upload_file_size)
67
68


69
70
@app.route("/run-test", methods=['GET'])
def run_test():
71
72
73
74
75
76
77
    if MODE == "webserver":
        print(config_reader.allowed_ip_tests)
        if request.remote_addr not in config_reader.allowed_ip_tests:
            return abort(404)
        with Session.connect():
            return Session.new()
    return abort(500)
78
79


80
81
82
# Launch analysis
@app.route("/launch_analysis", methods=['POST'])
def launch_analysis():
83
84
85
86
87
88
89
90
91
92
93
94
    if MODE == "webserver":
        try:
            with Session.connect():
                session = Session.get(s_id=request.form["s_id"])
        except DoesNotExist:
            return jsonify({"success": False, "errors": ["Session has expired. Please refresh the page and try again"]})
        upload_folder = session.upload_folder
        # Delete session:
        session.delete_instance()
    else:
        upload_folder = request.form["s_id"]

95
96
    id_job = request.form["id_job"]
    email = request.form["email"]
97
98
99
100
    file_query = request.form["query"]
    file_query_type = request.form["query_type"]
    file_target = request.form["target"]
    file_target_type = request.form["target_type"]
101
102
103

    # Check form:
    form_pass = True
104
    errors = []
105
    if id_job == "":
106
        errors.append("Id of job not given")
107
108
        form_pass = False

109
    if email == "" and MODE == "webserver":
110
        errors.append("Email not given")
111
        form_pass = False
112
113
    if file_target == "":
        errors.append("No target fasta selected")
114
115
116
117
        form_pass = False

    # Form pass
    if form_pass:
118
        # Get final job id:
119
        id_job = re.sub('[^A-Za-z0-9_\-]+', '', id_job.replace(" ", "_"))
120
        id_job_orig = id_job
121
        i = 2
122
        while os.path.exists(os.path.join(APP_DATA, id_job)):
123
124
            id_job = id_job_orig + ("_%d" % i)
            i += 1
125

126
        folder_files = os.path.join(APP_DATA, id_job)
127
128
129
        os.makedirs(folder_files)

        # Save files:
130
131
132
        query = None
        if file_query != "":
            query_name = os.path.splitext(file_query.replace(".gz", ""))[0] if file_query_type == "local" else None
133
            query_path = os.path.join(app.config["UPLOAD_FOLDER"], upload_folder, file_query) \
134
135
136
                if file_query_type == "local" else file_query
            query = Fasta(name=query_name, path=query_path, type_f=file_query_type)
        target_name = os.path.splitext(file_target.replace(".gz", ""))[0] if file_target_type == "local" else None
137
        target_path = os.path.join(app.config["UPLOAD_FOLDER"], upload_folder, file_target) \
138
139
            if file_target_type == "local" else file_target
        target = Fasta(name=target_name, path=target_path, type_f=file_target_type)
140
141

        # Launch job:
Floreal Cabanettes's avatar
Floreal Cabanettes committed
142
        job = JobManager(id_job, email, query, target, mailer)
143
144
145
146
        if MODE == "webserver":
            job.launch()
        else:
            job.launch_standalone()
147
        return jsonify({"success": True, "redirect": url_for(".status", id_job=id_job)})
148
    else:
149
        return jsonify({"success": False, "errors": errors})
150
151
152
153
154
155


# Status of a job
@app.route('/status/<id_job>', methods=['GET'])
def status(id_job):
    job = JobManager(id_job)
156
157
158
159
160
161
162
163
164
165
166
167
    j_status = job.status()
    mem_peak = j_status["mem_peak"] if "mem_peak" in j_status else None
    if mem_peak is not None:
        mem_peak = "%.1f G" % (mem_peak / 1024.0 / 1024.0)
    time_e = j_status["time_elapsed"] if "time_elapsed" in j_status else None
    if time_e is not None:
        if time_e < 60:
            time_e = "%d secs" % time_e
        else:
            minutes = time_e // 60
            seconds = time_e - minutes * 60
            time_e = "%d min %d secs" % (minutes, seconds)
168
169
170
171
172
173
174
175
176
    format = request.args.get("format")
    if format is not None and format == "json":
        return jsonify({
            "status": j_status["status"],
            "error": j_status["error"].replace("#ID#", ""),
            "id_job": id_job,
            "mem_peak": mem_peak,
            "time_elapsed": time_e
        })
Floreal Cabanettes's avatar
Floreal Cabanettes committed
177
    return render_template("status.html", status=j_status["status"],
178
179
                           error=j_status["error"].replace("#ID#", ""),
                           id_job=id_job, menu="results", mem_peak=mem_peak,
180
                           time_elapsed=time_e,
Floreal Cabanettes's avatar
Floreal Cabanettes committed
181
                           query_filtered=job.is_query_filtered(), target_filtered=job.is_target_filtered())
182
183


Floreal Cabanettes's avatar
Floreal Cabanettes committed
184
# Results path
185
@app.route("/result/<id_res>", methods=['GET'])
186
def result(id_res):
Floreal Cabanettes's avatar
Floreal Cabanettes committed
187
    return render_template("result.html", id=id_res, menu="result", current_result=id_res,
188
                           is_gallery=Functions.is_in_gallery(id_res, MODE))
189
190


191
192
@app.route("/gallery", methods=['GET'])
def gallery():
193
    if MODE == "webserver":
Floreal Cabanettes's avatar
Floreal Cabanettes committed
194
        return render_template("gallery.html", items=Functions.get_gallery_items(), menu="gallery")
195
    return abort(404)
196
197
198
199


@app.route("/gallery/<filename>", methods=['GET'])
def gallery_file(filename):
200
201
202
203
204
205
    if MODE == "webserver":
        try:
            return send_file(os.path.join(config_reader.app_data, "gallery", filename))
        except FileNotFoundError:
            abort(404)
    return abort(404)
206
207


208
def get_file(file, gzip=False):  # pragma: no cover
Floreal Cabanettes's avatar
Floreal Cabanettes committed
209
210
211
212
213
214
    try:
        # Figure out how flask returns static files
        # Tried:
        # - render_template
        # - send_file
        # This should not be so non-obvious
215
        return open(file, "rb" if gzip else "r").read()
216
217
    except FileNotFoundError:
        abort(404)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
218
    except IOError as exc:
219
        print(exc.__traceback__)
220
        abort(500)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
221
222


Floreal Cabanettes's avatar
Floreal Cabanettes committed
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
@app.route("/documentation/run", methods=['GET'])
def documentation_run():
    with open(os.path.join(app_folder, "doc_run.md" if MODE == "webserver" else "doc_run_standalone.md"), "r",
              encoding='utf-8') as install_instr:
        content = install_instr.read()
    md = Markdown(extensions=[TocExtension(baselevel=1)])
    max_upload_file_size = config_reader.max_upload_file_size
    if max_upload_file_size == -1:
        max_upload_file_size = "no limit"
    else:
        max_upload_file_size = Functions.get_readable_size(max_upload_file_size, 0)
    max_upload_size = config_reader.max_upload_size
    if max_upload_size == -1:
        max_upload_size = "no limit"
    else:
        max_upload_size = Functions.get_readable_size(max_upload_size, 0)
    max_upload_size_ava = config_reader.max_upload_size_ava
    if max_upload_size_ava == -1:
        max_upload_size_ava = "no limit"
    else:
        max_upload_size_ava = Functions.get_readable_size(max_upload_size_ava, 0)
    content = Markup(md.convert(content)).replace("###size###", max_upload_file_size)\
                                         .replace("###size_unc###", max_upload_size)\
                                         .replace("###size_ava###", max_upload_size_ava)
    toc = Markup(md.toc)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
248
    return render_template("documentation.html", menu="documentation", content=content, toc=toc)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
249
250


Floreal Cabanettes's avatar
Floreal Cabanettes committed
251
252
@app.route("/install", methods=['GET'])
def install():
253
    with open(os.path.join(app_folder, "INSTALL.md"), "r", encoding='utf-8') as install_instr:
Floreal Cabanettes's avatar
Floreal Cabanettes committed
254
255
256
257
        content = install_instr.read()
    md = Markdown(extensions=[TocExtension(baselevel=1)])
    content = Markup(md.convert(content))
    toc = Markup(md.toc)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
258
    return render_template("documentation.html", menu="install", content=content, toc=toc)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
259
260


Floreal Cabanettes's avatar
Floreal Cabanettes committed
261
262
263
264
265
266
267
268
269
270
271
@app.route("/paf/<id_res>", methods=['GET'])
def download_paf(id_res):
    map_file = os.path.join(APP_DATA, id_res, "map.paf.sorted")
    if not os.path.exists(map_file):
        map_file = os.path.join(APP_DATA, id_res, "map.paf")
    if not os.path.exists(map_file):
        abort(404)
    content = get_file(map_file)
    return Response(content, mimetype="text/plain")


Floreal Cabanettes's avatar
Floreal Cabanettes committed
272
# Get graph (ajax request)
273
274
275
@app.route('/get_graph', methods=['POST'])
def get_graph():
    id_f = request.form["id"]
276
277
278
    paf = os.path.join(APP_DATA, id_f, "map.paf")
    idx1 = os.path.join(APP_DATA, id_f, "query.idx")
    idx2 = os.path.join(APP_DATA, id_f, "target.idx")
279

280
    paf = Paf(paf, idx1, idx2)
281

282
283
    if paf.parsed:
        res = paf.get_d3js_data()
284
        res["success"] = True
285
286
287
288
289
290
        return jsonify(res)
    return jsonify({"success": False, "message": paf.error})


@app.route('/sort/<id_res>', methods=['POST'])
def sort_graph(id_res):
291
    if not os.path.exists(os.path.join(APP_DATA, id_res, ".all-vs-all")):
292
293
294
        paf_file = os.path.join(APP_DATA, id_res, "map.paf")
        idx1 = os.path.join(APP_DATA, id_res, "query.idx")
        idx2 = os.path.join(APP_DATA, id_res, "target.idx")
295
        paf = Paf(paf_file, idx1, idx2, False)
296
297
298
299
300
301
302
        paf.sort()
        if paf.parsed:
            res = paf.get_d3js_data()
            res["success"] = True
            return jsonify(res)
        return jsonify({"success": False, "message": paf.error})
    return jsonify({"success": False, "message": "Sort is not available for All-vs-All mode"})
Floreal Cabanettes's avatar
Floreal Cabanettes committed
303

Floréal Cabanettes's avatar
Floréal Cabanettes committed
304

305
306
307
308
@app.route('/reverse-contig/<id_res>', methods=['POST'])
def reverse_contig(id_res):
    contig_name = request.form["contig"]
    if not os.path.exists(os.path.join(APP_DATA, id_res, ".all-vs-all")):
309
310
311
        paf_file = os.path.join(APP_DATA, id_res, "map.paf")
        idx1 = os.path.join(APP_DATA, id_res, "query.idx")
        idx2 = os.path.join(APP_DATA, id_res, "target.idx")
312
313
314
315
316
317
318
319
320
321
        paf = Paf(paf_file, idx1, idx2, False)
        paf.reverse_contig(contig_name)
        if paf.parsed:
            res = paf.get_d3js_data()
            res["success"] = True
            return jsonify(res)
        return jsonify({"success": False, "message": paf.error})
    return jsonify({"success": False, "message": "Sort is not available for All-vs-All mode"})


322
323
@app.route('/freenoise/<id_res>', methods=['POST'])
def free_noise(id_res):
324
325
326
    paf_file = os.path.join(APP_DATA, id_res, "map.paf")
    idx1 = os.path.join(APP_DATA, id_res, "query.idx")
    idx2 = os.path.join(APP_DATA, id_res, "target.idx")
327
328
329
330
331
332
333
334
335
    paf = Paf(paf_file, idx1, idx2, False)
    paf.parse_paf(noise=request.form["noise"] == "1")
    if paf.parsed:
        res = paf.get_d3js_data()
        res["success"] = True
        return jsonify(res)
    return jsonify({"success": False, "message": paf.error})


336
337
@app.route('/get-fasta-query/<id_res>', methods=['POST'])
def build_fasta(id_res):
338
    res_dir = os.path.join(APP_DATA, id_res)
339
340
    lock_query = os.path.join(res_dir, ".query-fasta-build")
    is_sorted = os.path.exists(os.path.join(res_dir, ".sorted"))
341
    compressed = request.form["gzip"].lower() == "true"
342
343
344
345
346
    query_fasta = Functions.get_fasta_file(res_dir, "query", is_sorted)
    if query_fasta is not None:
        if is_sorted and not query_fasta.endswith(".sorted"):
            # Do the sort
            Path(lock_query).touch()
347
            if not compressed or MODE == "standalone":  # If compressed, it will took a long time, so not wait
348
                Path(lock_query + ".pending").touch()
349
350
351
352
353
            thread = threading.Timer(1, Functions.sort_fasta, kwargs={
                "job_name": id_res,
                "fasta_file": query_fasta,
                "index_file": os.path.join(res_dir, "query.idx.sorted"),
                "lock_file": lock_query,
354
                "compress": compressed,
355
356
357
                "mailer": mailer
            })
            thread.start()
358
            if not compressed or MODE == "standalone":
359
360
                i = 0
                time.sleep(5)
361
                while os.path.exists(lock_query) and (i < 2 or MODE == "standalone"):
362
363
                    i += 1
                    time.sleep(5)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
364
                os.remove(lock_query + ".pending")
365
366
367
                if os.path.exists(lock_query):
                    return jsonify({"success": True, "status": 1, "status_message": "In progress"})
                return jsonify({"success": True, "status": 2, "status_message": "Done",
368
                                "gzip": compressed})
369
370
            else:
                return jsonify({"success": True, "status": 1, "status_message": "In progress"})
371
372
373
374
375
        elif is_sorted and os.path.exists(lock_query):
            # Sort is already in progress
            return jsonify({"success": True, "status": 1, "status_message": "In progress"})
        else:
            # No sort to do or sort done
376
377
378
379
380
381
382
            if compressed and not query_fasta.endswith(".gz.fasta"):
                # If compressed file is asked, we must compress it now if not done before...
                Path(lock_query).touch()
                thread = threading.Timer(1, Functions.compress_and_send_mail, kwargs={
                    "job_name": id_res,
                    "fasta_file": query_fasta,
                    "index_file": os.path.join(res_dir, "query.idx.sorted"),
383
                    "lock_file": lock_query,
384
385
386
387
388
                    "compressed": compressed,
                    "mailer": mailer
                })
                thread.start()
                return jsonify({"success": True, "status": 1, "status_message": "In progress"})
389
390
391
392
393
394
395
            return jsonify({"success": True, "status": 2, "status_message": "Done",
                            "gzip": query_fasta.endswith(".gz") or query_fasta.endswith(".gz.sorted")})
    else:
        return jsonify({"success": False,
                        "message": "Unable to get fasta file for query. Please contact us to report the bug"})


396
def build_query_as_reference(id_res):
397
398
399
400
401
    paf_file = os.path.join(APP_DATA, id_res, "map.paf")
    idx1 = os.path.join(APP_DATA, id_res, "query.idx")
    idx2 = os.path.join(APP_DATA, id_res, "target.idx")
    paf = Paf(paf_file, idx1, idx2, False, mailer=mailer, id_job=id_res)
    paf.parse_paf(False, True)
402
403
404
405
406
407
408
409
410
411
    if MODE == "webserver":
        thread = threading.Timer(0, paf.build_query_chr_as_reference)
        thread.start()
        return True
    return paf.build_query_chr_as_reference()


@app.route('/build-query-as-reference/<id_res>', methods=['POST'])
def post_query_as_reference(id_res):
    build_query_as_reference(id_res)
412
413
414
    return jsonify({"success": True})


415
416
417
418
419
420
421
@app.route('/get-query-as-reference/<id_res>', methods=['GET'])
def get_query_as_reference(id_res):
    if MODE != "standalone":
        return abort(404)
    return send_file(build_query_as_reference(id_res))


422
423
424
425
426
427
428
429
@app.route('/download/<id_res>/<filename>')
def download_file(id_res, filename):
    file_dl = os.path.join(APP_DATA, id_res, filename)
    if os.path.isfile(file_dl):
        return send_file(file_dl)
    return abort(404)


430
431
432
@app.route('/fasta-query/<id_res>', defaults={'filename': ""}, methods=['GET'])
@app.route('/fasta-query/<id_res>/<filename>', methods=['GET'])  # Use fake URL in mail to set download file name
def dl_fasta(id_res, filename):
433
    res_dir = os.path.join(APP_DATA, id_res)
434
435
436
437
438
439
440
441
442
443
444
445
446
    lock_query = os.path.join(res_dir, ".query-fasta-build")
    is_sorted = os.path.exists(os.path.join(res_dir, ".sorted"))
    if not os.path.exists(lock_query) or not is_sorted:
        query_fasta = Functions.get_fasta_file(res_dir, "query", is_sorted)
        if query_fasta is not None:
            if query_fasta.endswith(".gz") or query_fasta.endswith(".gz.sorted"):
                content = get_file(query_fasta, True)
                return Response(content, mimetype="application/gzip")
            content = get_file(query_fasta)
            return Response(content, mimetype="text/plain")
    abort(404)


447
448
@app.route('/qt-assoc/<id_res>', methods=['GET'])
def qt_assoc(id_res):
449
    res_dir = os.path.join(APP_DATA, id_res)
450
    if os.path.exists(res_dir) and os.path.isdir(res_dir):
451
452
453
        paf_file = os.path.join(APP_DATA, id_res, "map.paf")
        idx1 = os.path.join(APP_DATA, id_res, "query.idx")
        idx2 = os.path.join(APP_DATA, id_res, "target.idx")
454
455
456
457
458
459
460
461
462
463
464
465
        try:
            paf = Paf(paf_file, idx1, idx2, False)
            paf.parse_paf(False)
        except FileNotFoundError:
            print("Unable to load data!")
            abort(404)
            return False
        csv_content = paf.build_query_on_target_association_file()
        return Response(csv_content, mimetype="text/plain")
    abort(404)


466
467
468
469
470
471
@app.route('/no-assoc/<id_res>', methods=['POST'])
def no_assoc(id_res):
    """
    Get contigs that match with None target
    :param id_res: id of the result
    """
472
    res_dir = os.path.join(APP_DATA, id_res)
473
    if os.path.exists(res_dir) and os.path.isdir(res_dir):
474
475
476
        paf_file = os.path.join(APP_DATA, id_res, "map.paf")
        idx1 = os.path.join(APP_DATA, id_res, "query.idx")
        idx2 = os.path.join(APP_DATA, id_res, "target.idx")
477
478
479
480
481
482
        try:
            paf = Paf(paf_file, idx1, idx2, False)
        except FileNotFoundError:
            print("Unable to load data!")
            abort(404)
            return False
483
        file_content = paf.build_list_no_assoc(request.form["to"])
484
485
486
487
488
489
490
491
        empty = file_content == "\n"
        return jsonify({
            "file_content": file_content,
            "empty": empty
        })
    abort(404)


Floreal Cabanettes's avatar
Floreal Cabanettes committed
492
493
@app.route('/summary/<id_res>', methods=['POST'])
def summary(id_res):
494
495
496
497
498
499
500
501
502
503
    paf_file = os.path.join(APP_DATA, id_res, "map.paf")
    idx1 = os.path.join(APP_DATA, id_res, "query.idx")
    idx2 = os.path.join(APP_DATA, id_res, "target.idx")
    try:
        paf = Paf(paf_file, idx1, idx2, False)
    except FileNotFoundError:
        return jsonify({
            "success": False,
            "message": "Unable to load data!"
        })
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
    percents = None
    s_status = "waiting"  # Accepted values: waiting, done, fail
    status_file = os.path.join(APP_DATA, id_res, ".summarize")
    fail_file = status_file + ".fail"
    if not os.path.exists(status_file):  # The job is finished or not started
        if not os.path.exists(fail_file):  # The job has not started yet or has successfully ended
            percents = paf.get_summary_stats()
            if percents is None:  # The job has not started yet
                Path(status_file).touch()
                thread = threading.Timer(0, paf.build_summary_stats, kwargs={"status_file": status_file})
                thread.start()
            else:  # The job has successfully ended
                s_status = "done"
        else:  # The job has failed
            s_status = "fail"

    if s_status == "waiting":  # The job is running
        # Check if the job end in the next 30 seconds
        nb_iter = 0
        while os.path.exists(status_file) and not os.path.exists(fail_file) and nb_iter < 10:
            time.sleep(3)
            nb_iter += 1
        if not os.path.exists(status_file):  # The job has ended
            percents = paf.get_summary_stats()
            if percents is None:  # The job has failed
                s_status = "fail"
            else:  # The job has successfully ended
                s_status = "done"

    if s_status == "fail":
534
535
536
537
538
539
        return jsonify({
            "success": False,
            "message": "Build of summary failed. Please contact us to report the bug"
        })
    return jsonify({
        "success": True,
540
541
        "percents": percents,
        "status": s_status
542
    })
Floreal Cabanettes's avatar
Floreal Cabanettes committed
543
544


545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
def get_filter_out(id_res, type_f):
    filter_file = os.path.join(APP_DATA, id_res, ".filter-" + type_f)
    return Response(get_file(filter_file), mimetype="text/plain")


@app.route('/filter-out/<id_res>/query')
def get_filter_out_query(id_res):
    return get_filter_out(id_res=id_res, type_f="query")


@app.route('/filter-out/<id_res>/target')
def get_filter_out_target(id_res):
    return get_filter_out(id_res=id_res, type_f="target")


560
561
@app.route("/ask-upload", methods=['POST'])
def ask_upload():
562
563
564
565
566
    if MODE == "standalone":
        return jsonify({
            "success": True,
            "allowed": True
        })
567
568
    try:
        s_id = request.form['s_id']
Floreal Cabanettes's avatar
Floreal Cabanettes committed
569
570
571
        with Session.connect():
            session = Session.get(s_id=s_id)
            allowed = session.ask_for_upload(True)
572
573
        return jsonify({
            "success": True,
Floreal Cabanettes's avatar
Floreal Cabanettes committed
574
            "allowed": allowed
575
576
577
        })
    except DoesNotExist:
        return jsonify({"success": False, "message": "Session not initialized. Please refresh the page."})
578
579


580
581
@app.route("/ping-upload", methods=['POST'])
def ping_upload():
582
583
584
585
586
    if MODE == "webserver":
        s_id = request.form['s_id']
        with Session.connect():
            session = Session.get(s_id=s_id)
            session.ping()
587
    return "OK"
588
589


590
591
592
593
@app.route("/upload", methods=['POST'])
def upload():
    try:
        s_id = request.form['s_id']
594
595
596
597
598
599
        if MODE == "webserver":
            try:
                with Session.connect():
                    session = Session.get(s_id=s_id)
                    if session.ask_for_upload(False):
                        folder = session.upload_folder
Floreal Cabanettes's avatar
Floreal Cabanettes committed
600
                    else:
601
602
603
604
605
606
                        return jsonify({"files": [], "success": "ERR", "message": "Not allowed to upload!"})
            except DoesNotExist:
                return jsonify(
                    {"files": [], "success": "ERR", "message": "Session not initialized. Please refresh the page."})
        else:
            folder = s_id
Floreal Cabanettes's avatar
Floreal Cabanettes committed
607

608
        files = request.files[list(request.files.keys())[0]]
Floreal Cabanettes's avatar
Floreal Cabanettes committed
609

610
611
612
613
614
615
616
        if files:
            filename = files.filename
            folder_files = os.path.join(app.config["UPLOAD_FOLDER"], folder)
            if not os.path.exists(folder_files):
                os.makedirs(folder_files)
            filename = Functions.get_valid_uploaded_filename(filename, folder_files)
            mime_type = files.content_type
Floreal Cabanettes's avatar
Floreal Cabanettes committed
617

618
619
620
            if not Functions.allowed_file(files.filename):
                result = UploadFile(name=filename, type_f=mime_type, size=0, not_allowed_msg="File type not allowed")
                shutil.rmtree(folder_files)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
621

622
623
624
625
626
627
628
629
630
631
632
633
634
635
            else:
                # save file to disk
                uploaded_file_path = os.path.join(folder_files, filename)
                files.save(uploaded_file_path)

                # get file size after saving
                size = os.path.getsize(uploaded_file_path)

                # return json for js call back
                result = UploadFile(name=filename, type_f=mime_type, size=size)

            return jsonify({"files": [result.get_file()], "success": "OK"})

        return jsonify({"files": [], "success": "404", "message": "No file provided"})
636
637
638
    except:  # Except all possible exceptions to prevent crashes
        return jsonify({"files": [], "success": "ERR", "message": "An unexpected error has occurred on upload. "
                                                                  "Please contact the support."})
639
640


641
642
643
644
645
646
@app.route("/send-mail/<id_res>", methods=['POST'])
def send_mail(id_res):
    allowed = False
    key_file = None
    if "key" in request.form:
        key = request.form["key"]
647
        res_dir = os.path.join(APP_DATA, id_res)
648
649
650
651
652
653
654
655
656
657
658
659
        key_file = os.path.join(res_dir, ".key")
        if os.path.exists(key_file):
            with open(key_file) as k_f:
                true_key = k_f.readline().strip("\n")
                allowed = key == true_key
    if allowed:
        os.remove(key_file)
        job_mng = JobManager(id_job=id_res, mailer=mailer)
        job_mng.set_inputs_from_res_dir()
        job_mng.send_mail()
        return "OK"
    else:
660
        abort(403)
661
662
663
664
665
666
667
668
669
670


@app.route("/delete/<id_res>", methods=['POST'])
def delete_job(id_res):
    job = JobManager(id_job=id_res)
    success, error = job.delete()
    return jsonify({
        "success": success,
        "error": error
    })