summaryrefslogtreecommitdiffstats
path: root/API/app.py
blob: e4cb7691701b4f7cc15e3b65253faeb52fa3d9d8 (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
from paste.translogger import TransLogger
from waitress import serve
import configparser
import werkzeug
import helpers
import flask
import sys
import os

# os.environ["UPLOADS_DIR"] = "/media/veracrypt1/Edencloud/UniStuff/3.0 - CMP 3rd Year Project/Smarker/API/.uploads"

sys.path.insert(1, os.path.join("..", "Smarker"))
import database

app = flask.Flask(__name__)
app.config['UPLOAD_FOLDER'] = ".uploads"
API_CONF = configparser.ConfigParser()
API_CONF.read("api.conf")


@app.route("/api/helloworld")
def helloworld():
    """GET ``/api/helloworld``

    Returns a friendly message to check the server is working
    """
    return flask.jsonify({"hello": "world!"})

@app.route("/api/mark", methods = ["post"])
def mark():
    """POST ``/api/mark``

    Expects to be a POST request of ``Content-Type: multipart/form-data``.

    * Expects a valid API key under the key ``key``

    * Expects an assessment name under the key ``assessment``

    * Expects a submission zip file under the key ``zip``

    * File dependences can be added with keys prefixed with ``filedep``, but a corrisponding key must also be present with the location of this file in the sandboxed environment: e.g. ``-F "filedep1=@../../aDependency.txt" -F "aDependency.txt=/aDependency.txt"``

    Returns a report in the JSON format.
    """
    # try:
    assessment_name = flask.request.form.get('assessment')
    api_key = flask.request.form.get('key')
    files = []

    with database.SmarkerDatabase(
        host = API_CONF.get("mysql", "host"), 
        user = API_CONF.get("mysql", "user"), 
        passwd = API_CONF.get("mysql", "passwd"), 
        db = "Smarker",
        port = API_CONF.getint("mysql", "port")) as db:

        if db.valid_apikey(api_key):
            f = flask.request.files["zip"]
            zip_name = f.filename
            f.save(os.path.join(".uploads/", f.filename))
            # even though this is inside docker, we are accessing the HOST docker daemon
            # so we have to pass through the HOST location for volumes... very annoying I know
            # so we set this environment variable
            #       https://serverfault.com/questions/819369/mounting-a-volume-with-docker-in-docker
            files.append("%s:/tmp/%s" % (os.path.join(os.environ["UPLOADS_DIR"], zip_name), zip_name))
   
            for file_dep in flask.request.files.keys():
                if file_dep.startswith("filedep"):
                    f = flask.request.files[file_dep]
                    f.save(os.path.join(".uploads/", f.filename))
                    dep_name = os.path.split(f.filename)[-1]
                    client_loc = flask.request.form.get(dep_name)
                    if client_loc is None:
                        return flask.abort(flask.Response("You need to specify a location to put file dependency '%s' e.g.  '%s=/%s'" % (dep_name, dep_name, dep_name), status=500))
                    
                    files.append("%s:%s" % (os.path.join(os.environ["UPLOADS_DIR"], dep_name), client_loc))
            
            
            try:
                return flask.jsonify(helpers.run_smarker_simple(db, zip_name, assessment_name, files))
            except Exception as e:
                flask.abort(flask.Response(str(e), status=500))
        else:
            flask.abort(403)
    # except (KeyError, TypeError, ValueError):
    #     flask.abort(400)


if __name__ == "__main__":
    try:
        if sys.argv[1] == "--production":
            serve(
                TransLogger(app), 
                host = API_CONF.get("production", "host"), 
                port = API_CONF.getint("production", "port"), 
                threads = 32
            )
        else:
            app.run(
                host = API_CONF.get("testing", "host"), 
                port = API_CONF.getint("testing", "port"), 
                debug = True
            )
    except IndexError:
        app.run(
            host = API_CONF.get("testing", "host"), 
            port = API_CONF.getint("testing", "port"), 
            debug = True
        )