diff options
-rw-r--r-- | yaksh/models.py | 3 | ||||
-rw-r--r-- | yaksh/templates/base.html | 1 | ||||
-rw-r--r-- | yaksh/templates/manage.html | 1 | ||||
-rw-r--r-- | yaksh/templates/user.html | 1 | ||||
-rw-r--r-- | yaksh/templates/yaksh/login.html | 1 | ||||
-rw-r--r-- | yaksh/templates/yaksh/quizzes_user.html | 8 | ||||
-rw-r--r-- | yaksh/templates/yaksh/user_data.html | 7 | ||||
-rw-r--r-- | yaksh/templates/yaksh/view_answerpaper.html | 61 | ||||
-rw-r--r-- | yaksh/test_models.py | 12 | ||||
-rw-r--r-- | yaksh/test_views.py | 151 | ||||
-rw-r--r-- | yaksh/urls.py | 1 | ||||
-rw-r--r-- | yaksh/views.py | 12 |
12 files changed, 251 insertions, 8 deletions
diff --git a/yaksh/models.py b/yaksh/models.py index d2bffbe..0f801b8 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -428,6 +428,9 @@ class Quiz(models.Model): is_trial = models.BooleanField(default=False) + view_answerpaper = models.BooleanField('Allow student to view their answer\ + paper', default=False) + objects = QuizManager() class Meta: diff --git a/yaksh/templates/base.html b/yaksh/templates/base.html index 17df0d9..7fe2d27 100644 --- a/yaksh/templates/base.html +++ b/yaksh/templates/base.html @@ -16,6 +16,7 @@ <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/bootstrap.min.css"> <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/base.css"> + <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/font-awesome.css" type="text/css" /> <style> body { diff --git a/yaksh/templates/manage.html b/yaksh/templates/manage.html index 334f6a2..2fd0f7a 100644 --- a/yaksh/templates/manage.html +++ b/yaksh/templates/manage.html @@ -13,6 +13,7 @@ <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/base.css" type="text/css" /> <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/manage.css" type="text/css" /> + <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/font-awesome.css" type="text/css" /> {% block css %} {% endblock %} <script language="JavaScript" type="text/javascript" src="{{ URL_ROOT }}/static/yaksh/js/jquery-1.4.2.min.js"></script> diff --git a/yaksh/templates/user.html b/yaksh/templates/user.html index 009dd2f..4805c2d 100644 --- a/yaksh/templates/user.html +++ b/yaksh/templates/user.html @@ -15,6 +15,7 @@ {% endblock %} <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/base.css" type="text/css" /> + <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/font-awesome.css" type="text/css" /> {% block css %} {% endblock %} diff --git a/yaksh/templates/yaksh/login.html b/yaksh/templates/yaksh/login.html index 0a78c1b..5694f75 100644 --- a/yaksh/templates/yaksh/login.html +++ b/yaksh/templates/yaksh/login.html @@ -6,7 +6,6 @@ {% block css %} <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/bootstrap-social.css" type="text/css" /> <link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/login.css" type="text/css" /> -<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/font-awesome.css" type="text/css" /> {% endblock %} {% block content %} diff --git a/yaksh/templates/yaksh/quizzes_user.html b/yaksh/templates/yaksh/quizzes_user.html index 6403a21..98c156b 100644 --- a/yaksh/templates/yaksh/quizzes_user.html +++ b/yaksh/templates/yaksh/quizzes_user.html @@ -48,6 +48,7 @@ {% endif %} <table> <th>Quiz</th> + <th>View Answer Paper</th> <th>Pre requisite quiz</th> {% for quiz in quizzes %} {% if quiz.course_id == course.id %} @@ -63,6 +64,13 @@ </td> {% endif %} <td> + {% if quiz.view_answerpaper %} + <a href="{{ URL_ROOT }}/exam/view_answerpaper/{{ quiz.questionpaper_set.get.id }}/"><i class="fa fa-eye" aria-hidden="true"></i> Can View </a> + {% else%} + <a><i class="fa fa-eye-slash" aria-hidden="true"></i> Cannot view now </a> + {% endif %} + </td> + <td> {% if quiz.prerequisite %} You have to pass {{ quiz.prerequisite.description }} for taking {{ paper.quiz.description }} {% else %} diff --git a/yaksh/templates/yaksh/user_data.html b/yaksh/templates/yaksh/user_data.html index 2e7db50..1060e2d 100644 --- a/yaksh/templates/yaksh/user_data.html +++ b/yaksh/templates/yaksh/user_data.html @@ -50,11 +50,12 @@ User IP address: {{ paper.user_ip }} <h3> Answers </h3> {% for question, answers in paper.get_question_answers.items %} <p><strong> Question: {{ question.id }}. {{ question.summary }} (Points: {{ question.points }})</strong> </p> -{% if question.type == "mcq" %} -<p> Choices: -{% for option in question.options.strip.splitlines %} {{option}}, {% endfor %} +{% if question.type == "mcq" or question.type == "mcc" %} +<p> Choices: +{% for testcase in question.get_test_cases %} <br>{{ testcase.options }} {% endfor %} </p> <p>Student answer: {{ answers.0 }}</p> +Autocheck: {{ answers.0.error }} {% else %}{# non-mcq questions #} {% for answer in answers %} {% if not answer.skipped %} diff --git a/yaksh/templates/yaksh/view_answerpaper.html b/yaksh/templates/yaksh/view_answerpaper.html new file mode 100644 index 0000000..ae70e69 --- /dev/null +++ b/yaksh/templates/yaksh/view_answerpaper.html @@ -0,0 +1,61 @@ +{% extends "user.html" %} + +{% block title %} Answer Paper for {{ quiz.description }}{% endblock title %} + +{% block manage %} + +{% block subtitle %} Answer Paper for {{ quiz.description }}{% endblock %} + + +{% if not data.papers %} + <p><b> You have not attempted the quiz {{ quiz.description }} </b></p> +{% else %} + {% for paper in data.papers %} + {% if forloop.counter == 2 and data.questionpaperid %} + <U><h2> Previous attempts </h2></U> + {% endif %} + <h2> Quiz: {{ paper.question_paper.quiz.description }} </h2> + + <p> + Attempt Number: {{ paper.attempt_number }}<br/> + Questions correctly answered: {{ paper.get_answered_str }} <br/> + Marks obtained: {{ paper.marks_obtained }} <br/> + Start time: {{ paper.start_time }} <br/> + </p> + + {% if paper.answers.count %} + <h3> Answers </h3> + {% for question, answers in paper.get_question_answers.items %} + <p><strong> Question: {{ question.id }}. {{ question.summary }} (Mark(s): {{ question.points }})</strong> </p> + {% if question.type == "mcq" or question.type == "mcc" %} + <p> Choices: + {% for testcase in question.get_test_cases %} <br>{{ testcase.options }} {% endfor %} + </p> + <p>Student answer: {{ answers.0 }}</p> + Autocheck: {{ answers.0.error }} + {% else %}{# non-mcq questions #} + <p>Student answer: </p> + {% for answer in answers %} + {% if not answer.skipped %} + <pre> + ############################################################################### + {{ answer.answer.strip }} + # Autocheck: {{ answer.error }} + </pre> + {% endif %} + {% endfor %} + {% endif %} + {% with answers|last as answer %} + <p><em>Obtained Marks: {{answer.marks}} </em> </p> + {% endwith %} + <hr> + {% endfor %} {# for question, answers ... #} + <h3>Teacher comments: </h3> + {{ paper.comments|default:"None" }} + <hr><hr> + {% endif %} {# if paper.answers.count #} + + {% endfor %} {# for paper in data.papers #} + +{% endif %} {# if not data.papers #} +{% endblock %} diff --git a/yaksh/test_models.py b/yaksh/test_models.py index 4861fee..ca64f8c 100644 --- a/yaksh/test_models.py +++ b/yaksh/test_models.py @@ -252,6 +252,18 @@ class QuizTestCases(unittest.TestCase): ) self.assertEqual(trial_quiz.time_between_attempts, 0) + def test_view_answerpaper(self): + self.assertFalse(self.quiz1.view_answerpaper) + self.assertFalse(self.quiz2.view_answerpaper) + + # When + self.quiz1.view_answerpaper = True + self.quiz1.save() + + # Then + self.assertTrue(self.quiz1.view_answerpaper) + + ############################################################################### class QuestionPaperTestCases(unittest.TestCase): diff --git a/yaksh/test_views.py b/yaksh/test_views.py index 2544276..e232bc0 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -5,6 +5,7 @@ from django.contrib.auth.models import Group from django.core.urlresolvers import reverse from django.test import TestCase from django.test import Client +from django.utils import timezone from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\ @@ -632,7 +633,7 @@ class TestRemoveTeacher(TestCase): target_status_code=301 ) for t_id in teacher_id_list: - teacher = User.objects.get(id=t_id) + teacher = User.objects.get(id=t_id) self.assertNotIn(teacher, self.course.teachers.all()) @@ -640,7 +641,7 @@ class TestCourses(TestCase): def setUp(self): self.client = Client() - self.mod_group = Group.objects.create(name='moderator') + self.mod_group = Group.objects.create(name='moderator') # Create Moderator with profile self.user1_plaintext_pass = 'demo1' @@ -754,7 +755,7 @@ class TestCourseDetail(TestCase): def setUp(self): self.client = Client() - self.mod_group = Group.objects.create(name='moderator') + self.mod_group = Group.objects.create(name='moderator') # Create Moderator with profile self.user1_plaintext_pass = 'demo1' @@ -848,7 +849,7 @@ class TestCourseDetail(TestCase): """ self.client.login( username=self.user2.username, - password=self.user2_plaintext_pass + password=self.user2_plaintext_pass ) response = self.client.get(reverse('yaksh:course_detail', kwargs={'course_id': self.user1_course.id} @@ -980,6 +981,148 @@ class TestEnrollRequest(TestCase): ) self.assertRedirects(response, '/exam/manage/') +class TestViewAnswerPaper(TestCase): + def setUp(self): + self.client = Client() + self.plaintext_pass = 'demo' + + for i in range(1, 4): + User.objects.create_user( + username='demo_user{0}'.format(i), + password=self.plaintext_pass, + first_name='first_name', + last_name='last_name', + email='demo@test.com' + ) + + self.user1 = User.objects.get(pk=1) + + self.course = Course.objects.create(name="Python Course", + enrollment="Enroll Request", + creator=self.user1) + + self.question = Question.objects.create(summary='Dummy', points=1, + type='code', user=self.user1) + + self.quiz = Quiz.objects.create(time_between_attempts=0, course=self.course, + description='demo quiz', language='Python') + + self.question_paper = QuestionPaper.objects.create(quiz=self.quiz, + total_marks=1.0) + + self.question_paper.fixed_questions.add(self.question) + self.question_paper.save() + + AnswerPaper.objects.create(user_id=3, + attempt_number=1, question_paper=self.question_paper, + start_time=timezone.now(), user_ip='101.0.0.1', + end_time=timezone.now()+timezone.timedelta(minutes=20)) + + def tearDown(self): + User.objects.all().delete() + Course.objects.all().delete() + Question.objects.all().delete() + Quiz.objects.all().delete() + QuestionPaper.objects.all().delete() + AnswerPaper.objects.all().delete() + + def test_anonymous_user(self): + # Given, user not logged in + redirect_destination = ('/exam/login/?next=/exam' + '/view_answerpaper/{0}/'.format(self.question_paper.id)) + + # When + response = self.client.get(reverse('yaksh:view_answerpaper', + kwargs={'questionpaper_id': self.question_paper.id} + ), + follow=True + ) + + # Then + self.assertRedirects(response, redirect_destination) + + def test_cannot_view(self): + # Given, enrolled user tries to view when not permitted by moderator + user2 = User.objects.get(pk=2) + self.course.students.add(user2) + self.course.save() + self.quiz.view_answerpaper = False + self.quiz.save() + self.client.login( + username=user2.username, + password=self.plaintext_pass + ) + + # When + response = self.client.get(reverse('yaksh:view_answerpaper', + kwargs={'questionpaper_id': self.question_paper.id} + ), + follow=True + ) + + # Then + self.assertRedirects(response, '/exam/quizzes/') + + def test_can_view(self): + # Given, user enrolled and can view + user3 = User.objects.get(pk=3) + self.course.students.add(user3) + self.course.save() + answerpaper = AnswerPaper.objects.get(pk=1) + self.quiz.view_answerpaper = True + self.quiz.save() + self.client.login( + username=user3.username, + password=self.plaintext_pass + ) + + # When + response = self.client.get(reverse('yaksh:view_answerpaper', + kwargs={'questionpaper_id': self.question_paper.id} + ), + follow=True + ) + + # Then + self.assertEqual(response.status_code, 200) + self.assertTrue('data' in response.context) + self.assertTrue('quiz' in response.context) + self.assertTemplateUsed(response, 'yaksh/view_answerpaper.html') + + + # When, wrong question paper id + response = self.client.get(reverse('yaksh:view_answerpaper', + kwargs={'questionpaper_id': 190} + ), + follow=True + ) + + # Then + self.assertEqual(response.status_code, 404) + + + def test_view_when_not_enrolled(self): + # Given, user tries to view when not enrolled in the course + user2 = User.objects.get(pk=2) + self.client.login( + username=user2.username, + password=self.plaintext_pass + ) + self.course.students.remove(user2) + self.course.save() + self.quiz.view_answerpaper = True + self.quiz.save() + + # When + response = self.client.get(reverse('yaksh:view_answerpaper', + kwargs={'questionpaper_id': self.question_paper.id} + ), + follow=True + ) + + # Then + self.assertRedirects(response, '/exam/quizzes/') + class TestSelfEnroll(TestCase): def setUp(self): diff --git a/yaksh/urls.py b/yaksh/urls.py index d14ed1d..69d7f87 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -49,6 +49,7 @@ urlpatterns += [ views.skip), url(r'^enroll_request/(?P<course_id>\d+)/$', views.enroll_request, name='enroll_request'), url(r'^self_enroll/(?P<course_id>\d+)/$', views.self_enroll, name='self_enroll'), + url(r'^view_answerpaper/(?P<questionpaper_id>\d+)/$', views.view_answerpaper, name='view_answerpaper'), url(r'^manage/$', views.prof_manage, name='manage'), url(r'^manage/addquestion/$', views.add_question), url(r'^manage/addquestion/(?P<question_id>\d+)/$', views.edit_question), diff --git a/yaksh/views.py b/yaksh/views.py index 923b3c2..1abf248 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1261,3 +1261,15 @@ def test_quiz(request, mode, quiz_id): trial_questionpaper = test_mode(current_user, godmode, None, quiz_id) return my_redirect("/exam/start/{0}".format(trial_questionpaper.id)) + + +@login_required +def view_answerpaper(request, questionpaper_id): + user = request.user + quiz = get_object_or_404(QuestionPaper, pk=questionpaper_id).quiz + if quiz.view_answerpaper and user in quiz.course.students.all(): + data = AnswerPaper.objects.get_user_data(user, questionpaper_id) + context = {'data': data, 'quiz': quiz} + return my_render_to_response('yaksh/view_answerpaper.html', context) + else: + return my_redirect('/exam/quizzes/') |