views.py 25 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
23
24
25

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


@app.route("/run", methods=['GET'])
def run():
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    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)
59
60


61
62
@app.route("/run-test", methods=['GET'])
def run_test():
63
64
65
66
67
68
69
    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)
70
71


72
73
74
# Launch analysis
@app.route("/launch_analysis", methods=['POST'])
def launch_analysis():
75
76
77
78
79
80
81
82
83
84
85
86
    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"]

87
88
    id_job = request.form["id_job"]
    email = request.form["email"]
89
90
91
92
    file_query = request.form["query"]
    file_query_type = request.form["query_type"]
    file_target = request.form["target"]
    file_target_type = request.form["target_type"]
93
94
95

    # Check form:
    form_pass = True
96
    errors = []
97
    if id_job == "":
98
        errors.append("Id of job not given")
99
100
        form_pass = False

101
    if email == "" and MODE == "webserver":
102
        errors.append("Email not given")
103
        form_pass = False
104
105
    if file_target == "":
        errors.append("No target fasta selected")
106
107
108
109
        form_pass = False

    # Form pass
    if form_pass:
110
        # Get final job id:
111
        id_job = re.sub('[^A-Za-z0-9_\-]+', '', id_job.replace(" ", "_"))
112
        id_job_orig = id_job
113
        i = 2
114
        while os.path.exists(os.path.join(APP_DATA, id_job)):
115
116
            id_job = id_job_orig + ("_%d" % i)
            i += 1
117

118
        folder_files = os.path.join(APP_DATA, id_job)
119
120
121
        os.makedirs(folder_files)

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

        # Launch job:
Floreal Cabanettes's avatar
Floreal Cabanettes committed
134
        job = JobManager(id_job, email, query, target, mailer)
135
136
137
138
        if MODE == "webserver":
            job.launch()
        else:
            job.launch_standalone()
139
        return jsonify({"success": True, "redirect": url_for(".status", id_job=id_job)})
140
    else:
141
        return jsonify({"success": False, "errors": errors})
142
143
144
145
146
147


# Status of a job
@app.route('/status/<id_job>', methods=['GET'])
def status(id_job):
    job = JobManager(id_job)
148
149
150
151
152
153
154
155
156
157
158
159
    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)
160
161
162
163
164
165
166
167
168
    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
        })
169
170
171
    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,
172
                           time_elapsed=time_e,
173
                           query_filtered=job.is_query_filtered(), target_filtered=job.is_target_filtered(), mode=MODE)
174
175


Floreal Cabanettes's avatar
Floreal Cabanettes committed
176
# Results path
177
@app.route("/result/<id_res>", methods=['GET'])
178
def result(id_res):
179
180
    return render_template("result.html", title=app_title, id=id_res, menu="result", current_result=id_res, mode=MODE,
                           is_gallery=Functions.is_in_gallery(id_res, MODE))
181
182


183
184
@app.route("/gallery", methods=['GET'])
def gallery():
185
186
187
188
    if MODE == "webserver":
        return render_template("gallery.html", items=Functions.get_gallery_items(), menu="gallery", title=app_title,
                               mode=MODE)
    return abort(404)
189
190
191
192


@app.route("/gallery/<filename>", methods=['GET'])
def gallery_file(filename):
193
194
195
196
197
198
    if MODE == "webserver":
        try:
            return send_file(os.path.join(config_reader.app_data, "gallery", filename))
        except FileNotFoundError:
            abort(404)
    return abort(404)
199
200


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


Floreal Cabanettes's avatar
Floreal Cabanettes committed
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
@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
244
245
@app.route("/install", methods=['GET'])
def install():
246
    with open(os.path.join(app_folder, "INSTALL.md"), "r", encoding='utf-8') as install_instr:
Floreal Cabanettes's avatar
Floreal Cabanettes committed
247
248
249
250
        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
251
    return render_template("documentation.html", menu="install", title=app_title, content=content, toc=toc, mode=MODE)
Floreal Cabanettes's avatar
Floreal Cabanettes committed
252
253


