From 44b64f9ad0f0618d15ee99026a33df14937d3ef4 Mon Sep 17 00:00:00 2001 From: prathamesh Date: Tue, 8 Sep 2020 02:49:24 +0530 Subject: Provide additional attempt to a selected student or extend time. --- yaksh/models.py | 127 +++++++++++++++++++++++++++- yaksh/templates/yaksh/micromanaged.html | 22 +++++ yaksh/templates/yaksh/micromonitor.html | 9 ++ yaksh/templates/yaksh/monitor.html | 31 ++++++- yaksh/templates/yaksh/quit.html | 4 + yaksh/templates/yaksh/quizzes_user.html | 2 + yaksh/templatetags/custom_filters.py | 20 +++++ yaksh/test_models.py | 141 +++++++++++++++++++++++++++++++- yaksh/urls.py | 9 ++ yaksh/views.py | 135 +++++++++++++++++++++++++++++- 10 files changed, 491 insertions(+), 9 deletions(-) create mode 100644 yaksh/templates/yaksh/micromanaged.html create mode 100644 yaksh/templates/yaksh/micromonitor.html (limited to 'yaksh') diff --git a/yaksh/models.py b/yaksh/models.py index 1094bb0..84cad1e 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -1045,6 +1045,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) @@ -1776,7 +1784,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, @@ -1795,6 +1803,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) @@ -2139,6 +2148,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: @@ -2221,11 +2234,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): @@ -2710,3 +2728,108 @@ class Comment(ForumBase): def __str__(self): return 'Comment by {0}: {1}'.format(self.creator.username, self.post_field.title) + + +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) diff --git a/yaksh/templates/yaksh/micromanaged.html b/yaksh/templates/yaksh/micromanaged.html new file mode 100644 index 0000000..336feec --- /dev/null +++ b/yaksh/templates/yaksh/micromanaged.html @@ -0,0 +1,22 @@ +{% if micromanagers %} +
+
+ {% for micromanager in micromanagers %} + {% if micromanager.attempts_permitted > 0 %} +
+

You have been given a special attempt to the {{ micromanager.quiz.description }} by the course creator

+
+
+ {% if micromanager.can_student_attempt %} + + Start Special Attempt + + {% else %} + Exhausted + {% endif %} +
+ {% endif %} + {% endfor %} +{% endif %} +
+ diff --git a/yaksh/templates/yaksh/micromonitor.html b/yaksh/templates/yaksh/micromonitor.html new file mode 100644 index 0000000..cc059aa --- /dev/null +++ b/yaksh/templates/yaksh/micromonitor.html @@ -0,0 +1,9 @@ +{% if micromanager %} + {% if micromanager.can_student_attempt %} + Revoke + {% else %} + Allow + {% endif %} +{% else %} + Allow +{% endif %} diff --git a/yaksh/templates/yaksh/monitor.html b/yaksh/templates/yaksh/monitor.html index ef7b033..183ba99 100644 --- a/yaksh/templates/yaksh/monitor.html +++ b/yaksh/templates/yaksh/monitor.html @@ -74,6 +74,18 @@ $(document).ready(function()
+
+ {% if messages %} + {% for message in messages %} +
+ + {{ message }} +
+ {% endfor %} + {% endif %} +
@@ -102,8 +114,9 @@ $(document).ready(function() Institute  Marks  Attempts  - Time  + Time Left  Status  + Special Attempt  @@ -118,7 +131,20 @@ $(document).ready(function() {{ paper.marks_obtained }} {{ paper.answers.count }} {{ paper.time_left }} - {{ paper.status }} + {% if paper.is_attempt_inprogress %} +
+ {% csrf_token %} +
+ + +
+ +
+ {% else %} + Completed + {% endif %} + + {% specail_attempt_monitor paper.user.id course.id quiz.id %} {% endfor %} @@ -126,7 +152,6 @@ $(document).ready(function()