Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Shopiley committed Sep 6, 2022
1 parent a7f6671 commit 7426fd5
Show file tree
Hide file tree
Showing 13 changed files with 19,910 additions and 20,519 deletions.
212 changes: 207 additions & 5 deletions backend/flaskr/__init__.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,71 @@
import os
from flask import Flask, request, abort, jsonify
from selectors import SelectSelector
from flask import Flask, request, abort, jsonify, render_template, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from flask_cors import CORS, cross_origin
from flask_migrate import Migrate
import random

from models import setup_db, Question, Category
# from models import setup_db, Question, Category
from models import *

QUESTIONS_PER_PAGE = 10

def paginate_questions(request, selection):
page = request.args.get("page", 1, type=int)
start = (page - 1) * QUESTIONS_PER_PAGE
end = start + QUESTIONS_PER_PAGE

formatted_questions = [question.format() for question in selection]
current_questions = formatted_questions[start:end]

return current_questions

def create_app(test_config=None):
# create and configure the app
app = Flask(__name__)
setup_db(app)
migrate = Migrate(app, db)
CORS(app)

"""
@TODO: Set up CORS. Allow '*' for origins. Delete the sample route after completing the TODOs
@TODO: Set up CORS. Allow '*' for origins. Delete the sample route after completing the TODOs
"""
# CORS(app, resources={r'*/questions/*' : {'origins': '*'}})


"""
@TODO: Use the after_request decorator to set Access-Control-Allow
@ 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,PUT,POST,DELETE,OPTIONS'
)
return response

"""
@TODO:
Create an endpoint to handle GET requests
for all available categories.
"""
@app.route('/categories')
def get_all_categories():
categories = Category.query.all()

if len(categories) == 0:
abort(404)

formatted_categories = {category.id:category.type for category in categories}

return jsonify({
'success': True,
'categories' : formatted_categories
})


"""
Expand All @@ -40,14 +80,65 @@ def create_app(test_config=None):
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 index():
questions = Question.query.order_by(Question.id).all()
paginated_questions = paginate_questions(request, questions)

categories = Category.query.all()
formatted_categories = {category.id:category.type for category in categories}


if len(paginated_questions) == 0 | (len(categories) == 0):
abort(404)

current_category = paginated_questions[0]['category']

return jsonify({
'success': True,
'questions': paginated_questions,
'total_questions': len(questions),
'current_category': current_category,
'categories': formatted_categories
})

"""
@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:question_id>', methods=['GET', 'DELETE'])
def delete_question(question_id):
try:
question = Question.query.get(question_id)
print(question)

if question is None:
abort(404)

else:
if request.method == 'DELETE':
question.delete()
questions = Question.query.order_by(Question.id).all()
paginated_questions = paginate_questions(request, questions)

return jsonify(
{
'success': True,
'questions': paginated_questions
}
)

else:
return jsonify({
'success': True,
'question': question.format()
})

except:
abort(422)

"""
@TODO:
Expand All @@ -70,6 +161,51 @@ def create_app(test_config=None):
only question that include that string within their question.
Try using the word "title" to start.
"""
@app.route('/questions', methods=['POST'])
def create_question():
body = request.get_json()

question = body.get("question", None)
answer = body.get("answer", None)
difficulty = body.get("difficulty", 1)
category = body.get("category", 1)
search_term = body.get("searchTerm", None)

# try:
if search_term:

search_result = Question.query.order_by(Question.id).filter(Question.question.ilike("%{}%".format(search_term))).all()

paginated_questions = paginate_questions(request, search_result)

if len(paginated_questions) == 0:
abort(404)

current_category = paginated_questions[0]['category']

return jsonify({
'success': True,
'questions': paginated_questions,
'total_questions': len(search_result),
'current_category': current_category,
})

else:
new_question = Question(
question = question,
answer = answer,
difficulty = difficulty,
category = category
)
new_question.insert()

return jsonify({
'success': True
})

# except:
# abort(422)
# return redirect(url_for('index'))

"""
@TODO:
Expand All @@ -79,6 +215,22 @@ def create_app(test_config=None):
categories in the left column will cause only questions of that
category to be shown.
"""
@app.route('/categories/<int:category_id>/questions')
def get_questions_by_category(category_id):
questions = Question.query.filter(Question.category == category_id).all()
paginated_questions = paginate_questions(request, questions)
current_category = paginated_questions[0]['category']

if len(paginated_questions) == 0:
abort(404)

return jsonify({
'success': True,
'questions': paginated_questions,
'total_questions': len(questions),
'current_category': current_category
})


"""
@TODO:
Expand All @@ -91,12 +243,62 @@ def create_app(test_config=None):
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 play_qiuz():
body = request.get_json()

quiz_category = body.get('quiz_category', None)
previous_questions = body.get('previous_questions', None)

category_id = quiz_category['id']

if category_id == 0:
questions_in_category = Question.query.all()

else:
questions_in_category = Question.query.filter(Question.category == category_id).all()

selected_ids = [question.id for question in questions_in_category]
print(selected_ids)
random_id = random.choice([r_id for r_id in selected_ids if r_id not in previous_questions])
random_question = Question.query.get(random_id)

return jsonify({
'question': random_question.format()
})




"""
@TODO:
Create error handlers for all expected errors
including 404 and 422.
"""
@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 not_found(error):
return (
jsonify({"success": False, "error": 405, "message": "method not allowed"}),
405,
)

return app

1 change: 1 addition & 0 deletions backend/migrations/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Single-database configuration for Flask.
50 changes: 50 additions & 0 deletions backend/migrations/alembic.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# A generic, single database configuration.

[alembic]
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s

# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false


# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic,flask_migrate

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[logger_flask_migrate]
level = INFO
handlers =
qualname = flask_migrate

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
Loading

0 comments on commit 7426fd5

Please sign in to comment.