Floreal Cabanettes's avatar
Floreal Cabanettes committed
254
255
256
257
258
259
260
261
262
263
264
@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
265
# Get graph (ajax request)
266
267
268
@app.route('/get_graph', methods=['POST'])
def get_graph():
    id_f = request.form["id"]
269
270
271
    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")
272

273
    paf = Paf(paf, idx1, idx2)
274

275
276
    if paf.parsed:
        res = paf.get_d3js_data()
277
        res["success"] = True
278
279
280
281
282
283
        return jsonify(res)
    return jsonify({"success": False, "message": paf.error})


@app.route('/sort/<id_res>', methods=['POST'])
def sort_graph(id_res):
284
    if not os.path.exists(os.path.join(APP_DATA, id_res, ".all-vs-all")):
285
286
287
        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")
288
        paf = Paf(paf_file, idx1, idx2, False)
289
290
291
292
293
294
295
        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
296

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

298
299
300
301
@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")):
302
303
304
        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")
305
306
307
308
309
310
311
312
313
314
        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"})


315
316
@app.route('/freenoise/<id_res>', methods=['POST'])
def free_noise(id_res):
317
318
319
    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")
320
321
322
323
324
325
326
327
328
    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})


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


389
def build_query_as_reference(id_res):
390
391
392
393
394
    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)
395
396
397
398
399
400
401
402
403
404
    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)
405
406
407
    return jsonify({"success": True})


408
409
410
411
412
413
414
@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))


415
416
417
418
419
420
421
422
@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)


423
424
425
@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):
426
    res_dir = os.path.join(APP_DATA, id_res)
427
428
429
430
431
432
433
434
435
436
437
438
439
    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)


440
441
@app.route('/qt-assoc/<id_res>', methods=['GET'])
def qt_assoc(id_res):
442
    res_dir = os.path.join(APP_DATA, id_res)
443
    if os.path.exists(res_dir) and os.path.isdir(res_dir):
444
445
446
        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")
447
448
449
450
451
452
453
454
455
456
457
458
        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)


459
460
461
462
463
464
@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
    """
465
    res_dir = os.path.join(APP_DATA, id_res)
466
    if os.path.exists(res_dir) and os.path.isdir(res_dir):
467
468
469
        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")
470
471
472
473
474
475
        try:
            paf = Paf(paf_file, idx1, idx2, False)
        except FileNotFoundError:
            print("Unable to load data!")
            abort(404)
            return False
476
        file_content = paf.build_list_no_assoc(request.form["to"])
477
478
479
480
481
482
483
484
        empty = file_content == "\n"
        return jsonify({
            "file_content": file_content,
            "empty": empty
        })
    abort(404)


Floreal Cabanettes's avatar
Floreal Cabanettes committed
485
486
@app.route('/summary/<id_res>', methods=['POST'])
def summary(id_res):
487
488
489
490
491
492
493
494
495
496
    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!"
        })
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
    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":
527
528
529
530
531
532
        return jsonify({
            "success": False,
            "message": "Build of summary failed. Please contact us to report the bug"
        })
    return jsonify({
        "success": True,
533
534
        "percents": percents,
        "status": s_status
535
    })
Floreal Cabanettes's avatar
Floreal Cabanettes committed
536
537


538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
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")


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


573
574
@app.route("/ping-upload", methods=['POST'])
def ping_upload():
575
576
577
578
579
    if MODE == "webserver":
        s_id = request.form['s_id']
        with Session.connect():
            session = Session.get(s_id=s_id)
            session.ping()
580
    return "OK"
581
582


583
584
585
586
@app.route("/upload", methods=['POST'])
def upload():
    try:
        s_id = request.form['s_id']
587
588
589
590
591
592
        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
593
                    else:
594
595
596
597
598
599
                        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
600

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

603
604
605
606
607
608
609
        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
610

611
612
613
            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
614

615
616
617
618
619
620
621
622
623
624
625
626
627
628
            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"})
629
630
631
    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."})
632
633


634
635
636
637
638
639
@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"]
640
        res_dir = os.path.join(APP_DATA, id_res)
641
642
643
644
645
646
647
648
649
650
651
652
        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:
653
        abort(403)
654
655
656
657
658
659
660
661
662
663


@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
    })