summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoradityacp2021-01-26 14:38:46 +0530
committeradityacp2021-01-26 14:38:46 +0530
commit7f28b418b616823f542faea8311e881faf6286c2 (patch)
tree4bb4c82c0adc7fb8f463fc03142dd310a03268c6
parent1e5c8af8748602d90a52f51d45799274155f8cd9 (diff)
downloadonline_test-7f28b418b616823f542faea8311e881faf6286c2.tar.gz
online_test-7f28b418b616823f542faea8311e881faf6286c2.tar.bz2
online_test-7f28b418b616823f542faea8311e881faf6286c2.zip
Refactor question statistics for a quiz
-rw-r--r--yaksh/models.py65
-rw-r--r--yaksh/templates/yaksh/statistics_question.html123
-rw-r--r--yaksh/templatetags/custom_filters.py4
-rw-r--r--yaksh/test_views.py8
-rw-r--r--yaksh/views.py11
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">&times;</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
)