diff options
Diffstat (limited to 'yaksh/models.py')
-rw-r--r-- | yaksh/models.py | 149 |
1 files changed, 135 insertions, 14 deletions
diff --git a/yaksh/models.py b/yaksh/models.py index f8b17d5..9ba4afd 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -1058,6 +1058,14 @@ class Course(models.Model): def get_learning_modules(self): return self.learning_module.order_by("order") + def get_learning_module(self, quiz): + modules = self.get_learning_modules() + for module in modules: + for unit in module.get_learning_units(): + if unit.quiz == quiz: + break + return module + def get_unit_completion_status(self, module, user, unit): course_module = self.learning_module.get(id=module.id) learning_unit = course_module.learning_unit.get(id=unit.id) @@ -1710,17 +1718,15 @@ class QuestionPaperManager(models.Manager): def create_trial_paper_to_test_quiz(self, trial_quiz, original_quiz_id): """Creates a trial question paper to test quiz.""" - if self.filter(quiz=trial_quiz).exists(): - trial_questionpaper = self.get(quiz=trial_quiz) - else: - trial_questionpaper, trial_questions = \ - self._create_trial_from_questionpaper(original_quiz_id) - trial_questionpaper.quiz = trial_quiz - trial_questionpaper.fixed_questions\ - .add(*trial_questions["fixed_questions"]) - trial_questionpaper.random_questions\ - .add(*trial_questions["random_questions"]) - trial_questionpaper.save() + trial_quiz.questionpaper_set.all().delete() + trial_questionpaper, trial_questions = \ + self._create_trial_from_questionpaper(original_quiz_id) + trial_questionpaper.quiz = trial_quiz + trial_questionpaper.fixed_questions\ + .add(*trial_questions["fixed_questions"]) + trial_questionpaper.random_questions\ + .add(*trial_questions["random_questions"]) + trial_questionpaper.save() return trial_questionpaper @@ -1744,7 +1750,7 @@ class QuestionPaper(models.Model): total_marks = models.FloatField(default=0.0, blank=True) # Sequence or Order of fixed questions - fixed_question_order = models.CharField(max_length=255, blank=True) + fixed_question_order = models.TextField(blank=True) # Shuffle testcase order. shuffle_testcases = models.BooleanField("Shuffle testcase for each user", @@ -1793,7 +1799,7 @@ class QuestionPaper(models.Model): all_questions = questions return all_questions - def make_answerpaper(self, user, ip, attempt_num, course_id): + def make_answerpaper(self, user, ip, attempt_num, course_id, special=False): """Creates an answer paper for the user to attempt the quiz""" try: ans_paper = AnswerPaper.objects.get(user=user, @@ -1812,6 +1818,7 @@ class QuestionPaper(models.Model): ans_paper.end_time = ans_paper.start_time + \ timedelta(minutes=self.quiz.duration) ans_paper.question_paper = self + ans_paper.is_special = special ans_paper.save() questions = self._get_questions_for_answerpaper() ans_paper.questions.add(*questions) @@ -2156,6 +2163,10 @@ class AnswerPaper(models.Model): # set question order questions_order = models.TextField(blank=True, default='') + extra_time = models.FloatField('Additional time in mins', default=0.0) + + is_special = models.BooleanField(default=False) + objects = AnswerPaperManager() class Meta: @@ -2238,11 +2249,16 @@ class AnswerPaper(models.Model): questions = list(self.questions.all()) return questions + def set_extra_time(self, time=0): + self.extra_time = time + self.save() + def time_left(self): """Return the time remaining for the user in seconds.""" secs = self._get_total_seconds() + extra_time = self.extra_time * 60 total = self.question_paper.quiz.duration*60.0 - remain = max(total - secs, 0) + remain = max(total - (secs - extra_time), 0) return int(remain) def time_left_on_question(self, question): @@ -2761,3 +2777,108 @@ class Topic(models.Model): def __str__(self): return f"{self.name}" + + +class MicroManager(models.Model): + manager = models.ForeignKey(User, on_delete=models.CASCADE, + related_name='micromanaging', null=True) + student = models.ForeignKey(User, on_delete=models.CASCADE, + related_name='micromanaged') + course = models.ForeignKey(Course, on_delete=models.CASCADE) + quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE, null=True) + special_attempt = models.BooleanField(default=False) + attempts_permitted = models.IntegerField(default=0) + permitted_time = models.DateTimeField(default=timezone.now) + attempts_utilised = models.IntegerField(default=0) + wait_time = models.IntegerField('Days to wait before special attempt', + default=0) + attempt_valid_for = models.IntegerField('Validity days', default=90) + + class Meta: + unique_together = ('student', 'course', 'quiz') + + def set_wait_time(self, days=0): + self.wait_time = days + self.save() + + def increment_attempts_permitted(self): + self.attempts_permitted += 1 + self.save() + + def update_permitted_time(self, permit_time=None): + time_now = timezone.now() + self.permitted_time = time_now if not permit_time else permit_time + self.save() + + def has_student_attempts_exhausted(self): + if self.quiz.attempts_allowed == -1: + return False + question_paper = self.quiz.questionpaper_set.first() + attempts = AnswerPaper.objects.get_total_attempt( + question_paper, self.student, course_id=self.course.id + ) + last_attempt = AnswerPaper.objects.get_user_last_attempt( + question_paper, self.student, self.course.id + ) + if last_attempt: + if last_attempt.is_attempt_inprogress(): + return False + return attempts >= self.quiz.attempts_allowed + + def is_last_attempt_inprogress(self): + question_paper = self.quiz.questionpaper_set.first() + last_attempt = AnswerPaper.objects.get_user_last_attempt( + question_paper, self.student, self.course.id + ) + if last_attempt: + return last_attempt.is_attempt_inprogress() + return False + + def has_quiz_time_exhausted(self): + return not self.quiz.active or self.quiz.is_expired() + + def is_course_exhausted(self): + return not self.course.active or not self.course.is_active_enrollment() + + def is_special_attempt_required(self): + return (self.has_student_attempts_exhausted() or + self.has_quiz_time_exhausted() or self.is_course_exhausted()) + + def allow_special_attempt(self, wait_time=0): + if (self.is_special_attempt_required() and + not self.is_last_attempt_inprogress()): + self.special_attempt = True + if self.attempts_utilised >= self.attempts_permitted: + self.increment_attempts_permitted() + self.update_permitted_time() + self.set_wait_time(days=wait_time) + self.save() + + def has_special_attempt(self): + return (self.special_attempt and + (self.attempts_utilised < self.attempts_permitted)) + + def is_attempt_time_valid(self): + permit_time = self.permitted_time + wait_time = permit_time + timezone.timedelta(days=self.wait_time) + valid_time = permit_time + timezone.timedelta( + days=self.attempt_valid_for) + return wait_time <= timezone.now() <= valid_time + + def can_student_attempt(self): + return self.has_special_attempt() and self.is_attempt_time_valid() + + def get_attempt_number(self): + return self.quiz.attempts_allowed + self.attempts_utilised + 1 + + def increment_attempts_utilised(self): + self.attempts_utilised += 1 + self.save() + + def revoke_special_attempt(self): + self.special_attempt = False + self.save() + + def __str__(self): + return 'MicroManager for {0} - {1}'.format(self.student.username, + self.course.name) |