From 4bb56b3ee2c28dfd684ac2e44f80187cfe522d10 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 17 Nov 2017 19:24:24 +0530 Subject: Change in models.py - Add new models LearningModule, LearningUnit, CourseStatus, Lesson, LessonFile - Change model associations - Add new model methods --- yaksh/models.py | 653 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 431 insertions(+), 222 deletions(-) (limited to 'yaksh') diff --git a/yaksh/models.py b/yaksh/models.py index 7e97e6c..a5dbd0a 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -121,6 +121,12 @@ def dict_to_yaml(dictionary): allow_unicode=True, ) + +def get_file_dir(instance, filename): + upload_dir = instance.lesson.name.replace(" ", "_") + return os.sep.join((upload_dir, filename)) + + ############################################################################### class CourseManager(models.Manager): @@ -134,6 +140,331 @@ class CourseManager(models.Manager): def get_hidden_courses(self, code): return self.filter(code=code, hidden=True) + +############################################################################# +class Lesson(models.Model): + name = models.CharField(max_length=255) + description = models.TextField() + html_data = models.TextField(null=True, blank=True) + creator = models.ForeignKey(User) + + def get_files(self): + return LessonFile.objects.filter(lesson=self) + + def get_lesson_completion_status(self, user, course): + course_status = CourseStatus.objects.filter(user=user, course=course) + if course_status.exists(): + learning_unit = course_status.first().completed_units.filter( + lesson_id=self.id) + if learning_unit.exists(): + return "completed" + else: + return "inprogress" + else: + return "not attempted" + + def __str__(self): + return "{0}".format(self.name) + + +############################################################################# +class LessonFile(models.Model): + lesson = models.ForeignKey(Lesson, related_name="lesson") + file = models.FileField(upload_to=get_file_dir) + + def remove(self): + if os.path.exists(self.file.path): + os.remove(self.file.path) + if os.listdir(os.path.dirname(self.file.path)) == []: + os.rmdir(os.path.dirname(self.file.path)) + self.delete() + + +############################################################################### +class QuizManager(models.Manager): + def get_active_quizzes(self): + return self.filter(active=True, is_trial=False) + + def create_trial_quiz(self, trial_course, user): + """Creates a trial quiz for testing questions""" + trial_quiz = self.create(duration=1000, + description="trial_questions", + is_trial=True, + time_between_attempts=0 + ) + return trial_quiz + + def create_trial_from_quiz(self, original_quiz_id, user, godmode, + course_id): + """Creates a trial quiz from existing quiz""" + trial_course_name = "Trial_course_for_course_{0}_{1}".format( + course_id, "godmode" if godmode else "usermode") + trial_quiz_name = "Trial_orig_id_{0}_{1}".format( + original_quiz_id, + "godmode" if godmode else "usermode" + ) + # Get or create Trial Course for usermode/godmode + trial_course = Course.objects.filter(name=trial_course_name) + if trial_course.exists(): + trial_course = trial_course.get(name=trial_course_name) + else: + trial_course = Course.objects.create( + name=trial_course_name, creator=user, enrollment="open", + is_trial=True) + + # Get or create Trial Quiz for usermode/godmode + if self.filter(description=trial_quiz_name).exists(): + trial_quiz = self.get(description=trial_quiz_name) + + else: + trial_quiz = self.get(id=original_quiz_id) + trial_quiz.user = user + trial_quiz.pk = None + trial_quiz.description = trial_quiz_name + trial_quiz.is_trial = True + trial_quiz.prerequisite = None + if godmode: + trial_quiz.time_between_attempts = 0 + trial_quiz.duration = 1000 + trial_quiz.attempts_allowed = -1 + trial_quiz.active = True + trial_quiz.start_date_time = timezone.now() + trial_quiz.end_date_time = datetime( + 2199, 1, 1, 0, 0, 0, 0, tzinfo=pytz.utc + ) + trial_quiz.save() + + # Get or create Trial Ordered Lesson for usermode/godmode + learning_modules = trial_course.get_learning_modules() + if learning_modules: + quiz = learning_modules[0].learning_unit.filter(quiz=trial_quiz) + if not quiz.exists(): + trial_learning_unit = LearningUnit.objects.create( + order=1, quiz=trial_quiz, learning_type="quiz", + check_prerequisite=False) + learning_modules[0].learning_unit.add(trial_learning_unit.id) + trial_learning_module = learning_modules[0] + else: + trial_learning_module = LearningModule.objects.create( + name="Trial for {}".format(trial_course), order=1, + check_prerequisite=False, creator=user, is_trial=True) + trial_learning_unit = LearningUnit.objects.create( + order=1, quiz=trial_quiz, learning_type="quiz", + check_prerequisite=False) + trial_learning_module.learning_unit.add(trial_learning_unit.id) + trial_course.learning_module.add(trial_learning_module.id) + + # Add user to trial_course + trial_course.enroll(False, user) + return trial_quiz, trial_course, trial_learning_module + + +############################################################################### +class Quiz(models.Model): + """A quiz that students will participate in. One can think of this + as the "examination" event. + """ + + # The start date of the quiz. + start_date_time = models.DateTimeField( + "Start Date and Time of the quiz", + default=timezone.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( + 2199, 1, 1, + tzinfo=pytz.timezone(timezone.get_current_timezone_name()) + ), + null=True + ) + + # This is always in minutes. + duration = models.IntegerField("Duration of quiz in minutes", default=20) + + # Is the quiz active. The admin should deactivate the quiz once it is + # complete. + active = models.BooleanField(default=True) + + # Description of quiz. + description = models.CharField(max_length=256) + + # Mininum passing percentage condition. + pass_criteria = models.FloatField("Passing percentage", default=40) + + # Number of attempts for the quiz + attempts_allowed = models.IntegerField(default=1, choices=attempts) + + time_between_attempts = models.IntegerField( + "Number of Days", choices=days_between_attempts + ) + + is_trial = models.BooleanField(default=False) + + instructions = models.TextField('Instructions for Students', + default=None, blank=True, null=True) + + view_answerpaper = models.BooleanField('Allow student to view their answer\ + paper', default=False) + + allow_skip = models.BooleanField("Allow students to skip questions", + default=True) + + weightage = models.FloatField(default=1.0) + + creator = models.ForeignKey(User, null=True) + + objects = QuizManager() + + class Meta: + verbose_name_plural = "Quizzes" + + def is_expired(self): + return not self.start_date_time <= timezone.now() < self.end_date_time + + def create_demo_quiz(self, user): + demo_quiz = Quiz.objects.create( + start_date_time=timezone.now(), + end_date_time=timezone.now() + timedelta(176590), + duration=30, active=True, + attempts_allowed=-1, + time_between_attempts=0, + description='Yaksh Demo quiz', pass_criteria=0, + prerequisite=None, creator=user + ) + return demo_quiz + + def get_total_students(self, course): + return AnswerPaper.objects.filter( + question_paper=self.questionpaper_set.get().id, + course=course + ).values_list("user", flat=True).distinct().count() + + def get_passed_students(self, course): + return AnswerPaper.objects.filter( + question_paper=self.questionpaper_set.get().id, + course=course, passed=True + ).values_list("user", flat=True).distinct().count() + + def get_failed_students(self, course): + return AnswerPaper.objects.filter( + question_paper=self.questionpaper_set.get().id, + course=course, passed=False + ).values_list("user", flat=True).distinct().count() + + def get_quiz_completion_status(self, user, course): + ans_pprs = AnswerPaper.objects.filter( + question_paper=self.questionpaper_set.get().id, + user=user, course=course).order_by("-attempt_number") + if ans_pprs.exists(): + latest_ans_ppr_status = ans_pprs.first().status + else: + latest_ans_ppr_status = "not attempted" + return latest_ans_ppr_status + + def __str__(self): + desc = self.description or 'Quiz' + return '%s: on %s for %d minutes' % (desc, self.start_date_time, + self.duration) + + +########################################################################## +class LearningUnit(models.Model): + """ Maintain order of lesson and quiz added in the course """ + order = models.IntegerField() + learning_type = models.CharField(max_length=16) + lesson = models.ForeignKey(Lesson, null=True, blank=True) + quiz = models.ForeignKey(Quiz, null=True, blank=True) + check_prerequisite = models.BooleanField(default=True) + + def toggle_check_prerequisite(self): + if self.check_prerequisite: + self.check_prerequisite = False + else: + self.check_prerequisite = True + + def get_completion_status(self, user, course): + if self.learning_type == "quiz": + status = self.quiz.get_quiz_completion_status(user, course) + else: + status = self.lesson.get_lesson_completion_status(user, course) + return status + + def has_prerequisite(self): + return self.check_prerequisite + + def is_prerequisite_passed(self, user, learning_module, course): + ordered_units = learning_module.learning_unit.order_by("order") + ordered_units_ids = list(ordered_units.values_list("id", flat=True)) + current_unit_index = ordered_units_ids.index(self.id) + if current_unit_index == 0: + success = True + else: + prev_unit = ordered_units.get( + id=ordered_units_ids[current_unit_index-1]) + if prev_unit.learning_type == "quiz": + success = prev_unit.quiz.get_quiz_completion_status( + user, course) + else: + success = prev_unit.lesson.get_lesson_completion_status( + user, course) + return success + + +############################################################################### +class LearningModule(models.Model): + """ Learning Module to maintain learning units""" + learning_unit = models.ManyToManyField(LearningUnit, + related_name="learning_unit") + name = models.CharField(max_length=255) + description = models.TextField(default=None, null=True, blank=True) + order = models.IntegerField(default=0) + creator = models.ForeignKey(User, related_name="module_creator") + check_prerequisite = models.BooleanField(default=True) + html_data = models.TextField(null=True, blank=True) + is_trial = models.BooleanField(default=False) + + def get_quiz_units(self): + return [unit.quiz for unit in self.learning_unit.filter( + learning_type="quiz")] + + def get_learning_units(self): + return self.learning_unit.order_by("order") + + def get_added_quiz_lesson(self): + learning_units = self.learning_unit.order_by("order") + added_quiz_lessons = [] + if learning_units.exists(): + for unit in learning_units: + if unit.learning_type == "quiz": + added_quiz_lessons.append(("quiz", unit.quiz)) + else: + added_quiz_lessons.append(("lesson", unit.lesson)) + return added_quiz_lessons + + def toggle_check_prerequisite(self): + if self.check_prerequisite: + self.check_prerequisite = False + else: + self.check_prerequisite = True + + def get_next_unit(self, current_unit_id): + ordered_units = self.learning_unit.order_by("order") + ordered_units_ids = list(ordered_units.values_list("id", flat=True)) + current_unit_index = ordered_units_ids.index(current_unit_id) + if current_unit_index + 1 == len(ordered_units_ids): + next_index = 0 + else: + next_index = current_unit_index + 1 + return ordered_units.get(id=ordered_units_ids[next_index]) + + def __str__(self): + return self.name + + ############################################################################### class Course(models.Model): """ Course for students""" @@ -150,6 +481,8 @@ class Course(models.Model): teachers = models.ManyToManyField(User, related_name='teachers') is_trial = models.BooleanField(default=False) instructions = models.TextField(default=None, null=True, blank=True) + learning_module = models.ManyToManyField(LearningModule, + related_name='learning_module') # The start date of the course enrollment. start_enroll_time = models.DateTimeField( @@ -179,30 +512,12 @@ class Course(models.Model): return new_course def create_duplicate_course(self, user): - quizzes = self.quiz_set.all() - prerequisite_map = [] - duplicate_quiz_map = {} + learning_modules = self.learning_module.all() new_course_name = "Copy Of {0}".format(self.name) new_course = self._create_duplicate_instance(user, new_course_name) - for q in quizzes: - new_quiz = q._create_duplicate_quiz(new_course) - if q.id not in duplicate_quiz_map.keys(): - duplicate_quiz_map[q.id] = new_quiz.id - - for orig_qid, dupl_qid in duplicate_quiz_map.items(): - original_quiz = Quiz.objects.get(id=orig_qid) - if original_quiz.prerequisite: - duplicate_quiz = Quiz.objects.get(id=dupl_qid) - prereq_id = original_quiz.prerequisite.id - duplicate_prereq_id = duplicate_quiz_map.get(prereq_id) - if duplicate_prereq_id: - duplicate_prerequisite = Quiz.objects.get( - id=duplicate_prereq_id - ) - duplicate_quiz.prerequisite = duplicate_prerequisite - duplicate_quiz.save() + new_course.learning_module.add(*learning_modules) return new_course @@ -247,9 +562,6 @@ class Course(models.Model): def is_self_enroll(self): return True if self.enrollment == enrollment_methods[1][0] else False - def get_quizzes(self): - return self.quiz_set.filter(is_trial=False) - def activate(self): self.active = True @@ -266,18 +578,25 @@ class Course(models.Model): self.teachers.remove(*teachers) def create_demo(self, user): - course = Course.objects.filter(creator=user, name="Yaksh Demo course") + course = Course.objects.filter(creator=user, + name="Yaksh Demo course").exists() if not course: course = Course.objects.create(name="Yaksh Demo course", enrollment="open", creator=user) quiz = Quiz() - demo_quiz = quiz.create_demo_quiz(course) + demo_quiz = quiz.create_demo_quiz(user) demo_ques = Question() demo_ques.create_demo_questions(user) demo_que_ppr = QuestionPaper() demo_que_ppr.create_demo_quiz_ppr(demo_quiz, user) success = True + ordered_unit = LearningUnit.objects.create( + order=1, learning_type="quiz", quiz=demo_quiz) + learning_module = LearningModule.objects.create( + name="demo module", description="demo module", creator=user) + learning_module.learning_unit.add(ordered_unit) + course.learning_module.add(learning_module) else: success = False return success @@ -288,10 +607,50 @@ class Course(models.Model): students = self.students.exclude(id__in=teachers) return students + def get_learning_modules(self): + return self.learning_module.order_by("order") + + 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) + return learning_unit.get_completion_status(user, self) + + def get_quizzes(self): + learning_modules = self.learning_module.all() + quiz_list = [] + for module in learning_modules: + quiz_list.extend(module.get_quiz_units()) + return quiz_list + + def get_quiz_details(self): + return [(self, quiz, quiz.get_total_students(self), + quiz.get_passed_students(self), + quiz.get_failed_students(self)) + for quiz in self.get_quizzes()] + + def get_learning_units(self): + learning_modules = self.learning_module.all() + learning_unit_list = [] + for module in learning_modules: + learning_unit_list.extend(module.get_learning_units()) + return learning_unit_list + def __str__(self): return self.name +############################################################################### +class CourseStatus(models.Model): + completed_units = models.ManyToManyField(LearningUnit, + related_name="completed_units") + current_unit = models.ForeignKey(LearningUnit, related_name="last_lesson", + null=True, blank=True) + course = models.ForeignKey(Course) + user = models.ForeignKey(User) + grade = models.CharField(max_length=255, null=True, blank=True) + total_marks = models.FloatField(default=0.0) + + ############################################################################### class ConcurrentUser(models.Model): concurrent_user = models.OneToOneField(User) @@ -620,165 +979,6 @@ class Answer(models.Model): return self.answer -############################################################################### -class QuizManager(models.Manager): - def get_active_quizzes(self): - return self.filter(active=True, is_trial=False) - - def create_trial_quiz(self, trial_course, user): - """Creates a trial quiz for testing questions""" - trial_quiz = self.create(course=trial_course, - duration=1000, - description="trial_questions", - is_trial=True, - time_between_attempts=0 - ) - return trial_quiz - - def create_trial_from_quiz(self, original_quiz_id, user, godmode): - """Creates a trial quiz from existing quiz""" - trial_quiz_name = "Trial_orig_id_{0}_{1}".format( - original_quiz_id, - "godmode" if godmode else "usermode" - ) - - if self.filter(description=trial_quiz_name).exists(): - trial_quiz = self.get(description=trial_quiz_name) - - else: - trial_quiz = self.get(id=original_quiz_id) - trial_quiz.course.enroll(False, user) - trial_quiz.pk = None - trial_quiz.description = trial_quiz_name - trial_quiz.is_trial = True - trial_quiz.prerequisite = None - if godmode: - trial_quiz.time_between_attempts = 0 - trial_quiz.duration = 1000 - trial_quiz.attempts_allowed = -1 - trial_quiz.active = True - trial_quiz.start_date_time = timezone.now() - trial_quiz.end_date_time = datetime( - 2199, 1, 1, 0, 0, 0, 0, tzinfo=pytz.utc - ) - trial_quiz.save() - return trial_quiz - - -############################################################################### -class Quiz(models.Model): - """A quiz that students will participate in. One can think of this - as the "examination" event. - """ - - course = models.ForeignKey(Course) - - # The start date of the quiz. - start_date_time = models.DateTimeField( - "Start Date and Time of the quiz", - default=timezone.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( - 2199, 1, 1, - tzinfo=pytz.timezone(timezone.get_current_timezone_name()) - ), - null=True - ) - - # This is always in minutes. - duration = models.IntegerField("Duration of quiz in minutes", default=20) - - # Is the quiz active. The admin should deactivate the quiz once it is - # complete. - active = models.BooleanField(default=True) - - # Description of quiz. - description = models.CharField(max_length=256) - - # Mininum passing percentage condition. - pass_criteria = models.FloatField("Passing percentage", default=40) - - # List of prerequisite quizzes to be passed to take this quiz - prerequisite = models.ForeignKey("Quiz", null=True, blank=True) - - # Programming language for a quiz - language = models.CharField(max_length=20, choices=languages) - - # Number of attempts for the quiz - attempts_allowed = models.IntegerField(default=1, choices=attempts) - - time_between_attempts = models.IntegerField( - "Number of Days", choices=days_between_attempts - ) - - is_trial = models.BooleanField(default=False) - - instructions = models.TextField('Instructions for Students', - default=None, blank=True, null=True) - - view_answerpaper = models.BooleanField('Allow student to view their answer\ - paper', default=False) - - allow_skip = models.BooleanField("Allow students to skip questions", - default=True) - - objects = QuizManager() - - class Meta: - verbose_name_plural = "Quizzes" - - def is_expired(self): - return not self.start_date_time <= timezone.now() < self.end_date_time - - def has_prerequisite(self): - return True if self.prerequisite else False - - def _create_duplicate_quiz(self, course): - questionpaper = self.questionpaper_set.all() - new_quiz = Quiz.objects.create(course=course, - start_date_time=self.start_date_time, - end_date_time=self.end_date_time, - duration=self.duration, - active=self.active, - description="Copy Of {0}".format(self.description), - pass_criteria=self.pass_criteria, - language=self.language, - attempts_allowed=self.attempts_allowed, - time_between_attempts=self.time_between_attempts, - is_trial=self.is_trial, - instructions=self.instructions, - allow_skip=self.allow_skip - ) - - for qp in questionpaper: - qp._create_duplicate_questionpaper(new_quiz) - - return new_quiz - - def create_demo_quiz(self, course): - demo_quiz = Quiz.objects.create( - start_date_time=timezone.now(), - end_date_time=timezone.now() + timedelta(176590), - duration=30, active=True, - attempts_allowed=-1, - time_between_attempts=0, - description='Yaksh Demo quiz', pass_criteria=0, - language='Python', prerequisite=None, - course=course - ) - return demo_quiz - - def __str__(self): - desc = self.description or 'Quiz' - return '%s: on %s for %d minutes' % (desc, self.start_date_time, - self.duration) - - ############################################################################### class QuestionPaperManager(models.Manager): @@ -884,18 +1084,20 @@ class QuestionPaper(models.Model): all_questions = questions return all_questions - def make_answerpaper(self, user, ip, attempt_num): + def make_answerpaper(self, user, ip, attempt_num, course_id): """Creates an answer paper for the user to attempt the quiz""" try: ans_paper = AnswerPaper.objects.get(user=user, attempt_number=attempt_num, - question_paper=self + question_paper=self, + course_id=course_id ) except AnswerPaper.DoesNotExist: ans_paper = AnswerPaper( user=user, user_ip=ip, - attempt_number=attempt_num + attempt_number=attempt_num, + course_id=course_id ) ans_paper.start_time = timezone.now() ans_paper.end_time = ans_paper.start_time + \ @@ -911,25 +1113,22 @@ class QuestionPaper(models.Model): except AnswerPaper.MultipleObjectsReturned: ans_paper = AnswerPaper.objects.get(user=user, attempt_number=attempt_num, - question_paper=self + question_paper=self, + course_id=course_id ).order_by('-id') ans_paper = ans_paper[0] 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): + def can_attempt_now(self, user, course_id): if self._is_attempt_allowed(user): last_attempt = AnswerPaper.objects.get_user_last_attempt( - user=user, questionpaper=self + user=user, questionpaper=self, course_id=course_id ) if last_attempt: time_lag = (timezone.now() - last_attempt.start_time).days @@ -939,14 +1138,6 @@ class QuestionPaper(models.Model): else: return False - def _get_prequisite_paper(self): - return self.quiz.prerequisite.questionpaper_set.get() - - def is_prerequisite_passed(self, user): - if self.quiz.has_prerequisite(): - prerequisite = self._get_prequisite_paper() - return prerequisite._is_questionpaper_passed(user) - def create_demo_quiz_ppr(self, demo_quiz, user): question_paper = QuestionPaper.objects.create(quiz=demo_quiz, shuffle_questions=False @@ -1038,27 +1229,32 @@ class AnswerPaperManager(models.Manager): questions_answered.append(question.id) return Counter(questions_answered) - def get_attempt_numbers(self, questionpaper_id, status='completed'): + def get_attempt_numbers(self, questionpaper_id, course_id, + status='completed'): ''' Return list of attempt numbers''' attempt_numbers = self.filter( - question_paper_id=questionpaper_id, status=status + question_paper_id=questionpaper_id, status=status, + course_id=course_id ).values_list('attempt_number', flat=True).distinct() return attempt_numbers - def has_attempt(self, questionpaper_id, attempt_number, + def has_attempt(self, questionpaper_id, attempt_number, course_id, status='completed'): ''' Whether question paper is attempted''' return self.filter( question_paper_id=questionpaper_id, - attempt_number=attempt_number, status=status + attempt_number=attempt_number, status=status, + course_id=course_id ).exists() - def get_count(self, questionpaper_id, attempt_number, status='completed'): + def get_count(self, questionpaper_id, attempt_number, course_id, + status='completed'): ''' Return count of answerpapers for a specfic question paper and attempt number''' return self.filter( question_paper_id=questionpaper_id, - attempt_number=attempt_number, status=status + attempt_number=attempt_number, status=status, + course_id=course_id ).count() def get_question_statistics(self, questionpaper_id, attempt_number, @@ -1083,18 +1279,21 @@ class AnswerPaperManager(models.Manager): question_stats[question] = [0, questions[question.id]] return question_stats - def _get_answerpapers_for_quiz(self, questionpaper_id, status=False): + def _get_answerpapers_for_quiz(self, questionpaper_id, course_id, + status=False): if not status: - return self.filter(question_paper_id=questionpaper_id) + return self.filter(question_paper_id=questionpaper_id, + course_id=course_id) else: return self.filter(question_paper_id=questionpaper_id, + course_id=course_id, status="completed") def _get_answerpapers_users(self, answerpapers): return answerpapers.values_list('user', flat=True).distinct() - def get_latest_attempts(self, questionpaper_id): - papers = self._get_answerpapers_for_quiz(questionpaper_id) + def get_latest_attempts(self, questionpaper_id, course_id): + papers = self._get_answerpapers_for_quiz(questionpaper_id, course_id) users = self._get_answerpapers_users(papers) latest_attempts = [] for user in users: @@ -1106,9 +1305,10 @@ class AnswerPaperManager(models.Manager): user_id=user_id ).order_by('-attempt_number')[0] - def get_user_last_attempt(self, questionpaper, user): + def get_user_last_attempt(self, questionpaper, user, course_id): attempts = self.filter(question_paper=questionpaper, - user=user).order_by('-attempt_number') + user=user, + course_id=course_id).order_by('-attempt_number') if attempts: return attempts[0] @@ -1118,23 +1318,28 @@ class AnswerPaperManager(models.Manager): def get_total_attempt(self, questionpaper, user): return self.filter(question_paper=questionpaper, user=user).count() - def get_users_for_questionpaper(self, questionpaper_id): - return self._get_answerpapers_for_quiz(questionpaper_id, status=True)\ + def get_users_for_questionpaper(self, questionpaper_id, course_id): + return self._get_answerpapers_for_quiz(questionpaper_id, course_id, + status=True)\ .values("user__id", "user__first_name", "user__last_name")\ .order_by("user__first_name")\ .distinct() - def get_user_all_attempts(self, questionpaper, user): - return self.filter(question_paper=questionpaper, user=user)\ + def get_user_all_attempts(self, questionpaper, user, course_id): + return self.filter(question_paper=questionpaper, user=user, + course_id=course_id)\ .order_by('-attempt_number') - def get_user_data(self, user, questionpaper_id, attempt_number=None): + def get_user_data(self, user, questionpaper_id, course_id, + attempt_number=None): if attempt_number is not None: papers = self.filter(user=user, question_paper_id=questionpaper_id, + course_id=course_id, attempt_number=attempt_number) else: papers = self.filter( - user=user, question_paper_id=questionpaper_id + user=user, question_paper_id=questionpaper_id, + course_id=course_id ).order_by("-attempt_number") data = {} profile = user.profile if hasattr(user, 'profile') else None @@ -1144,9 +1349,9 @@ class AnswerPaperManager(models.Manager): data['questionpaperid'] = questionpaper_id return data - def get_user_best_of_attempts_marks(self, quiz, user_id): + def get_user_best_of_attempts_marks(self, quiz, user_id, course_id): best_attempt = 0.0 - papers = self.filter(question_paper__quiz=quiz, + papers = self.filter(question_paper__quiz=quiz, course_id=course_id, user=user_id).values("marks_obtained") if papers: best_attempt = max([marks["marks_obtained"] for marks in papers]) @@ -1165,6 +1370,9 @@ class AnswerPaper(models.Model): # The Quiz to which this question paper is attached to. question_paper = models.ForeignKey(QuestionPaper) + # Answepaper will be unique to the course + course = models.ForeignKey(Course) + # The attempt number for the question paper. attempt_number = models.IntegerField() @@ -1438,6 +1646,7 @@ class AnswerPaper(models.Model): result['error'] = ['Correct answer'] elif question.type == 'float': + user_answer = float(user_answer) tc_status = [] user_answer = float(user_answer) for tc in question.get_test_cases(): -- cgit From 15850f69a19c9c22782bd49d17f54521701a9140 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 17 Nov 2017 19:26:55 +0530 Subject: Change in forms.py - Add model forms for Lesson, LearningModule - Add forms for LessonFile --- yaksh/forms.py | 237 +++++++++++++++++++++++++++------------------------------ 1 file changed, 114 insertions(+), 123 deletions(-) (limited to 'yaksh') diff --git a/yaksh/forms.py b/yaksh/forms.py index 2740497..25eb2c5 100644 --- a/yaksh/forms.py +++ b/yaksh/forms.py @@ -1,15 +1,11 @@ from django import forms -from yaksh.models import get_model_class, Profile, Quiz, Question, TestCase, Course,\ - QuestionPaper, StandardTestCase, StdIOBasedTestCase, \ - HookTestCase, IntegerTestCase, StringTestCase +from yaksh.models import ( + get_model_class, Profile, Quiz, Question, Course, QuestionPaper, Lesson, + LearningModule +) from django.contrib.auth import authenticate from django.contrib.auth.models import User -from django.contrib.contenttypes.models import ContentType from django.conf import settings -from taggit.managers import TaggableManager -from taggit.forms import TagField -from django.forms.models import inlineformset_factory -from django.db.models import Q from django.utils import timezone from textwrap import dedent try: @@ -17,7 +13,6 @@ try: except ImportError: from string import ascii_letters as letters from string import punctuation, digits -import datetime import pytz from .send_emails import generate_activation_key @@ -29,7 +24,7 @@ languages = ( ("cpp", "C++ Language"), ("java", "Java Language"), ("scilab", "Scilab"), - ) +) question_types = ( ("select", "Select Question Type"), @@ -40,17 +35,17 @@ question_types = ( ("integer", "Answer in Integer"), ("string", "Answer in String"), ("float", "Answer in Float"), - ) +) test_case_types = ( - ("standardtestcase", "Standard Testcase"), - ("stdiobasedtestcase", "StdIO Based Testcase"), - ("mcqtestcase", "MCQ Testcase"), - ("hooktestcase", "Hook Testcase"), - ("integertestcase", "Integer Testcase"), - ("stringtestcase", "String Testcase"), - ("floattestcase", "Float Testcase"), - ) + ("standardtestcase", "Standard Testcase"), + ("stdiobasedtestcase", "StdIO Based Testcase"), + ("mcqtestcase", "MCQ Testcase"), + ("hooktestcase", "Hook Testcase"), + ("integertestcase", "Integer Testcase"), + ("stringtestcase", "String Testcase"), + ("floattestcase", "Float Testcase"), +) UNAME_CHARS = letters + "._" + digits PWD_CHARS = letters + punctuation + digits @@ -59,8 +54,10 @@ attempts = [(i, i) for i in range(1, 6)] attempts.append((-1, 'Infinite')) days_between_attempts = ((j, j) for j in range(401)) -def get_object_form(model, exclude_fields=None): + +def get_object_form(model, exclude_fields=None): model_class = get_model_class(model) + class _ObjectForm(forms.ModelForm): class Meta: model = model_class @@ -77,20 +74,21 @@ class UserRegisterForm(forms.Form): period and underscores only.') email = forms.EmailField() password = forms.CharField(max_length=30, widget=forms.PasswordInput()) - confirm_password = forms.CharField\ - (max_length=30, widget=forms.PasswordInput()) + confirm_password = forms.CharField( + max_length=30, widget=forms.PasswordInput()) first_name = forms.CharField(max_length=30) last_name = forms.CharField(max_length=30) - roll_number = forms.CharField\ - (max_length=30, help_text="Use a dummy if you don't have one.") - institute = forms.CharField\ - (max_length=128, help_text='Institute/Organization') - department = forms.CharField\ - (max_length=64, help_text='Department you work/study at') - position = forms.CharField\ - (max_length=64, help_text='Student/Faculty/Researcher/Industry/etc.') - timezone = forms.ChoiceField(choices=[(tz, tz) for tz in pytz.common_timezones], - initial=pytz.utc) + roll_number = forms.CharField( + max_length=30, help_text="Use a dummy if you don't have one.") + institute = forms.CharField( + max_length=128, help_text='Institute/Organization') + department = forms.CharField( + max_length=64, help_text='Department you work/study at') + position = forms.CharField( + max_length=64, help_text='Student/Faculty/Researcher/Industry/etc.') + timezone = forms.ChoiceField( + choices=[(tz, tz) for tz in pytz.common_timezones], + initial=pytz.utc) def clean_username(self): u_name = self.cleaned_data["username"] @@ -146,9 +144,10 @@ class UserRegisterForm(forms.Form): if settings.IS_DEVELOPMENT: new_profile.is_email_verified = True else: - new_profile.activation_key = generate_activation_key(new_user.username) - new_profile.key_expiry_time = timezone.now() + \ - timezone.timedelta(minutes=20) + new_profile.activation_key = generate_activation_key( + new_user.username) + new_profile.key_expiry_time = timezone.now() + timezone.timedelta( + minutes=20) new_profile.save() return u_name, pwd, new_user.email, new_profile.activation_key @@ -166,8 +165,9 @@ class UserLoginForm(forms.Form): self.cleaned_data["password"] user = authenticate(username=u_name, password=pwd) except Exception: - raise forms.ValidationError\ - ("Username and/or Password is not entered") + raise forms.ValidationError( + "Username and/or Password is not entered" + ) if not user: raise forms.ValidationError("Invalid username/password") return user @@ -178,82 +178,38 @@ class QuizForm(forms.ModelForm): It has the related fields and functions required.""" def __init__(self, *args, **kwargs): - user = kwargs.pop('user') - course_id = kwargs.pop('course') super(QuizForm, self).__init__(*args, **kwargs) - prerequisite_list = Quiz.objects.filter( - course__id=course_id, - is_trial=False - ).exclude(id=self.instance.id) - - self.fields['prerequisite'] = forms.ModelChoiceField(prerequisite_list) - self.fields['prerequisite'].required = False - self.fields['course'] = forms.ModelChoiceField( - queryset=Course.objects.filter(id=course_id), empty_label=None) - self.fields["instructions"].initial = dedent("""\ -
- This examination system has been - developed with the intention of - making you learn programming and - be assessed in an interactive and - fun manner. - You will be presented with a - series of programming questions - and problems that you will answer - online and get immediate - feedback for. -
-- Here are some important - instructions and rules that you - should understand carefully.
-- We hope you enjoy taking this - exam !!! -
- """) - - def clean_prerequisite(self): - prereq = self.cleaned_data['prerequisite'] - if prereq and prereq.prerequisite: - if prereq.prerequisite.id == self.instance.id: - raise forms.ValidationError("Please set another prerequisite quiz") - return prereq + self.fields["instructions"].initial = dedent("""\ ++ This examination system has been developed with the intention of + making you learn programming and be assessed in an interactive and + fun manner.You will be presented with a series of programming + questions and problems that you will answer online and get + immediate feedback for. +
Here are some important instructions and rules that you + should understand carefully.
+We hope you enjoy taking this exam !!!
+ """) class Meta: model = Quiz - exclude = ["is_trial"] + exclude = ["is_trial", "creator"] class QuestionForm(forms.ModelForm): @@ -266,15 +222,19 @@ class QuestionForm(forms.ModelForm): class FileForm(forms.Form): - file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}), - required=False) + file_field = forms.FileField(widget=forms.ClearableFileInput( + attrs={'multiple': True}), + required=False) class RandomQuestionForm(forms.Form): - question_type = forms.CharField(max_length=8, widget=forms.Select\ - (choices=question_types)) - marks = forms.CharField(max_length=8, widget=forms.Select\ - (choices=(('select', 'Select Marks'),))) + question_type = forms.CharField( + max_length=8, widget=forms.Select(choices=question_types) + ) + marks = forms.CharField( + max_length=8, widget=forms.Select( + choices=(('select', 'Select Marks'),)) + ) shuffle_questions = forms.BooleanField(required=False) @@ -286,13 +246,14 @@ class QuestionFilterForm(forms.Form): points_list = questions.values_list('points', flat=True).distinct() points_options = [(None, 'Select Marks')] points_options.extend([(point, point) for point in points_list]) - self.fields['marks'] = forms.FloatField(widget=forms.Select\ - (choices=points_options)) - - language = forms.CharField(max_length=8, widget=forms.Select\ - (choices=languages)) - question_type = forms.CharField(max_length=8, widget=forms.Select\ - (choices=question_types)) + self.fields['marks'] = forms.FloatField( + widget=forms.Select(choices=points_options) + ) + language = forms.CharField( + max_length=8, widget=forms.Select(choices=languages)) + question_type = forms.CharField( + max_length=8, widget=forms.Select(choices=question_types) + ) class CourseForm(forms.ModelForm): @@ -311,8 +272,7 @@ class CourseForm(forms.ModelForm): class Meta: model = Course - exclude = ['creator', 'requests', 'students', 'rejected', - 'created_on', 'is_trial', 'hidden', 'teachers'] + fields = ['name', 'enrollment', 'active', 'code', 'instructions'] class ProfileForm(forms.ModelForm): @@ -342,3 +302,34 @@ class QuestionPaperForm(forms.ModelForm): class Meta: model = QuestionPaper fields = ['shuffle_questions'] + + +class LessonForm(forms.ModelForm): + def __init__(self, *args, **kwargs): + super(LessonForm, self).__init__(*args, **kwargs) + des_msg = "Enter Lesson Description as Markdown text" + name_msg = "Enter Lesson Name" + self.fields['description'].widget.attrs['placeholder'] = des_msg + self.fields['name'].widget.attrs['placeholder'] = name_msg + + class Meta: + model = Lesson + exclude = ['completed_lessons', 'creator', 'html_data'] + + +class LessonFileForm(forms.Form): + Lesson_files = forms.FileField(widget=forms.ClearableFileInput( + attrs={'multiple': True}), + required=False) + + +class LearningModuleForm(forms.ModelForm): + def __init__(self, *args, **kwargs): + super(LearningModuleForm, self).__init__(*args, **kwargs) + name_msg = "Enter Learning Module Name" + self.fields['name'].widget.attrs['placeholder'] = name_msg + self.fields['name'].widget.attrs['size'] = 30 + + class Meta: + model = LearningModule + fields = ['name', 'description'] -- cgit From b70cc57b29c17a3f4c600a120c5cbc7f5259ac3d Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 17 Nov 2017 19:28:51 +0530 Subject: Change in urls.py - Add new urls for creating course with module and units - Fix existing urls --- yaksh/urls.py | 83 +++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 19 deletions(-) (limited to 'yaksh') diff --git a/yaksh/urls.py b/yaksh/urls.py index 7f484b9..8348666 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -11,50 +11,66 @@ urlpatterns = [ url(r'^quizzes/$', views.quizlist_user, name='quizlist_user'), url(r'^quizzes/(?PDeveloped by FOSSEE group, IIT Bombay