def create_question(): data = request.get_json() fields = { 'question': data.get('question') or None, # Force none if empty string 'answer': data.get('answer') or None, # Force none if empty string 'difficulty': data.get('difficulty', None), 'category': data.get('category', None), } # Check required fields are populated validate_required_fields(fields) # Validate specific fields errors = [] if fields['difficulty'] <= 0 or fields['difficulty'] > 5: errors.append( {'difficulty': 'Difficulty must be an integer between 1 and 5'}) category = Category.query.get(int(fields['category'])) if not category: errors.append({'category': 'Category is not supported'}) if errors: abort(400, errors) # Create the question try: with db_session(): question = Question(**fields) question.insert() return jsonify(question.format()) # TODO: Test side effect return 422 except: abort(422)
def add_questions(): resposta = request.get_json() print(resposta) cat = int(resposta['category']) print(cat) question = Question(question=resposta['question'], answer=resposta['answer'], category=cat + 1, difficulty=resposta['difficulty']) Question.insert(question) return jsonify({'success': True})
def test_delete_question_by_id(self): question = Question(question='How long is a mile in kilometers?', answer='Answer', category='Math', difficulty=5) question.insert() res = self.client().delete(f'/questions/{question.id}') assert res.status_code == 200 data = json.loads(res.data) assert data == {'success': True, 'deleted': question.id}
def create_question(): try: body = request.get_json() search_term = body.get('search', None) if body == {}: abort(422) # question search if search_term is not None: search = "{}".format(search_term.lower()) search_results = Question.query.filter( Question.question.ilike('%' + search + '%')).all() formatted_search_results = [ question.format for question in search_results ] paginated_results = paginate_questions(request, search_results) return jsonify({ 'success': True, 'questions': paginated_results, 'total_questions': len(search_results) }) # question add else: question = body.get('question', None) answer = body.get('answer', None) category = body.get('category', None) difficulty = body.get('difficulty', None) if question is None or answer is None or category is None or difficulty is None: abort(422) new_question = Question(question=question, answer=answer, category=category, difficulty=difficulty) new_question.insert() selection = Question.query.order_by('id').all() paged_questions = paginate_questions(request, selection) return jsonify({ 'success': True, 'created': new_question.id, 'questions': paged_questions, 'total_questions': len(selection) }), 201 except (): abort(422)
def test_delete_question(self): question = Question(question='new question', answer='new answer', difficulty=1, category=1) question.insert() record_id = question.id res = self.client().delete(f'/questions/{record_id}/') data = json.loads(res.data) self.assertEqual(res.status_code, 200) self.assertEqual(data['success'], True) self.assertEqual(data['deleted'], record_id)
def create_questions(db, count=12): some_category = Category(type='Some Category') some_category.insert() questions = [] for index in range(count): question = Question(question=f'The question? {index}', answer='The answer', category=some_category.id, difficulty=5) question.insert() questions.append(question) return questions, some_category
def post_new_question(): body = request.get_json() if not body: # posting an envalid json should return a 400 error. abort(400) if (body.get('question') and body.get('answer') and body.get('difficulty') and body.get('category')): # posted a new question new_question = body.get('question') new_answer = body.get('answer') new_category = body.get('category') new_difficulty = body.get('difficulty') # insure that difficulty is only from 1 to 5 if not 1 <= int(new_difficulty) < 6: abort(400) try: # insert the new question to the database question = Question(new_question, new_answer, new_category, new_difficulty) question.insert() # query the database for all questions page = request.args.get('page', 1, type=int) selection = Question.query.order_by(Question.id).paginate( page, current_app.config['QUESTIONS_PER_PAGE'], True) total_questions = selection.total if total_questions == 0: # no questions were found, return a 404 error. abort(404) current_questions = [ question.format() for question in selection.items ] return jsonify({ 'success': True, 'id': question.id, 'question': question.question, 'questions': current_questions, 'total_questions': total_questions }) except: # creating the question failed, rollback and close the connection db.session.rollback() abort(422) else: # anything else posted in the body should return a 400 error abort(400)
def create_question(): body = request.get_json() # if the body containts a search term execute the search function # otherwise continue with creating a new question search_term = body.get("searchTerm") if search_term: return search_questions(search_term) else: for key in ["question", "answer", "category", "difficulty"]: if body.get(key) is None: abort(422) new_question = body.get("question", None) answer = body.get("answer", None) category = body.get("category", None) difficulty = body.get("difficulty", None) try: question = Question( question=new_question, answer=answer, category=category, difficulty=difficulty, ) question.insert() questions = Question.query.order_by(Question.id).all() current_questions = paginate_questions(request, questions) return jsonify({ "success": True, "created": question.id, "questions": current_questions, "total_questions": len(questions), }) except: abort(422)
def create_question(): try: data = request.get_json() if request.is_json else None if data is None or len(data) == 0: abort(400) ''' verify that data does contain (question and answer, category, and difficulty), as well as no additional key/value data that is not required, and finally verify that data has not empty strings. Note: I executed it this way so it can dynamically verify needed data from user if i added extra column to Question Model. all i should do when adding a new column to Question is to represent this coulumn as name:type pair in required_question_args ''' required_question_args = { 'question': str, 'answer': str, 'difficulty': int, 'category': str } for k in data: if len(data) != len(required_question_args) \ or k not in required_question_args \ or type(data[k]) is not required_question_args[k] \ or isinstance(data[k], str) and len(data[k]) == 0: abort(400) new_question = Question(**data) new_question.insert() return jsonify({'success': True, 'created': new_question.id}) except BadRequest: abort(400) except: abort(500)
def test_questions_search(self): question1 = Question(question='How long is a mile in kilometers?', answer='Answer', category='Math', difficulty=5) question1.insert() question2 = Question(question='How many centimeters is an inch?', answer='Answer', category='Math', difficulty=5) question2.insert() search_term = 'kilometers' res = self.client().post('/questions/search', json={'searchTerm': search_term}) assert res.status_code == 200 data = json.loads(res.data) assert len(data['questions']) == 1 assert data == { 'questions': [question1.format()], 'total_questions': 1 }
def test_question_list(self): """Test that hitting the question list GET endpoint returns a list of question(s), as well as categories""" third_question = Question('Why is science?', 'Only Bill Nye knows', self.science.id, 5) third_question.insert() # Test list with pagination only returns 2 result resp = self.client.get('/api/questions') assert resp.status_code == 200 data = json.loads(resp.data) assert 'categories' in data assert 'questions' in data assert 'total_questions' in data assert data == { 'categories': { '1': 'Science', '2': 'Geography' }, 'questions': [ { 'answer': 'Nobody knows', 'category': 1, 'difficulty': 5, 'id': 1, 'question': 'What is science?' }, { 'answer': 'Probably not', 'category': 2, 'difficulty': 5, 'id': 2, 'question': 'Is geography real?' } ], 'total_questions': 3 }
def add_question(): """ Adds a question :return: Id of the question that has been created """ body = request.get_json() if ('question' not in body) or ('answer' not in body) or \ ('difficulty' not in body) or ('category' not in body): abort(422) try: question = Question(question=body.get('question'), answer=body.get('answer'), difficulty=body.get('difficulty'), category=body.get('category'), ) question.insert() return jsonify({ 'success': True, 'created': question.id, }) except: abort(422)
class TriviaApiTestCase(BaseTestClass): def setUp(self): super().setUp() # Add some categories self.science = Category('Science') self.geography = Category('Geography') db.session.add(self.science) db.session.add(self.geography) db.session.commit() # Add some questions self.science_question = Question('What is science?', 'Nobody knows', self.science.id, 5) self.geog_question = Question('Is geography real?', 'Probably not', self.geography.id, 5) self.science_question.insert() self.geog_question.insert() def test_question_list(self): """Test that hitting the question list GET endpoint returns a list of question(s), as well as categories""" third_question = Question('Why is science?', 'Only Bill Nye knows', self.science.id, 5) third_question.insert() # Test list with pagination only returns 2 result resp = self.client.get('/api/questions') assert resp.status_code == 200 data = json.loads(resp.data) assert 'categories' in data assert 'questions' in data assert 'total_questions' in data assert data == { 'categories': { '1': 'Science', '2': 'Geography' }, 'questions': [ { 'answer': 'Nobody knows', 'category': 1, 'difficulty': 5, 'id': 1, 'question': 'What is science?' }, { 'answer': 'Probably not', 'category': 2, 'difficulty': 5, 'id': 2, 'question': 'Is geography real?' } ], 'total_questions': 3 } def test_question_by_category_404(self): """Test that a 404 is returned when a category id is not found""" resp = self.client.get(f'/api/categories/5000/questions') assert resp.status_code == 404 assert json.loads(resp.data) == { 'error': '404: Not Found', 'message': 'Category matching the provided ID was not found' } def test_question_by_category(self): """ Test that only questions for a given category will be returned when hitting the GET category questions endpoint. """ assert Category.query.count() == 2 cat = self.science resp = self.client.get(f'/api/categories/{cat.id}/questions') assert resp.status_code == 200 data = json.loads(resp.data) assert 'current_category' in data assert data['current_category'] == cat.id assert 'questions' in data assert data['questions'][0]['category'] == cat.id # Check response shape assert data == { 'current_category': 1, 'questions': [ { 'answer': 'Nobody knows', 'category': 1, 'difficulty': 5, 'id': 1, 'question': 'What is science?' } ], 'total_questions': 1 } def test_category_list(self): """Test that hitting the categories list GET endpoint returns a dictionary of supported categories""" resp = self.client.get('/api/categories') assert resp.status_code == 200 data = json.loads(resp.data) assert data == { 'categories': { '1': 'Science', '2': 'Geography' } } def test_create_question_fields(self): """Test field validations on question create POST endpoint""" url = '/api/questions' # Test missing fields data = {} resp = self.client.post(url, json=data) assert resp.status_code == 400 assert json.loads(resp.data) == { 'error': '400: Bad Request', 'message': [ {'question': 'Field is required'}, {'answer': 'Field is required'}, {'difficulty': 'Field is required'}, {'category': 'Field is required'} ] } data = { 'question': 'Why is science?', 'answer': 'Only Bill Nye knows', 'category': 5000, 'difficulty': 0 } # Test difficulty and cateory validation resp = self.client.post(url, json=data) assert resp.status_code == 400 assert json.loads(resp.data) == { 'error': '400: Bad Request', 'message': [ {'difficulty': 'Difficulty must be an integer between 1 and 5'}, {'category': 'Category is not supported'} ] } @patch('flaskr.api.views.Question.insert') def test_create_question_422(self, mock_insert): """Test that a 422 error is returned when there is an error raised while processing question creation""" # Force raise an exeception while creating question in DB mock_insert.side_effect = Exception assert Question.query.count() == 2 data = { 'question': 'Why is science?', 'answer': 'Only Bill Nye knows', 'category': self.science.id, 'difficulty': 3 } resp = self.client.post('/api/questions', json=data) assert resp.status_code == 422 assert json.loads(resp.data) == { 'error': '422: Unprocessable Entity', 'message': 'The request was well-formed but was unable to be followed due to semantic errors.' } # Ensure changes dont persist assert Question.query.count() == 2 def test_create(self): """Test successful creation of a question""" assert Question.query.count() == 2 data = { 'question': 'Why is science?', 'answer': 'Only Bill Nye knows', 'category': self.science.id, 'difficulty': 3 } resp = self.client.post('/api/questions', json=data) assert resp.status_code == 200 resp_data = json.loads(resp.data) assert resp_data == { 'answer': 'Only Bill Nye knows', 'category': 1, 'difficulty': 3, 'id': 3, 'question': 'Why is science?' } # Ensure changes persist in DB assert Question.query.count() == 3 question = Question.query.get(resp_data['id']) assert question is not None assert question.format() == resp_data def test_delete_question_404(self): """Test the trying to delete a non-existent question id returns a 404""" resp = self.client.delete('/api/questions/5000') assert resp.status_code == 404 assert json.loads(resp.data) == { 'error': '404: Not Found', 'message': 'Question matching the provided ID was not found for delete' } @patch('flaskr.api.views.Question.delete') def test_delete_question_500(self, mock_delete): """Test that a 500 error is returned when there is an error raised while processing question deletion""" # Force raise an exeception while deleting question in DB mock_delete.side_effect = Exception assert Question.query.count() == 2 resp = self.client.delete(f'/api/questions/{self.geog_question.id}') assert resp.status_code == 500 assert json.loads(resp.data) == { 'error': '500: Internal Server Error', 'message': 'The server encountered an internal error and was unable to complete your request. ' 'Either the server is overloaded or there is an error in the application.' } # Ensure delete does not persist in DB assert Question.query.count() == 2 def test_delete_question(self): """Test question deletion response and persists in the database""" assert Question.query.count() == 2 resp = self.client.delete(f'/api/questions/{self.geog_question.id}') assert resp.status_code == 204 # Ensure changes persist in DB assert Question.query.count() == 1 assert Question.query.get(self.geog_question.id) is None def test_search(self): """ Test that question search can search by: - partial category match - partial question match - partial answer match - Specify a category search under """ url = '/api/questions/search' search = { 'searchTerm': 'sci' } # test category contains resp = self.client.post(url, json=search) assert resp.status_code == 200 assert json.loads(resp.data) == { 'questions': [ {'answer': 'Nobody knows', 'category': 1, 'difficulty': 5, 'id': 1, 'question': 'What is science?'} ], 'total_questions': 1 } # Test question contains search['searchTerm'] = 'what is' resp = self.client.post(url, json=search) assert resp.status_code == 200 assert json.loads(resp.data) == { 'questions': [ {'answer': 'Nobody knows', 'category': 1, 'difficulty': 5, 'id': 1, 'question': 'What is science?'} ], 'total_questions': 1 } # Test answer contains search['searchTerm'] = 'nobody' resp = self.client.post(url, json=search) assert resp.status_code == 200 assert json.loads(resp.data) == { 'questions': [ {'answer': 'Nobody knows', 'category': 1, 'difficulty': 5, 'id': 1, 'question': 'What is science?'} ], 'total_questions': 1 } # test within category new_question = Question('Why is science?', 'Only Bill Nye knows', self.science.id, 5) new_question.insert() search['categoryId'] = self.science.id search['searchTerm'] = 'knows' resp = self.client.post(url, json=search) assert resp.status_code == 200 assert json.loads(resp.data) == { 'questions': [ {'answer': 'Nobody knows', 'category': 1, 'difficulty': 5, 'id': 1, 'question': 'What is science?'}, {'answer': 'Only Bill Nye knows', 'category': 1, 'difficulty': 5, 'id': 3, 'question': 'Why is science?'} ], 'total_questions': 2 } def test_quiz_question_categorized(self): """Test get random quiz question from specific category""" url = '/api/quizzes' previous_questions = [] data = { 'previous_questions': previous_questions, 'quiz_category': self.science.id } # There is only 1 possible question in the science category resp = self.client.post(url, json=data) assert resp.status_code == 200 resp_data = json.loads(resp.data) assert resp_data == { 'question': { 'answer': 'Nobody knows', 'category': 1, 'difficulty': 5, 'id': 1, 'question': 'What is science?' } } # Get depleted questions previous_questions.append(resp_data['question']['id']) data['previous_questions'] = previous_questions resp = self.client.post(url, json=data) assert resp.status_code == 200 resp_data = json.loads(resp.data) assert resp_data['question'] is None def test_quiz_question_uncategorized(self): """Test get random quiz question from any category""" url = '/api/quizzes' previous_questions = [] data = {'previous_questions': previous_questions} resp = self.client.post(url, json=data) assert resp.status_code == 200 resp_data = json.loads(resp.data) # Check response for question that was returned assert 'question' in resp_data assert 'id' in resp_data['question'] question1 = Question.query.get(resp_data['question']['id']) assert question1 is not None assert resp_data['question'] == question1.format() # Get next question previous_questions.append(question1.id) data['previous_questions'] = previous_questions resp = self.client.post(url, json=data) assert resp.status_code == 200 resp_data = json.loads(resp.data) question2 = Question.query.get(resp_data['question']['id']) assert question2 is not None assert question2.id not in previous_questions # Get depleted questions previous_questions.append(question2.id) data['previous_questions'] = previous_questions resp = self.client.post(url, json=data) assert resp.status_code == 200 resp_data = json.loads(resp.data) assert resp_data['question'] is None
def test_search(self): """ Test that question search can search by: - partial category match - partial question match - partial answer match - Specify a category search under """ url = '/api/questions/search' search = { 'searchTerm': 'sci' } # test category contains resp = self.client.post(url, json=search) assert resp.status_code == 200 assert json.loads(resp.data) == { 'questions': [ {'answer': 'Nobody knows', 'category': 1, 'difficulty': 5, 'id': 1, 'question': 'What is science?'} ], 'total_questions': 1 } # Test question contains search['searchTerm'] = 'what is' resp = self.client.post(url, json=search) assert resp.status_code == 200 assert json.loads(resp.data) == { 'questions': [ {'answer': 'Nobody knows', 'category': 1, 'difficulty': 5, 'id': 1, 'question': 'What is science?'} ], 'total_questions': 1 } # Test answer contains search['searchTerm'] = 'nobody' resp = self.client.post(url, json=search) assert resp.status_code == 200 assert json.loads(resp.data) == { 'questions': [ {'answer': 'Nobody knows', 'category': 1, 'difficulty': 5, 'id': 1, 'question': 'What is science?'} ], 'total_questions': 1 } # test within category new_question = Question('Why is science?', 'Only Bill Nye knows', self.science.id, 5) new_question.insert() search['categoryId'] = self.science.id search['searchTerm'] = 'knows' resp = self.client.post(url, json=search) assert resp.status_code == 200 assert json.loads(resp.data) == { 'questions': [ {'answer': 'Nobody knows', 'category': 1, 'difficulty': 5, 'id': 1, 'question': 'What is science?'}, {'answer': 'Only Bill Nye knows', 'category': 1, 'difficulty': 5, 'id': 3, 'question': 'Why is science?'} ], 'total_questions': 2 }
def post(self): errors = [] good_data = True data = json.loads(request.data) if data['question'] is None or data['question'].__class__ != str or len( data['question'].strip()) < 1: good_data = False errors.append('Question text cannot be blank, or was not a string') if data['answer'] is None or data['answer'].__class__ != str or len( data['answer'].strip()) < 1: good_data = False errors.append('Answer text cannot be blank, or was not a string') try: if data['difficulty'] is None or data[ 'difficulty'].__class__ != str or int( data['difficulty']) < 1 or int(data['difficulty']) > 5: good_data = False errors.append( 'Difficulty integer cannot be blank, and must be an integer between 1 and 5' ) except: good_data = False errors.append( 'Difficulty integer cannot be blank, and must be an integer between 1 and 5' ) try: if data['category'] is None or data[ 'category'].__class__ != str or db.session.query( Category).filter_by( id=int(data['category'])).one_or_none() is None: good_data = False errors.append( 'Category integer cannot be blank, or is set to an invalid category' ) except: good_data = False errors.append( 'Category integer cannot be blank, or is set to an invalid category' ) if good_data: q = Question(question=bleach.clean(data['question']), answer=bleach.clean(data['answer']), difficulty=bleach.clean(data['difficulty']), category=bleach.clean(data['category'])) q.insert() payload = { 'message': 'New question successfully added', 'question': q.format() } return payload, 201 return { 'success': False, 'error': 400, 'message': 'New question was not added, check errors for reasons', 'errors': errors }, 400