def setUp(self): """Define test variables and initialize app.""" self.app = create_app() self.client = self.app.test_client self.database_name = "trivia" self.database_path = 'postgresql://*****:*****@localhost:5432/{}'.format( self.database_name) setup_db(self.app, self.database_path) # sample question for use in tests self.new_question = { 'question': 'Which four states make up the 4 Corners region of the US?', 'answer': 'Colorado, New Mexico, Arizona, Utah', 'difficulty': 3, 'category': '3' } # binds the app to the current context with self.app.app_context(): self.db = SQLAlchemy() self.db.init_app(self.app) # create all tables self.db.create_all()
def setUp(self): """Define test variables and initialize app.""" self.database_name = "trivia_test" self.database_path = "postgresql://*****:*****@localhost:54321/trivia_test" self.app = create_app({"SQLALCHEMY_DATABASE_URI": self.database_path}) self.client = self.app.test_client self.app_context = self.app.app_context() self.app_context.push() setup_db(self.app) self.db = db
def setUp(self): """Define test variables and initialize app.""" self.database_name = "trivia_test" self.app = create_app() self.client = self.app.test_client self.database_path = "postgresql://{}/{}".format( 'localhost:5432', self.database_name) setup_db(self.app, self.database_path) # binds the app to the current context with self.app.app_context(): self.db = SQLAlchemy() self.db.init_app(self.app) # create all tables self.db.create_all()
def setUp(self): """ Setup db. :param self: """ self.app = create_app() self.client = self.app.test_client setup_db(self.app) # binds the app to the current context with self.app.app_context(): self.db = SQLAlchemy() self.db.init_app(self.app) # create all tables self.db.create_all()
def setUp(self): """Define test variables and initialize app.""" self.app = create_app() self.client = self.app.test_client self.token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik56RTBSa1pDTXpZMk5qWTRRelU0T0RFMFJFUTNORVE0TmpFek9FTkdOVFUxTkRZNU9ETkdOdyJ9.eyJpc3MiOiJodHRwczovL2NhcHN0b25lLWF1dGguYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDVkY2M0ZTM2YmMyMDQyMGUyODc4ZGJhNyIsImF1ZCI6ImNhcHN0b25lIiwiaWF0IjoxNTc0MjgwNjQ4LCJleHAiOjE1NzQyODc4NDgsImF6cCI6ImlURlROMXJRMWtvNXdTVHkzU1F1ODdQZTBuTVdlS0YzIiwic2NvcGUiOiIiLCJwZXJtaXNzaW9ucyI6WyJhZGQ6YWN0b3IiLCJhZGQ6bW92aWUiLCJjaGFuZ2U6YWN0b3IiLCJjaGFuZ2U6bW92aWUiLCJkZWxldGU6YWN0b3IiLCJkZWxldGU6bW92aWUiLCJyZWFkOmFjdG9ycyIsInJlYWQ6bW92aWVzIl19.IPpiGLmSGr87MkCSRTzteScV6VdZL1tpmQFD_29ApFKXll2MD_nXzp-_Y6apluBauiYApVSNi9XYVo4o6HiiBXWiVuHEZL57EYHufmvGPxieH4pU5vrhIOcMFjIRXwnj5GxyalL_hHmWtBFjRnorkKBhEj_-rRWQ11GoacfqTXSV0fRcbq7ZXt3feZmW6lZ2SFQgarfK_ZIR4bkvHywaUu9381Hq3AgSuRxz6UAQk-hkOUhs2ppQcMapRcS9PWjUv7axJiLnwvAGdytGg0DshmuF2Qy50I_vnnZ_YM2JCllb027eq9vK7TLgJr5Aazt9zJDvk8bHK57OTeA660VtCw" self.database_name = "casting" self.database_path = "postgres://{}/{}".format( 'tomaswingord:tomasw87@localhost:5432', self.database_name) setup_db(self.app, self.database_path) # binds the app to the current context with self.app.app_context(): self.db = SQLAlchemy() self.db.init_app(self.app) # create all tables self.db.create_all()
def create_app(test_config=None): # create and configure the app app = Flask(__name__) setup_db(app) CORS(app) # CORS Headers @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS,PATCH') return response @app.route('/categories', methods=['GET']) def get_categories(): selection = Category.query.order_by(Category.id).all() return jsonify({ "categories": format_categories(selection), "success": True }) @app.route('/questions', methods=['GET']) def get_questions(): if len(search_term := request.args.get('search', '')) > 0: return search_questions(search_term) all_questions = Question.query.order_by(Question.id).all() current_questions = paginate_questions(request, all_questions) if len(current_questions) == 0: abort(404) return jsonify({ 'total_questions': len(all_questions), 'questions': current_questions, 'categories': format_categories(Category.query.order_by(Category.id).all()), # 'current_category': Category.query.all()[0].format(), 'success': True })
def setUp(self): """Define test variables and initialize app.""" self.app = create_app() self.client = self.app.test_client self.database_name = "trivia_test" self.database_path = "postgres://{}/{}".format('localhost:5432', self.database_name) setup_db(self.app, self.database_path) self.question = { "question": "Whats the name of California's capitol", "answer": "Sacramento", "difficulty": 1, 'category': 2 } # binds the app to the current context with self.app.app_context(): self.db = SQLAlchemy() self.db.init_app(self.app) # create all tables self.db.create_all()
def setUp(self): """Define test variables and initialize app.""" self.app = create_app() self.client = self.app.test_client self.database_name = "trivia_test" self.database_path = "postgres://{}/{}".format('localhost:5432', self.database_name) setup_db(self.app, self.database_path) self.new_question = { 'category': '1', 'question': 'Neil Gaiman', 'answer': 'aaaaaaa', 'difficulty': 5 } self.new_category = { 'id': '1', 'type': 'test_type', } # binds the app to the current context with self.app.app_context(): self.db = SQLAlchemy(self.app) self.db.init_app(self.app) self.db.create_all() # # create all tables # create all tables self.question = Question(difficulty=self.new_question['difficulty'], question=self.new_question['question'], answer=self.new_question['answer'], category=self.new_question['category']) self.question.insert() if Category.query.first() is None: category = Category(type=self.new_category['type']) category.insert()
import sys from sqlalchemy import exc # from flask import Flask, request, jsonify, abort from flask import (Flask, request, jsonify, abort) from flask_sqlalchemy import SQLAlchemy from flask_cors import CORS from backend.auth import AuthError, requires_auth # from backend.models import db_drop_and_create_all, # setup_db, migrate_db, Movie, Actor, Cast from backend.models import (db_drop_and_create_all, setup_db, migrate_db, Movie, Actor, Cast) # def create_app(test_config=None): app = Flask(__name__) setup_db(app) CORS(app) db_drop_and_create_all() @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET,PATCH,POST,DELETE,OPTIONS') return response @app.route('/')
def create_app(test_config=None): # create and configure the app app = Flask(__name__) setup_db(app) CORS(app) # CORS by default allows '*' for all origins. @app.errorhandler(400) def bad_request(*args): return jsonify({ 'success': False, 'error': 400, 'message': 'Bad request' }), 400 @app.errorhandler(404) def not_found(*args): return jsonify({ 'success': False, 'error': 404, 'message': 'Not found' }), 404 @app.errorhandler(405) def method_not_allowed(*args): return jsonify({ 'success': False, 'error': 405, 'message': 'Method not allowed' }), 405 @app.errorhandler(422) def unprocessable_entity(*args): return jsonify({ 'success': False, 'error': 422, 'message': 'Unprocessable entity' }), 422 @app.errorhandler(500) def internal_server_error(*args): return jsonify({ 'success': False, 'error': 500, 'message': 'Internal server error' }), 500 @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type, Authorization') response.headers.add('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE, OPTIONS') return response @app.route('/categories') def get_categories(): try: categories = Category.query.order_by(Category.id).all() return jsonify({ 'success': True, 'categories': {category.id: category.type for category in categories} }) except: abort(500) @app.route('/questions') def get_questions(): try: page = request.args.get('page', 1, type=int) questions = Question.query.order_by(Question.id).all() start = (page - 1) * QUESTIONS_PER_PAGE end = start + QUESTIONS_PER_PAGE questions_list = [question.format() for question in questions][start:end] if not questions_list: abort(400) categories = { category.id: category.type for category in Category.query.order_by(Category.id).all() } return jsonify({ 'success': True, 'questions': questions_list, 'total_questions': len(questions), 'categories': categories, 'current_category': "Science" # Since the front end doesn't tell us what category it's on in this request, }) # we default to Science. except: abort(400) @app.route('/questions/<question_id>', methods=['DELETE']) def delete_question(question_id): question = Question.query.filter( Question.id == question_id).one_or_none() if question: question.delete() return jsonify({'success': True}) else: abort(404) @app.route('/questions', methods=['POST']) def create_question(): try: data = json.loads(request.data) if 'searchTerm' not in data.keys(): question = data['question'] answer = data['answer'] difficulty = data['difficulty'] category = data['category'] if not (question and answer and difficulty and category): abort(400) new_question = Question(question, answer, category, difficulty) new_question.insert() return jsonify({ 'success': True, }) elif 'searchTerm' in data.keys(): search_term = data['searchTerm'] questions = Question.query.filter( Question.question.ilike(f'%{search_term}%')).all() return jsonify({ 'success': True, 'questions': [question.format() for question in questions] }) except: abort(422) @app.route('/categories/<category_id>/questions') def get_questions_by_category(category_id): category = Category.query.filter( Category.id == category_id).one_or_none() if not category: abort(404) questions = Question.query.filter( Question.category == category.id).all() return jsonify({ 'success': True, 'total_questions': len(questions), 'current_category': category.type, 'questions': [question.format() for question in questions] }) @app.route('/quizzes', methods=['POST']) def create_quiz(): data = json.loads(request.data) previous_questions = data[ 'previous_questions'] # will have ids of questions quiz_category_id = data['quiz_category']['id'] questions = Question.query.filter( Question.category == quiz_category_id).all() unseen_questions = [ question.format() for question in questions if question.id not in previous_questions ] print('unseen questions are: ', unseen_questions) return jsonify({ 'success': True, 'question': random.choice(unseen_questions) if unseen_questions else False }) return app
def create_app(test_config=None): # create and configure the app app = Flask(__name__) setup_db(app) CORS(app) db = SQLAlchemy(app) migrate = Migrate(app, db) # this # GET /actors and /movies --> DONE @app.route('/actors', methods=['GET']) @requires_auth(permission='read:actors') def get_actors(payload): all_actors = actors.query.all() results = [] for actor in all_actors: one_actor = {} one_actor["id"] = actor.id one_actor["name"] = actor.name one_actor["age"] = actor.age one_actor["gender"] = actor.gender results.append(one_actor) if not results: abort(404) return jsonify({"success": True, "Actors": results}) @app.route('/movies', methods=['GET']) @requires_auth(permission='read:movies') def get_movies(payload): all_movies = movies.query.all() results = [] for movie in all_movies: one_movie = {} one_movie["id"] = movie.id one_movie["title"] = movie.title one_movie["release_date"] = movie.release_date results.append(one_movie) if not results: abort(404) return jsonify({"success": True, "Movies": results}) # DELETE / actors / and / movies/ @app.route('/actors/<int:id>', methods=['DELETE']) @requires_auth(permission='delete:actor') def delete_actor(payload, id): actor = actors.query.get(id) if not actor: abort(404) try: actor.delete() except Exception: abort(401) return jsonify({"success": True, "Deleted": id}) @app.route('/movies/<int:id>', methods=['DELETE']) @requires_auth(permission='delete:movie') def delete_movie(payload, id): movie = movies.query.get(id) if not movie: abort(404) try: movie.delete() except Exception: abort(401) return jsonify({"success": True, "Deleted": id}) # POST / actors and / movies and @app.route('/actors', methods=['POST']) @requires_auth(permission='add:actor') def post_actor(payload): data = request.get_json() if not data: abort(400) new_name = data.get("name") new_age = data.get("age") new_gender = data.get("gender") if not new_name or not new_age or not new_gender: abort(400) # Create a new actor new_actor = actors(name=new_name, age=new_age, gender=new_gender) try: new_actor.insert() except Exception: abort(401) # Query for new actor new_actor_id = new_actor.id new_actors = actors.query.get(new_actor_id) one_actor = {} one_actor["id"] = new_actors.id one_actor["name"] = new_actors.name one_actor["age"] = new_actors.age one_actor["gender"] = new_actors.gender return jsonify({"success": True, "Actor": one_actor}) @app.route('/movies', methods=['POST']) @requires_auth(permission='add:movie') def post_movie(payload): data = request.get_json() if not data: abort(400) new_title = data.get("title") new_release_date = data.get("release_date") if not new_title or not new_release_date: abort(400) # Create a new movie new_movie = movies(title=new_title, release_date=new_release_date) try: new_movie.insert() except Exception: abort(401) # Query for new actor new_movie_id = new_movie.id new_movies = movies.query.get(new_movie_id) one_movie = {} one_movie["id"] = new_movies.id one_movie["title"] = new_movies.title one_movie["release_date"] = new_movies.release_date return jsonify({"success": True, "Movie": one_movie}) # PATCH / actors / and / movies/ @app.route('/actors/<int:id>', methods=['PATCH']) @requires_auth(permission='change:actor') def patch_actor(payload, id): # Get actor to patch new_actor = actors.query.get(id) if not new_actor: abort(404) # Check what values are available to patch data = request.get_json() if 'name' in data: new_actor.name = data.get("name") if 'age' in data: new_actor.age = data.get("age") if 'gender' in data: new_actor.gender = data.get("gender") try: new_actor.update() except Exception: abort(401) patched_actor = actors.query.get(new_actor.id) one_actor = {} one_actor["id"] = patched_actor.id one_actor["name"] = patched_actor.name one_actor["age"] = patched_actor.age one_actor["gender"] = patched_actor.gender return jsonify({"success": True, "Actor": one_actor}) @app.route('/movies/<int:id>', methods=['PATCH']) @requires_auth(permission='change:movie') def patch_movie(payload, id): # Get actor to patch new_movie = movies.query.get(id) if not new_movie: abort(404) # Check what values are available to patch data = request.get_json() if 'title' in data: new_movie.title = data.get("title") if 'release_date' in data: new_movie.release_date = data.get("release_date") try: new_movie.update() except Exception: abort(401) patched_movie = movies.query.get(new_movie.id) one_movie = {} one_movie["id"] = new_movie.id one_movie["title"] = new_movie.title one_movie["release_date"] = new_movie.release_date return jsonify({"success": True, "Movie": one_movie}) # @TODO: Create error handlers for all expected errors \ # including 404 and 422. --> DONE @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "Not found" }), 404 @app.errorhandler(401) def unauthorized(error): return jsonify({ "success": False, "error": 401, "message": "Unauthorized" }), 401 @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "Unprocessable Entity" }), 422 @app.errorhandler(405) def method_not_allowed(error): return jsonify({ "success": False, "error": 405, "message": "Method not allowed" }), 405 @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "Bad Request error" }), 400 @app.errorhandler(500) def internal_server_error(error): return jsonify({ "success": False, "error": 500, "message": "Internal server error" }), 500 return app
def create_app(test_config=None): app = Flask(__name__) setup_db(app) CORS(app) """ @TODO: Set up CORS. Allow '*' for origins. Delete the sample route after completing the TODOs # """ cors = CORS(app) app.config["CORS_HEADERS"] = "Content-Type" """ @TODO: Use the after_request decorator to set Access-Control-Allow """ # CORS Headers @app.after_request def after_request(response): response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization,true") response.headers.add("Access-Control-Allow-Methods", "GET,PATCH,POST,DELETE,OPTIONS") return response """ @TODO: Create an endpoint to handle GET requests for all available categories. """ @app.route("/categories") def get_categories(): categories = db.session.query(Category).all() categories_dict = [cat.format() for cat in categories] if len(categories) == 0: abort(404) return jsonify({"success": True, "categories": categories_dict}) # """ # @TODO- Done: # Create an endpoint to handle GET requests for questions, # including pagination (every 10 questions). # This endpoint should return a list of questions, # number of total questions, current category, categories. @app.route("/questions") def get_paginated_questions(): # selection = Question.query.order_by(Question.id).all() selection = Question.query.all() questions = paginate_questions(request, selection) categories = db.session.query(Category).all() # categories = Category.query.all() categories_dict = {cat.id: cat.type for cat in categories} if len(questions) == 0: abort(404) return jsonify({ "success": True, "categories": categories_dict, "questions": questions, "total_questions": len(questions), "current_category": None, }) # """ # @TODO: # Create an endpoint to DELETE question using a question ID. @app.route("/questions/<int:id>", methods=["GET", "DELETE"]) def delete_question(id): # might page be out of index try: question = Question.query.get(id) except: abort(404) question.delete() return jsonify({"success": True, "deleted_id": id}) # """ # @TODO: # Create an endpoint to POST a new question, # which will require the question and answer text, # category, and difficulty score. # TEST: When you submit a question on the "Add" tab, # the form will clear and the question will appear at the end of the last page # of the questions list in the "List" tab. # """ @app.route("/add", methods=["POST"]) def add_question(): body = request.get_json() if not all([len(str(value)) > 0 for value in body.values()]): abort(422) question = Question( question=body.get("question"), answer=body["answer"], category=body["category"], difficulty=body["difficulty"], ) try: question.insert() questions = Question.query.all() print("question was added") current_questions = paginate_questions(request, questions) print(len(questions)) return jsonify({ "success": True, "created": question.id, "questions": current_questions, }) except: abort(404) # """ # @TODO: # Create a POST endpoint to get questions based on a search term. # It should return any questions for whom the search term # is a substring of the question. # TEST: Search by any phrase. The questions list will update to include # only question that include that string within their question. # Try using the word "title" to start. # """ # -- Error Handling -- @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "Not found" }), 404 @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "bad request" }), 400 @app.errorhandler(422) def unprocessable(error): return ( jsonify({ "success": False, "error": 422, "message": "unprocessable" }), 422, ) return app
def create_app(test_config=None): app = Flask(__name__) setup_db(app) CORS(app) # CORS Headers @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET,PATCH,POST,DELETE,OPTIONS') return response # --------------------------------------- GET CATEGORIES ------------------------------------------------------------- # @app.route("/categories") def get_categories(): try: categories = Category.query.all() categories_dict = { category.id: category.type for category in categories } if len(categories) == 0: abort(404) return jsonify( { "success": True, "categories": categories_dict, "total_categories": len(categories), }, 200) except: abort(500) # ------------------------------------------ GET QUESTIONS ----------------------------------------------------------- # @app.route("/questions") def get_questions(): try: selection = Question.query.order_by(Question.id).all() current_questions = paginate_questions(request, selection) if len(current_questions) == 0: abort(404) categories = Category.query.all() categories_dict = { category.id: category.type for category in categories } return jsonify( { "success": True, "questions": current_questions, "total_questions": len(selection), "current_category": None, "categories": categories_dict, }, 200) except: abort(500) # --------------------------------------- DELETE QUESTION ------------------------------------------------------------ # @app.route("/questions/<int:question_id>", methods=["DELETE"]) def delete_question(question_id): try: question = Question.query.filter( Question.id == question_id).one_or_none() if question is None: abort(422) question.delete() return jsonify( { "success": True, "deleted": question_id, "total_questions": len(Question.query.all()), }, 200) except: abort(500) # ------------------------------------------ ADD QUESTIONS ----------------------------------------------------------- # @app.route("/questions", methods=["POST"]) def add_questions(): body = request.get_json() new_question = body.get("question", None) new_answer = body.get("answer", None) new_category = body.get("category", None) new_difficulty = body.get("difficulty", None) if (new_question is None) or (new_answer is None) or ( new_category is None) or (new_difficulty is None): abort(422) try: add_question = Question(question=new_question, answer=new_answer, category=new_category, difficulty=new_difficulty) add_question.insert() return jsonify( { "success": True, "created": add_question.id, "total_questions": len(Question.query.all()), }, 200) except: abort(500) # --------------------------------------- SEARCH QUESTIONS ----------------------------------------------------------- # @app.route("/questions/search", methods=["POST"]) def search_questions(): body = request.get_json() search = body.get("search_term", None) if not search: abort(400) try: search_results = Question.query.filter( Question.question.ilike("%{}%".format(search))).all() return jsonify( { "success": True, "questions": [questions.format() for questions in search_results], "total_questions": len(search_results), }, 200) except: abort(422) # ------------------------------------ CATEGORIES FOR EACH QUESTION -------------------------------------------------- # @app.route("/categories/<int:category_id>/questions") def get_category_questions(category_id): try: selection = Question.query.filter( Question.category == category_id).all() questions = paginate_questions(request, selection) categories = Category.query.order_by(Category.type).all() if len(questions) == 0: abort(404) return jsonify( { "success": True, "questions": questions, "total_questions": len(Question.query.all()), "current_category": category_id, "categories": [category.format() for category in categories], }, 200) except: abort(500) # --------------------------------------------- Play Quiz ------------------------------------------------------------ # @app.route("/quizzes", methods=["POST"]) def play_quiz(): body = request.get_json() try: category = body.get("quiz_category", None) previous_questions = body.get("previous_questions", None) if (category is None) or (previous_questions is None): abort(400) if category["id"] == 0: available_questions = Question.query.filter( Question.id.notin_((previous_questions))).all() else: available_questions = Question.query.filter_by( category=category["id"]).filter( Question.id.notin_(previous_questions)).all() if len(available_questions) > 0: new_question = available_questions[random.randrange( 0, len(available_questions))].format() else: new_question = None return jsonify({ "success": True, "question": new_question, }, 200) except: abort(500) # --------------------------------------------- Error Handlers ------------------------------------------------------- # @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "Bad request" }), 400 @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "Not Found" }), 404 @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "Unprocessable Entity" }), 422 @app.errorhandler(405) def bad_request(error): return jsonify({ "success": False, "error": 405, "message": "Method Not Allowed" }), 405 @app.errorhandler(500) def bad_request(error): return jsonify({ "success": False, "error": 500, "message": "Internal Server Error" }), 500 return app
def create_app(test_config=None): # noqa # create and configure the app app = Flask(__name__) if test_config is None: # load the instance config, if it exists, when not testing app.config.from_pyfile(Path(__name__).absolute().parent / "config.py") setup_db(app) else: # load the test config if passed in app.config.from_mapping(test_config) cors = CORS(app, resources={r"/*": {"origins": "*"}}) # noqa @app.after_request def after_request(response): response.headers.add("Access-Control-Allow-Headers", "Content-Type, Authorization") response.headers.add("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE, OPTIONS") return response @app.route("/") def hello(): return jsonify(dict(r, message="hello world")) @app.route("/categories") def get_categories(): data = Category.query.all() return jsonify(dict(r, categories=[cat.format() for cat in data])) @app.route("/questions") def get_questions(): q = Question.query paged = q.paginate( per_page=QUESTIONS_PER_PAGE, error_out=True, max_per_page=QUESTIONS_PER_PAGE, ) try: return jsonify( dict( r, categories=[cat.format() for cat in Category.query.all()], total_questions=q.count(), questions=[quest.format() for quest in paged.items], current_category="All", )) except AttributeError: abort(404) """ At this point, when you start the application you should see questions and categories generated, ten questions per page and pagination at the bottom of the screen for three pages. Clicking on the page numbers should update the questions. """ @app.route("/questions/<int:id>", methods=["DELETE"]) def delete_question(id): try: question_to_delete = Question.query.get(id) question_to_delete.delete() return jsonify( dict(r, message=f"successfully deleted question with id {id}")) except AttributeError: abort(404) """ When you click the trash icon next to a question, the question will be removed. This removal will persist in the database and when you refresh the page. """ @app.route("/questions", methods=["POST"]) def post_question(): question = Question( question=request.json.get("question"), answer=request.json.get("answer"), difficulty=request.json.get("difficulty"), category=request.json.get("category"), ) question.insert() return jsonify( dict( r, message="Successfully added question to database", data=question.format(), )) """ When you submit a question on the "Add" tab, the form will clear and the question will appear at the end of the last page of the questions list in the "List" tab. """ @app.route("/questions/search", methods=["POST"]) def search_question(): search_term = request.json.get("searchTerm") results = Question.query.filter( Question.question.ilike(f"%{search_term}%")) return jsonify( dict( r, message="Successfully added question to database", questions=[question.format() for question in results.all()], total_questions=results.count(), current_category="All", )) """ Search by any phrase. The questions list will update to include only question that include that string within their question. Try using the word "title" to start. """ @app.route("/categories/<int:id>/questions") def category_questions(id): q = Question.query if id != 0: q = q.filter_by(category=str(id)) paged = q.paginate( per_page=QUESTIONS_PER_PAGE, error_out=True, max_per_page=QUESTIONS_PER_PAGE, ) try: category = Category.query.get(id) return jsonify( dict( r, message= f"Successfully fetched all questions with category id={id}", questions=[quest.format() for quest in paged.items], total_questions=q.count(), current_category=category.format()["type"] if category else "All", )) except AttributeError: abort(404) """ In the "List" tab / main screen, clicking on one of the categories in the left column will cause only questions of that category to be shown. """ @app.route("/quizzes/<int:id>", methods=["POST"]) def quiz_questions(id): r_json = request.json previous_questions = r_json.get("previous_questions") cat_questions = category_questions(id).json.get("questions") eligible_qs = [ q for q in cat_questions if q["id"] not in previous_questions ] question = None if eligible_qs: question = random.sample(eligible_qs, k=1)[0] return jsonify(dict(r, question=question)) """ In the "Play" tab, after a user selects "All" or a category, one question at a time is displayed, the user is allowed to answer and shown whether they were correct or not. """ @app.errorhandler(400) def not_understood(error): return ( jsonify( dict( success=False, status_code=400, message= "The request could not be understood by the server due to malformed syntax", )), 400, ) @app.errorhandler(404) def not_found(error): return ( jsonify( dict( success=False, status_code=404, message="The record you requested was not found", )), 404, ) @app.errorhandler(422) def not_processable(error): return ( jsonify( dict( success=False, status_code=422, message="Your request is not processable", )), 422, ) @app.errorhandler(500) def server_error(error): return ( jsonify( dict( success=False, status_code=500, message="The server encountered an unexpected condition " "which prevented it from fulfilling the request", )), 500, ) return app
def create_app(test_env: str = None): """ create and configure the app @type test_env: str relative path for .env or .env.test from __init__.py """ app = Flask(__name__) if test_env is None: # load the instance config, from app settings environment variable if not testing app.config.from_envvar('APP_SETTINGS') else: # load the test env file if passed in app.config.from_pyfile(test_env) # making sure .env file is loaded for db and other modules env_path = Path(flaskr_dir_path, test_env or '.env').absolute() load_dotenv(env_path) db = setup_db(app, {'name': os.getenv('DB_NAME')}) Migrate( app, db, # to make sure migrations inside backend folder directory=Path(flaskr_dir_path.parent, 'migrations').absolute()) # @DONE: Set up CORS. Allow '*' for origins. Delete the sample route after completing the TODOs cors = CORS(app, resources={ r"^/api/*": { 'origin': '*' }, }) # @DONE: Use the after_request decorator to set Access-Control-Allow @app.after_request def after_request(response: Response): if match(r'^/api/*', request.path ): # to make sure it's only allowed for api endpoints response.headers.add('Access-Control-Allow-Headers', 'Content-Type') response.headers.add('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE') return response @app.route('/api/categories') def get_all_categories(): """ @DONE: Create an endpoint to handle GET requests for all available categories. """ categories = Category.query.all() res = {'categories': {c.id: c.type for c in categories}} return jsonify(res) @app.route('/api/questions') def get_questions(): """ @DONE: Create an endpoint to handle GET requests for questions, including pagination (every 10 questions). This endpoint should return a list of questions, number of total questions, current category, categories. TEST: At this point, when you start the application you should see questions and categories generated, ten questions per page and pagination at the bottom of the screen for three pages. Clicking on the page numbers should update the questions. """ questions = Question.query.paginate(per_page=QUESTIONS_PER_PAGE) categories = Category.query.all() data = { 'questions': [q.format() for q in questions.items], 'total_questions': questions.total, 'categories': {c.id: c.type for c in categories}, 'current_category': None, } return jsonify(data) @app.route('/api/questions/<question_id>', methods=['DELETE']) def delete_question(question_id): """ @DONE: Create an endpoint to DELETE question using a question ID. TEST: When you click the trash icon next to a question, the question will be removed. This removal will persist in the database and when you refresh the page. """ question = Question.query.get_or_404(question_id) try: db.session.delete(question) db.session.commit() except SQLAlchemyError: db.session.rollback() db.session.close() raise InternalServerError return '', 204 @app.route('/api/questions', methods=['POST']) def add_question(): """ @DONE: Create an endpoint to POST a new question, which will require the question and answer text, category, and difficulty score. TEST: When you submit a question on the "Add" tab, the form will clear and the question will appear at the end of the last page of the questions list in the "List" tab. """ data: dict = request.get_json() question = data.get('question') answer = data.get('answer') category = data.get('category') difficulty = int(data.get('difficulty', 0)) # Simple validation all_values_exist = all([question, answer, category, difficulty]) category_exist = db.session.query( Category.query.filter_by(id=category).exists()).scalar() difficulty_in_range = 1 <= difficulty <= 5 if not all([all_values_exist, category_exist, difficulty_in_range]): message = "" if not all_values_exist: message = "There is an empty required field. " if not category_exist: message += "Category doesn't exist. " if not difficulty_in_range: message += "Difficulty range is between 1 to 5." return jsonify({'message': message}), 422 question_model = Question(question=question, answer=answer, category_id=category, difficulty=difficulty) try: db.session.add(question_model) db.session.commit() _id = question_model.id except SQLAlchemyError: db.session.rollback() db.session.close() raise InternalServerError return jsonify({ 'id': _id, }), 201 @app.route('/api/questions/search', methods=['POST']) def search_question(): """ @DONE: Create a POST endpoint to get questions based on a search term. It should return any questions for whom the search term is a substring of the question. TEST: Search by any phrase. The questions list will update to include only question that include that string within their question. Try using the word "title" to start. """ data = request.get_json() q = data.get('q') or '' questions = Question.query.filter( Question.question.ilike(f"%{q}%")).paginate( page=data.get('page') or 1, per_page=QUESTIONS_PER_PAGE) data = { 'questions': [q.format() for q in questions.items], 'total_questions': questions.total, 'current_category': None, } return jsonify(data) @app.route('/api/categories/<category_id>/questions') def get_questions_by_category(category_id): """ @DONE: Create a GET endpoint to get questions based on category. TEST: In the "List" tab / main screen, clicking on one of the categories in the left column will cause only questions of that category to be shown. """ category: Category = Category.query.get_or_404(category_id) questions = Question.query.filter_by(category_id=category_id).paginate( per_page=QUESTIONS_PER_PAGE) data = { 'questions': [q.format() for q in questions.items], 'total_questions': questions.total, 'current_category': category_id } return jsonify(data) @app.route('/api/quizzes', methods=['POST']) def quizzes_handler(): """ @DONE: Create a POST endpoint to get questions to play the quiz. This endpoint should take category and previous question parameters and return a random questions within the given category, if provided, and that is not one of the previous questions. TEST: In the "Play" tab, after a user selects "All" or a category, one question at a time is displayed, the user is allowed to answer and shown whether they were correct or not. """ data: dict = request.get_json() quiz_category = data.get('quiz_category') previous_questions = data.get('previous_questions') or [] questions = Question.query if quiz_category: questions = questions.filter_by(category_id=quiz_category) question = questions.filter( Question.id.notin_(previous_questions)).order_by( func.random()).first() data = {'question': question.format() if question else None} return jsonify(data) ''' @DONE: Create error handlers for all expected errors including 404 and 422. ''' # There is no need for this one for me @app.errorhandler(400) def handle_400(error): return jsonify({ "message": "Bad Request.", }), 400 @app.errorhandler(404) def handle_404(error): return jsonify({ "message": "Not found.", }), 404 # There is no need for this one for me @app.errorhandler(422) def handle_422(error): return jsonify({ "message": "Unprocessable Entity.", }), 422 @app.errorhandler(500) def handle_500(error): return jsonify({ "message": "Internal Server Error.", }), 500 return app
def create_app(test_config=None): # create and configure the app app = Flask(__name__) setup_db(app) # Set up CORS. Allow '*' for origins. CORS(app, resources={r"/api/*": {"origins": "*"}}) # Use the after_request decorator to set Access-Control-Allow @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS') response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') return response def paginate_questions(request, selection): page = request.args.get('page', 1, type=int) start = (page - 1) * QUESTIONS_PER_PAGE end = start + QUESTIONS_PER_PAGE items = [item.format() for item in selection] current_items = items[start:end] return current_items @app.route('/categories', methods=['GET']) def get_categories(): selection = Category.query.all() categories = [category.format() for category in selection] if len(categories) == 0: abort(404) return jsonify({ 'categories': categories, }), 200 @app.route('/questions', methods=['GET']) def get_questions(): selection = Question.query.order_by(Question.id).all() questions = paginate_questions(request, selection) current_category = [question['category'] for question in questions] current_category = list(set(current_category)) categories = [cat.format() for cat in Category.query.all()] if len(questions) == 0: abort(404) return jsonify({ 'questions': questions, 'total_questions': len(selection), 'categories': categories, 'current_category': current_category, }), 200 @app.route('/questions/<int:question_id>', methods=['DELETE']) def delete_question(question_id): try: question = Question.query.get(question_id) if not question: abort(404) question.delete() return jsonify({ 'success': True, 'deleted': question_id, }), 200 except Exception as e: abort(404) @app.route('/questions', methods=['POST']) def add_question(): try: data = request.json to_add = Question(data['question'], data['answer'], data['category'], data['difficulty']) to_add.insert() return jsonify({'success': True}), 200 except Exception as e: abort(422) @app.route('/questions/search', methods=['POST']) def search_questions(): try: search_term = request.json['searchTerm'] selection = Question.query.filter( Question.question.ilike('%' + search_term + '%')).all() search_results = paginate_questions(request, selection) return jsonify({ 'success': True, 'questions': search_results, 'total_questions': len(search_results), 'current_category': None }), 200 except Exception as e: abort(404) @app.route('/categories/<int:category_id>/questions', methods=['GET']) def get_questions_by_category(category_id): selection = Question.query.filter_by(category=category_id).all() questions = paginate_questions(request, selection) if len(questions) == 0: abort(404) return jsonify({ 'questions': questions, 'total_questions': len(selection), 'current_category': category_id, }) @app.route("/quizzes", methods=['POST']) def quizzes(): previous_questions = request.json.get('previous_questions') quiz_category = request.json.get('quiz_category') if previous_questions is None or quiz_category is None: abort(404) if quiz_category['id'] == 0: # all categories questions_by_category = Question.query. \ filter(Question.id.notin_(previous_questions)). \ all() else: # specific category questions_by_category = Question.query. \ filter_by(category=quiz_category['id']). \ filter(Question.id.notin_(previous_questions)). \ all() if questions_by_category: formatted_questions = \ [question.format() for question in questions_by_category] return jsonify({'question': formatted_questions[0]}), 200 else: return jsonify({'question': False}), 200 @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "resource not found" }), 404 @app.errorhandler(422) def not_found(error): return jsonify({ "success": False, "error": 422, "message": "Unprocessable" }), 422 return app
def create_app(test_config=None): # create and configure the app app = Flask(__name__) setup_db(app) app.config['SECRET_KEY'] = 'dev' app.config['CORS_HEADERS'] = 'Content-Type' ''' Set up CORS. Allow '*' for origins. ''' CORS(app, resources={r"/.*": {"origins": "*"}}) ''' Use the after_request decorator to set Access-Control-Allow headers ''' @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS') return response ''' Endpoint to GET all available categories. ''' @app.route("/categories") def retrieve_categories(): categories = Category.query.order_by(Category.type).all() if len(categories) == 0: abort(404) return jsonify({ "success": True, "categories": {category.id: category.type for category in categories} }) ''' Endpoint to handle GET requests for questions, including pagination (every 10 questions). This endpoint will return a list of questions, number of total questions, current category, categories. ''' @app.route('/questions') def retrieve_questions(): questions = Question.query.order_by(Question.id).all() current_questions = paginate_questions(request, questions) categories = Category.query.order_by(Category.id).all() if len(current_questions) == 0: abort(404) return jsonify({ "success": True, "questions": current_questions, "total_questions": len(questions), "categories": {category.id: category.type for category in categories}, "currentCategory": None }) ''' Endpoint to DELETE question using a question ID. ''' @app.route("/questions/<int:question_id>", methods=["DELETE"]) def delete_question(question_id): try: question_obj = Question.query.filter( Question.id == question_id).one() print(question_obj.format()) question_obj.delete() return jsonify({"success": True, "deleted": question_id}) except: abort(422) ''' Endpoint to POST a new question, which will require the question and answer text, category, and difficulty level. ''' @app.route("/questions", methods=["POST"]) def add_question(): body = request.get_json() if not ('question' in body and 'answer' in body and 'category' in body and 'difficulty' in body): abort(422) question_val = body.get('question') answer_val = body.get('answer') category_val = body.get('category') difficulty_val = body.get('difficulty') try: question_obj = Question(question_val, answer_val, category_val, difficulty_val) question_obj.insert() questions = Question.query.order_by(Question.id).all() current_questions = paginate_questions(request, questions) return jsonify({ "success": True, "created": question_obj.id, "questions": current_questions, "total_questions": len(questions) }) except: question_obj.rollback() abort(422) ''' Endpoint to get questions based on a search term sent using POST body. It returns one/more questions that matches the substring of the question. ''' @app.route("/questions/search", methods=["POST"]) def search_questions(): body = request.get_json() search_term = body.get('searchTerm') if search_term: questions_obj = Question.query.filter( Question.question.ilike("%" + search_term + "%")).all() if not questions_obj: abort(404) current_questions = paginate_questions(request, questions_obj) return jsonify({ "success": True, "questions": current_questions, "total_questions": len(questions_obj), "current_category": None }) else: abort(404) ''' GET endpoint to get questions based on category. ''' @app.route("/categories/<int:category_id>/questions", methods=["GET"]) def retrieve_questions_by_category(category_id): try: category_ids = [ cat.id for cat in Category.query.with_entities( Category.id, Category.type).order_by(Category.id).all() ] if category_id not in category_ids: abort(404) questions_obj = Question.query.filter( Question.category == str(category_id)).all() current_questions = paginate_questions(request, questions_obj) return jsonify({ "success": True, "questions": current_questions, "total_questions": len(questions_obj), "currentCategory": category_id }) except: abort(404) ''' POST endpoint to get questions to play the quiz. This endpoint takes category and previous question parameters and returns a random question within the given category, if provided, and that is not one of the previous questions. ''' @app.route("/quizzes", methods=["POST"]) def play_quiz(): try: body = request.get_json() if not ('previous_questions' in body and 'quiz_category' in body): abort(422) previous_questions = body.get('previous_questions') quiz_category = body.get('quiz_category') if quiz_category['type'] == 'click': available_questions = Question.query.filter( Question.id.notin_((previous_questions))).all() else: available_questions = Question.query.filter( Question.category == quiz_category['id']).filter( Question.id.notin_((previous_questions))).all() question = available_questions[random.randrange( 0, len(available_questions))].format( ) if len(available_questions) > 0 else None return jsonify({'success': True, 'question': question}) except: abort(422) ''' Error handlers for all expected errors including 404 and 422. ''' @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "bad request" }), 400 @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "resource not found" }), 404 @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "unprocessable" }), 422 @app.errorhandler(500) def internal_server_error(error): return jsonify({ "success": False, "error": 500, "messgae": "An internal error has occured" }), 500 return app
def create_app(test_config=None): app = Flask(__name__) setup_db(app) CORS(app) @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS') return response @app.route('/categories', Methods=['GET']) def get_categories(): categories = Category.query.order_by(Category.id).all() categories_list = {} for category in categories: categories_list[category.id] = category.type if (len(categories_list)) == 0: abort(404) return jsonify({ 'success': True, 'categories': categories_list }) @app.route('/questions', Methods=['GET']) def get_questions(): selection = Question.query.order_by(Question.id).all() current_questions = paginate_questions(request, selection) categories = Category.query.order_by(Category.id).all() categories_list = {} for category in categories: categories_list[category.id] = category.type if len(current_questions) == 0 or len(categories_list) == 0: abort(404) return jsonify({ 'success': True, 'questions': current_questions, 'total_questions': len(Question.query.all()), 'categories': categories_list, }) @app.route('/questions/<int:question_id>', Methods=['DELETE']) def delete_question(question_id): try: question = Question.query.filter(id=question_id).one_or_none() if (question is None): abort(404) question.delete() selection = Question.query.order_by(Question.id).all() current_questions = paginate_questions(request, selection) categories = Category.query.order_by(Category.id).all() categories_list = {} for category in categories: categories_list[category.id] = category.type return jsonify({ 'success': True, 'deleted': question_id, 'questions': current_questions, 'total_questions': len(Question.query.all()), 'categories': categories_list }) except: abort(422) @app.route('/questions', Methods=['POST']) def add_question(): data = request.get_json() question = data.get('question', None) answer = data.get('answer', None) difficulty = data.get('difficulty', None) category = data.get('category', None) try: question = Question(question=question, answer=answer, difficulty=difficulty, category=category) question.insert() return jsonify({ 'success': True }) except: abort(422) @app.route('/questions/serach', Methods=['POST']) def serach_questions(): data = request.get_json() search_term = data.get('searchTerm', None) questions = Question.query.filter( Question.question.ilike('%' + search_term + '%')).all() if len(questions) == 0: abort(404) return jsonify({ 'success': True, 'questions': questions, 'total_questions': len(questions) }) @app.route('/categories/<int:category_id>/questions', Methods=['GET']) def get_questions_by_category(category_id): category = Category.query.filter_by(id=category_id).one_or_none() if (category is None): abort(404) selection = Question.query.filter_by(category=Category.id).all() current_questions = paginate_questions(request, selection) return jsonify({ 'success': True, 'questions': current_questions, 'total_questions': len(Question.query.all()), 'current_category': category.type }) @app.route('/quizzes', Methods=['POST']) def quiz(): data = request.get_json() previous_questions = data.get('previous_questions') quiz_category = data.get('quiz_category') if ((quiz_category is None) or (previous_questions is None)): abort(400) if (quiz_category['id'] == 0): questions = Question.query.all() else: questions = Question.query.filter_by( category=quiz_category['id']).all() def get_random_question(): return questions[random.randrange(0, len(questions), 1)] next_question = get_random_question() used = True while used: if next_question.id in previous_questions: next_question = get_random_question() else: used = False return jsonify({ 'success': True, 'question': next_question.format() }) @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "bad request" }), 400 @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "resource not found" }), 404 @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "unprocessable" }), 422 return app
from flask_script import Manager from flask_migrate import Migrate, MigrateCommand from backend.flaskr.__init__ import create_app from backend.models import setup_db app = create_app() db = setup_db(app) migrate = Migrate(app, db) manager = Manager(app) manager.add_command('db', MigrateCommand) if __name__ == '__main__': manager.run()
def create_app(test_config=None): # create and configure the app app = Flask(__name__) setup_db(app) CORS(app) ''' Set up CORS. Allow '*' for origins. Delete the sample route after completing the TODOs ''' # cors = CORS(app, resources={r"/api/*": {"origins": "*"}}) ''' Use the after_request decorator to set Access-Control-Allow ''' @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET,PATCH,POST,DELETE,OPTIONS') return response ''' Create an endpoint to handle GET requests for all available categories. ''' # @app.route('/index' , methods=['GET', 'POST']) # def index(): # if request.method == 'GET': # place = request.args.get('place', None) # if place: # return place # return "No place information is given" # global_query categories = Category.query.all() questions = Question.query.all() def row2dict(row): d = {} for column in row.__table__.columns: d[column.id] = str(getattr(row, column.type)) return d QUESTION_PER_SHELF = 10 def paginate_qustion(request, selection): page = request.args.get('page', 1, type=int) start = (page - 1) * QUESTION_PER_SHELF end = start + QUESTION_PER_SHELF question = [q.format() for q in selection] current_question = question[start:end] return current_question @app.route('/categories') def get_all_catogaries(): try: id_list = [] type_list = [] for u in categories: id_list.append(u.id) type_list.append(u.type) cat_dict = dict(zip(id_list, type_list)) except: # abort 404 if no categories found if (len(cat_dict) == 0): abort(404) # return data to view return jsonify({'success': True, 'categories': cat_dict}) ''' Create an endpoint to handle GET requests for questions, including pagination (every 10 questions). This endpoint should return a list of questions, number of total questions, current category, categories. ''' @app.route('/questions') def get_questions(): current_questions = Question.query.first() current_categories = Category.query.filter( Category.type == current_questions.category).one_or_none().format() try: error = False question_list = [] for q in Question.query.filter_by( category=current_categories.get('type')): question_list.append(q) except: error = True abort(422) finally: p_q = paginate_qustion(request, current_questions.format()) return jsonify({ 'Catogaries': [c.format() for c in categories], 'Current category': current_categories, 'totalQustion': len(question_list), 'questions': current_questions.format() }) ''' Create an endpoint to DELETE question using a question ID. TEST: When you click the trash icon next to a question, the question will be removed. This removal will persist in the database and when you refresh the page. ''' @app.route('/questions/<int:id>/delete', methods=['DELETE']) @cross_origin() def delete_question(id): # delete via id question = Question.query.filter(Question.id == id).one_or_none() if question is None: abort(404) question.delete() return jsonify({ "total books": len(Question.query.all()), 'qustions state': 'deleted successfully', 'delete Queston ID': id }) # return render_template(url_for(get_questions)) ''' Create an endpoint to POST a new question, which will require the question and answer text, category, and difficulty score. ''' @app.route('/create/question', methods=['POST']) def create_question(): try: body = request.get_json() new_qus = body.get('question', None) new_answer = body.get('answer', None) catog = body.get('rate', None) diff = body.get('difficulty', None) question = Question(question=new_qus, answer=new_answer, category=catog, difficulty=diff) question.insert() except: abort(202) finally: return jsonify({ "successful": True, "total books": len(Question.query.all()), "messege": 'Created', 'created_question_id': question.id }) ''' Create a POST endpoint to get questions based on a search term. It should return any questions for whom the search term is a substring of the question. ''' @app.route('/search/question', methods=['GET', 'POST']) def search_question(): body = request.get_json() search_term = body.get('title', None) qSearch = Question.query.filter( Question.question.ilike("%{}%".format(search_term))).all() # method search_qustion declared in the Question class r = Question.search_qustion(search_term, qSearch) count = len(qSearch) if count is None: abort(202) return jsonify({ "count": count, "question searched": [r for r in qSearch] }) ''' Create a GET endpoint to get questions based on category. ''' @app.route('/questions/categories') def category_question(): error = False try: id_list_q = [] question_list = [] answer_list = [] category_list = [] difficulty_list = [] #questions and categories in global variable for Questions and Categories query defined top of the code for u in questions: id_list_q.append(u.id) question_list.append(u.question) answer_list.append(u.answer) category_list.append(u.category) difficulty_list.append(u.difficulty) # categories_dict = dict(zip(question_list, category_list)) question_list_per_cat = [] cat_for_qus = [] for c in categories: for q in questions: if q.category == c.type: question_list_per_cat.append(q.question) cat_for_qus.append(c.type) # qus_cat_dict = dict(zip(cat_for_qus, question_list_per_cat)) allQues = [] for q in zip(cat_for_qus, question_list_per_cat): allQues.append("{}: {}".format(*q)) except: error = True abort(404) finally: return jsonify({ 'statues: ': 'Successfully done', 'error': error, 'Questions per catogery': [q for q in allQues] }) ''' Create a POST endpoint to get questions to play the quiz. This endpoint should take category and previous question parameters and return a random questions within the given category, if provided, and that is not one of the previous questions. ''' @app.route('/questions/quiz', methods=['POST', 'GET']) def quiz_question(): question_list_per_cat = [] cat_for_qus = [] # body = request.get_json() # answer = body.get('answer', None) try: #questions and categories in global variable for Questions and Categories query defined top of the code for c in categories: for q in questions: if c.type == q.category: question_list_per_cat.append(q.question) cat_for_qus.append(q.category) answer = 'football' except: abort(404) finally: random_qus = random.choice(question_list_per_cat) random_qus_query = Question.query.filter( Question.question == random_qus).one_or_none() if random_qus_query.answer == answer: true_or_false = True else: true_or_false = False return jsonify({ 'random question ': random_qus, 'Answer': true_or_false, 'categories': cat_for_qus }) ''' Error handlers for all expected errors including 404, 422 and 500. ''' # error handlers @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "sorry not found try another things" }), 404 @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "unprocessable " }), 422 @app.errorhandler(500) def unprocessable(error): return jsonify({ "success": False, "error": 500, "message": "internal server error " }), 500 return app
def create_app(test_config=None): # create and configure the app app = Flask(__name__) setup_db(app) ''' Set up CORS. Allow '*' for origins. Delete the sample route after completing the TODOs ''' CORS(app) ''' Use the after_request decorator to set Access-Control-Allow ''' @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type, Authorization') response.headers.add('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE, OPTIONS') return response ''' Create an endpoint to handle GET requests for all available categories. ''' @app.route('/categories', methods=['GET']) def retrieve_all_categories(): categories = Category.query.all() formatted_categories = [category.format() for category in categories] if categories is None: abort(404) else: return jsonify({ 'Success': True, 'Categories': formatted_categories, 'Total_Nums': len(formatted_categories) }) ''' @TODO: Create an endpoint to handle GET requests for questions, including pagination (every 10 questions). This endpoint should return a list of questions, number of total questions, current category, categories. ''' @app.route('/questions', methods=['GET']) def retrive_all_questions_by_page(): nums_per_page = 10 page = request.args.get('page', 1, type=int) start = (page - 1) * nums_per_page end = start + nums_per_page questions = Question.query.all() if questions is None: abort(404) else: formatted_questions = [question.format() for question in questions] return jsonify({ 'success': True, 'questions': formatted_questions[start:end], 'total_nums': len(formatted_questions), 'current_page': page }) ''' @TODO: TEST: At this point, when you start the application you should see questions and categories generated, ten questions per page and pagination at the bottom of the screen for three pages. Clicking on the page numbers should update the questions. ''' ''' @TODO: Create an endpoint to DELETE question using a question ID. TEST: When you click the trash icon next to a question, the question will be removed. This removal will persist in the database and when you refresh the page. ''' @app.router('/questions/<int:question_id>', methods=['DELETE']) def delete_specific_question(question_id): question = Question.query.filter( Question.id == question_id).one_or_none() if question is None: abort(404) else: return jsonify({'success': True, 'question': question.format()}) ''' @TODO: Create an endpoint to POST a new question, which will require the question and answer text, category, and difficulty score. TEST: When you submit a question on the "Add" tab, the form will clear and the question will appear at the end of the last page of the questions list in the "List" tab. ''' @app.router('/questions', methods=['POST']) def post_new_question(): body = request.get_json() new_content = body.get('question', None) new_answer = body.get('answer', None) new_category = body.get('category', None) new_score = body.get('score', None) search_term = body.get('search', None) if search_term is None: try: question = Question(question=new_content, answer=new_answer, category=new_category, difficulty=new_score) question.insert() return jsonify({ 'success': True, 'created': question.id, 'question': question, 'total_questions': len(Question.query.all()) }) except: abort(422) else: questions = Question.query.filter( Question.question.ilike('%' + search_term + '%')).all() if questions is None: abort(404) else: formatted_questions = [ question.format() for question in questions ] return jsonify({ 'success': True, 'questions': formatted_questions }) ''' @TODO: Create a POST endpoint to get questions based on a search term. It should return any questions for whom the search term is a substring of the question. TEST: Search by any phrase. The questions list will update to include only question that include that string within their question. Try using the word "title" to start. ''' ''' @TODO: Create a GET endpoint to get questions based on category. TEST: In the "List" tab / main screen, clicking on one of the categories in the left column will cause only questions of that category to be shown. ''' @app.router('/questions/categories/<string:category_name>', methods=['GET']) def retrive_questions_for_specific_category(category_name): questions = Question.query.filter( Question.category == category_name).all() if questions is None: abort(404) else: formatted_questions = [question.format() for question in questions] return jsonify({ 'success': True, 'category': category_name, 'questions': formatted_questions, 'total_nums': len(formatted_questions) }) ''' @TODO: Create a POST endpoint to get questions to play the quiz. This endpoint should take category and previous question parameters and return a random questions within the given category, if provided, and that is not one of the previous questions. TEST: In the "Play" tab, after a user selects "All" or a category, one question at a time is displayed, the user is allowed to answer and shown whether they were correct or not. ''' @app.router('/questions/random', methods=['POST']) def get_random_question(): body = request.get_json() previous_questions_id = body.get('previous_questions', None) category = body.get('category', None) if category is None: abort(404) else: questions = Question.query.filter( Question.category == category, ~Question.id.in_(previous_questions_id)).all() if questions is None: abort(404) else: formatted_questions = [ question.format() for question in questions ] formatted_previous_questions_ids = [ question.id for question in previous_questions_id ] length = len(formatted_questions) index = random.randint(0, length - 1) formatted_previous_questions_ids.append({id: index}) return jsonify({ 'success': True, 'question': formatted_questions[index], 'previous_questions': formatted_previous_questions_ids }) ''' @TODO: Create error handlers for all expected errors including 404 and 422. ''' @app.errorhandler(404) def not_fount(error): return jsonify({ 'success': False, 'error': 404, 'message': 'Not Found' }), 404 @app.errorhandler(422) def not_fount(error): return jsonify({ 'success': False, 'error': 422, 'message': 'Unprocessable Entity' }), 422 return app
def create_app(): # app = Flask(__name__) setup_db(app) cors = CORS(app, resources={r"/*": {"origins": "*"}}) # CORS Headers @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET,POST,DELETE,PATCH,INFO') return response # Endpoints ''' Get /actors requires get:actors permission returns success boolean and an array of actors or appropriate status code indicating reason for failure ''' @app.route('/actors') @requires_auth('get:actors') def actors(payload): actors_list = Actors.query.all() actors_formatted = [actor.format() for actor in actors_list] return jsonify({'success': True, 'actors': actors_formatted}) ''' POST /actors creates a new actor requires create:actor permission paramter : a json object containts actor's attribue : string name, int age, string gender, and boolean is_available returns success boolean and new actor's id or appropriate status code indicating reason for failure ''' @app.route('/actors', methods=['POST']) @requires_auth('create:actor') def add_actor(payload): # read form data from post request body = request.get_json() if (not body): abort(400) # read post data name = body.get('name', None) age = body.get('age', None) gender = body.get('gender', None) # check if all required data is available, # if so, then add the actor added = False new_actor_id = 0 if (name and age and gender): # create Actor instance using form data new_actor = Actors(name=name, age=age, gender=gender) # add new actor to db added = False try: new_actor.insert() new_actor_id = new_actor.id added = True except: db.session.rollback() finally: db.session.close() else: # in case one or more of required fields are missing, # return 422 error abort(422) # return json response for success return jsonify({ 'success': added, 'inserted': new_actor_id, }) ''' GET /actors/<int : actor_id> get actor's info requires get:actor-info permission paramter : no parameter returns success boolean and actor's info whose Id = actor_id or appropriate status code indicating reason for failure ''' @app.route('/actors/<int:actor_id>', methods=['GET']) @requires_auth('get:actor-info') def get_actor_info(payload, actor_id): # get list of actor identified by actor_id actor = Actors.query.get(actor_id) # if actor does not exist, return 404 code if (not actor): abort(404) # if exist, return json object with success and actor's info return jsonify({ 'success': True, 'info': actor.format(), }) ''' DELETE /actors/<int : actor_id> deletes an actor requires delete:actor permission paramter : no parameter returns success boolean and the Id of the deleted actor or appropriate status code indicating reason for failure ''' @app.route('/actors/<int:actor_id>', methods=['DELETE']) @requires_auth('delete:actor') def delete_actor(payload, actor_id): # check if actor exist by querying Actors by the actor_id actor = Actors.query.get(actor_id) deleted = False if (actor): # if actor exist, try delete try: actor.delete() deleted = True except: db.session.rollback() finally: db.session.close() else: # if actor not exist, return 404 code abort(404) # return json response for success, with the deleted actor's id return jsonify({ 'success': deleted, 'deleted': actor_id, }) ''' PATCH /actors/<int : actor_id> modify an actor's info requires edit:actor permission paramter : no parameter returns success boolean and the Id of the edited actor or appropriate status code indicating reason for failure ''' @app.route('/actors/<int:actor_id>', methods=['PATCH']) @requires_auth('edit:actor') def update_actor(payload, actor_id): actor_to_update = Actors.query.get(actor_id) # check if actor exist, if not return 404 code if (not actor_to_update): abort(404) # read form data from post request body = request.get_json() # check if data not provided, then return 422 code if (not body): abort(422) # read post data name = body.get('name', None) age = body.get('age', None) gender = body.get('gender', None) # check for each attribute if provided # updated provided info updated = False if (name): actor_to_update.name = name if (age): actor_to_update.age = age if (gender): actor_to_update.gender = gender try: actor_to_update.update() updated = True except: db.session.rollback() abort(422) finally: db.session.close() # return json response for success with the updated actor's id return jsonify({ 'success': updated, 'updated': actor_id, }) ''' GET /actors/<actor_id>/movies get a list of all actor's movies requires get:actor-movies permission paramter : no parameter returns success boolean and a list of all movies or appropriate status code indicating reason for failure ''' @app.route('/actors/<actor_id>/movies') @requires_auth('get:actor-movies') def get_actor_movies(payload, actor_id): actor = Actors.query.get(actor_id) # check if actor exist, if not return 404 code if (not actor): abort(404) # prepare a list of movies for that actor movies = [movie.format() for movie in actor.movies] # return json response with success and movie's list return jsonify({'success': True, 'movies': movies}) ''' GET /movies get a list of all movies requires get:movies permission paramter : no parameter returns success boolean and a list of all movies or appropriate status code indicating reason for failure ''' @app.route('/movies') @requires_auth('get:movies') def get_movies(payload): # get all movies list movies = Movies.query.all() # prepare as formatted objects, to jsonify movies_formatted = [movie.format() for movie in movies] # return json reponse with success and movies list return jsonify({'success': True, 'movies': movies_formatted}) ''' GET /movies/<int:movie_id> get a movie's info requires get:movie-info permission paramter : no parameter returns success boolean and movie's info or appropriate status code indicating reason for failure ''' @app.route('/movies/<int:movie_id>') @requires_auth('get:movie-info') def get_movie_info(payload, movie_id): # query Movies to get movie identified by movie_id movie = Movies.query.get(movie_id) # if movie not exist, return 404 code if (not movie): abort(404) # return json reponse with success and movie's info return jsonify({'success': True, 'movie': movie.format()}) ''' POST /movies creates a new movie requires create:movie permission paramter : a json object contains the info of the movie returns success boolean and the new movie's id or appropriate status code indicating reason for failure ''' @app.route('/movies', methods=['POST']) @requires_auth('create:movie') def create_movie(payload): # get request paramtetes body = request.get_json() # if not provided, return 400 code if (not body): abort(400) # read parameters title = body.get('title', None) release_date = body.get('release_date', None) movie_category = body.get('movie_category', None) movie_rating = body.get('movie_rating', None) created = False new_movie_id = 0 # check all required attribute is provided if (title and release_date and movie_category and movie_rating): try: # prepare new movie object from provided data and insert to db new_movie = Movies(title=title, release_date=release_date, movie_rating=movie_rating, movie_category=movie_category) new_movie.insert() created = True new_movie_id = new_movie.id except: db.session.rollback() finally: db.session.close() else: # if not all required attributes provided, return 422 code abort(422) # return json reponse with success and new movie's id return jsonify({'success': created, 'new_movie_id': new_movie_id}) ''' PATCH /movies/<int:movie_id> get a movie's info requires edit:movie permission paramter : a json object of the new info to be updated returns success boolean and the id of the movie just updated or appropriate status code indicating reason for failure ''' @app.route('/movies/<int:movie_id>', methods=['PATCH']) @requires_auth('edit:movie') def update_movie(payload, movie_id): # get the movie to be updated movie_to_update = Movies.query.get(movie_id) # if movie not exist, return 404 if (not movie_to_update): abort(404) # read request data body = request.get_json() # if data not provided, return 400 if (not body): abort(400) # read data and update availabe attributes title = body.get('title', None) release_date = body.get('release_date', None) movie_category = body.get('movie_category', None) movie_rating = body.get('movie_rating', None) updated = False if (title): movie_to_update.title = title if (release_date): movie_to_update.release_date = release_date if (movie_category): movie_to_update.movie_category = movie_category if (movie_rating): movie_to_update.movie_rating = movie_rating # try saving changes try: movie_to_update.update() updated = True except: db.session.rollback() finally: db.session.close() # return json with success result and the movie's id just updated return jsonify({'success': updated, 'updated': movie_id}) ''' GET /movies/<int:movie_id>/actors get a list of all movie's actors requires get:movie-actors permission paramter : no parameter returns success boolean and a list of all movie's actor or appropriate status code indicating reason for failure ''' @app.route('/movies/<int:movie_id>/actors') @requires_auth('get:movie-actors') def get_movie_actors(payload, movie_id): # get the movie to read actors list movie = Movies.query.get(movie_id) # if movie not available, return 404 code if (not movie): abort(404) # prepare a list of all movie's actors actors = [actor.format() for actor in movie.actors] # return json response with successs and actors list return jsonify({'success': True, 'actors': actors}) ''' POST /movies/<int:movie_id>/actors add an actor to a movie's actors list requires add:movie-actor permission paramter : the id of the actor to be added returns success boolean and the id of the movie and the actor or appropriate status code indicating reason for failure ''' @app.route('/movies/<int:movie_id>/actors', methods=['POST']) @requires_auth('add:movie-actor') def add_movie_actors(payload, movie_id): # get the movie to add actors for movie = Movies.query.get(movie_id) # if movie not exist, return 404 code if (not movie): abort(404) # read request body = request.get_json() # if request not available, return 400 code if (not body): abort(400) # get the actor's id from request actor_id = body.get("actor_id", 0) # get the actor identified by actor_id actor = Actors.query.get(actor_id) # if actor not exist, return 402 code if (not actor): abort(422) added = False # try to add actor to movie's actors list try: movie.actors.append(actor) db.session.commit() added = True except: db.session.rollback() finally: db.session.close() # return json response with success result, movie's id and actor's id return jsonify({ 'success': added, 'movie': movie_id, 'actor': actor_id }) ''' DELETE /movies/<int:movie_id>/actors remove an actor to a movie's actors list requires delete:movie-actor permission paramter : the id of the actor to be removed returns success boolean and the id of the movie and the actor or appropriate status code indicating reason for failure ''' @app.route('/movies/<int:movie_id>/actors', methods=['DELETE']) @requires_auth('delete:movie-actor') def delete_movie_actors(payload, movie_id): # get the movie to delete actor from movie = Movies.query.get(movie_id) # if movie not exist, return 404 code if (not movie): abort(404) # read request body = request.get_json() # if request not available, return 400 code if (not body): abort(400) # get the actor's id from request actor_id = body.get("actor_id", 0) # get the actor identified by actor_id actor = Actors.query.get(actor_id) # if actor not exist, return 402 code if (not actor): abort(422) # try to delete actor to movie's actors list deleted = False try: movie.actors.remove(actor) db.session.commit() deleted = True except: db.session.rollback() finally: db.session.close() # return json response with success result, movie's id and actor's id return jsonify({ 'success': deleted, 'movie': movie_id, 'deleted': actor_id }) ''' DELETE /movies/<int:movie_id> delete a movie requires delete:movie permission paramter : no parameter returns success boolean and the id of the movie just deleted or appropriate status code indicating reason for failure ''' @app.route('/movies/<int:movie_id>', methods=['DELETE']) @requires_auth('delete:movie') def delete_movie(payload, movie_id): # get the movie to be deleted movie_to_delete = Movies.query.get(movie_id) # if the movie not exist, return 404 if (not movie_to_delete): abort(404) deleted = False # try to delete the movie try: movie_to_delete.delete() deleted = True except: db.session.rollback() finally: db.session.close() # return json response with success result and movie's id just deleted return jsonify({'success': deleted, 'deleted': movie_id}) # Errors Handlers ''' Create error handlers for all expected errors ''' # error handler for 404 (Not Found) @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "resource not found" }), 404 # error handler for 422 (unprocessable) @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "unprocessable" }), 422 # error handler for 400 (bad request) @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "bad request" }), 400 # error handler for 405 (method not allowed) @app.errorhandler(405) def method_not_allowed(error): return jsonify({ "success": False, "error": 405, "message": "method not allowed" }), 405 # error handler for 500 @app.errorhandler(500) def server_error(error): return jsonify({ "success": False, "error": 500, "message": "internal server error" }), 500 # error handling for AuthError @app.errorhandler(AuthError) def auth_error_handler(auth_error): status_code = auth_error.status_code message = auth_error.error return jsonify({ "success": False, "error": status_code, "message": message }), status_code return app
def create_app(test_config=None): app = Flask(__name__) setup_db(app) CORS(app) # CORS Headers @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS') return response # Fetches a list of categories @app.route('/categories') def get_categories(): categories = Category.query.all() formatted_categories = {} for category in categories: formatted_categories[f'{category.id}'] = f'{category.type}' return jsonify({'success': True, 'categories': formatted_categories}) # Fetches a list of paginated questions, where each page contains 10 @app.route('/questions') def get_questions(): questions = Question.query.all() paginated_questions = paginate_questions(request, questions) categories = Category.query.all() formatted_categories = [category.format() for category in categories] if len(paginated_questions) == 0: abort(404) return jsonify({ 'success': True, 'questions': paginated_questions, 'total_questions': len(questions), 'current_category': None, 'categories': formatted_categories }) # Deletes the provided question id and returns a list of paginated questions @app.route('/questions/<int:question_id>', methods=['DELETE']) def delete_question(question_id): question = Question.query.filter( Question.id == question_id).one_or_none() if question is None: abort(404) try: question.delete() questions = Question.query.all() paginated_questions = paginate_questions(request, questions) return jsonify({ 'success': True, 'deleted': question_id, 'questions': paginated_questions, 'total_questions': len(questions) }) except: abort(422) # Creates a question from request's json body and returns a list of paginated questions @app.route('/questions', methods=['POST']) def create_question(): body = request.get_json() try: question = Question(question=body.get('question', None), answer=body.get('answer', None), category=body.get('category', None), difficulty=body.get('difficulty', None)) question.insert() questions = Question.query.all() paginated_questions = paginate_questions(request, questions) return jsonify({ 'success': True, 'created': question.id, 'questions': paginated_questions, 'total_questions': len(questions) }) except: abort(422) # Fetches a list of paginated questions based on the provided search term using form @app.route('/questions/search', methods=['POST']) def search_question(): search_term = request.form['search_term'] questions = Question.query.filter( Question.question.ilike(f'%{search_term}%')).all() if not len(questions): abort(422) paginated_questions = paginate_questions(request, questions) return jsonify({ 'success': True, 'questions': paginated_questions, 'total_questions': len(questions), }) # Fetches a list of paginated questions based on the provided Category Type using request variable @app.route('/categories/<category_type>/questions', methods=['POST']) def get_questions_by_category(category_type): questions = Question.query.filter( Question.category.ilike(category_type)).all() if not len(questions): abort(422) paginated_questions = paginate_questions(request, questions) return jsonify({ 'success': True, 'questions': paginated_questions, 'total_questions': len(questions) }) # Fetches a single question to play the quiz @app.route('/play', methods=['POST']) def play(): body = request.get_json() previous_questions = body['previous_questions'] previous_questions_ids = [] for question in previous_questions: previous_questions_ids.append(question.id) category_id = body["quiz_category"]["id"] if category_id == 0: if previous_questions is not None: questions = Question.query.filter( Question.id.notin_(previous_questions_ids)).all() else: questions = Question.query.all() else: category = Category.query.get(category_id) if previous_questions is not None: questions = Question.query. \ filter( Question.id.notin_(previous_questions_ids), Question.category == category_id) \ .all() else: questions = Question.query.filter( Question.category == category.id).all() random_question = random.choice(questions).format() return jsonify({'success': True, 'question': random_question}) @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "resource not found" }), 404 @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "unprocessable" }), 422 @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "bad_request" }), 400 @app.errorhandler(405) def method_not_allowed(error): return jsonify({ "success": False, "error": 405, "message": "method_not_allowed" }), 405 return app
def create_app(test_config=None): # create and configure the app app = Flask(__name__) setup_db(app) CORS(app, resources={r"*": {"origins": "*"}}) @app.after_request def after_request(response): """ Handler for after a request has been made. :param response: """ response.headers.add( 'Access-Control-Allow-Headers', 'Content-Type, Authorization' ) response.headers.add( 'Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS' ) return response @app.route('/questions') def list_questions(): """ Get questions for a given page. :return: """ page = request.args.get('page', 1, type=int) selection = Question.query.all() selection, total_selection_count = paginate_selection(selection, page=page, limit=QUESTIONS_PER_PAGE) categories = { category.id: category.type for category in Category.query.all() } if len(selection) == 0: abort(StatusCode.HTTP_404_NOT_FOUND.value) return jsonify({ 'success': True, 'current_category': None, 'categories': categories, 'questions': format_selection(selection), 'total_questions': total_selection_count }) @app.route('/questions', methods=['POST']) def create_question(): """ Add a question to database. :return: """ question = request.get_json() if not question: abort(StatusCode.HTTP_400_BAD_REQUEST.value) question = Question(**question) question.insert() return jsonify({ 'success': True, 'id': question.id }), StatusCode.HTTP_201_CREATED.value @app.route('/questions/<int:question_id>', methods=['DELETE']) def delete_question(question_id): """ Delete question by given id. :param question_id: :return: """ question = Question.query.get(question_id) if not question: abort(StatusCode.HTTP_404_NOT_FOUND.value) question.delete() return jsonify({ 'success': True }), StatusCode.HTTP_204_NO_CONTENT.value @app.route('/questions/filter', methods=['POST']) def filter_questions(): """ Return the list of questions after filteration. :return: """ page = request.args.get('page', 1, type=int) search_term = request.get_json().get('searchTerm') or '' selection = Question.query.filter(Question.question.ilike(f'%{search_term}%')).all() selection, total_selection_count = paginate_selection(selection, page=page, limit=QUESTIONS_PER_PAGE) return jsonify({ 'success': True, 'questions': format_selection(selection), 'total_questions': total_selection_count, }) @app.route('/categories') def list_categories(): """ Return the categories with id and type. :return: """ result = { "success": True, "categories": { category.id: category.type for category in Category.query.all() } } return jsonify(result) @app.route('/categories/<int:category_id>/questions') def get_questions_by_category(category_id): """ Get questions by category. :param category_id: :return: """ category = Category.query.get(category_id) if not category: abort(StatusCode.HTTP_404_NOT_FOUND.value) page = request.args.get('page', 1, type=int) selection = Question.query.filter_by(category=category_id).all() selection, total_selection_count = paginate_selection(selection, page=page, limit=QUESTIONS_PER_PAGE) return jsonify({ "success": True, "questions": format_selection(selection), "total_questions": total_selection_count, "current_category": category.format(), }) @app.route('/quizzes', methods=['POST']) def play_quiz(): """ Play quiz route to get questions for quizzes. :return: """ request_data = request.get_json() previous_questions = request_data.get('previous_questions', []) quiz_category = request_data.get('quiz_category') if not quiz_category: abort(StatusCode.HTTP_400_BAD_REQUEST.value) category_id = quiz_category.get('id', None) if category_id: questions = Question.query.filter_by(category=category_id) else: questions = Question.query questions = format_selection(questions.filter(~Question.id.in_(previous_questions)).all()) random_question = random.choice(questions) if questions else None return jsonify({ 'question': random_question, 'success': True }) @app.errorhandler(StatusCode.HTTP_400_BAD_REQUEST.value) def bad_request(error): """ Error handler for bad request with status code 400. :param: error :return: """ return jsonify({ 'success': False, 'error': StatusCode.HTTP_400_BAD_REQUEST.value, 'message': StatusCode.HTTP_400_BAD_REQUEST.name }), StatusCode.HTTP_400_BAD_REQUEST.value @app.errorhandler(StatusCode.HTTP_401_UNAUTHORIZED.value) def unauthorized(error): """ Error handler for unauthorized with status code 401. :param: error :return: """ return jsonify({ 'success': False, 'error': StatusCode.HTTP_401_UNAUTHORIZED.value, 'message': StatusCode.HTTP_401_UNAUTHORIZED.name }), StatusCode.HTTP_401_UNAUTHORIZED.value @app.errorhandler(StatusCode.HTTP_403_FORBIDDEN.value) def forbidden(error): """ Error handler for forbidden with status code 403. :param: error :return: """ return jsonify({ 'success': False, 'error': StatusCode.HTTP_403_FORBIDDEN.value, 'message': StatusCode.HTTP_403_FORBIDDEN.name }), StatusCode.HTTP_403_FORBIDDEN.value @app.errorhandler(StatusCode.HTTP_404_NOT_FOUND.value) def not_found(error): """ Error handler for not found with status code 404. :param: error :return: """ return jsonify({ 'success': False, 'error': StatusCode.HTTP_404_NOT_FOUND.value, 'message': StatusCode.HTTP_404_NOT_FOUND.name }), StatusCode.HTTP_404_NOT_FOUND.value @app.errorhandler(StatusCode.HTTP_405_METHOD_NOT_ALLOWED.value) def method_not_allowed(error): """ Error handler for method not allowed with status code 405. :param: error :return: """ return jsonify({ 'success': False, 'error': StatusCode.HTTP_405_METHOD_NOT_ALLOWED.value, 'message': StatusCode.HTTP_405_METHOD_NOT_ALLOWED.name }), StatusCode.HTTP_405_METHOD_NOT_ALLOWED.value @app.errorhandler(StatusCode.HTTP_422_UNPROCESSABLE_ENTITY.value) def unprocessable_entity(error): """ Error handler for unprocessable entity with status code 422. :param: error :return: """ return jsonify({ 'success': False, 'error': StatusCode.HTTP_422_UNPROCESSABLE_ENTITY.value, 'message': StatusCode.HTTP_422_UNPROCESSABLE_ENTITY.name }), StatusCode.HTTP_422_UNPROCESSABLE_ENTITY.value @app.errorhandler(StatusCode.HTTP_500_INTERNAL_SERVER_ERROR.value) def internal_server_error(error): """ Error handler for internal server error with status code 500. :param: error :return: """ return jsonify({ 'success': False, 'error': StatusCode.HTTP_500_INTERNAL_SERVER_ERROR.value, 'message': StatusCode.HTTP_500_INTERNAL_SERVER_ERROR.name }), StatusCode.HTTP_500_INTERNAL_SERVER_ERROR.value return app
def create_app(test_config=None): # create and configure the app app = Flask(__name__) setup_db(app) # set up CORS, allowing all origins CORS(app, resources={'/': {'origins': '*'}}) @app.after_request def after_request(response): ''' Sets access control. ''' response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS') return response ''' @TODO: Create an endpoint to handle GET requests for all available categories. ''' @app.route("/categories") def categories(): # get all categories and add to dict categories = Category.query.all() categories_dict = {} for category in categories: categories_dict[category.id] = category.type # abort 404 if no categories found if (len(categories_dict) == 0): abort(404) # return data to view return jsonify({'success': True, 'categories': categories_dict}) ''' @TODO: Create an endpoint to handle GET requests for questions, including pagination (every 10 questions). This endpoint should return a list of questions, number of total questions, current category, categories. TEST: At this point, when you start the application you should see questions and categories generated, ten questions per page and pagination at the bottom of the screen for three pages. Clicking on the page numbers should update the questions. ''' @app.route("/questions") def questions(): # get all questions and paginate selection = Question.query.all() total_questions = len(selection) current_questions = paginate_questions(request, selection) # get all categories and add to dict categories = Category.query.all() categories_dict = {} for category in categories: categories_dict[category.id] = category.type # abort 404 if no questions if (len(current_questions) == 0): abort(404) # return data to view return jsonify({ 'success': True, 'questions': current_questions, 'total_questions': total_questions, 'categories': categories_dict }) ''' @TODO: Create an endpoint to DELETE question using a question ID. TEST: When you click the trash icon next to a question, the question will be removed. This removal will persist in the database and when you refresh the page. ''' @app.route("/questions/<int:id>", methods=["DELETE"]) def delete_question(id): try: selected_row = Question.query.filter_by(id=id).one_or_none() if selected_row is None: abort(404) selected_row.delete() # return data to view return jsonify({'success': True}) except: abort(422) ''' @TODO: Create an endpoint to POST a new question, which will require the question and answer text, category, and difficulty score. TEST: When you submit a question on the "Add" tab, the form will clear and the question will appear at the end of the last page of the questions list in the "List" tab. ''' @app.route("/questions", methods=["POST"]) def create_questions(): details = request.get_json() # if search term is present if "searchTerm" in details: search_term = details.get('searchTerm') # query the database using search term selection = Question.query.filter( Question.question.ilike(f'%{search_term}%')).all() if len(selection) == 0: abort(404) # paginate the results paginated = paginate_questions(request, selection) # return results return jsonify({ 'success': True, 'questions': paginated, 'total_questions': len(Question.query.all()) }) else: #save data question = details["question"] answer = details["answer"] difficulty = details["difficulty"] category = details["category"] # ensure all fields have data if ((question is None) or (answer is None) or (difficulty is None) or (category is None)): abort(422) try: # create and insert new question question = Question(question=question, answer=answer, difficulty=difficulty, category=category) question.insert() # return data to view return jsonify({'success': True}) except: # abort unprocessable if exception abort(422) ''' @TODO: Create a GET endpoint to get questions based on category. TEST: In the "List" tab / main screen, clicking on one of the categories in the left column will cause only questions of that category to be shown. ''' @app.route('/categories/<int:id>/questions') def get_questions_by_category(id): category = Category.query.filter_by(id=id).one_or_none() if (category is None): abort(400) selection = Question.query.filter_by(category=category.id).all() paginated = paginate_questions(request, selection) return jsonify({ 'success': True, 'questions': paginated, 'total_questions': len(Question.query.all()), 'current_category': category.type }) ''' @TODO: Create a POST endpoint to get questions to play the quiz. This endpoint should take category and previous question parameters and return a random questions within the given category, if provided, and that is not one of the previous questions. TEST: In the "Play" tab, after a user selects "All" or a category, one question at a time is displayed, the user is allowed to answer and shown whether they were correct or not. ''' @app.route('/quizzes', methods=['POST']) def get_random_quiz_question(): ''' Handles POST requests for playing quiz. ''' # load the request body body = request.get_json() previous = body.get('previous_questions') category = body.get('quiz_category') if ((category is None) or (previous is None)): abort(400) if (category['id'] == 0): questions = Question.query.all() # load questions for given category else: questions = Question.query.filter_by(category=category['id']).all() total = len(questions) def check_if_used(question): used = False for q in previous: if (q == question.id): used = True return used question = questions[random.randrange(0, len(questions), 1)] while (check_if_used(question)): question = questions[random.randrange(0, len(questions), 1)] if (len(previous) == total): return jsonify({'success': True}) return jsonify({'success': True, 'question': question.format()}) @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "resource not found" }), 404 @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "unprocessable" }), 422 @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "bad request" }), 400 return app