views.py 25.2 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
@app.context_processor
def get_launched_results():
    cookie = request.cookies.get("results")
    return {"results": cookie.split("|") if cookie is not None else set()}


29
30
31
# Main
@app.route("/", methods=['GET'])
def main():
32
33
34
35
36
37
    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
38
39
    else:
        pict = None
40
    return render_template("index.html", title=app_title, menu="index", pict=pict, mode=MODE)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
41
42
43
44


@app.route("/run", methods=['GET'])
def run():
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
    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"]
    return render_template("run.html", title=app_title, id_job=id_job, email=email,
                           menu="run", allowed_ext=ALLOWED_EXTENSIONS, s_id=s_id,
                           max_upload_file_size=config_reader.max_upload_file_size, mode=MODE)
65
66


67
68
@app.route("/run-test", methods=['GET'])
def run_test():
69
70
71
72
73
74
75
    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)
76
77


78
79
80
# Launch analysis
@app.route("/launch_analysis", methods=['POST'])
def launch_analysis():
81
82
83
84
85
86
87
88
89
90
91
92
    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"]

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

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

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

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

124
        folder_files = os.path.join(APP_DATA, id_job)
125
126
127
        os.makedirs(folder_files)

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

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


# Status of a job
@app.route('/status/<id_job>', methods=['GET'])
def status(id_job):
    job = JobManager(id_job)
154
155
156
157
158
159
160
161
162
163
164
165
    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)
166
167
168
169
170
171
172
173
174
    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
        })
175
176
177
    return render_template("status.html", title=app_title, status=j_status["status"],
                           error=j_status["error"].replace("#ID#", ""),
                           id_job=id_job, menu="results", mem_peak=mem_peak,
178
                           time_elapsed=time_e,
179
                           query_filtered=job.is_query_filtered(), target_filtered=job.is_target_filtered(), mode=MODE)
180
181


Floreal Cabanettes's avatar
Floreal Cabanettes committed
182
# Results path
183
@app.route("/result/<id_res>", methods=['GET'])
184
def result(id_res):
185
186
    my_render = render_template("result.html", title=app_title, id=id_res, menu="result", current_result=id_res,
                                mode=MODE)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
187
188
189
190
191
192
193
    response = app.make_response(my_render)
    cookie = request.cookies.get("results")
    cookie = cookie.split("|") if cookie is not None else []
    if id_res not in cookie:
        cookie.insert(0, id_res)
    response.set_cookie(key="results", value="|".join(cookie), path="/")
    return response
194
195


196
197
@app.route("/gallery", methods=['GET'])
def gallery():
198
199
200
201
    if MODE == "webserver":
        return render_template("gallery.html", items=Functions.get_gallery_items(), menu="gallery", title=app_title,
                               mode=MODE)
    return abort(404)
202
203
204
205


@app.route("/gallery/<filename>", methods=['GET'])
def gallery_file(filename):
206
207
208
209
210
211
    if MODE == "webserver":
        try:
            return send_file(os.path.join(config_reader.app_data, "gallery", filename))
        except FileNotFoundError:
            abort(404)
    return abort(404)
212
213


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


Floreal Cabanettes's avatar
Floreal Cabanettes committed
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
@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)
    return render_template("documentation.html", menu="documentation", title=app_title, content=content, toc=toc, mode=MODE)


Floreal Cabanettes's avatar
Floreal Cabanettes committed
257
258
@app.route("/install", methods=['GET'])
def install():
259
    with open(os.path.join(app_folder, "INSTALL.md"), "r", encoding='utf-8') as install_instr:
Floreal Cabanettes's avatar
Floreal Cabanettes committed
260
261
262
263
        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
264
    return render_template("documentation.html", menu="install", title=app_title, content=content, toc=toc, mode=MODE)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
265
266


Floreal Cabanettes's avatar
Floreal Cabanettes committed
267
268
269
270
271
272
273
274
275
276
277
@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
278
# Get graph (ajax request)
279
280
281
@app.route('/get_graph', methods=['POST'])
def get_graph():
    id_f = request.form["id"]
282
283
284
    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")
285

286
    paf = Paf(paf, idx1, idx2)
287

288
289
    if paf.parsed:
        res = paf.get_d3js_data()
290
        res["success"] = True
291
292
293
294
295
296
        return jsonify(res)
    return jsonify({"success": False, "message": paf.error})


@app.route('/sort/<id_res>', methods=['POST'])
def sort_graph(id_res):
297
    if not os.path.exists(os.path.join(APP_DATA, id_res, ".all-vs-all")):
298
299
300
        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")
301
        paf = Paf(paf_file, idx1, idx2, False)
302
303
304
305
306
307
308
        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
309

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

311
312
313
314
@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")):
315
316
317
        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")
