diff options
author | Prabhu Ramachandran | 2016-05-04 18:31:28 +0530 |
---|---|---|
committer | Prabhu Ramachandran | 2016-05-04 18:31:28 +0530 |
commit | 8841e5ed4f8f79b7067ddb3523f4a3ec50f362b3 (patch) | |
tree | a9db341f13abdf47f994d8823f233219561dbff2 | |
parent | 03f0aa839e4d91a3d83d68d5301f1425f18cca73 (diff) | |
parent | f150f1c54ee5941d72f2e4057af8c90cca588292 (diff) | |
download | online_test-8841e5ed4f8f79b7067ddb3523f4a3ec50f362b3.tar.gz online_test-8841e5ed4f8f79b7067ddb3523f4a3ec50f362b3.tar.bz2 online_test-8841e5ed4f8f79b7067ddb3523f4a3ec50f362b3.zip |
Merge pull request #97 from maheshgudi/grade_user_2
Grade User
-rw-r--r-- | yaksh/models.py | 51 | ||||
-rw-r--r-- | yaksh/static/yaksh/css/gradeuser.css | 8 | ||||
-rw-r--r-- | yaksh/templates/yaksh/grade_user.html | 110 | ||||
-rw-r--r-- | yaksh/templates/yaksh/monitor.html | 10 | ||||
-rw-r--r-- | yaksh/templates/yaksh/user_data.html | 15 | ||||
-rw-r--r-- | yaksh/tests.py | 12 | ||||
-rw-r--r-- | yaksh/urls.py | 12 | ||||
-rw-r--r-- | yaksh/views.py | 80 |
8 files changed, 212 insertions, 86 deletions
diff --git a/yaksh/models.py b/yaksh/models.py index a4ea6c3..561b334 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -235,10 +235,15 @@ class Answer(models.Model): # Whether skipped or not. skipped = models.BooleanField(default=False) + def set_marks(self, marks): + if marks > self.question.points: + self.marks = self.question.points + else: + self.marks = marks + def __unicode__(self): return self.answer - ############################################################################### class QuizManager(models.Manager): def get_active_quizzes(self): @@ -470,8 +475,13 @@ class AnswerPaperManager(models.Manager): question_stats[question] = [0, questions[question.id]] return question_stats - def get_answerpapers_for_quiz(self, questionpaper_id): - return self.filter(question_paper_id=questionpaper_id) + def _get_answerpapers_for_quiz(self, questionpaper_id, status=False): + if not status: + return self.filter(question_paper_id=questionpaper_id) + else: + return self.filter(question_paper_id=questionpaper_id, + status="completed") + def _get_answerpapers_users(self, answerpapers): return answerpapers.values_list('user', flat=True).distinct() @@ -499,6 +509,31 @@ class AnswerPaperManager(models.Manager): def get_total_attempt(self, questionpaper, user): return self.filter(question_paper=questionpaper, user=user).count() + def get_users_for_questionpaper(self, questionpaper_id): + return self._get_answerpapers_for_quiz(questionpaper_id, status=True)\ + .values("user__id", "user__first_name", "user__last_name")\ + .distinct() + + def get_user_all_attempts(self, questionpaper, user): + return self.filter(question_paper=questionpaper, user=user)\ + .order_by('-attempt_number') + + def get_user_data(self, user, questionpaper_id, attempt_number=None): + if attempt_number is not None: + papers = self.filter(user=user, question_paper_id=questionpaper_id, + attempt_number=attempt_number) + else: + papers = self.filter(user=user, question_paper_id=questionpaper_id)\ + .order_by("-attempt_number") + data = {} + profile = user.profile if hasattr(user, 'profile') else None + data['user'] = user + data['profile'] = profile + data['papers'] = papers + data['questionpaperid'] = questionpaper_id + return data + + ############################################################################### class AnswerPaper(models.Model): """A answer paper for a student -- one per student typically. @@ -563,7 +598,7 @@ class AnswerPaper(models.Model): def completed_question(self, question_id): """ - Adds the completed question to the list of answered + Adds the completed question to the list of answered questions and returns the next question. """ self.questions_answered.add(question_id) @@ -629,7 +664,11 @@ class AnswerPaper(models.Model): self._update_percent() self._update_passed() self._update_status(state) - self.end_time = datetime.now() + self.save() + + def set_end_time(self, datetime): + """ Sets end time """ + self.end_time = datetime self.save() def get_question_answers(self): @@ -695,5 +734,3 @@ class TestCase(models.Model): # Test case Expected answer in list form expected_answer = models.TextField(blank=True, null = True) - - diff --git a/yaksh/static/yaksh/css/gradeuser.css b/yaksh/static/yaksh/css/gradeuser.css index 07b1079..af5de3f 100644 --- a/yaksh/static/yaksh/css/gradeuser.css +++ b/yaksh/static/yaksh/css/gradeuser.css @@ -36,7 +36,7 @@ color: #9EB6FF; border: 1px solid #C9C9C9; border-radius: 5px 5px 5px 5px; margin-bottom: 10px; - min-width: 805px; + min-width: 685px; } @@ -50,3 +50,9 @@ margin: 10px 10px 5px 5px; } #headerDiv a:hover { color: #FFFFFF; +} + +#attempt { + width: 157px; + position: relative; left:20%; +} diff --git a/yaksh/templates/yaksh/grade_user.html b/yaksh/templates/yaksh/grade_user.html index dd05670..2c5403c 100644 --- a/yaksh/templates/yaksh/grade_user.html +++ b/yaksh/templates/yaksh/grade_user.html @@ -1,46 +1,101 @@ {% extends "manage.html" %} -{% block title %} Grading papers for {{ data.user.get_full_name.title }} {% endblock title %} +{% block title %} Grade User {% endblock title %} -{% block subtitle %}Grading papers for {{ data.user.get_full_name.title }}{% endblock %} +{% block subtitle %} Grade User {% endblock %} {% block css %} - <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/gradeuser.css" type="text/css" /> + <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/gradeuser.css" type="text/css" /> {% endblock %} {% block script %} -<script src= "{{ URL_ROOT }}/static/yaksh/js/edit_question.js"></script> + <script src= "{{ URL_ROOT }}/static/yaksh/js/edit_question.js"></script> {% endblock %} + {% block manage %} -<p> -Name: {{ data.user.get_full_name.title }} +{% if course_details %} + <table id = "course-details" class = "zebra-striped"> + <tr> + <th>Courses</th> + <th> Quizzes </th> + </tr> + + {% for course in course_details %} + <tr> + <td><ul>{{course.name}} </td> + + {% if course.get_quizzes %} + <td> + {% for quiz in course.get_quizzes %} + <li><a href = "{{URL_ROOT}}/exam/manage/gradeuser/{{quiz.id}}">{{quiz.description}}</a></li> + + {% endfor %} + </td> + {% else %} + <td> No quiz</td> + {% endif %} + </ul></tr> + {% endfor %} + </table> +{% endif %} + +<div class="row"> +{%if users %} + <div id = "student" class="span2"> + {% for user in users %} + <p><a href = "{{URL_ROOT}}/exam/manage/gradeuser/{{quiz_id}}/{{user.user__id}}">{{user.user__first_name}} {{user.user__last_name}}</a></p> + {% endfor %} + </div> +{% endif %} + + + +<div id = "paper" class="span12"> +{% if data %} + + +<p> <h3> <center> Showing paper for {{data.user.get_full_name.title}} </center></h3> +<p><b>Name:</b>{{ data.user.get_full_name.title }} {% if data.profile %} -(roll number: {{ data.profile.roll_number }}) <br/> -Position: {{ data.profile.position }} <br/> -Department: {{ data.profile.department }} <br/> -Institute: {{ data.profile.institute }} <br/> + +<p><b> Roll number:</b> {{ data.profile.roll_number }} +<p><b>Position: </b> {{ data.profile.position }} +<p><b>Department: </b>{{ data.profile.department }} +<p><b>Institute: </b>{{ data.profile.institute }} {% endif %} -</p> {% if data.papers %} {% for paper in data.papers %} - -{% if forloop.counter == 2 and data.questionpaperid %} <hr> -<u><h2> Previous attempts </h2></u> -{% endif %} {{ paper.total_marks }} -<h2> Quiz: {{ paper.question_paper.quiz.description }} </h2> +<h3> Quiz: {{ paper.question_paper.quiz.description }} </h3> <p> -Attempt Number: {{ paper.attempt_number }}<br/> -Questions correctly answered: {{ paper.get_answered_str }} <br/> +Attempt Number: <b>{{paper.attempt_number}} </b> +<select id = "attempt" onchange="window.location.href=this.value"> +<option selected="">Select attempt number</option> +{%for attempt in attempts %} +<option value = "{{URL_ROOT}}/exam/manage/gradeuser/{{quiz_id}}/{{user_id}}/{{attempt.attempt_number}}/"> +{{attempt.attempt_number}} +</option> +{% endfor %} +</select> + +<br/>Questions correctly answered: {{ paper.get_answered_str }} <br/> Total attempts at questions: {{ paper.answers.count }} <br/> -Marks obtained: {{ paper.get_total_marks }} <br/> +Marks obtained: {{ paper.marks_obtained }} <br/> Start time: {{ paper.start_time }} <br/> +{%if paper.percent%} +Percentage obtained: {{paper.percent}}% <br/> +{% endif %} +{% if paper.passed == 0 %} +Status : <b style="color: red;"> Failed </b><br/> +{% else %} +Status : <b style="color: green;"> Passed </b><br/> +{% endif %} </p> {% if paper.answers.count %} @@ -59,12 +114,13 @@ Start time: {{ paper.start_time }} <br/> {% endfor %} </table> + <h3> Answers </h3><br> <form name=frm id="q{{ paper.quiz.id }}_form" {% if data.questionpaperid %} - action="{{URL_ROOT}}/exam/manage/gradeuser/{{data.user.username}}/{{data.questionpaperid}}/" + action="{{URL_ROOT}}/exam/manage/gradeuser/{{quiz_id}}/{{user_id}}/{{paper.attempt_number}}/" {% else %} - action="{{URL_ROOT}}/exam/manage/gradeuser/{{data.user.username}}/" + action="{{URL_ROOT}}/exam/manage/gradeuser/{{quiz_id}}/{{user_id}}/{{paper.attempt_number}}/" {% endif %} method="post"> {% csrf_token %} @@ -72,13 +128,13 @@ Start time: {{ paper.start_time }} <br/> <div class="for-question"> <p><strong> - Question: {{ question.id }}. {{ question.summary }} (Points: {{ question.points }}) + Question: {{ question.id }}. {{ question.summary }} (Points: {{ question.points }}) </strong> <strong><a href="" onClick="grade_data('myContent{{question.id}}'); return false;" style="cursor:pointer;" />Details</strong></p></a> <div id="contentDiv"> <div id="myContent{{question.id}}" style="padding:5px; display:none;"> <p> Description : {{ question.description }} </p> - <p> Test : {{ question.test }} </p> + <p> Test : {{ question.test }} </p> </div> </div> <div class="question-form"> @@ -113,10 +169,16 @@ Marks: <input id="q{{ question.id }}" type="text" <br><button class="btn" type="submit" name="submit_{{paper.quiz.id}}">Save Marks</button> </form> +</div> + {% endif %} {# if paper.answers.count #} {% endfor %} {# for paper in data.papers #} -{% endif %} {# if data.papers #} +{% endif %} {# if data.papers #} +{% else %} + </div> +{% endif %} {#if data#} +</div> {% endblock%} diff --git a/yaksh/templates/yaksh/monitor.html b/yaksh/templates/yaksh/monitor.html index b81c9f2..80ad06c 100644 --- a/yaksh/templates/yaksh/monitor.html +++ b/yaksh/templates/yaksh/monitor.html @@ -9,13 +9,13 @@ {% endblock %} {% block subtitle %} {% if not quizzes and not quiz %} - Quiz Results + Quiz Results {% endif %} {% if quizzes %} - Available Quizzes + Available Quizzes {% endif %} {% if quiz %} - {{ quiz.description }} Results + {{ quiz.description }} Results {% endif %} {% endblock %} {% block manage %} @@ -54,7 +54,7 @@ </tr> {% for paper in latest_attempts %} <tr> - <td> <a href="{{URL_ROOT}}/exam/manage/user_data/{{paper.user.username}}/{{paper.question_paper.id}}">{{ paper.user.get_full_name.title }}</a> </td> + <td> <a href="{{URL_ROOT}}/exam/manage/user_data/{{paper.user.id}}/{{paper.question_paper.id}}">{{ paper.user.get_full_name.title }}</a> </td> <td> {{ paper.user.username }} </td> <td> {{ paper.user.profile.roll_number }} </td> <td> {{ paper.user.profile.institute }} </td> @@ -69,4 +69,4 @@ <p> No answer papers so far. </p> {% endif %} {# if papers #} {% endif %} -{% endblock %} +{% endblock %}
\ No newline at end of file diff --git a/yaksh/templates/yaksh/user_data.html b/yaksh/templates/yaksh/user_data.html index 22be3ed..04544f9 100644 --- a/yaksh/templates/yaksh/user_data.html +++ b/yaksh/templates/yaksh/user_data.html @@ -22,11 +22,11 @@ Last login: {{ data.user.last_login }} {% if data.papers %} {% if data.questionpaperid %} -<p><a href="{{URL_ROOT}}/exam/manage/gradeuser/{{ data.user.username }}/{{ data.questionpaperid }}"> +<p><a href="{{URL_ROOT}}/exam/manage/gradeuser/{{data.papers.0.question_paper.quiz.id}}/{{ data.user.id }}"> Grade/correct paper</a> </p> {% else %} -<p><a href="{{URL_ROOT}}/exam/manage/gradeuser/{{ data.user.username }}"> +<p><a href="{{URL_ROOT}}/exam/manage/gradeuser/{{data.papers.0.question_paper.quiz.id}}/{{ data.user.id }}"> Grade/correct paper</a> {% endif %} @@ -41,7 +41,7 @@ Last login: {{ data.user.last_login }} Attempt Number: {{ paper.attempt_number }}<br/> Questions correctly answered: {{ paper.get_answered_str }} <br/> Total attempts at questions: {{ paper.answers.count }} <br/> -Marks obtained: {{ paper.get_total_marks }} <br/> +Marks obtained: {{ paper.marks_obtained }} <br/> Start time: {{ paper.start_time }} <br/> User IP address: {{ paper.user_ip }} </p> @@ -75,9 +75,12 @@ User IP address: {{ paper.user_ip }} {% endif %} {# if data.papers #} <br /> <hr /> -<a href="{{URL_ROOT}}/exam/manage/gradeuser/{{ data.user.username }}/"> - Grade/correct paper</a> -<br/> + +{% with data.papers.0 as paper %} +<a href="{{URL_ROOT}}/exam/manage/gradeuser/{{paper.question_paper.quiz.id}}/{{ data.user.id }}/">Grade/correct paper</a> +{% endwith %} +<br /> + {% if data.papers.count > 1 %} <a href="{{URL_ROOT}}/exam/manage/monitor/">Monitor quiz</a> {% else %} diff --git a/yaksh/tests.py b/yaksh/tests.py index cc3100e..d3ff4fc 100644 --- a/yaksh/tests.py +++ b/yaksh/tests.py @@ -355,6 +355,11 @@ class AnswerPaperTestCases(unittest.TestCase): self.assertTrue(self.answerpaper.passed) self.assertFalse(self.answerpaper.is_attempt_inprogress()) + def test_set_end_time(self): + current_time = datetime.now() + self.answerpaper.set_end_time(current_time) + self.assertEqual(self.answerpaper.end_time,current_time) + def test_get_question_answer(self): """ Test get_question_answer() method of Answer Paper""" answered = self.answerpaper.get_question_answers() @@ -375,6 +380,13 @@ class AnswerPaperTestCases(unittest.TestCase): self.assertEqual(answers.count(), 1) self.assertTrue(answers[0], self.answer_wrong) + def test_set_marks (self): + self.answer_wrong.set_marks(0.5) + self.assertEqual(self.answer_wrong.marks, 0.5) + self.answer_wrong.set_marks(10.0) + self.assertEqual(self.answer_wrong.marks,1.0) + + ############################################################################### class CourseTestCases(unittest.TestCase): def setUp(self): diff --git a/yaksh/urls.py b/yaksh/urls.py index ea1922d..b32bc36 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -44,19 +44,19 @@ urlpatterns += [ url(r'^manage/addquestion/(?P<question_id>\d+)/$', views.add_question), url(r'^manage/addquiz/$', views.add_quiz), url(r'^manage/addquiz/(?P<quiz_id>\d+)/$', views.add_quiz), - url(r'^manage/gradeuser/$', views.show_all_users), - url(r'^manage/gradeuser/(?P<username>.*)/(?P<questionpaper_id>\d+)/$', - views.grade_user), - url(r'^manage/gradeuser/(?P<username>.*)/$', views.grade_user), + url(r'^manage/gradeuser/$', views.grade_user), + url(r'^manage/gradeuser/(?P<quiz_id>\d+)/$',views.grade_user), + url(r'^manage/gradeuser/(?P<quiz_id>\d+)/(?P<user_id>\d+)/$',views.grade_user), + url(r'^manage/gradeuser/(?P<quiz_id>\d+)/(?P<user_id>\d+)/(?P<attempt_number>\d+)/$',views.grade_user), url(r'^manage/questions/$', views.show_all_questions), url(r'^manage/monitor/$', views.monitor), url(r'^manage/showquestionpapers/$', views.show_all_questionpapers), url(r'^manage/showquestionpapers/(?P<questionpaper_id>\d+)/$',\ views.show_all_questionpapers), url(r'^manage/monitor/(?P<questionpaper_id>\d+)/$', views.monitor), - url(r'^manage/user_data/(?P<username>.*)/(?P<questionpaper_id>\d+)/$', + url(r'^manage/user_data/(?P<user_id>\d+)/(?P<questionpaper_id>\d+)/$', views.user_data), - url(r'^manage/user_data/(?P<username>.*)/$', views.user_data), + url(r'^manage/user_data/(?P<user_id>\d+)/$', views.user_data), url(r'^manage/designquestionpaper/$', views.design_questionpaper), url(r'^manage/designquestionpaper/(?P<questionpaper_id>\d+)/$',\ views.design_questionpaper), diff --git a/yaksh/views.py b/yaksh/views.py index 5cc9f99..a91da29 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -3,7 +3,7 @@ import string import os import stat from os.path import dirname, pardir, abspath, join, exists -import datetime +from datetime import datetime import collections import csv from django.http import HttpResponse @@ -472,6 +472,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): new_answer.error = result.get('error') new_answer.save() paper.update_marks('inprogress') + paper.set_end_time(datetime.now()) if not result.get('success'): # Should only happen for non-mcq questions. new_answer.answer = user_code new_answer.save() @@ -548,6 +549,7 @@ def complete(request, reason=None, attempt_num=None, questionpaper_id=None): paper = AnswerPaper.objects.get(user=user, question_paper=q_paper, attempt_number=attempt_num) paper.update_marks() + paper.set_end_time(datetime.now()) if paper.percent == 100: message = "You answered all the questions correctly.\ You have been logged out successfully,\ @@ -760,25 +762,6 @@ def monitor(request, questionpaper_id=None): context_instance=ci) -def get_user_data(username, questionpaper_id=None): - """For a given username, this returns a dictionary of important data - related to the user including all the user's answers submitted. - """ - user = User.objects.get(username=username) - papers = AnswerPaper.objects.filter(user=user) - if questionpaper_id is not None: - papers = papers.filter(question_paper_id=questionpaper_id).order_by( - '-attempt_number') - - data = {} - profile = user.profile if hasattr(user, 'profile') else None - data['user'] = user - data['profile'] = profile - data['papers'] = papers - data['questionpaperid'] = questionpaper_id - return data - - @login_required def show_all_users(request): """Shows all the users who have taken various exams/quiz.""" @@ -842,14 +825,13 @@ def show_all_questions(request): context_instance=ci) @login_required -def user_data(request, username, questionpaper_id=None): +def user_data(request, user_id, questionpaper_id=None): """Render user data.""" - current_user = request.user if not current_user.is_authenticated() or not is_moderator(current_user): raise Http404('You are not allowed to view this page!') - - data = get_user_data(username, questionpaper_id) + user = User.objects.get(id=user_id) + data = AnswerPaper.objects.get_user_data(user, questionpaper_id) context = {'data': data} return my_render_to_response('yaksh/user_data.html', context, @@ -903,7 +885,7 @@ def download_csv(request, questionpaper_id): @login_required -def grade_user(request, username, questionpaper_id=None): +def grade_user(request, quiz_id=None, user_id=None, attempt_number=None): """Present an interface with which we can easily grade a user's papers and update all their marks and also give comments for each paper. """ @@ -911,26 +893,50 @@ def grade_user(request, username, questionpaper_id=None): ci = RequestContext(request) if not current_user.is_authenticated() or not is_moderator(current_user): raise Http404('You are not allowed to view this page!') - data = get_user_data(username, questionpaper_id) - if request.method == 'POST': + course_details = Course.objects.filter(Q(creator=current_user)| + Q(teachers=current_user)).distinct() + context = {"course_details": course_details} + if quiz_id is not None: + questionpaper_id = QuestionPaper.objects.filter(quiz_id=quiz_id)\ + .values("id") + user_details = AnswerPaper.objects.get_users_for_questionpaper\ + (questionpaper_id) + context = {"users": user_details, "quiz_id": quiz_id} + if user_id is not None: + + attempts = AnswerPaper.objects.get_user_all_attempts\ + (questionpaper_id, user_id) + try: + if attempt_number is None: + attempt_number = attempts[0].attempt_number + except IndexError: + raise Http404('No attempts for paper') + + user = User.objects.get(id=user_id) + data = AnswerPaper.objects.get_user_data(user, questionpaper_id, + attempt_number + ) + + context = {'data': data, "quiz_id": quiz_id, "users": user_details, + "attempts": attempts, "user_id": user_id + } + if request.method == "POST": papers = data['papers'] for paper in papers: for question, answers in paper.get_question_answers().iteritems(): marks = float(request.POST.get('q%d_marks' % question.id, 0)) - last_ans = answers[-1] - last_ans.marks = marks - last_ans.save() + answers = answers[-1] + answers.set_marks(marks) + answers.save() + paper.update_marks() paper.comments = request.POST.get( 'comments_%d' % paper.question_paper.id, 'No comments') paper.save() - context = {'data': data} - return my_render_to_response('yaksh/user_data.html', context, - context_instance=ci) - else: - context = {'data': data} - return my_render_to_response('yaksh/grade_user.html', context, - context_instance=ci) + + return my_render_to_response('yaksh/grade_user.html', + context, context_instance=ci + ) @csrf_exempt |