aboutsummaryrefslogtreecommitdiff
path: root/qc_app/entry.py
blob: 0cd34c5806ec382c8e89fd8e5b9e5949b460632f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
"""Entry-point module"""
import os
import mimetypes
from typing import Tuple
from zipfile import ZipFile, is_zipfile

from werkzeug.utils import secure_filename
from flask import (
    flash,
    request,
    url_for,
    redirect,
    Blueprint,
    render_template,
    current_app as app)

from qc_app.db import species
from qc_app.db_utils import with_db_connection

entrybp = Blueprint("entry", __name__)

def errors(rqst) -> Tuple[str, ...]:
    """Return a tuple of the errors found in the request `rqst`. If no error is
    found, then an empty tuple is returned."""
    def __filetype_error__():
        return (
            ("Invalid file type provided.",)
            if rqst.form.get("filetype") not in ("average", "standard-error")
            else tuple())

    def __file_missing_error__():
        return (
            ("No file was uploaded.",)
            if ("qc_text_file" not in rqst.files or
                rqst.files["qc_text_file"].filename == "")
            else tuple())

    def __file_mimetype_error__():
        text_file = rqst.files["qc_text_file"]
        return (
            (
                ("Invalid file! Expected a tab-separated-values file, or a zip "
                 "file of the a tab-separated-values file."),)
            if text_file.mimetype not in (
                    "text/plain", "text/tab-separated-values",
                    "application/zip")
            else tuple())

    return (
        __filetype_error__() +
        (__file_missing_error__() or __file_mimetype_error__()))

def zip_file_errors(filepath, upload_dir) -> Tuple[str, ...]:
    """Check the uploaded zip file for errors."""
    zfile_errors: Tuple[str, ...] = tuple()
    if is_zipfile(filepath):
        with ZipFile(filepath, "r") as zfile:
            infolist = zfile.infolist()
            if len(infolist) != 1:
                zfile_errors = zfile_errors + (
                    ("Expected exactly one (1) member file within the uploaded zip "
                     f"file. Got {len(infolist)} member files."),)
            if len(infolist) == 1 and infolist[0].is_dir():
                zfile_errors = zfile_errors + (
                    ("Expected a member text file in the uploaded zip file. Got a "
                     "directory/folder."),)

            if len(infolist) == 1 and not infolist[0].is_dir():
                zfile.extract(infolist[0], path=upload_dir)
                mime = mimetypes.guess_type(f"{upload_dir}/{infolist[0].filename}")
                if mime[0] != "text/tab-separated-values":
                    zfile_errors = zfile_errors + (
                        ("Expected the member text file in the uploaded zip file to"
                         " be a tab-separated file."),)

    return zfile_errors

@entrybp.route("/", methods=["GET", "POST"])
def upload_file():
    """Enables uploading the files"""
    if request.method == "GET":
        return render_template(
            "index.html", species=with_db_connection(species))

    upload_dir = app.config["UPLOAD_FOLDER"]
    request_errors = errors(request)
    if request_errors:
        for error in request_errors:
            flash(error, "alert-error")
        return render_template(
            "index.html", species=with_db_connection(species)), 400

    filename = secure_filename(request.files["qc_text_file"].filename)
    if not os.path.exists(upload_dir):
        os.mkdir(upload_dir)

    filepath = os.path.join(upload_dir, filename)
    request.files["qc_text_file"].save(os.path.join(upload_dir, filename))

    zip_errors = zip_file_errors(filepath, upload_dir)
    if zip_errors:
        for error in zip_errors:
            flash(error, "alert-error")
        return render_template(
            "index.html", species=with_db_connection(species)), 400

    return redirect(url_for(
        "parse.parse", filename=filename,
        filetype=request.form["filetype"]))

@entrybp.route("/data-review", methods=["GET"])
def data_review():
    """Provide some help on data expectations to the user."""
    return render_template("data_review.html")