diff --git a/server/api.py b/server/api.py index 5b799d9..e324ce4 100644 --- a/server/api.py +++ b/server/api.py @@ -1,4 +1,14 @@ -from flask import current_app, jsonify, request +""" +api.py + +Organizes all API routes for acquiring, checking, removing questions, as well as other important and miscellaneous +API interactions, static or dynamic. +""" +import copy +import random + +from flask import jsonify +from flask_restful import Resource, reqparse, abort from server import questions from server.helpers import generate_id @@ -7,25 +17,78 @@ from server.helpers import generate_id # Keys represent random question IDs. Values are Question objects. active_questions = {} +# Question categories, key name, value function for acquiring a random question generator. categories = { 'arithmetic': questions.get_arithmetic } - -@current_app.route('/api//new') -def new_question(category: str): - q_id = None - while q_id in active_questions.keys() or q_id is None: - q_id = generate_id(5) - - active_questions[q_id] = categories[category]()() - - return jsonify(active_questions[q_id]) +parser = reqparse.RequestParser() +parser.add_argument( + 'category', + required=False, type=str, choices=tuple(categories.keys()), help='Invalid Category: {error_msg}' +) -@current_app.route('/api//check') -def check_question(category: str): - if 'id' not in request.args.keys(): - return jsonify({'error': ''}), 400 +class Question(Resource): + """ + Questions are small objects represented generated questions for users with a prompt and a answer. + They are identified by a String ID. + """ + + def get(self, question_id): + """Retrieve information about a given question, including the answer for it.""" + pass + + def put(self, question_id = None): + """ + Request a new question. + + A category can be specified in the query arguments in order to filter the question type. + Furthermore, a question type can be also specified . + The Question object is returned, although the answer is omitted. + """ + args = parser.parse_args() + q_id = None + while q_id in active_questions.keys() or q_id is None: + q_id = generate_id(5) + + # Get category arg or choose one if not specified. + if args.get('category') is not None: + category = args.get('category') + if category not in categories.keys(): + abort(404, message=f'Category {category} is not valid.') + else: + category = random.choice(list(categories.keys())) + + # Acquire a question generator and generate a function, then store the result. + active_questions[q_id] = categories[category]()() + + # Make a shallow copy, hide 'answer' key. + question = copy.copy(active_questions[q_id]) + del question['answer'] + + return question, 200 + + +class Category(Resource): + """ + A category is a static designation for a problem to be classified as. + Any question will only have one category it belongs to. + """ + + def get(self, category_id): + """Get all information about a category""" + pass + + +class Categories(Resource): + """ + Categories are static identifiers for the different types of questions available to users. + They are identified with pre-chosen identifiers. + """ + + def get(self): + """Get a list of all categories.""" + return list(categories.keys()), 200 + - return jsonify({'answer': active_questions.get(request.args[id])}) diff --git a/server/create_app.py b/server/create_app.py index e06cced..d432d66 100644 --- a/server/create_app.py +++ b/server/create_app.py @@ -1,24 +1,33 @@ from flask import Flask, render_template +from flask_restful import Api from server.config import configs -def create_app(env = None): + +def create_app(env=None): app = Flask( __name__, static_folder="./../dist/static", template_folder="./../dist" ) + # Instantiate Flask-Restful API and register appropriate routes + from server.api import Question, Category, Categories + api = Api(app, prefix='/api/') + api.add_resource(Question, '/question/', '/question/') + api.add_resource(Category, '/category/') + api.add_resource(Categories, '/categories/') + if not env: env = app.config['ENV'] app.config.from_object(configs[env]) - @app.shell_context_processor - def shell_context(): - pass + # @app.shell_context_processor + # def shell_context(): + # pass with app.app_context(): - #noinspection PyUnresolvedReferences + # noinspection PyUnresolvedReferences from server import api @app.route('/', defaults={'path': ''})