diff options
-rw-r--r-- | yaksh/models.py | 182 | ||||
-rw-r--r-- | yaksh/templates/yaksh/complete.html | 14 | ||||
-rw-r--r-- | yaksh/templates/yaksh/intro.html | 30 | ||||
-rw-r--r-- | yaksh/templates/yaksh/question.html | 45 | ||||
-rw-r--r-- | yaksh/templates/yaksh/quit.html | 44 | ||||
-rw-r--r-- | yaksh/templates/yaksh/quizzes_user.html | 19 | ||||
-rw-r--r-- | yaksh/templates/yaksh/results_user.html | 9 | ||||
-rw-r--r-- | yaksh/tests.py | 179 | ||||
-rw-r--r-- | yaksh/urls.py | 9 | ||||
-rw-r--r-- | yaksh/views.py | 553 |
10 files changed, 362 insertions, 722 deletions
diff --git a/yaksh/models.py b/yaksh/models.py index 7bd0a9e..30257ef 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -1,4 +1,4 @@ -import datetime +from datetime import datetime, timedelta import json from random import sample, shuffle from itertools import islice, cycle @@ -70,7 +70,7 @@ class Course(models.Model): students = models.ManyToManyField(User, related_name='students') requests = models.ManyToManyField(User, related_name='requests') rejected = models.ManyToManyField(User, related_name='rejected') - created_on = models.DateTimeField(default=datetime.datetime.now()) + created_on = models.DateTimeField(default=datetime.now()) def request(self, *users): self.requests.add(*users) @@ -227,6 +227,10 @@ class Answer(models.Model): ############################################################################### +class QuizManager(models.Manager): + def get_active_quizzes(self): + return self.filter(active=True) +############################################################################### class Quiz(models.Model): """A quiz that students will participate in. One can think of this as the "examination" event. @@ -236,12 +240,12 @@ class Quiz(models.Model): # The start date of the quiz. start_date_time = models.DateTimeField("Start Date and Time of the quiz", - default=datetime.datetime.now(), + default=datetime.now(), null=True) # The end date and time of the quiz end_date_time = models.DateTimeField("End Date and Time of the quiz", - default=datetime.datetime(2199, 1, 1, 0, 0, 0, 0), + default=datetime(2199, 1, 1, 0, 0, 0, 0), null=True) # This is always in minutes. @@ -269,9 +273,18 @@ class Quiz(models.Model): time_between_attempts = models.IntegerField("Number of Days",\ choices=days_between_attempts) + objects = QuizManager() + class Meta: verbose_name_plural = "Quizzes" + + def is_expired(self): + return not self.start_date_time <= datetime.now() < self.end_date_time + + def has_prerequisite(self): + return True if self.prerequisite else False + def __unicode__(self): desc = self.description or 'Quiz' return '%s: on %s for %d minutes' % (desc, self.start_date_time, @@ -318,18 +331,36 @@ class QuestionPaper(models.Model): def make_answerpaper(self, user, ip, attempt_num): """Creates an answer paper for the user to attempt the quiz""" ans_paper = AnswerPaper(user=user, user_ip=ip, attempt_number=attempt_num) - ans_paper.start_time = datetime.datetime.now() + ans_paper.start_time = datetime.now() ans_paper.end_time = ans_paper.start_time \ - + datetime.timedelta(minutes=self.quiz.duration) + + timedelta(minutes=self.quiz.duration) ans_paper.question_paper = self - questions = self._get_questions_for_answerpaper() - question_ids = [str(x.id) for x in questions] - if self.shuffle_questions: - shuffle(question_ids) - ans_paper.questions = "|".join(question_ids) ans_paper.save() + questions = self._get_questions_for_answerpaper() + ans_paper.questions.add(*questions) + ans_paper.questions_unanswered.add(*questions) return ans_paper + def is_questionpaper_passed(self, user): + return AnswerPaper.objects.filter(question_paper=self, user=user, + passed = True).exists() + + def is_attempt_allowed(self, user): + attempts = AnswerPaper.objects.get_total_attempt(questionpaper=self, + user=user) + return attempts != self.quiz.attempts_allowed + + def can_attempt_now(self, user): + if self.is_attempt_allowed(user): + last_attempt = AnswerPaper.objects.get_user_last_attempt(user=user, + questionpaper=self) + if last_attempt: + time_lag = (datetime.today() - last_attempt.start_time).days + return time_lag >= self.quiz.time_between_attempts + else: + return True + else: + return False ############################################################################### class QuestionSet(models.Model): @@ -358,10 +389,13 @@ class AnswerPaperManager(models.Manager): ''' Return a dict of question id as key and count as value''' papers = self.filter(question_paper_id=questionpaper_id, attempt_number=attempt_number, status=status) + all_questions = list() questions = list() for paper in papers: - questions += paper.get_questions() - return Counter(map(int, questions)) + all_questions += paper.get_questions() + for question in all_questions: + questions.append(question.id) + return Counter(questions) def get_all_questions_answered(self, questionpaper_id, attempt_number, status='completed'): @@ -372,8 +406,8 @@ class AnswerPaperManager(models.Manager): for paper in papers: for question in filter(None, paper.get_questions_answered()): if paper.is_answer_correct(question): - questions_answered.append(question) - return Counter(map(int, questions_answered)) + questions_answered.append(question.id) + return Counter(questions_answered) def get_attempt_numbers(self, questionpaper_id, status='completed'): ''' Return list of attempt numbers''' @@ -431,6 +465,17 @@ class AnswerPaperManager(models.Manager): def _get_latest_attempt(self, answerpapers, user_id): return answerpapers.filter(user_id=user_id).order_by('-attempt_number')[0] + def get_user_last_attempt(self, questionpaper, user): + attempts = self.filter(question_paper=questionpaper, + user=user).order_by('-attempt_number') + if attempts: + return attempts[0] + + def get_user_answerpapers(self, user): + return self.filter(user=user) + + def get_total_attempt(self, questionpaper, user): + return self.filter(question_paper=questionpaper, user=user).count() ############################################################################### class AnswerPaper(models.Model): @@ -439,9 +484,7 @@ class AnswerPaper(models.Model): # The user taking this question paper. user = models.ForeignKey(User) - # All questions that remain to be attempted for a particular Student - # (a list of ids separated by '|') - questions = models.CharField(max_length=128) + questions = models.ManyToManyField(Question, related_name='questions') # The Quiz to which this question paper is attached to. question_paper = models.ForeignKey(QuestionPaper) @@ -458,8 +501,13 @@ class AnswerPaper(models.Model): # User's IP which is logged. user_ip = models.CharField(max_length=15) - # The questions successfully answered (a list of ids separated by '|') - questions_answered = models.CharField(max_length=128) + # The questions unanswered + questions_unanswered = models.ManyToManyField(Question, + related_name='questions_unanswered') + + # The questions answered + questions_answered = models.ManyToManyField(Question, + related_name='questions_answered') # All the submitted answers. answers = models.ManyToManyField(Answer) @@ -484,66 +532,37 @@ class AnswerPaper(models.Model): def current_question(self): """Returns the current active question to display.""" - qu = self.get_unanswered_questions() - if len(qu) > 0: - return qu[0] - else: - return '' + if self.questions_unanswered.all(): + return self.questions_unanswered.all()[0] def questions_left(self): """Returns the number of questions left.""" - qu = self.get_unanswered_questions() - return len(qu) - - def get_unanswered_questions(self): - """Returns the list of unanswered questions.""" - qa = self.questions_answered.split('|') - qs = self.questions.split('|') - qu = [q for q in qs if q not in qa] - return qu + return self.questions_unanswered.count() def completed_question(self, question_id): """ Adds the completed question to the list of answered questions and returns the next question. """ - qa = self.questions_answered - if len(qa) > 0: - self.questions_answered = '|'.join([qa, str(question_id)]) - else: - self.questions_answered = str(question_id) - self.save() + self.questions_answered.add(question_id) + self.questions_unanswered.remove(question_id) - return self.skip(question_id) + return self.current_question() def skip(self, question_id): """ Skips the current question and returns the next sequentially available question. """ - qu = self.get_unanswered_questions() - qs = self.questions.split('|') - - if len(qu) == 0: - return '' - - try: - q_index = qs.index(unicode(question_id)) - except ValueError: - return qs[0] - - start = q_index + 1 - stop = q_index + 1 + len(qs) - q_list = islice(cycle(qs), start, stop) - for next_q in q_list: - if next_q in qu: - return next_q - - return qs[0] + questions = self.questions_unanswered.all() + question_cycle = cycle(questions) + for question in question_cycle: + if question.id==int(question_id): + return question_cycle.next() def time_left(self): """Return the time remaining for the user in seconds.""" - dt = datetime.datetime.now() - self.start_time.replace(tzinfo=None) + dt = datetime.now() - self.start_time.replace(tzinfo=None) try: secs = dt.total_seconds() except AttributeError: @@ -553,25 +572,22 @@ class AnswerPaper(models.Model): remain = max(total - secs, 0) return int(remain) - def get_answered_str(self): - """Returns the answered questions, sorted and as a nice string.""" - qa = self.questions_answered.split('|') - answered = ', '.join(sorted(qa)) - return answered if answered else 'None' - - def update_marks_obtained(self): + def _update_marks_obtained(self): """Updates the total marks earned by student for this paper.""" marks = sum([x.marks for x in self.answers.filter(marks__gt=0.0)]) - self.marks_obtained = marks + if not marks: + self.marks_obtained = 0 + else: + self.marks_obtained = marks - def update_percent(self): + def _update_percent(self): """Updates the percent gained by the student for this paper.""" total_marks = self.question_paper.total_marks if self.marks_obtained is not None: percent = self.marks_obtained/self.question_paper.total_marks*100 self.percent = round(percent, 2) - def update_passed(self): + def _update_passed(self): """ Checks whether student passed or failed, as per the quiz passing criteria. @@ -582,10 +598,18 @@ class AnswerPaper(models.Model): else: self.passed = False - def update_status(self, state): + def _update_status(self, state): """ Sets status as inprogress or completed """ self.status = state + def update_marks(self, state='completed'): + self._update_marks_obtained() + self._update_percent() + self._update_passed() + self._update_status(state) + self.end_time = datetime.now() + self.save() + def get_question_answers(self): """ Return a dictionary with keys as questions and a list of the @@ -601,18 +625,24 @@ class AnswerPaper(models.Model): return q_a def get_questions(self): - ''' Return a list of questions''' - return self.questions.split('|') + return self.questions.all() def get_questions_answered(self): - ''' Return a list of questions answered''' - return self.questions_answered.split('|') + return self.questions_answered.all() + + def get_questions_unanswered(self): + return self.questions_unanswered.all() def is_answer_correct(self, question_id): ''' Return marks of a question answered''' return self.answers.filter(question_id=question_id, correct=True).exists() + def is_attempt_inprogress(self): + if self.status == 'inprogress': + return self.time_left()> 0 + + def __unicode__(self): u = self.user return u'Question paper for {0} {1}'.format(u.first_name, u.last_name) diff --git a/yaksh/templates/yaksh/complete.html b/yaksh/templates/yaksh/complete.html index 08abe76..07cbf3a 100644 --- a/yaksh/templates/yaksh/complete.html +++ b/yaksh/templates/yaksh/complete.html @@ -5,27 +5,27 @@ {% block pagetitle %}Online Test{% endblock %} {% block content %} {% csrf_token %} - {% if submitted or unattempted %} + {% if paper.questions_answered.all or paper.questions_unanswered.all %} <br><center><table class="bordered-table zebra-striped span8" style="text-align:left;"> <tr><td><b>Submitted Questions</b></td> <td> - {% if submitted %} - {{ submitted|join:", " }} + {% if paper.questions_answered.all %} + {{ paper.questions_answered.all|join:", " }} {% else %} <p><b>No Questions have been Submitted</b></p> {% endif %} </td></tr> <tr><td><b>Unattempted Questions</b></td> <td> - {% if unattempted %} - {{ unattempted|join:", " }} + {% if paper.questions_unanswered.all %} + {{ paper.questions_unanswered.all|join:", " }} {% else %} <p><b>All Questions have been Submitted</b></p> {% endif %} </td></tr> - </table></center> - {% endif %} + </table></center> + {% endif %} <center><h2> Good bye! </h2></center> <center><h4> {{message}} </h4></center> <br><center><h4>You may now close the browser.</h4></center><br> diff --git a/yaksh/templates/yaksh/intro.html b/yaksh/templates/yaksh/intro.html index 2542795..1ed82e2 100644 --- a/yaksh/templates/yaksh/intro.html +++ b/yaksh/templates/yaksh/intro.html @@ -5,21 +5,19 @@ {% block formtitle %}Important instructions & rules {% endblock %} {% block content %} - {% if enable_quiz_time or disable_quiz_time %} - {% if quiz_expired %} - <div class="alert alert-error"> - This Quiz has expired. You can no longer attempt this Quiz. + {% if questionpaper.quiz.is_expired %} + <div class="alert alert-error"> + This Quiz has expired. You can no longer attempt this Quiz. + <br/> + </div> + {% else %} + <div class="alert"> + You can attempt this Quiz at any time between {{ questionpaper.quiz.start_date_time }} GMT and {{ questionpaper.quiz.end_date_time }} GMT <br/> - </div> - {% else %} - <div class="alert"> - You can attempt this Quiz at any time between {{ enable_quiz_time }} GMT and {{ disable_quiz_time }} GMT - <br/> - You are not allowed to attempt the Quiz before or after this duration - <br/> - </div> - {% endif %} - {% endif %} + You are not allowed to attempt the Quiz before or after this duration + <br/> + </div> + {% endif %} <p> Welcome <strong>{{user.first_name.title}} {{user.last_name.title}}</strong>, to the programming quiz! </p> <p> This examination system has been developed with the intention of making you @@ -44,8 +42,8 @@ {% csrf_token %} <center><button class="btn" name="home">Home</button></center> </form> - {% if not quiz_expired %} - <form action="{{URL_ROOT}}/exam/start/{{ attempt_num }}/{{ paper_id }}/" method="post" align="center"> + {% if not questionpaper.quiz.is_expired %} + <form action="{{URL_ROOT}}/exam/start/{{ attempt_num }}/{{ questionpaper.id }}/" method="post" align="center"> {% csrf_token %} <center><button class="btn" type="submit" name="start">Start Exam!</button></center> </form> diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html index 0d1daee..0c48167 100644 --- a/yaksh/templates/yaksh/question.html +++ b/yaksh/templates/yaksh/question.html @@ -15,7 +15,7 @@ <script src="{{ URL_ROOT }}/static/yaksh/js/bootstrap-modal.js"></script> <script> -var time_left = {{ time_left }} +var time_left = {{ paper.time_left }} function getTimeRemaining(endtime){ var t = Date.parse(endtime) - Date.parse(new Date()); @@ -90,11 +90,11 @@ function call_skip(url) form.action = url form.submit(); } - {% if question.type == 'code' and success == 'True'%} - {% if to_attempt|length != 0 %} + {% if question.type == 'code' and success == 'True'%} + {% if paper.questions_left %} window.setTimeout(function() { - {% for qid, num in questions.items %} + {% for qid in paper.questions.all %} location.href="{{ URL_ROOT }}/exam/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" {% endfor %} }, 1000); @@ -117,10 +117,10 @@ function call_skip(url) <div class="container"> <h3 class="brand"><strong>Online Test</h3></strong> <ul> - <li><h5><a> Hi {{user.first_name.title}} {{user.last_name.title}} </a></h5></li> + <li><h5><a> Hi {{ paper.user.first_name.title}} {{ paper.user.last_name.title}} </a></h5></li> </ul><br> <div class=time-div id="time_left"></div> - <h5 class=td1-class>You have {{ paper.questions_left }} question(s) left in {{ quiz_name }}</h5> + <h5>You have {{ paper.questions_left }} question(s) left in {{ paper.question_paper.quiz.description }}</h5> </div> </div> </div> @@ -128,20 +128,21 @@ function call_skip(url) <div class="sidebar"> <p>Question Navigator </p> <div class="pagination"> - <ul> - {% for qid, num in questions.items %} - {% if qid.id|slugify in to_attempt %} - {% if qid.id|slugify == question.id|slugify %} - <li class="active"><a href="#" data-toggle="tooltip" title="{{ qid.description }}" onclick="call_skip('{{ URL_ROOT }}/exam/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')">{{ num }}</a></li> - {% else %} - <li><a href="#" data-toggle="tooltip" title="{{ qid.description }}" onclick="call_skip('{{ URL_ROOT }}/exam/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')">{{ num }}</a></li> - {% endif %} - {% endif %} - {% if qid.id|slugify in submitted %} - <li class="disabled"><a href="#">{{ num }}</a></li> - {% endif %} - {% endfor %} - </ul> + <ul> + {% for qid in paper.questions.all %} + {% if qid in paper.questions_unanswered.all %} + {% if qid.id == question.id %} + {{ q.id}} + <li class="active"><a href="#" data-toggle="tooltip" title="{{ qid.description }}" onclick="call_skip('{{ URL_ROOT }}/exam/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')">{{ forloop.counter }}</a></li> + {% else %} + <li><a href="#" data-toggle="tooltip" title="{{ qid.description }}" onclick="call_skip('{{ URL_ROOT }}/exam/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')">{{ forloop.counter }}</a></li> + {% endif %} + {% endif %} + {% if qid in paper.questions_answered.all %} + <li class="disabled"><a href="#">{{ forloop.counter }}</a></li> + {% endif %} + {% endfor %} + </ul> </div> </div> </div> @@ -191,7 +192,7 @@ function call_skip(url) <script type="text/javascript"> addLineNumbers('answer'); - </script> + </script> <script>addLineNumbers('snippet');</script> {% endif %} @@ -202,7 +203,7 @@ function call_skip(url) {% else %} <button class="btn" type="submit" name="check" id="check" onClick="submitCode();">Check Answer</button> {% endif %} - {% if to_attempt|length != 1 %} + {% if paper.unanswered.all|length != 1 %} <button class="btn" type="submit" name="skip" id="skip">Attempt Later</button> {% endif %} </form> diff --git a/yaksh/templates/yaksh/quit.html b/yaksh/templates/yaksh/quit.html index 91bce64..7be8ad5 100644 --- a/yaksh/templates/yaksh/quit.html +++ b/yaksh/templates/yaksh/quit.html @@ -3,33 +3,31 @@ {% block title %}Quit exam {% endblock %} {% block pagetitle %}Online Test {% endblock %} {% block content %} - {% if submitted or unattempted %} - <br><center><table class="bordered-table zebra-striped span8" - style="text-align:left;"> - <tr><td><b>Submitted Questions</b></td> - <td> - {% if submitted %} - {{ submitted|join:", " }} - {% else %} - <p><b>No Questions have been Submitted</b></p> - {% endif %} - </td></tr> - <tr><td><b>Unattempted Questions</b></td> - <td> - {% if unattempted %} - {{ unattempted|join:", " }} - {% else %} - <p><b>All Questions have been Submitted</b></p> - {% endif %} - </td></tr> - </table></center> - {% endif %} + <br><center><table class="bordered-table zebra-striped span8" + style="text-align:left;"> + <tr><td><b>Submitted Questions</b></td> + <td> + {% if paper.questions_answered.all %} + {{ paper.questions_answered.all|join:", " }} + {% else %} + <p><b>No Questions have been Submitted</b></p> + {% endif %} + </td></tr> + <tr><td><b>Unattempted Questions</b></td> + <td> + {% if paper.questions_unanswered.all %} + {{ paper.questions_unanswered.all|join:", " }} + {% else %} + <p><b>All Questions have been Submitted</b></p> + {% endif %} + </td></tr> + </table></center> <center><h4>Your current answers are saved.</h4></center> <center><h4> Are you sure you wish to quit the exam?</h4></center> <center><h4> Be sure, as you won't be able to restart this exam.</h4></center> - <form action="{{URL_ROOT}}/exam/complete/{{ attempt_num }}/{{ id }}/" method="post"> + <form action="{{URL_ROOT}}/exam/complete/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" method="post"> {% csrf_token %} - <center><button class="btn" type="submit" name="yes">Yes!</button> <button class="btn" type="button" name="no" onClick="window.location='{{ URL_ROOT }}/exam/start/{{ attempt_num }}/{{ id }}/'">No!</button></center> + <center><button class="btn" type="submit" name="yes">Yes!</button> <button class="btn" type="button" name="no" onClick="window.location='{{ URL_ROOT }}/exam/start/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/'">No!</button></center> </form> {% endblock content %} diff --git a/yaksh/templates/yaksh/quizzes_user.html b/yaksh/templates/yaksh/quizzes_user.html index a800e68..69cf3ba 100644 --- a/yaksh/templates/yaksh/quizzes_user.html +++ b/yaksh/templates/yaksh/quizzes_user.html @@ -21,7 +21,7 @@ <div class="span14"> <div class="row"> <div class="span6"> - <h4><b><u> {{ course.name }} by {{ course.creator}}</u></b></h4> + <h4><b><u> {{ course.name }} by {{ course.creator }}</u></b></h4> </div> <div class="span6"> {% if user in course.requests.all %} <span class="label warning">Request Pending </span> @@ -49,23 +49,24 @@ <table> <th>Quiz</th> <th>Pre requisite quiz</th> - {% for paper in quizzes %} - {% if paper.quiz.course_id == course.id %} + {% for quiz in quizzes %} + {% if quiz.course_id == course.id %} <tr> - {% if paper in unexpired_quizzes %} + {% if not quiz.is_expired %} <td> - <a href="{{ URL_ROOT }}/exam/intro/{{paper.id}}">{{ paper.quiz.description }}</a><br> + <a href="{{ URL_ROOT }}/exam/intro/{{quiz.questionpaper_set.get.id}}">{{ quiz.description }}</a><br> </td> {% else %} <td> - {{ paper.quiz.description }} <span class="label important">Expired</span><br> + <a href="{{ URL_ROOT }}/exam/intro/{{quiz.questionpaper_set.get.id}}">{{ quiz.description }}</a><br> + {{ quiz.description }} <span class="label important">INACTIVE</span><br> </td> {% endif %} <td> - {% if paper.quiz.prerequisite %} - You have to pass {{ paper.quiz.prerequisite.description }} for taking {{ paper.quiz.description }} + {% if quiz.prerequisite %} + You have to pass {{ quiz.prerequisite.description }} for taking {{ paper.quiz.description }} {% else %} - No pre requisites for {{ paper.quiz.description }} + No pre requisites for {{ quiz.description }} {% endif %} </td> </tr> diff --git a/yaksh/templates/yaksh/results_user.html b/yaksh/templates/yaksh/results_user.html index 0f35c0d..3a6450d 100644 --- a/yaksh/templates/yaksh/results_user.html +++ b/yaksh/templates/yaksh/results_user.html @@ -17,10 +17,11 @@ <th>Percentage {% for paper in papers %} <tr> - {% for i in paper %} - <td>{{ i }} - {% endfor %} - <br> + <td>{{ paper.question_paper.quiz.description }}</td> + <td>{{ paper.marks_obtained }}</td> + <td>{{ paper.question_paper.total_marks }}</td> + <td>{{ paper.percent }}</td> + </tr> {% endfor %} </table></center> </form> diff --git a/yaksh/tests.py b/yaksh/tests.py index 848df74..bdc9584 100644 --- a/yaksh/tests.py +++ b/yaksh/tests.py @@ -1,7 +1,8 @@ from django.utils import unittest from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ QuestionSet, AnswerPaper, Answer, TestCase, Course -import datetime, json +import json +from datetime import datetime, timedelta def setUpModule(): @@ -29,14 +30,22 @@ def setUpModule(): Question.objects.create(summary='Q%d' % (i), points=1) # create a quiz - Quiz.objects.create(start_date_time=datetime.datetime(2015, 10, 9, 10, 8, 15, 0), + quiz = Quiz.objects.create(start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0), + end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0), + duration=30, active=True, + attempts_allowed=1, time_between_attempts=0, + description='demo quiz', pass_criteria=0, + language='Python', prerequisite=None, + course=course) + + Quiz.objects.create(start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0), + end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0), duration=30, active=False, attempts_allowed=-1, time_between_attempts=0, description='demo quiz', pass_criteria=40, - language='Python', prerequisite=None, + language='Python', prerequisite=quiz, course=course) - def tearDownModule(): User.objects.all().delete() Question.objects.all().delete() @@ -133,20 +142,34 @@ class TestCaseTestCases(unittest.TestCase): ############################################################################### class QuizTestCases(unittest.TestCase): def setUp(self): - self.quiz = Quiz.objects.get(pk=1) + self.quiz1 = Quiz.objects.get(pk=1) + self.quiz2 = Quiz.objects.get(pk=2) def test_quiz(self): """ Test Quiz""" - self.assertEqual((self.quiz.start_date_time).strftime('%Y-%m-%d'), + self.assertEqual((self.quiz1.start_date_time).strftime('%Y-%m-%d'), '2015-10-09') - self.assertEqual((self.quiz.start_date_time).strftime('%H:%M:%S'), + self.assertEqual((self.quiz1.start_date_time).strftime('%H:%M:%S'), '10:08:15') - self.assertEqual(self.quiz.duration, 30) - self.assertTrue(self.quiz.active is False) - self.assertEqual(self.quiz.description, 'demo quiz') - self.assertEqual(self.quiz.language, 'Python') - self.assertEqual(self.quiz.pass_criteria, 40) - self.assertEqual(self.quiz.prerequisite, None) + self.assertEqual(self.quiz1.duration, 30) + self.assertTrue(self.quiz1.active) + self.assertEqual(self.quiz1.description, 'demo quiz') + self.assertEqual(self.quiz1.language, 'Python') + self.assertEqual(self.quiz1.pass_criteria, 0) + self.assertEqual(self.quiz1.prerequisite, None) + + def test_is_expired(self): + self.assertFalse(self.quiz1.is_expired()) + self.assertTrue(self.quiz2.is_expired()) + + def test_has_prerequisite(self): + self.assertFalse(self.quiz1.has_prerequisite()) + self.assertTrue(self.quiz2.has_prerequisite()) + + def test_get_active_quizzes(self): + quizzes = Quiz.objects.get_active_quizzes() + for quiz in quizzes: + self.assertTrue(quiz.active) ############################################################################### @@ -212,6 +235,8 @@ class QuestionPaperTestCases(unittest.TestCase): """ Test get_random_questions() method of Question Paper""" random_questions_set_1 = self.question_set_1.get_random_questions() random_questions_set_2 = self.question_set_2.get_random_questions() + total_random_questions = len(random_questions_set_1 + random_questions_set_2) + self.assertEqual(total_random_questions, 5) # To check whether random questions are from random_question_set questions_set_1 = set(self.question_set_1.questions.all()) @@ -227,20 +252,6 @@ class QuestionPaperTestCases(unittest.TestCase): except AssertionError: self.assertTrue(random_questions_set_1 != random_questions_set_2) - def test_get_questions_for_answerpaper(self): - """ Test get_questions_for_answerpaper() method of Question Paper""" - questions = self.question_paper._get_questions_for_answerpaper() - fixed = list(self.question_paper.fixed_questions.all()) - question_set = self.question_paper.random_questions.all() - total_random_questions = 0 - available_questions = [] - for qs in question_set: - total_random_questions += qs.num_questions - available_questions += qs.questions.all() - self.assertEqual(total_random_questions, 5) - self.assertEqual(len(available_questions), 8) - self.assertEqual(len(questions), 7) - def test_make_answerpaper(self): """ Test make_answerpaper() method of Question Paper""" already_attempted = self.attempted_papers.count() @@ -248,11 +259,18 @@ class QuestionPaperTestCases(unittest.TestCase): answerpaper = self.question_paper.make_answerpaper(self.user, self.ip, attempt_num) self.assertIsInstance(answerpaper, AnswerPaper) - paper_questions = set((answerpaper.questions).split('|')) + paper_questions = answerpaper.questions.all() self.assertEqual(len(paper_questions), 7) - fixed = {'4', '6'} - boolean = fixed.intersection(paper_questions) == fixed - self.assertTrue(boolean) + fixed_questions = set(self.question_paper.fixed_questions.all()) + self.assertTrue(fixed_questions.issubset(set(paper_questions))) + # test is_questionpaper_passed() + answerpaper.passed = True + answerpaper.save() + self.assertTrue(self.question_paper.is_questionpaper_passed(self.user)) + # test is_attempt_allowed() + self.assertFalse(self.question_paper.is_attempt_allowed(self.user)) + # test can_attempt_now(self): + self.assertFalse(self.question_paper.can_attempt_now(self.user)) ############################################################################### @@ -265,21 +283,24 @@ class AnswerPaperTestCases(unittest.TestCase): self.quiz = Quiz.objects.get(pk=1) self.question_paper = QuestionPaper(quiz=self.quiz, total_marks=3) self.question_paper.save() + self.questions = Question.objects.filter(id__in=[1,2,3]) + self.start_time = datetime.now() + self.end_time = self.start_time + timedelta(minutes=20) # create answerpaper self.answerpaper = AnswerPaper(user=self.user, - questions='1|2|3', question_paper=self.question_paper, - start_time='2014-06-13 12:20:19.791297', - end_time='2014-06-13 12:50:19.791297', + start_time=self.start_time, + end_time=self.end_time, user_ip=self.ip) - self.answerpaper.questions_answered = '1' self.attempted_papers = AnswerPaper.objects.filter(question_paper=self.question_paper, user=self.user) already_attempted = self.attempted_papers.count() self.answerpaper.attempt_number = already_attempted + 1 self.answerpaper.save() - + self.answerpaper.questions.add(*self.questions) + self.answerpaper.questions_unanswered.add(*self.questions) + self.answerpaper.save() # answers for the Answer Paper self.answer_right = Answer(question=Question.objects.get(id=1), answer="Demo answer", correct=True, marks=1) @@ -294,57 +315,48 @@ class AnswerPaperTestCases(unittest.TestCase): """ Test Answer Paper""" self.assertEqual(self.answerpaper.user.username, 'demo_user') self.assertEqual(self.answerpaper.user_ip, self.ip) - questions = self.answerpaper.questions - num_questions = len(questions.split('|')) - self.assertEqual(questions, '1|2|3') + questions = self.answerpaper.get_questions() + num_questions = len(questions) + self.assertSequenceEqual(list(questions), list(self.questions)) self.assertEqual(num_questions, 3) self.assertEqual(self.answerpaper.question_paper, self.question_paper) - self.assertEqual(self.answerpaper.start_time, - '2014-06-13 12:20:19.791297') - self.assertEqual(self.answerpaper.end_time, - '2014-06-13 12:50:19.791297') + self.assertEqual(self.answerpaper.start_time, self.start_time) self.assertEqual(self.answerpaper.status, 'inprogress') - def test_current_question(self): - """ Test current_question() method of Answer Paper""" + def test_questions(self): + # Test questions_left() method of Answer Paper + self.assertEqual(self.answerpaper.questions_left(), 3) + # Test current_question() method of Answer Paper current_question = self.answerpaper.current_question() - self.assertEqual(current_question, '2') - - def test_completed_question(self): - """ Test completed_question() method of Answer Paper""" + self.assertEqual(current_question.id, 1) + # Test completed_question() method of Answer Paper question = self.answerpaper.completed_question(1) self.assertEqual(self.answerpaper.questions_left(), 2) - - def test_questions_left(self): - """ Test questions_left() method of Answer Paper""" - self.assertEqual(self.answerpaper.questions_left(), 2) - - def test_skip(self): - """ Test skip() method of Answer Paper""" + # Test skip() method of Answer Paper current_question = self.answerpaper.current_question() - next_question_id = self.answerpaper.skip(current_question) + self.assertEqual(current_question.id, 2) + next_question_id = self.answerpaper.skip(current_question.id) self.assertTrue(next_question_id is not None) - self.assertEqual(next_question_id, '3') - - def test_answered_str(self): - """ Test answered_str() method of Answer Paper""" - answered_question = self.answerpaper.get_answered_str() - self.assertEqual(answered_question, '1') - - def test_update_marks_obtained(self): - """ Test get_marks_obtained() method of Answer Paper""" - self.answerpaper.update_marks_obtained() + self.assertEqual(next_question_id.id, 3) + questions_answered = self.answerpaper.get_questions_answered() + self.assertEqual(questions_answered.count(), 1) + self.assertSequenceEqual(questions_answered, [self.questions[0]]) + questions_unanswered = self.answerpaper.get_questions_unanswered() + self.assertEqual(questions_unanswered.count(), 2) + self.assertSequenceEqual(questions_unanswered, + [self.questions[1], self.questions[2]]) + + def test_update_marks(self): + """ Test update_marks method of AnswerPaper""" + self.answerpaper.update_marks('inprogress') + self.assertEqual(self.answerpaper.status, 'inprogress') + self.assertTrue(self.answerpaper.is_attempt_inprogress()) + self.answerpaper.update_marks() + self.assertEqual(self.answerpaper.status, 'completed') self.assertEqual(self.answerpaper.marks_obtained, 1.0) - - def test_update_percent(self): - """ Test update_percent() method of Answerpaper""" - self.answerpaper.update_percent() self.assertEqual(self.answerpaper.percent, 33.33) - - def test_update_passed(self): - """ Test update_passed method of AnswerPaper""" - self.answerpaper.update_passed() - self.assertFalse(self.answerpaper.passed) + self.assertTrue(self.answerpaper.passed) + self.assertFalse(self.answerpaper.is_attempt_inprogress()) def test_get_question_answer(self): """ Test get_question_answer() method of Answer Paper""" @@ -354,13 +366,9 @@ class AnswerPaperTestCases(unittest.TestCase): self.assertTrue(first_answer.correct) self.assertEqual(len(answered), 2) - def test_update_status(self): - """ Test update_status method of Answer Paper""" - self.answerpaper.update_status('inprogress') - self.assertEqual(self.answerpaper.status, 'inprogress') - self.answerpaper.update_status('completed') - self.assertEqual(self.answerpaper.status, 'completed') - + def test_is_answer_correct(self): + self.assertTrue(self.answerpaper.is_answer_correct(self.questions[0])) + self.assertFalse(self.answerpaper.is_answer_correct(self.questions[1])) ############################################################################### class CourseTestCases(unittest.TestCase): @@ -369,7 +377,8 @@ class CourseTestCases(unittest.TestCase): self.creator = User.objects.get(pk=1) self.student1 = User.objects.get(pk=2) self.student2 = User.objects.get(pk=3) - self.quiz = Quiz.objects.get(pk=1) + self.quiz1 = Quiz.objects.get(pk=1) + self.quiz2 = Quiz.objects.get(pk=2) def test_is_creator(self): """ Test is_creator method of Course""" @@ -422,4 +431,4 @@ class CourseTestCases(unittest.TestCase): def test_get_quizzes(self): """ Test get_quizzes method of Courses""" - self.assertSequenceEqual(self.course.get_quizzes(), [self.quiz]) + self.assertSequenceEqual(self.course.get_quizzes(), [self.quiz1, self.quiz2]) diff --git a/yaksh/urls.py b/yaksh/urls.py index be33051..53c7b8a 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -22,11 +22,10 @@ urlpatterns += patterns('yaksh.views', url(r'^complete/(?P<attempt_num>\d+)/(?P<questionpaper_id>\d+)/$',\ 'complete'), url(r'^register/$', 'user_register'), - url(r'^(?P<q_id>\d+)/$', 'question'), url(r'^(?P<q_id>\d+)/check/$', 'check'), url(r'^(?P<q_id>\d+)/check/(?P<attempt_num>\d+)/(?P<questionpaper_id>\d+)/$',\ 'check'), - url(r'^intro/$', 'start'), + url(r'^intro/$', 'intro'), url(r'^(?P<q_id>\d+)/(?P<attempt_num>\d+)/(?P<questionpaper_id>\d+)/$', 'show_question'), url(r'^enroll_request/(?P<course_id>\d+)/$', 'enroll_request'), url(r'^self_enroll/(?P<course_id>\d+)/$', 'self_enroll'), @@ -55,12 +54,6 @@ urlpatterns += patterns('yaksh.views', url(r'^manage/designquestionpaper/$', 'design_questionpaper'), url(r'^manage/designquestionpaper/(?P<questionpaper_id>\d+)/$',\ 'design_questionpaper'), - url(r'^manage/designquestionpaper/automatic/(?P<questionpaper_id>\d+)/$',\ - 'automatic_questionpaper'), - url(r'^manage/designquestionpaper/automatic$', 'automatic_questionpaper'), - url(r'^manage/designquestionpaper/manual$', 'manual_questionpaper'), - url(r'^manage/designquestionpaper/manual/(?P<questionpaper_id>\d+)/$',\ - 'manual_questionpaper'), url(r'^manage/statistics/question/(?P<questionpaper_id>\d+)/$', 'show_statistics'), url(r'^manage/statistics/question/(?P<questionpaper_id>\d+)/(?P<attempt_number>\d+)/$', diff --git a/yaksh/views.py b/yaksh/views.py index 03f4f61..314814d 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -47,12 +47,6 @@ def my_render_to_response(template, context=None, **kwargs): return render_to_response(template, context, **kwargs) -def gen_key(no_of_chars): - """Generate a random key of the number of characters.""" - allowed_chars = string.digits+string.uppercase - return ''.join([random.choice(allowed_chars) for i in range(no_of_chars)]) - - def get_user_dir(user): """Return the output directory for the user.""" @@ -72,43 +66,6 @@ def is_moderator(user): return True -def fetch_questions(request): - """Fetch questions from database based on the given search conditions & - tags""" - set1 = set() - set2 = set() - first_tag = request.POST.get('first_tag') - first_condition = request.POST.get('first_condition') - second_tag = request.POST.get('second_tag') - second_condition = request.POST.get('second_condition') - third_tag = request.POST.get('third_tag') - question1 = set(Question.objects.filter(tags__name__in=[first_tag])) - question2 = set(Question.objects.filter(tags__name__in=[second_tag])) - question3 = set(Question.objects.filter(tags__name__in=[third_tag])) - if first_condition == 'and': - set1 = question1.intersection(question2) - if second_condition == 'and': - set2 = set1.intersection(question3) - else: - set2 = set1.union(question3) - else: - set1 = question1.union(question2) - if second_condition == 'and': - set2 = set1.intersection(question3) - else: - set2 = set1.union(question3) - return set2 - - -def _update_marks(answer_paper, state='completed'): - answer_paper.update_marks_obtained() - answer_paper.update_percent() - answer_paper.update_passed() - answer_paper.update_status(state) - answer_paper.end_time = datetime.datetime.now() - answer_paper.save() - - def index(request): """The start page. """ @@ -151,135 +108,56 @@ def user_register(request): def quizlist_user(request): """Show All Quizzes that is available to logged-in user.""" user = request.user - avail_quizzes = list(QuestionPaper.objects.filter(quiz__active=True)) + avail_quizzes = Quiz.objects.get_active_quizzes() user_answerpapers = AnswerPaper.objects.filter(user=user) - pre_requisites = [] - enabled_quizzes = [] - disabled_quizzes = [] - unexpired_quizzes = [] - courses = Course.objects.filter(active=True) - for paper in avail_quizzes: - quiz_enable_time = paper.quiz.start_date_time - quiz_disable_time = paper.quiz.end_date_time - if quiz_enable_time <= datetime.datetime.now() <= quiz_disable_time: - unexpired_quizzes.append(paper) - - cannot_attempt = True if 'cannot_attempt' in request.GET else False - quizzes_taken = None if user_answerpapers.count() == 0 else user_answerpapers - - context = {'cannot_attempt': cannot_attempt, - 'quizzes': avail_quizzes, + context = { 'quizzes': avail_quizzes, 'user': user, - 'quizzes_taken': quizzes_taken, - 'unexpired_quizzes': unexpired_quizzes, - 'courses': courses + 'courses': courses, + 'quizzes_taken': user_answerpapers, } return my_render_to_response("yaksh/quizzes_user.html", context) @login_required -def intro(request, questionpaper_id): +def intro(request, questionpaper_id=None): """Show introduction page before quiz starts""" user = request.user ci = RequestContext(request) + if questionpaper_id is None: + return my_redirect('/exam/quizzes/') quest_paper = QuestionPaper.objects.get(id=questionpaper_id) - if not quest_paper.quiz.course.is_enrolled(user): + if not quest_paper.quiz.course.is_enrolled(user) : raise Http404('You are not allowed to view this page!') attempt_number = quest_paper.quiz.attempts_allowed - time_lag = quest_paper.quiz.time_between_attempts - quiz_enable_time = quest_paper.quiz.start_date_time - quiz_disable_time = quest_paper.quiz.end_date_time - - quiz_expired = False if quiz_enable_time <= datetime.datetime.now() \ - <= quiz_disable_time else True - - if quest_paper.quiz.prerequisite: - try: - pre_quest = QuestionPaper.objects.get( - quiz=quest_paper.quiz.prerequisite) - answer_papers = AnswerPaper.objects.filter( - question_paper=pre_quest, user=user) - answer_papers_failed = AnswerPaper.objects.filter( - question_paper=pre_quest, user=user, passed=False) - if answer_papers.count() == answer_papers_failed.count(): - context = {'user': user, 'cannot_attempt': True} - return my_redirect("/exam/quizzes/?cannot_attempt=True") - except: - context = {'user': user, 'cannot_attempt': True} - return my_redirect("/exam/quizzes/?cannot_attempt=True") - - attempted_papers = AnswerPaper.objects.filter(question_paper=quest_paper, - user=user) - already_attempted = attempted_papers.count() - inprogress, previous_attempt, next_attempt = _check_previous_attempt(attempted_papers, - already_attempted, - attempt_number) - - if previous_attempt: - if inprogress: - return show_question(request, - previous_attempt.current_question(), - previous_attempt.attempt_number, - previous_attempt.question_paper.id) - days_after_attempt = (datetime.datetime.today() - \ - previous_attempt.start_time).days - - if next_attempt: - if days_after_attempt >= time_lag: - context = {'user': user, - 'paper_id': questionpaper_id, - 'attempt_num': already_attempted + 1, - 'enable_quiz_time': quiz_enable_time, - 'disable_quiz_time': quiz_disable_time, - 'quiz_expired': quiz_expired - } - return my_render_to_response('yaksh/intro.html', context, - context_instance=ci) - else: - return my_redirect("/exam/quizzes/") - - else: - context = {'user': user, - 'paper_id': questionpaper_id, - 'attempt_num': already_attempted + 1, - 'enable_quiz_time': quiz_enable_time, - 'disable_quiz_time': quiz_disable_time, - 'quiz_expired': quiz_expired - } + if quest_paper.quiz.has_prerequisite(): + pre_quest = QuestionPaper.objects.get(quiz=quest_paper.quiz.prerequisite) + if not pre_quest.is_questionpaper_passed(user): + return quizlist_user(request) + + last_attempt = AnswerPaper.objects.get_user_last_attempt( + questionpaper=quest_paper, user=user) + if last_attempt and last_attempt.is_attempt_inprogress(): + return show_question(request, last_attempt.current_question().id, + last_attempt.attempt_number, + last_attempt.question_paper.id) + + attempt_number = 1 if not last_attempt else last_attempt.attempt_number +1 + if quest_paper.is_attempt_allowed(user) and quest_paper.can_attempt_now(user): + context = {'user': user, 'questionpaper': quest_paper, + 'attempt_num': attempt_number} return my_render_to_response('yaksh/intro.html', context, context_instance=ci) + return my_redirect("/exam/quizzes/") -def _check_previous_attempt(attempted_papers, already_attempted, attempt_number): - next_attempt = False if already_attempted == attempt_number else True - if already_attempted == 0: - return False, None, next_attempt - else: - previous_attempt = attempted_papers[already_attempted-1] - if previous_attempt.status == 'inprogress': - if previous_attempt.time_left() > 0: - return True, previous_attempt, next_attempt - else: - return False, previous_attempt, next_attempt - else: - return False, previous_attempt, next_attempt - @login_required def results_user(request): """Show list of Results of Quizzes that is taken by logged-in user.""" user = request.user - papers = AnswerPaper.objects.filter(user=user) - quiz_marks = [] - for paper in papers: - marks_obtained = paper.marks_obtained - max_marks = paper.question_paper.total_marks - percentage = round((marks_obtained/max_marks)*100, 2) - temp = paper.question_paper.quiz.description, marks_obtained,\ - max_marks, percentage - quiz_marks.append(temp) - context = {'papers': quiz_marks} + papers = AnswerPaper.objects.get_user_answerpapers(user) + context = {'papers': papers} return my_render_to_response("yaksh/results_user.html", context) @@ -592,174 +470,6 @@ def show_all_questionpapers(request, questionpaper_id=None): @login_required -def automatic_questionpaper(request, questionpaper_id=None): - """Generate automatic question paper for a particular quiz""" - - user = request.user - ci = RequestContext(request) - if not user.is_authenticated() or not is_moderator(user): - raise Http404('You are not allowed to view this page!') - - if questionpaper_id is None: - if request.method == "POST": - if request.POST.get('save') == 'save': - quiz = Quiz.objects.order_by("-id")[0] - quest_paper = QuestionPaper() - questions = request.POST.getlist('questions') - tot_marks = 0 - for quest in questions: - q = Question.objects.get(id=quest) - tot_marks += q.points - quest_paper.quiz = quiz - quest_paper.total_marks = tot_marks - quest_paper.save() - for quest in questions: - q = Question.objects.get(id=quest) - quest_paper.fixed_questions.add(q) - return my_redirect('/exam/manage/showquiz') - else: - no_questions = int(request.POST.get('num_questions')) - fetched_questions = fetch_questions(request) - n = len(fetched_questions) - msg = '' - if (no_questions < n): - i = n - no_questions - for i in range(0, i): - fetched_questions.pop() - elif (no_questions > n): - msg = 'The given Criteria does not satisfy the number\ - of Questions...' - tags = Tag.objects.all() - context = {'data': {'questions': fetched_questions, - 'tags': tags, - 'msg': msg}} - return my_render_to_response( - 'yaksh/automatic_questionpaper.html', context, - context_instance=ci) - else: - tags = Tag.objects.all() - context = {'data': {'tags': tags}} - return my_render_to_response('yaksh/automatic_questionpaper.html', - context, context_instance=ci) - - else: - if request.method == "POST": - if request.POST.get('save') == 'save': - quest_paper = QuestionPaper.objects.get(id=questionpaper_id) - questions = request.POST.getlist('questions') - tot_marks = quest_paper.total_marks - for quest in questions: - q = Question.objects.get(id=quest) - tot_marks += q.points - quest_paper.total_marks = tot_marks - quest_paper.save() - for quest in questions: - q = Question.objects.get(id=quest) - quest_paper.questions.add(q) - return my_redirect('/yaksh/manage/showquiz') - else: - no_questions = int(request.POST.get('num_questions')) - fetched_questions = fetch_questions(request) - n = len(fetched_questions) - msg = '' - if(no_questions < n): - i = n - no_questions - for i in range(0, i): - fetched_questions.pop() - elif(no_questions > n): - msg = 'The given Criteria does not satisfy the number of \ - Questions...' - tags = Tag.objects.all() - context = {'data': {'questions': fetched_questions, - 'tags': tags, - 'msg': msg}} - return my_render_to_response( - 'yaksh/automatic_questionpaper.html', context, - context_instance=ci) - else: - tags = Tag.objects.all() - context = {'data': {'tags': tags}} - return my_render_to_response('yaksh/automatic_questionpaper.html', - context, context_instance=ci) - - -@login_required -def manual_questionpaper(request, questionpaper_id=None): - user = request.user - ci = RequestContext(request) - if not user.is_authenticated() or not is_moderator(user): - raise Http404('You are not allowed to view this page!') - - if questionpaper_id is None: - if request.method == "POST": - if request.POST.get('save') == 'save': - questions = request.POST.getlist('questions') - quest_paper = QuestionPaper() - quiz = Quiz.objects.order_by("-id")[0] - tot_marks = 0 - for quest in questions: - q = Question.objects.get(id=quest) - tot_marks += q.points - quest_paper.quiz = quiz - quest_paper.total_marks = tot_marks - quest_paper.save() - for i in questions: - q = Question.objects.get(id=i) - quest_paper.questions.add(q) - return my_redirect('/exam/manage/showquiz') - else: - fetched_questions = fetch_questions(request) - n = len(fetched_questions) - msg = '' - if (n == 0): - msg = 'No matching Question found...' - tags = Tag.objects.all() - context = {'data': {'questions': fetched_questions, - 'tags': tags, 'msg': msg}} - return my_render_to_response('yaksh/manual_questionpaper.html', - context, - context_instance=ci) - else: - tags = Tag.objects.all() - context = {'data': {'tags': tags}} - return my_render_to_response('yaksh/manual_questionpaper.html', - context, context_instance=ci) - - else: - if request.method == "POST": - if request.POST.get('save') == 'save': - quest_paper = QuestionPaper.objects.get(id=questionpaper_id) - questions = request.POST.getlist('questions') - tot_marks = quest_paper.total_marks - for quest in questions: - q = Question.objects.get(id=quest) - tot_marks += q.points - quest_paper.total_marks = tot_marks - quest_paper.save() - for i in questions: - q = Question.objects.get(id=i) - quest_paper.questions.add(q) - return my_redirect('/exam/manage/showquiz') - else: - fetched_questions = fetch_questions(request) - n = len(fetched_questions) - msg = '' - if (n == 0): - msg = 'No matching Question found...' - tags = Tag.objects.all() - context = {'data': {'questions': fetched_questions, - 'tags': tags, 'msg': msg}} - return my_render_to_response('yaksh/manual_questionpaper.html', - context, - context_instance=ci) - else: - tags = Tag.objects.all() - context = {'data': {'tags': tags}} - return my_render_to_response('yaksh/manual_questionpaper.html', - context, context_instance=ci) - - -@login_required def prof_manage(request): """Take credentials of the user with professor/moderator rights/permissions and log in.""" @@ -814,11 +524,9 @@ def start(request, attempt_num=None, questionpaper_id=None): """Check the user cedentials and if any quiz is available, start the exam.""" user = request.user - if questionpaper_id is None: + if questionpaper_id is None or attempt_num is None: return my_redirect('/exam/quizzes/') try: - """Right now the app is designed so there is only one active quiz - at a particular time.""" questionpaper = QuestionPaper.objects.get(id=questionpaper_id) except QuestionPaper.DoesNotExist: msg = 'Quiz not found, please contact your '\ @@ -831,11 +539,10 @@ def start(request, attempt_num=None, questionpaper_id=None): try: old_paper = AnswerPaper.objects.get( question_paper=questionpaper, user=user, attempt_number=attempt_num) - q = old_paper.current_question() + q = old_paper.current_question().id return show_question(request, q, attempt_num, questionpaper_id) except AnswerPaper.DoesNotExist: ip = request.META['REMOTE_ADDR'] - key = gen_key(10) try: profile = user.get_profile() except Profile.DoesNotExist: @@ -845,91 +552,39 @@ def start(request, attempt_num=None, questionpaper_id=None): new_paper = questionpaper.make_answerpaper(user, ip, attempt_num) # Make user directory. user_dir = get_user_dir(user) - return start(request, attempt_num, questionpaper_id) - - -def get_questions(paper): - ''' - Takes answerpaper as an argument. Returns the total questions as - ordered dictionary, the questions yet to attempt and the questions - attempted - ''' - to_attempt = [] - submitted = [] - all_questions = [] - questions = {} - if paper.questions: - all_questions = (paper.questions).split('|') - if paper.questions_answered: - q_answered = (paper.questions_answered).split('|') - q_answered.sort() - submitted = q_answered - if paper.get_unanswered_questions(): - q_unanswered = paper.get_unanswered_questions() - q_unanswered.sort() - to_attempt = q_unanswered - question = Question.objects.filter(id__in=all_questions) - for index, value in enumerate(question, 1): - questions[value] = index - questions = collections.OrderedDict(sorted(questions.items(), key=lambda x:x[1])) - return questions, to_attempt, submitted + return show_question(request, new_paper.current_question().id, attempt_num, questionpaper_id) @login_required -def question(request, q_id, attempt_num, questionpaper_id, success_msg=None): - """Check the credentials of the user and start the exam.""" - +def show_question(request, q_id, attempt_num, questionpaper_id, success_msg=None): + """Show a question if possible.""" user = request.user - if not user.is_authenticated(): - return my_redirect('/exam/login/') - q = get_object_or_404(Question, pk=q_id) try: q_paper = QuestionPaper.objects.get(id=questionpaper_id) paper = AnswerPaper.objects.get( user=request.user, attempt_number=attempt_num, question_paper=q_paper) except AnswerPaper.DoesNotExist: return my_redirect('/exam/start/') - if not paper.question_paper.quiz.active: - reason = 'The quiz has been deactivated!' - return complete(request, reason, attempt_num, questionpaper_id) - time_left = paper.time_left() - if time_left == 0: - return complete(request, attempt_num, questionpaper_id, reason='Your time is up!') - quiz_name = paper.question_paper.quiz.description - questions, to_attempt, submitted = get_questions(paper) - if success_msg is None: - context = {'question': q, 'questions': questions, 'paper': paper, - 'user': user, 'quiz_name': quiz_name, 'time_left': time_left, - 'to_attempt': to_attempt, 'submitted': submitted} - else: - context = {'question': q, 'questions': questions, 'paper': paper, - 'user': user, 'quiz_name': quiz_name, 'time_left': time_left, - 'success_msg': success_msg, 'to_attempt': to_attempt, - 'submitted': submitted} - if q.type == 'code': - skipped_answer = paper.answers.filter(question=q, skipped=True) - if skipped_answer: - context['last_attempt'] = skipped_answer[0].answer - ci = RequestContext(request) - return my_render_to_response('yaksh/question.html', context, - context_instance=ci) - - -@login_required -def show_question(request, q_id, attempt_num, questionpaper_id, success_msg=None): - """Show a question if possible.""" - user = request.user - q_paper = QuestionPaper.objects.get(id=questionpaper_id) - paper = AnswerPaper.objects.get(user=request.user, attempt_number=attempt_num, - question_paper=q_paper) - if not user.is_authenticated(): - return my_redirect('/exam/login/') - if len(q_id) == 0: + if not q_id: msg = 'Congratulations! You have successfully completed the quiz.' return complete(request, msg, attempt_num, questionpaper_id) else: - return question(request, q_id, attempt_num, questionpaper_id, success_msg) - + q = get_object_or_404(Question, pk=q_id) + if not paper.question_paper.quiz.active: + reason = 'The quiz has been deactivated!' + return complete(request, reason, attempt_num, questionpaper_id) + time_left = paper.time_left() + if time_left == 0: + reason='Your time is up!' + return complete(request, reason, attempt_num, questionpaper_id) + context = {'question': q, 'paper': paper} + if q.type == 'code': + skipped_answer = paper.answers.filter(question=q, skipped=True) + if skipped_answer: + context['last_attempt'] = skipped_answer[0].answer + ci = RequestContext(request) + return my_render_to_response('yaksh/question.html', context, + context_instance=ci) def _save_skipped_answer(old_skipped, user_answer, paper, question): @@ -947,7 +602,7 @@ def _save_skipped_answer(old_skipped, user_answer, paper, question): skipped_answer.save() paper.answers.add(skipped_answer) - +@login_required def check(request, q_id, attempt_num=None, questionpaper_id=None): """Checks the answers of the user for particular question""" user = request.user @@ -955,13 +610,10 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): paper = get_object_or_404(AnswerPaper, user=request.user, attempt_number=attempt_num, question_paper=q_paper) - if q_id in paper.questions_answered: - next_q = paper.skip(q_id) + if q_id in paper.questions_answered.all(): + next_q = paper.skip(q_id).id return show_question(request, next_q, attempt_num, questionpaper_id) - if not user.is_authenticated(): - return my_redirect('/exam/login/') - question = get_object_or_404(Question, pk=q_id) test_cases = TestCase.objects.filter(question=question) @@ -974,7 +626,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): if question.type == 'code': old_skipped = paper.answers.filter(question=question, skipped=True) _save_skipped_answer(old_skipped, user_code, paper, question) - next_q = paper.skip(q_id) + next_q = paper.skip(q_id).id if paper.skip(q_id) else q_id return show_question(request, next_q, attempt_num, questionpaper_id) # Add the answer submitted, regardless of it being correct or not. @@ -995,7 +647,6 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): else: user_code = request.POST.get('answer') user_answer = snippet_code + "\n" + user_code if snippet_code else user_code - new_answer = Answer(question=question, answer=user_answer, correct=False) new_answer.save() @@ -1017,60 +668,45 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None): new_answer.error = result.get('error') new_answer.save() - _update_marks(paper, 'inprogress') - time_left = paper.time_left() + paper.update_marks('inprogress') + if paper.time_left() <= 0: + reason = 'Your time is up!' + return complete(request, reason, attempt_num, questionpaper_id) + if not paper.question_paper.quiz.active: + reason = 'The quiz has been deactivated!' + return complete(request, reason, attempt_num, questionpaper_id) if not result.get('success'): # Should only happen for non-mcq questions. - if time_left <= 0: - reason = 'Your time is up!' - return complete(request, reason, attempt_num, questionpaper_id) - if not paper.question_paper.quiz.active: - reason = 'The quiz has been deactivated!' - return complete(request, reason, attempt_num, questionpaper_id) - questions, to_attempt, submitted = get_questions(paper) old_answer = paper.answers.filter(question=question, skipped=True) if old_answer: old_answer[0].answer = user_code old_answer[0].save() context = {'question': question, 'error_message': result.get('error'), - 'paper': paper, 'last_attempt': user_code, - 'quiz_name': paper.question_paper.quiz.description, - 'time_left': time_left, 'questions': questions, - 'to_attempt': to_attempt, 'submitted': submitted} + 'paper': paper} ci = RequestContext(request) return my_render_to_response('yaksh/question.html', context, context_instance=ci) else: - if time_left <= 0: - reason = 'Your time is up!' - return complete(request, reason, attempt_num, questionpaper_id) # Display the same question if user_answer is None - elif not user_answer: - msg = "Please submit a valid option or code" - time_left = paper.time_left() - questions, to_attempt, submitted = get_questions(paper) - context = {'question': question, 'paper': paper, - 'quiz_name': paper.question_paper.quiz.description, - 'time_left': time_left, 'questions': questions, - 'to_attempt': to_attempt, 'submitted': submitted, - 'error_message': msg} + if not user_answer: ci = RequestContext(request) - + msg = "Please submit a valid option or code" + context = {'question': question, 'error_message': msg, + 'paper': paper} elif question.type == 'code' and user_answer: msg = "Correct Output" success = "True" paper.completed_question(question.id) - time_left = paper.time_left() - questions, to_attempt, submitted = get_questions(paper) context = {'question': question, 'paper': paper, - 'quiz_name': paper.question_paper.quiz.description, - 'time_left': time_left, 'questions': questions, - 'to_attempt': to_attempt, 'submitted': submitted, 'error_message': msg, 'success': success} ci = RequestContext(request) + return my_render_to_response('yaksh/question.html', context, + context_instance=ci) else: next_q = paper.completed_question(question.id) + if next_q: + next_q = next_q.id return show_question(request, next_q, attempt_num, questionpaper_id, success_msg) @@ -1108,34 +744,13 @@ def validate_answer(user, user_answer, question, json_data=None): correct = True return correct, result -def get_question_labels(request, attempt_num=None, questionpaper_id=None): - """Get the question number show in template for corresponding - question id.""" - unattempted_questions = [] - submitted_questions = [] - try: - q_paper = QuestionPaper.objects.get(id=questionpaper_id) - paper = AnswerPaper.objects.get( - user=request.user, attempt_number=attempt_num, question_paper=q_paper) - except AnswerPaper.DoesNotExist: - return my_redirect('/exam/start/') - questions, to_attempt, submitted = get_questions(paper) - for q_id, question_label in questions.items(): - if q_id in to_attempt: - unattempted_questions.append(question_label) - else: - submitted_questions.append(question_label) - unattempted_questions.sort() - submitted_questions.sort() - return unattempted_questions, submitted_questions -def quit(request, attempt_num=None, questionpaper_id=None): +def quit(request, reason=None, attempt_num=None, questionpaper_id=None): """Show the quit page when the user logs out.""" - unattempted_questions, submitted_questions = get_question_labels(request, - attempt_num, questionpaper_id) - context = {'id': questionpaper_id, 'attempt_num': attempt_num, - 'unattempted': unattempted_questions, - 'submitted': submitted_questions} + paper = AnswerPaper.objects.get(user=request.user, + attempt_number=attempt_num, + question_paper=questionpaper_id) + context = {'paper': paper, 'message': reason} return my_render_to_response('yaksh/quit.html', context, context_instance=RequestContext(request)) @@ -1150,27 +765,20 @@ def complete(request, reason=None, attempt_num=None, questionpaper_id=None): context = {'message': message} return my_render_to_response('yaksh/complete.html', context) else: - unattempted_questions, submitted_questions = get_question_labels(request, - attempt_num, questionpaper_id) q_paper = QuestionPaper.objects.get(id=questionpaper_id) paper = AnswerPaper.objects.get(user=user, question_paper=q_paper, attempt_number=attempt_num) - _update_marks(paper) - obt_marks = paper.marks_obtained - tot_marks = paper.question_paper.total_marks - if obt_marks == paper.question_paper.total_marks: + paper.update_marks() + if paper.percent == 100: context = {'message': "Hurray ! You did an excellent job.\ you answered all the questions correctly.\ You have been logged out successfully,\ Thank You !", - 'unattempted': unattempted_questions, - 'submitted': submitted_questions} + 'paper': paper} return my_render_to_response('yaksh/complete.html', context) else: message = reason or "You are successfully logged out" - context = {'message': message, - 'unattempted': unattempted_questions, - 'submitted': submitted_questions} + context = {'message': message, 'paper': paper} return my_render_to_response('yaksh/complete.html', context) no = False message = reason or 'The quiz has been completed. Thank you.' @@ -1589,7 +1197,7 @@ def download_csv(request, questionpaper_id): 'total_marks', 'percentage', 'questions', - 'questions_answererd', + 'questions_answered', 'status' ] writer.writerow(header) @@ -1602,7 +1210,8 @@ def download_csv(request, questionpaper_id): paper.marks_obtained, paper.question_paper.total_marks, paper.percent, - paper.questions, paper.questions_answered, + paper.questions.all(), + paper.questions_answered.all(), paper.status ] writer.writerow(row) |