diff options
author | prathamesh | 2016-03-30 09:49:19 +0530 |
---|---|---|
committer | prathamesh | 2016-04-01 17:04:56 +0530 |
commit | 0b2a7623a7a5e225ee7a29b438872705b2c4ba5b (patch) | |
tree | 04e80f4dc8a474f23ea18c756e1b9ef770086b10 /yaksh/models.py | |
parent | d65ba142c47780fe3ff818f583f110453d31a92f (diff) | |
download | online_test-0b2a7623a7a5e225ee7a29b438872705b2c4ba5b.tar.gz online_test-0b2a7623a7a5e225ee7a29b438872705b2c4ba5b.tar.bz2 online_test-0b2a7623a7a5e225ee7a29b438872705b2c4ba5b.zip |
views functions related to exam flow are cleaned-up
Cleaned views various functions related to the exam flow.
That is, introduction, start, check, show questions.
To check prerequisite, can attempt the quiz, start quiz in progress if
time available, get all active quizzes all these functionalities are
shifted from views to models. Still further it has to be cleaned.
For Answerpaper model, made questions, questions_answered and
questions_unanswered manytomany relation with the Question model.
Corrected the testcases.
Diffstat (limited to 'yaksh/models.py')
-rw-r--r-- | yaksh/models.py | 166 |
1 files changed, 95 insertions, 71 deletions
diff --git a/yaksh/models.py b/yaksh/models.py index 7bd0a9e..4d52794 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,34 @@ 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 + ans_paper.save() 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.questions.add(*questions) + ans_paper.questions_unanswered.add(*questions) ans_paper.save() 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): + 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 ############################################################################### class QuestionSet(models.Model): @@ -431,6 +460,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 +479,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 +496,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 +527,36 @@ 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 '' + return self.questions_unanswered.first() 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 +566,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 +592,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 +619,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) |