aboutsummaryrefslogtreecommitdiff
path: root/qc_app/entry.py
blob: f2db8784a1c2dec3f4b0d062d7de497973b950f5 (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
115
116
117
118
119
120
121
122
123
124
125
126
127
"""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,
    send_from_directory)

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

entrybp = Blueprint("entry", __name__)

@entrybp.route("/favicon.ico", methods=["GET"])
def favicon():
    """Return the favicon."""
    return send_from_directory(os.path.join(app.root_path, "static"),
                               "images/CITGLogo.png",
                               mimetype="image/png")


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"])
def index():
    """Load the landing page"""
    return render_template("index.html")

@entrybp.route("/upload", methods=["GET", "POST"])
def upload_file():
    """Enables uploading the files"""
    if request.method == "GET":
        return render_template(
            "select_species.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-danger error-expr-data")
        return redirect(url_for("entry.upload_file"))

    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-danger error-expr-data")
        return redirect(url_for("entry.upload_file"))

    return redirect(url_for("parse.parse",
                            speciesid=request.form["speciesid"],
                            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")