diff options
author | adityacp | 2021-01-26 14:38:46 +0530 |
---|---|---|
committer | adityacp | 2021-01-26 14:38:46 +0530 |
commit | 7f28b418b616823f542faea8311e881faf6286c2 (patch) | |
tree | 4bb4c82c0adc7fb8f463fc03142dd310a03268c6 | |
parent | 1e5c8af8748602d90a52f51d45799274155f8cd9 (diff) | |
download | online_test-7f28b418b616823f542faea8311e881faf6286c2.tar.gz online_test-7f28b418b616823f542faea8311e881faf6286c2.tar.bz2 online_test-7f28b418b616823f542faea8311e881faf6286c2.zip |
Refactor question statistics for a quiz
-rw-r--r-- | yaksh/models.py | 65 | ||||
-rw-r--r-- | yaksh/templates/yaksh/statistics_question.html | 123 | ||||
-rw-r--r-- | yaksh/templatetags/custom_filters.py | 4 | ||||
-rw-r--r-- | yaksh/test_views.py | 8 | ||||
-rw-r--r-- | yaksh/views.py | 11 |
5 files changed, 99 insertions, 112 deletions
diff --git a/yaksh/models.py b/yaksh/models.py index 3e3e2d1..bbb6900 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -2063,36 +2063,45 @@ class AnswerPaperManager(models.Manager): def get_question_statistics(self, questionpaper_id, attempt_number, course_id, status='completed'): ''' Return dict with question object as key and list as value - The list contains two value, first the number of times a question - was answered correctly, and second the number of times a question - appeared in a quiz''' + The list contains four values, first total attempts, second correct + attempts, third correct percentage, fourth per test case answers + a question + ''' question_stats = {} - questions_answered = self.get_all_questions_answered(questionpaper_id, - attempt_number, - course_id) - questions = self.get_all_questions(questionpaper_id, attempt_number, - course_id) - per_answer_stats = self.get_per_answer_stats( - questionpaper_id, attempt_number, course_id + qp = QuestionPaper.objects.get(id=questionpaper_id) + all_questions = qp.get_question_bank() + que_ids = [que.id for que in all_questions] + papers = self.filter( + question_paper_id=questionpaper_id, course_id=course_id, + attempt_number=attempt_number + ).values_list("id", flat=True) + answers = Answer.objects.filter( + answerpaper__id__in=papers, question_id__in=que_ids + ).order_by("id").values( + "answerpaper__id", "question_id", "correct", "answer" ) - all_questions = Question.objects.filter( - id__in=set(questions), - active=True - ).order_by('type') - for question in all_questions: - if question.id in questions_answered: - question_stats[question] = { - 'answered': [questions_answered[question.id], - questions[question.id]], - 'per_answer': per_answer_stats[question], - } - - else: - question_stats[question] = { - 'answered': [0, questions[question.id]], - 'per_answer': per_answer_stats[question], - } - + def _get_per_tc_data(answers, q_type): + tc = [] + for answer in answers["answer"]: + ans = literal_eval(answer) if answer else None + tc.extend(ans) if q_type == "mcc" else tc.append(str(ans)) + return dict(Counter(tc)) + df = pd.DataFrame(answers) + if not df.empty: + for question in all_questions: + que = df[df["question_id"]==question.id].groupby( + "answerpaper__id").tail(1) + if not que.empty: + total_attempts = que.shape[0] + correct_attempts = que[que["correct"]==True].shape[0] + per_tc_ans = {} + if question.type in ["mcq", "mcc"]: + per_tc_ans = _get_per_tc_data(que, question.type) + question_stats[question] = ( + total_attempts, correct_attempts, + round((correct_attempts/total_attempts)*100), + per_tc_ans + ) return question_stats def _get_answerpapers_for_quiz(self, questionpaper_id, course_id, diff --git a/yaksh/templates/yaksh/statistics_question.html b/yaksh/templates/yaksh/statistics_question.html index d70256b..588e131 100644 --- a/yaksh/templates/yaksh/statistics_question.html +++ b/yaksh/templates/yaksh/statistics_question.html @@ -18,10 +18,25 @@ </ul> </div> <div class="col-md-9"> + {% if messages %} + {% for message in messages %} + <div class="alert alert-dismissible alert-{{ message.tags }}"> + <button type="button" class="close" data-dismiss="alert"> + <i class="fa fa-close"></i> + </button> + <strong>{{ message }}</strong> + </div> + {% endfor %} + {% endif %} {% if question_stats %} <p><b>Total number of participants: {{ total }}</b></p> <table class="table table-responsive-sm"> - <tr class="bg-light yakshred"><th>Question</th><th></th><th>Type</th><th>Total</th><th>Answered Correctly</th></tr> + <tr class="bg-light"> + <th>Question</th> + <th>Type</th> + <th>Total attempts</th> + <th>Answered Correctly</th> + </tr> {% for question, data in question_stats.items %} <tr> <td style="width: 45%"> @@ -40,7 +55,7 @@ <strong> Description: </strong> - <p> + <p width="100%"> {{ question.description|safe }} </p> <strong> @@ -55,87 +70,49 @@ <p> {{ question.get_type_display }} </p> - {% if question.type in 'mcq mcc' %} - <strong> - Options: - </strong> - <p> - <ol> - {% for tc in question.testcase_set.all %} - <li> - {{ tc.mcqtestcase.options }} - {% if tc.mcqtestcase.correct %} - <span class="badge badge-primary">Correct</span> - {% endif %} - {% get_dict_value data.per_answer tc.id|stringformat:"i" as num %} - <span class="badge badge-info">Answered: {{ num }}</span> - </li> + {% if question.type in "mcq mcc" %} + {% for tc in question.get_test_cases %} + {% if tc.correct %} + <span class="badge badge-pill badge-success"> + {{forloop.counter}}. + </span> + {% else %} + <span class="badge badge-pill badge-dark"> + {{ forloop.counter }}. + </span> + {% endif %} + {{tc.options}} + {% get_percent_value data.3 tc.id total as percent %} + <div class="progress-wrapper col-md-4"> + <div class="progress-info"> + <div class="progress-percentage"> + <span> + {% if percent %} {{percent|floatformat}} {% else %} 0 {% endif %}% + </span> + </div> + </div> + <div class="progress"> + {% if percent %} + <div class="progress-bar bg-success" role="progressbar" aria-valuenow="{{percent}}" + aria-valuemin="0" aria-valuemax="100" style="width:{{percent|floatformat}}%"> + </div> + {% endif %} + </div> + </div> + <br> {% endfor %} - </ol> - </p> {% endif %} </div> </div> </div> </td> - <td>{{ question.type }}</td> - <td>{{data.answered.1}}</td><td>{{ data.answered.0 }} ({% widthratio data.answered.0 data.answered.1 100 %}%)</td> - - + <td>{{ question.get_type_display }}</td> + <td>{{data.0}} out of {{total}}</td> + <td>{{ data.1 }} out of {{data.0}} ({{data.2}}%)</td> </tr> {% endfor %} </table> {% endif %} - - <!-- The Modal --> - <div class="modal" id="question_detail_modal"> - <div class="modal-dialog"> - <div class="modal-content"> - - <!-- Modal Header --> - <div class="modal-header"> - <h4 class="modal-title">Question Details</h4> - <button type="button" class="close" data-dismiss="modal">×</button> - </div> - - <!-- Modal body --> - <div class="modal-body"> - <table> - <tr> - <td>Summary</td> - <td>{{ question.summary }}</td> - </tr> - <tr> - <td>Description</td> - <td>{{ question.description }}</td> - </tr> <tr> - <td>Type</td> - <td>{{ question.type }}</td> - </tr> <tr> - <td>Points</td> - <td>{{ question.points }}</td> - </tr> - <tr> - {% for tc in question.testcase_set.all %} - tc - {% endfor %} - <br><br> - </tr> - </table> - </div> - - <!-- Modal footer --> - <div class="modal-footer"> - <button type="button" class="btn btn-danger" data-dismiss="modal">Close</button> - </div> - - </div> - </div> - </div> - - </div> - </div> - <!-- end Modal outer --> </div> </div> </div> diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py index 81572a7..201ec50 100644 --- a/yaksh/templatetags/custom_filters.py +++ b/yaksh/templatetags/custom_filters.py @@ -214,7 +214,7 @@ def get_lesson_views(course_id, lesson_id): @register.simple_tag -def get_dict_value(dictionary, key): - return dictionary.get(key, None) +def get_percent_value(dictionary, key, total): + return round((dictionary.get(str(key), 0)/total)*100) diff --git a/yaksh/test_views.py b/yaksh/test_views.py index 89d209e..8973d9f 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -5671,11 +5671,9 @@ class TestShowStatistics(TestCase): self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'yaksh/statistics_question.html') self.assertIn(self.question, list(question_stats.keys())) - self.assertSequenceEqual( - list(question_stats.values())[0]['answered'], [1, 1] - ) - self.assertEqual(response.context['attempts'][0], 1) - self.assertEqual(response.context['total'], 1) + q_data = list(question_stats.values())[0] + self.assertSequenceEqual(q_data[0:2], [1, 1]) + self.assertEqual(100, q_data[2]) class TestQuestionPaper(TestCase): diff --git a/yaksh/views.py b/yaksh/views.py index 5b67570..731d781 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1326,10 +1326,10 @@ def show_statistics(request, questionpaper_id, attempt_number=None, attempt_numbers = AnswerPaper.objects.get_attempt_numbers( questionpaper_id, course_id) quiz = get_object_or_404(QuestionPaper, pk=questionpaper_id).quiz + context = {'quiz': quiz, 'attempts': attempt_numbers, + 'questionpaper_id': questionpaper_id, + 'course_id': course_id} if attempt_number is None: - context = {'quiz': quiz, 'attempts': attempt_numbers, - 'questionpaper_id': questionpaper_id, - 'course_id': course_id} return my_render_to_response( request, 'yaksh/statistics_question.html', context ) @@ -1338,7 +1338,10 @@ def show_statistics(request, questionpaper_id, attempt_number=None, course_id) if not AnswerPaper.objects.has_attempt(questionpaper_id, attempt_number, course_id): - return my_redirect('/exam/manage/') + messages.warning(request, "No answerpapers found") + return my_render_to_response( + request, 'yaksh/statistics_question.html', context + ) question_stats = AnswerPaper.objects.get_question_statistics( questionpaper_id, attempt_number, course_id ) |