From 1e93e263aca5feca76bfae142fa4431ea70e5ba9 Mon Sep 17 00:00:00 2001 From: Roosted7 <thomasroos@live.nl> Date: Fri, 26 Jan 2018 17:22:32 +0100 Subject: [PATCH] Use Flask_restful for better API handeling --- requirements.txt | 1 + zesje/__init__.py | 13 ++-- zesje/api.py | 136 ++----------------------------------- zesje/resources/graders.py | 59 ++++++++++++++++ 4 files changed, 73 insertions(+), 136 deletions(-) create mode 100644 zesje/resources/graders.py diff --git a/requirements.txt b/requirements.txt index a38ef9d3c..02d97ad3c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ flask Flask-BasicAuth +flask_restful pony opencv-python diff --git a/zesje/__init__.py b/zesje/__init__.py index 4b4ef9eb1..767bca696 100644 --- a/zesje/__init__.py +++ b/zesje/__init__.py @@ -1,16 +1,16 @@ from os import path -from os.path import abspath, dirname, isfile +from os.path import abspath, dirname -from werkzeug.exceptions import NotFound from flask import Flask from flask_basicauth import BasicAuth +from werkzeug.exceptions import NotFound from . import db, api -static_folder_path = path.join(abspath(dirname(__file__)), 'static') +STATIC_FOLDER_PATH = path.join(abspath(dirname(__file__)), 'static') -app = Flask(__name__, static_folder=static_folder_path) -db.use_db() +app = Flask(__name__, static_folder=STATIC_FOLDER_PATH) +app.register_blueprint(api.api_bp, url_prefix='/api') ####### Very Very VERY import to use this outsite dev/sandbox environment @@ -20,11 +20,12 @@ app.config['BASIC_AUTH_FORCE'] = False # Don't forget to set this to 'True' basic_auth = BasicAuth(app) -app.register_blueprint(api.app, url_prefix='/api') +db.use_db() @app.route('/') @app.route('/<file>') def index(file='index.html'): + """Serve the static react content, otherwise fallback to the index.html""" try: return app.send_static_file(file) except NotFound: diff --git a/zesje/api.py b/zesje/api.py index 9097a00c0..0b39b7c19 100644 --- a/zesje/api.py +++ b/zesje/api.py @@ -1,133 +1,9 @@ -import json -import yaml +from flask import Blueprint +from flask_restful import Api -from flask import Blueprint, Response, jsonify, request, abort +from .resources.graders import Graders -from . import db +api_bp = Blueprint(__name__, __name__) +api = Api(api_bp) -app = Blueprint(__name__, __name__) - -# TODO: when making new database structure, have only a single -# 'name' field: it is just an identifier - -@app.route('/graders', methods=['GET']) -@db.session -def get_graders(): - """get all graders. - - - Returns - ------- - list of: - id: int - first_name: str - last_name: str - """ - - return jsonify([ - dict(id=g.id, first_name=g.first_name, last_name=g.last_name) - for g in db.Grader.select() - ]) - - -@app.route('/graders', methods=['POST']) -@db.session -def post_graders(): - """add a grader. - - Parameters - ---------- - first_name: str - last_name: str - - Returns - ------- - id: int - first_name: str - last_name: str - """ - grader_spec = request.get_json(silent=False, force=True) - try: - new_grader = db.Grader(first_name=grader_spec['first_name'], - last_name=grader_spec['last_name']) - db.orm.commit() - except KeyError: - abort(400) - - return jsonify( - id=new_grader.id, - first_name=new_grader.first_name, - last_name=new_grader.last_name, - ) - -@app.route('/exams', methods=['POST']) -def post_new_yaml(): - if request.method == 'POST': - # check if the post request has the file part - if 'file' not in request.files: - print('No file part') - abort(400) - file = request.files['file'] - # if user does not select file, browser also - # submit a empty part without filename - if file.filename == '': - print('No selected file') - abort(400) - if file: - try: - - yml = yaml.safe_load(file.read()) - - try: - yml = db.clean_yaml(yml) # attempt to clean - except Exception: - pass # if the YAML is not valid parsing will raise anyway - version, exam_name, qr, widgets = db.parse_yaml(yml) - - # If there is already yaml for this exam, load it now so we can - # compute a diff later - existing_exam_data = get_exam_data(exam_name) - if existing_exam_data: - existing_yml_version, *_, existing_widgets = existing_exam_data - if not all(v == 1 for v in (version, existing_yml_version)): - raise ValueError('Exam data for {} already exists, and updating it requires both the old ' - 'and new YAML to be version 1'.format(exam_name)) - if not existing_widgets.shape == widgets.shape: - raise ValueError('Exam data for {} already exists, and contains a different number of ' - 'exam problems than {}'.format(exam_name, yaml_upload.filename)) - except ValueError as exc: - print(str(exc)) - abort(400) - except Exception as exc: - print("Invalid metadata") - print(exc) - abort(400) - - # save yaml - yaml_filename = exam_name + '.yml' - print("Saving " + yaml_filename) - try: - with open(yaml_filename, 'w') as f: - f.write(yaml.safe_dump(yml, default_flow_style=False)) - except Exception: - print("Failed to save " + yaml_filename) - abort(400) - - print("Adding exam to database") - - try: - if existing_exam_data: - db.update_exam(exam_name, yaml_filename) - print("Updated problem names for {}".format(exam_name)) - else: - db.add_exam(yaml_filename) - print("Added exam {} to database".format(exam_name)) - os.makedirs(exam_name + '_data', exist_ok=True) - except Exception as exc: - print("Failed to add exam to database") - return - finally: - # XXX: use of global variables - # update list of exams - print("Metadata imported successfully") - \ No newline at end of file +api.add_resource(Graders, '/graders') diff --git a/zesje/resources/graders.py b/zesje/resources/graders.py new file mode 100644 index 000000000..3c520bf6b --- /dev/null +++ b/zesje/resources/graders.py @@ -0,0 +1,59 @@ +from flask_restful import Resource, reqparse + +from .. import db + +parser = reqparse.RequestParser() +parser.add_argument('first_name', type=str, required=True) +parser.add_argument('last_name', type=str, required=True) +# TODO: when making new database structure, have only a single +# 'name' field: it is just an identifier + + +class Graders(Resource): + @db.session + def get(self): + """get all graders. + + + Returns + ------- + list of: + id: int + first_name: str + last_name: str + """ + + return [ + { + 'id' : g.id, + 'first_name' : g.first_name, + 'last_name' : g.last_name + } + for g in db.Grader.select() + ] + + @db.session + def post(self): + + args = parser.parse_args() + + """add a grader. + + Parameters + ---------- + first_name: str + last_name: str + + Returns + ------- + id: int + first_name: str + last_name: str + """ + + db.Grader(first_name= args['first_name'], + last_name=args['last_name']) + db.orm.commit() + + return { + } \ No newline at end of file -- GitLab