318
319
320
321
322
323
324
325
326
327
        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"})


328
329
@app.route('/freenoise/<id_res>', methods=['POST'])
def free_noise(id_res):
330
331
332
    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")
333
334
335
336
337
338
339
340
341
    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})


342
343
@app.route('/get-fasta-query/<id_res>', methods=['POST'])
def build_fasta(id_res):
344
    res_dir = os.path.join(APP_DATA, id_res)
345
346
    lock_query = os.path.join(res_dir, ".query-fasta-build")
    is_sorted = os.path.exists(os.path.join(res_dir, ".sorted"))
347
    compressed = request.form["gzip"].lower() == "true"
348
349
350
351
352
    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()
353
            if not compressed or MODE == "standalone":  # If compressed, it will took a long time, so not wait
354
                Path(lock_query + ".pending").touch()
355
356
357
358
359
            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,
360
                "compress": compressed,
361
362
363
                "mailer": mailer
            })
            thread.start()
364
            if not compressed or MODE == "standalone":
365
366
                i = 0
                time.sleep(5)
367
                while os.path.exists(lock_query) and (i < 2 or MODE == "standalone"):
368
369
                    i += 1
                    time.sleep(5)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
370
                os.remove(lock_query + ".pending")
371
372
373
                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",
374
                                "gzip": compressed})
375
376
            else:
                return jsonify({"success": True, "status": 1, "status_message": "In progress"})
377
378
379
380
381
        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
382
383
384
385
386
387
388
            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"),
389
                    "lock_file": lock_query,
390
391
392
393
394
                    "compressed": compressed,
                    "mailer": mailer
                })
                thread.start()
                return jsonify({"success": True, "status": 1, "status_message": "In progress"})
395
396
397
398
399
400
401
            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"})


402
def build_query_as_reference(id_res):
403
404
405
406
407
    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)
408
409
410
411
412
413
414
415
416
417
    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)
418
419
420
    return jsonify({"success": True})


421
422
423
424
425
426
427
@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))


428
429
430
431
432
433
434
435
@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)


436
437
438
@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):
439
    res_dir = os.path.join(APP_DATA, id_res)
440
441
442
443
444
445
446
447
448
449
450
451
452
    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)


453
454
@app.route('/qt-assoc/<id_res>', methods=['GET'])
def qt_assoc(id_res):
455
    res_dir = os.path.join(APP_DATA, id_res)
456
    if os.path.exists(res_dir) and os.path.isdir(res_dir):
457
458
459
        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")
460
461
462
463
464
465
466
467
468
469
470
471
        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)


472
473
474
475
476
477
@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
    """
478
    res_dir = os.path.join(APP_DATA, id_res)
479
    if os.path.exists(res_dir) and os.path.isdir(res_dir):
480
481
482
        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")
483
484
485
486
487
488
        try:
            paf = Paf(paf_file, idx1, idx2, False)
        except FileNotFoundError:
            print("Unable to load data!")
            abort(404)
            return False
489
        file_content = paf.build_list_no_assoc(request.form["to"])
490
491
492
493
494
495
496
497
        empty = file_content == "\n"
        return jsonify({
            "file_content": file_content,
            "empty": empty
        })
    abort(404)


Floreal Cabanettes's avatar
Floreal Cabanettes committed
498
499
@app.route('/summary/<id_res>', methods=['POST'])
def summary(id_res):
500
501
502
503
504
505
506
507
508
509
    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!"
        })
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
    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":
540
541
542
543
544
545
        return jsonify({
            "success": False,
            "message": "Build of summary failed. Please contact us to report the bug"
        })
    return jsonify({
        "success": True,
546
547
        "percents": percents,
        "status": s_status
548
    })
Floreal Cabanettes's avatar
Floreal Cabanettes committed
549
550


551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
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")


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


586
587
@app.route("/ping-upload", methods=['POST'])
def ping_upload():
588
589
590
591
592
    if MODE == "webserver":
        s_id = request.form['s_id']
        with Session.connect():
            session = Session.get(s_id=s_id)
            session.ping()
593
    return "OK"
594
595


596
597
598
599
@app.route("/upload", methods=['POST'])
def upload():
    try:
        s_id = request.form['s_id']
600
601
602
603
604
605
        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
606
                    else:
607
608
609
610
611
612
                        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
613

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

616
617
618
619
620
621
622
        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
623

624
625
626
            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
627

628
629
630
631
632
633
634
635
636
637
638
639
640
641
            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"})
642
643
644
    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."})
645
646


647
648
649
650
651
652
@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"]
653
        res_dir = os.path.join(APP_DATA, id_res)
654
655
656
657
658
659
660
661
662
663
664
665
        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:
666
        abort(403)