Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update #34

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
292 changes: 206 additions & 86 deletions backend/flaskr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,92 +11,212 @@
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__)
setup_db(app)

"""
@TODO: Set up CORS. Allow '*' for origins. Delete the sample route after completing the TODOs
"""

"""
@TODO: Use the after_request decorator to set Access-Control-Allow
"""

"""
@TODO:
Create an endpoint to handle GET requests
for all available 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.

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.
"""

"""
@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.
"""

"""
@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.
"""

"""
@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.
"""

"""
@TODO:
Create error handlers for all expected errors
including 404 and 422.
"""
with app.app_context():
setup_db(app)

"""
@TODO: Set up CORS. Allow '*' for origins. Delete the sample route after completing the TODOs
Use Flask-CORS to enable cross-domain requests and set response headers.
"""
CORS(app, resources={r"/*": {"origins": "*"}})


"""
@TODO: Use the after_request decorator to set Access-Control-Allow
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.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


"""
@TODO:
Create an endpoint to handle GET requests
for all available categories.
"""
@app.route('/categories')
def get_categories():
# get all category objects
categories = Category.query.all()
# create a dictionary with the category id as the key and the category type as the value
formatted_categories = {category.id: category.type for category in categories}
# return the dictionary as a json response
return jsonify({
'success': True,
'categories': 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.

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 get_questions():
page = request.args.get('page', 1, type=int)
start = (page - 1) * QUESTIONS_PER_PAGE
end = start + QUESTIONS_PER_PAGE
questions = Question.query.all()
formatted_questions = [question.format() for question in questions]
categories = Category.query.all()
formatted_categories = {category.id: category.type for category in categories}
return jsonify({
'success': True,
'questions': formatted_questions[start:end],
'total_questions': len(formatted_questions),
'categories': formatted_categories,
'current_category': None
})


"""
@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=['DELETE'])
def delete_question(question_id):
try:
question = Question.query.get(question_id)
question.delete()
return jsonify({
'success': True,
'deleted': question_id
})
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_question():
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)
try:
question = Question(question=new_question, answer=new_answer, category=new_category, difficulty=new_difficulty)
question.insert()
return jsonify({
'success': True,
'created': question.id
})
except:
abort(422)


"""
@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.
"""
@app.route('/questions/search', methods=['POST'])
def search_questions():
body = request.get_json()
search_term = body.get('searchTerm', None)
questions = Question.query.filter(Question.question.ilike(f'%{search_term}%')).all()
formatted_questions = [question.format() for question in questions]
return jsonify({
'success': True,
'questions': formatted_questions,
'total_questions': len(formatted_questions),
'current_category': None
})


"""
@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:category_id>/questions')
def get_questions_by_category(category_id):
questions = Question.query.filter(Question.category == category_id).all()
formatted_questions = [question.format() for question in questions]
return jsonify({
'success': True,
'questions': formatted_questions,
'total_questions': len(formatted_questions),
'current_category': category_id
})

"""
@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_quiz_question():
body = request.get_json()
previous_questions = body.get('previous_questions', None)
quiz_category = body.get('quiz_category', None)
if quiz_category['id'] == 0:
questions = Question.query.all()
else:
questions = Question.query.filter(Question.category == quiz_category['id']).all()
formatted_questions = [question.format() for question in questions]
def get_random_question():
return formatted_questions[random.randint(0, len(formatted_questions) - 1)]
question = get_random_question()
while question['id'] in previous_questions:
question = get_random_question()
return jsonify({
'success': True,
'question': question
})

"""
@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': 'Not found'
}), 404

return app




11 changes: 9 additions & 2 deletions backend/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@
from flask_sqlalchemy import SQLAlchemy
import json

database_name = 'trivia'
database_path = 'postgresql://{}/{}'.format('localhost:5432', database_name)
db_username = 'student'
db_password = '123456'
db_name = 'trivia'

# Create the connection string
database_path = f"postgresql://{db_username}:{db_password}@localhost:5432/{db_name}"

# Old Query
#database_path = 'postgresql://{db_username}/{db_password}'.format('localhost:5432', database_name)

db = SQLAlchemy()

Expand Down
22 changes: 19 additions & 3 deletions backend/test_flaskr.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
from flaskr import create_app
from models import setup_db, Question, Category

db_username = 'student'
db_password = '123456'
db_name = 'trivia'

class TriviaTestCase(unittest.TestCase):
"""This class represents the trivia test case"""
Expand All @@ -14,8 +17,9 @@ 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)
#self.database_name = "trivia_test"
#self.database_path = "postgres://{}/{}".format('localhost:5432', self.database_name)
self.database_path = f"postgresql://{db_username}:{db_password}@localhost:5432/{db_name}"
setup_db(self.app, self.database_path)

# binds the app to the current context
Expand All @@ -26,13 +30,25 @@ def setUp(self):
self.db.create_all()

def tearDown(self):

"""Executed after reach test"""
pass

"""
TODO
Write at least one test for each test for successful operation and for expected errors.
"""
def test_get_paginated_questions(self):
res = self.client().get('/questions')
data = json.loads(res.data)

self.assertEqual(res.status_code, 200)
self.assertEqual(data['success'], True)
self.assertTrue(data['total_questions'])
self.assertTrue(len(data['questions']))
self.assertTrue(len(data['categories']))




# Make the tests conveniently executable
Expand Down