def test_answer_many_incorrect_questions_many_topics(self): """ check that answering many incorrect questions affects competency children""" author_course = self._bootstrap_courses(1) author_user = self._bootstrap_user(1) author = CourseUser.objects.create(user=author_user, course=author_course) self._bootstrap_topics(author_course) topic_selected = Topic.objects.all().filter(id__in=[1, 2]) self._bootstrap_questions_same_topics(author, topic_selected, 20) self._bootstrap_question_choices(correct_id=2) QuestionService.respond_to_question(3, author) for i in range(1, 20): offset = 4 * i first_old_competency = Competency.objects.all()[0].competency second_old_competency = Competency.objects.all()[1].competency third_old_competency = Competency.objects.all()[2].competency QuestionService.respond_to_question(3 + offset, author) first_new_competency = Competency.objects.all()[0].competency second_new_competency = Competency.objects.all()[1].competency third_new_competency = Competency.objects.all()[2].competency self.assertTrue(first_old_competency >= first_new_competency) self.assertTrue(second_old_competency >= second_new_competency) self.assertTrue(third_old_competency >= third_new_competency) self.assertEqual(QuestionResponse.objects.count(), 20) num_topics = combinations( QuestionScore.objects.all().first().question.topics.all()) self.assertEqual(Competency.objects.count(), len(num_topics))
def test_decay_function(self): """ Test decay function influences question competency over time """ author_course = self._bootstrap_courses(1) author_user = self._bootstrap_user(1) responder_user = self._bootstrap_user(2) responder = CourseUser.objects.create(user=responder_user, course=author_course) author = CourseUser.objects.create(user=author_user, course=author_course) self._bootstrap_topics(author_course) topic_selected = Topic.objects.all().filter(id__in=[1]) self._bootstrap_questions_same_topics(author, topic_selected, 20) self._bootstrap_question_choices(correct_id=2) for question in Question.objects.all(): question.difficulty = 10 question.save() for i in range(0, 10): offset = 4 * i QuestionService.respond_to_question(2 + offset, author) QuestionService.respond_to_question(3 + offset, responder) for i in range(10, 20): offset = 4 * i QuestionService.respond_to_question(3 + offset, author) QuestionService.respond_to_question(2 + offset, responder) author_competency = Competency.objects.get(user=author).competency responder_competency = Competency.objects.get( user=responder).competency self.assertEqual(QuestionResponse.objects.count(), 40) self.assertEqual(QuestionScore.objects.count(), 40) self.assertTrue(author_competency <= responder_competency)
def next_recommended_question(request): logged_in_user = UserService.logged_in_user(request) question = QuestionService.next_recommended_question(logged_in_user) if question is None: return JsonResponse({"error": "You are not enrolled in the course for this question"}, status=403) return JsonResponse({"data": question.id})
def rate(request): if request.method != 'POST': return JsonResponse({ "error": "Must use POST to this endpoint" }, status=405) post_request = loads(request.body.decode("utf-8")) distractor_id = post_request.get("distractorID", None) difficulty = post_request.get("difficulty", None) quality = post_request.get("quality", None) if difficulty is None and quality is None: return JsonResponse({"error": "At least response.rating or response.quality must be specified"}, status=422) if difficulty is not None: if is_number(difficulty) is False or not 0 <= int(difficulty) <= 5: return JsonResponse({"error": "response.difficulty must be between 0 and 5 inclusive"}, status=422) difficulty = int(difficulty) if quality is not None: if is_number(quality) is False or not 0 <= int(quality) <= 5: return JsonResponse({"error": "response.quality must be between 0 and 5 inclusive"}, status=422) quality = int(quality) if distractor_id is None: return JsonResponse({"error": "Missing integer distractorID in request"}, status=422) user_ratings = {"difficulty": difficulty, "quality": quality} if QuestionService.rate_question(distractor_id, user_ratings, UserService.logged_in_user(request)) is False: return JsonResponse({"error": "Invalid distractorID"}, status=422) else: return HttpResponse(status=204)
def test_leaderboard_fetch_multiple(self): """ Tests fetching leaderboard results with multiple courses""" courses = [self._bootstrap_courses(1), self._bootstrap_courses(2)] course_user = self._bootstrap_course_user(-1, courses[0], True) course_user_2 = self._bootstrap_course_user(-2, courses[1], True) for count, course in enumerate(courses): self._bootstrap_topics(course) for i in range((count+1) * 5): id = i + count*5 self._boostrap_leaderboard_users(course, id, id == count) leaderboard = QuestionService.get_course_leaders(course_user, False, "questionsAuthored", "") self.assertEqual(len(leaderboard), 5) leaderboard = QuestionService.get_course_leaders(course_user_2, False, "questionsAuthored", "") self.assertEqual(len(leaderboard), 10)
def id(request, id): question = QuestionService.getQuestion(id) if question is None: return JsonResponse({}, status=404) return JsonResponse(question.toJSON())
def all_reports(request): user = UserService.logged_in_user(request) result = QuestionService.all_reports(user) if type(result) != list and result.get("error", None) is not None: return JsonResponse(result) else: return JsonResponse({"data": result})
def id(request, id): logged_in_user = UserService.logged_in_user(request) question = QuestionService.get_question_by_id(logged_in_user, id) if question is None: return JsonResponse({"error": "You are not enrolled in the course for this question"}, status=403) return JsonResponse({"data": question})
def get_reasons(request): if request.method != 'POST': return JsonResponse({ "error": "Must use POST to this endpoint" }, status=405) user = UserService.logged_in_user(request) return JsonResponse({"data": QuestionService.get_reason_list(user)})
def test_leaderboard_sort_direction(self): course = self._bootstrap_courses(1) course_user = self._bootstrap_course_user(-1, course, True) self._bootstrap_topics(course) for i in [1,2,3,4,5]: self._boostrap_leaderboard_users(course, i, i == 1) leaderboard = QuestionService.get_course_leaders(course_user, False, "questionsAuthored", "DESC") self.assertEqual(len(leaderboard), 5) self.assertEqual(leaderboard[0]["questionsAuthored"], 5) self.assertEqual(leaderboard[1]["questionsAuthored"], 0) # Sort in other direction leaderboard = QuestionService.get_course_leaders(course_user, False, "questionsAuthored", "ASC") self.assertEqual(len(leaderboard), 5) self.assertEqual(leaderboard[0]["questionsAuthored"], 0) self.assertEqual(leaderboard[-1]["questionsAuthored"], 5)
def test_leaderboard_fetch_many_instructor(self): """ Tests limiting of leaderboard fetching results for instructor""" course = self._bootstrap_courses(1) course_user = self._bootstrap_course_user(-1, course, True) self._bootstrap_topics(course) for i in range(0, 100): self._boostrap_leaderboard_users(course, i, i == 1) leaderboard = QuestionService.get_course_leaders(course_user, False, "questionsAuthored", "DESC", limit=-1) self.assertEqual(len(leaderboard), 100)
def report(request): if request.method != 'POST': return JsonResponse({ "error": "Must use POST to this endpoint" }, status=405) post_request = loads(request.body.decode("utf-8")) user = UserService.logged_in_user(request) return JsonResponse({"data": QuestionService.report_question(user, post_request)})
def get_all_stats(user): if not util.is_administrator(user): return {"error": "User is not authorized"} leaderboard = QuestionService.create_leaderboard(user, False, ["lastName", "firstName"], "ASC") for person in leaderboard: person.pop("rank", None) return {"data": leaderboard}
def test_leaderboard_fetch(self): """ Tests fetching leaderboard results""" course = self._bootstrap_courses(1) course_user = self._bootstrap_course_user(-1, course, True) self._bootstrap_topics(course) for i in [1,2,3,4,5]: self._boostrap_leaderboard_users(course, i, i == 1) leaderboard = QuestionService.get_course_leaders(course_user, False, "questionsAuthored", "") self.assertEqual(len(leaderboard), 5)
def test_course_topics_multiple(self): """ Tests fetching topics for a course when there are multiple courses""" courses = [self._bootstrap_courses(1), self._bootstrap_courses(2)] for _c, course in enumerate(courses): self._bootstrap_topics(course) user = self._bootstrap_user(_c + 1) author = CourseUser.objects.create(user=user, course=course) self._bootstrap_questions(author) def _e(s): return [x for x in s] course_topics = QuestionService.get_course_topics(courses[0]) self.assertEqual(_e(course_topics), _e(Topic.objects.filter(course=courses[0]))) course_topics = QuestionService.get_course_topics(courses[1]) self.assertEqual(_e(course_topics), _e(Topic.objects.filter(course=courses[1])))
def make_question_responses(user, correct, incorrect, ability): if chance(2): user_choice = choose_answer(correct, incorrect, ability) response = QuestionService.respond_to_question(user_choice.id, user) if chance(2): rating = QuestionRating(quality=randrange(0, 10), difficulty=randrange(0, 10), response=user_choice, user=user) rating.save()
def leaderboard(request, sort_field, sort_order): logged_in_user = UserService.logged_in_user(request) user_roles = (str(x) for x in logged_in_user.roles.all()) limit = -1 if is_administrator(logged_in_user) else 20 if sort_order != "DESC" and sort_order != "ASC": sort_order = "DESC" leaderboard_scores = QuestionService.get_course_leaders(logged_in_user, False, sort_field, sort_order, limit) return JsonResponse({"data": leaderboard_scores})
def test_course_topics(self): """ Tests fetching topics for a course""" course = self._bootstrap_courses(1) self._bootstrap_topics(course) user = self._bootstrap_user(1) author = CourseUser.objects.create(user=user, course=course) self._bootstrap_questions(author) course_topics = QuestionService.get_course_topics(course) self.assertEqual(len(course_topics), 6)
def update(request, qid): val = validate_request(request) if not val[0]: return val[1] root_path, post_request = val response = QuestionService.update_question( post_request, root_path, UserService.logged_in_user(request), qid) return validate_response(response)
def test_add_filter_answered(self): """test that questions can be filtered if they have been answered before, QuestionResponse exists""" course = self._bootstrap_courses(1) user = self._bootstrap_user(1) author = CourseUser.objects.create(user=user, course=course) self._bootstrap_topics(course) self._bootstrap_questions(author) self._bootstrap_question_choices(correct_id=2) #answer the first two questions QuestionService.respond_to_question(2, author) QuestionService.respond_to_question(6, author) test_search = SearchService.SearchService(course) test_search.add_filter("answered", author) answered_questions = test_search.execute() # Check searched questions should be the first 2 answered ones for i in answered_questions: self.assertTrue(i in Question.objects.all()[0:2]) self.assertEqual(test_search.execute().count(), 2)
def test_add_filter_unanswered(self): """Test that questions are filtered when they have not been answered before, no QuestionResponse""" course = self._bootstrap_courses(1) user = self._bootstrap_user(1) author = CourseUser.objects.create(user=user, course=course) self._bootstrap_topics(course) self._bootstrap_questions(author) self._bootstrap_question_choices(correct_id=2) #answer the first two questions QuestionService.respond_to_question(2, author) QuestionService.respond_to_question(6, author) test_search = SearchService.SearchService(course) test_search.add_filter("unanswered", author) unanswered_questions = test_search.execute() # Check searched questions should be the last 3 unanaswered ones for i in unanswered_questions: self.assertTrue(i in Question.objects.all()[2:5]) self.assertEqual(test_search.execute().count(), 3)
def test_answer_many_correct_questions_single_topic(self): """ checks that answering many correct questions bring competency upwards""" author_course = self._bootstrap_courses(1) author_user = self._bootstrap_user(1) author = CourseUser.objects.create(user=author_user, course=author_course) self._bootstrap_topics(author_course) topic_selected = Topic.objects.all().filter(id__in=[1]) self._bootstrap_questions_same_topics(author, topic_selected, 20) self._bootstrap_question_choices(correct_id=2) QuestionService.respond_to_question(2, author) for i in range(1, 20): offset = 4 * i old_competency = Competency.objects.all().first().competency QuestionService.respond_to_question(2 + offset, author) new_competency = Competency.objects.all().first().competency self.assertTrue(old_competency <= new_competency) self.assertEqual(QuestionResponse.objects.count(), 20) self.assertEqual(Competency.objects.count(), 1)
def test_rate_nonexistent(self): """ Tests rating a question which does not exists """ course = self._bootstrap_courses(1) user = self._bootstrap_user(1) author = CourseUser.objects.create(user=user, course=course) self._bootstrap_topics(course) self._bootstrap_questions(author) self._bootstrap_question_choices(correct_id=2) self.assertFalse( QuestionService.rate_question(-1, { "difficulty": 2, "quality": 4 }, author))
def test_add_filter_improve(self): """ Test that questions can be filtered by questions answered where user did not get maximun score. QuestionResponse exists but Question Score < 1 """ course = self._bootstrap_courses(1) user = self._bootstrap_user(1) author = CourseUser.objects.create(user=user, course=course) self._bootstrap_topics(course) self._bootstrap_questions(author) self._bootstrap_question_choices(correct_id=2) #answer the first question correctly after two tries QuestionService.respond_to_question(3, author) QuestionService.respond_to_question(2, author) test_search = SearchService.SearchService(course) test_search.add_filter("improve", author) improve_questions = test_search.execute() self.assertEqual(Question.objects.all().first(), improve_questions.first()) self.assertEqual(test_search.execute().count(), 1)
def test_rate_update(self): """ Tests rating a question which the user has rated before""" course = self._bootstrap_courses(1) user = self._bootstrap_user(1) author = CourseUser.objects.create(user=user, course=course) self._bootstrap_topics(course) self._bootstrap_questions(author) self._bootstrap_question_choices(correct_id=2) QuestionService.rate_question(2, { "difficulty": 2, "quality": 4 }, author) self.assertTrue( QuestionService.rate_question(2, {"difficulty": 4}, author)) user_rating = QuestionRating.objects.all() self.assertEqual(len(user_rating), 1) self.assertEqual(user_rating.first().quality, 4.0) self.assertEqual(user_rating.first().difficulty, 4.0) answered_question = Question.objects.get(pk=1).toJSON() self.assertEqual(answered_question["difficultyCount"], 1) self.assertEqual(answered_question["difficulty"], 4.0)
def search(request): if request.method != 'POST': return JsonResponse({ "error": "Must use POST to this endpoint" }, status=405) search = SearchService.SearchService() post_request = loads(request.body) sort_field = post_request.get("sortField", None) filter_field = post_request.get("filterField", None) filter_topics = post_request.get("filterTopics", None) query = post_request.get("query", None) sort_order = post_request.get("sortOrder", None) page = post_request.get("page", None) if sort_field is None and filter_field is None and query is None: found_questions = QuestionService.allQuestions() return page_response(found_questions, page) if sort_field is not None: search.addSort(sort_field, sort_order) if filter_field is not None: search.addFilter(filter_field) if filter_topics is not None: search.addTopicFilter(filter_topics) if query is not None: search.textSearch(query) try: search_result = search.execute() return page_response(search_result, page) except TypeError: all_questions = QuestionService.allQuestions() return page_response(all_questions, page)
def test_answer_question_different_difficulties(self): """ Test for checking that difficulty influences competency scores """ author_course = self._bootstrap_courses(1) author_user = self._bootstrap_user(1) responder_user = self._bootstrap_user(2) responder = CourseUser.objects.create(user=responder_user, course=author_course) author = CourseUser.objects.create(user=author_user, course=author_course) self._bootstrap_topics(author_course) topic_selected = Topic.objects.all().filter(id__in=[1]) self._bootstrap_questions_same_topics(author, topic_selected, 20) for question in Question.objects.all(): question.difficulty = 10 question.save() topic_selected = Topic.objects.all().filter(id__in=[2]) self._bootstrap_questions_same_topics(author, topic_selected, 20) self._bootstrap_question_choices(correct_id=2) starting_point = 20 * 4 QuestionService.respond_to_question(2, author) QuestionService.respond_to_question(2 + starting_point, responder) for i in range(1, 20): offset = 4 * i author_old_competency = Competency.objects.get( user=author).competency responder_old_competency = Competency.objects.get( user=responder).competency QuestionService.respond_to_question(2 + offset, author) QuestionService.respond_to_question((2 + starting_point) + offset, responder) author_new_competency = Competency.objects.get( user=author).competency responder_new_competency = Competency.objects.get( user=responder).competency self.assertTrue(author_old_competency <= author_new_competency) self.assertTrue( responder_old_competency <= responder_new_competency) self.assertTrue(author_new_competency >= responder_new_competency)
def respond(request): if request.method != 'POST': return JsonResponse({ "error": "Must use POST to this endpoint" }, status=405) post_request = loads(request.body) distractor_id = post_request.get("distractorID", None) if distractor_id is None: return JsonResponse({"error": "Missing integer distractorID in request"}, status=422) if QuestionService.respond_to_question(distractor_id, UserService.logged_in_user(request)) is None: return JsonResponse({"error": "Invalid distractorID"}, status=422) else: return HttpResponse(status=204)
def respond(request): if request.method != 'POST': return JsonResponse({ "error": "Must use POST to this endpoint" }, status=405) post_request = loads(request.body.decode("utf-8")) distractor_id = post_request.get("distractorID", None) try: if distractor_id is None: return JsonResponse({"error": "Missing integer distractorID in request"}, status=422) if QuestionService.respond_to_question(distractor_id, UserService.logged_in_user(request)) is False: return JsonResponse({"error": "Invalid distractorID"}, status=422) else: return JsonResponse({}) except ValueError: return JsonResponse({"error": "You are not enrolled in the course for this course"}, status=403)
def test_answer_alternating_questions_many_topics(self): """ answer alternating questions to ensure competency scores bounce accordingly """ author_course = self._bootstrap_courses(1) author_user = self._bootstrap_user(1) author = CourseUser.objects.create(user=author_user, course=author_course) self._bootstrap_topics(author_course) topic_selected = Topic.objects.all().filter(id__in=[1, 2]) self._bootstrap_questions_same_topics(author, topic_selected, 20) self._bootstrap_question_choices(correct_id=2) # Get question wrong QuestionService.respond_to_question(2, author) for i in range(1, 10): offset = (4 * i) + (4 * (i - 1)) first_old_competency = Competency.objects.all()[0].competency second_old_competency = Competency.objects.all()[1].competency third_old_competency = Competency.objects.all()[2].competency # Get question wrong QuestionService.respond_to_question(3 + offset, author) first_new_competency = Competency.objects.all()[0].competency second_new_competency = Competency.objects.all()[1].competency third_new_competency = Competency.objects.all()[2].competency self.assertTrue(first_old_competency >= first_new_competency) self.assertTrue(second_old_competency >= second_new_competency) self.assertTrue(third_old_competency >= third_new_competency) QuestionService.respond_to_question(2 + offset, author) self.assertTrue( first_new_competency <= Competency.objects.all()[0].competency) self.assertTrue( second_new_competency <= Competency.objects.all()[1].competency ) self.assertTrue( third_new_competency <= Competency.objects.all()[2].competency) self.assertEqual(QuestionResponse.objects.count(), 19) num_topics = combinations( QuestionScore.objects.all().first().question.topics.all()) self.assertEqual(Competency.objects.count(), len(num_topics))