diff options
-rw-r--r-- | yaksh/models.py | 69 | ||||
-rw-r--r-- | yaksh/templates/yaksh/course_detail.html | 4 | ||||
-rw-r--r-- | yaksh/test_models.py | 202 | ||||
-rw-r--r-- | yaksh/urls.py | 2 | ||||
-rw-r--r-- | yaksh/views.py | 15 |
5 files changed, 292 insertions, 0 deletions
diff --git a/yaksh/models.py b/yaksh/models.py index f6867f0..ad64f2a 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -151,6 +151,42 @@ class Course(models.Model): objects = CourseManager() + def create_duplicate_course(self, user): + quizzes = self.quiz_set.all() + prerequisite_map = [] + duplicate_quiz_map = {} + new_course = Course.objects.create(creator=user, + name="Copy Of {0}".format(self.name), + enrollment=self.enrollment, + active=self.active, + is_trial=self.is_trial, + instructions=self.instructions, + start_enroll_time=self.start_enroll_time, + end_enroll_time=self.end_enroll_time + ) + + new_course.teachers.add(*self.teachers.all()) + + 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() + + return new_course + def request(self, *users): self.requests.add(*users) @@ -656,6 +692,28 @@ class Quiz(models.Model): 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(), @@ -741,6 +799,17 @@ class QuestionPaper(models.Model): objects = QuestionPaperManager() + def _create_duplicate_questionpaper(self, quiz): + new_questionpaper = QuestionPaper.objects.create(quiz=quiz, + shuffle_questions=self.shuffle_questions, + total_marks=self.total_marks, + fixed_question_order=self.fixed_question_order + ) + new_questionpaper.fixed_questions.add(*self.fixed_questions.all()) + new_questionpaper.random_questions.add(*self.random_questions.all()) + + return new_questionpaper + def update_total_marks(self): """ Updates the total marks for the Question Paper""" marks = 0.0 diff --git a/yaksh/templates/yaksh/course_detail.html b/yaksh/templates/yaksh/course_detail.html index 81569fa..cd4144f 100644 --- a/yaksh/templates/yaksh/course_detail.html +++ b/yaksh/templates/yaksh/course_detail.html @@ -20,6 +20,10 @@ <a href="{{URL_ROOT}}/exam/manage/toggle_status/{{ course.id }}/"> {% if course.active %}Deactivate Course {% else %} Activate Course {% endif %}</a> </li> + <li> + <a href="{{URL_ROOT}}/exam/manage/duplicate_course/{{ course.id }}/"> + Clone Course</a> + </li> </ul> </div> </div> diff --git a/yaksh/test_models.py b/yaksh/test_models.py index 9bd8492..5a61c0f 100644 --- a/yaksh/test_models.py +++ b/yaksh/test_models.py @@ -29,6 +29,14 @@ def setUpModule(): Profile.objects.create(user=student, roll_number=3, institute='IIT', department='Chemical', position='Student') + user4 = User.objects.create_user(username='demo_user4', + password='demo', + email='demo4@test.com' + ) + Profile.objects.create(user=user4, roll_number=4, institute='IIT', + department='Chemical', position='Student') + + # create a course course = Course.objects.create(name="Python Course", enrollment="Enroll Request", creator=user) @@ -833,10 +841,12 @@ class CourseTestCases(unittest.TestCase): def setUp(self): self.course = Course.objects.get(name="Python Course") self.creator = User.objects.get(username="demo_user") + self.template_course_user = User.objects.get(username="demo_user4") self.student1 = User.objects.get(username="demo_user2") self.student2 = User.objects.get(username="demo_user3") self.quiz1 = Quiz.objects.get(description='demo quiz 1') self.quiz2 = Quiz.objects.get(description='demo quiz 2') + self.questions = Question.objects.filter(active=True) # create courses with disabled enrollment self.enroll_request_course = Course.objects.create( @@ -862,6 +872,198 @@ class CourseTestCases(unittest.TestCase): ), ) + # create a course that will be cloned + self.template_course = Course.objects.create( + name="Template Course to clone", + enrollment="Open Course", + creator=self.creator, + start_enroll_time=datetime(2015, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc + ), + end_enroll_time=datetime(2015, 11, 9, 10, 8, 15, 0, + tzinfo=pytz.utc + ), + ) + + self.template_quiz = Quiz.objects.create( + start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc + ), + end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, + tzinfo=pytz.utc + ), + duration=30, + active=False, + attempts_allowed=-1, + time_between_attempts=0, + description='template quiz 1', + pass_criteria=40, + language='Python', + course=self.template_course, + instructions="Demo Instructions" + ) + + self.template_question_paper = QuestionPaper.objects.create( + quiz=self.template_quiz, + total_marks=0.0, + shuffle_questions=True + ) + + self.template_question_paper.fixed_questions.add(self.questions[1], + self.questions[2], + self.questions[3] + ) + + self.template_quiz2 = Quiz.objects.create( + start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc), + duration=30, + active=True, + attempts_allowed=1, + time_between_attempts=0, + description='template quiz 2', + pass_criteria=0, + language='Python', + prerequisite=self.template_quiz, + course=self.template_course, + instructions="Demo Instructions" + ) + + self.template_question_paper2 = QuestionPaper.objects.create( + quiz=self.template_quiz2, + total_marks=0.0, + shuffle_questions=True + ) + + self.template_question_paper2.fixed_questions.add(self.questions[1], + self.questions[2], + self.questions[3] + ) + + def test_create_duplicate_course(self): + """ Test create_duplicate_course method of course """ + # create a duplicate course + cloned_course = self.template_course.create_duplicate_course( + self.template_course_user + ) + self.assertEqual(cloned_course.name, + 'Copy Of Template Course to clone') + self.assertEqual(cloned_course.enrollment, + self.template_course.enrollment + ) + self.assertEqual(cloned_course.creator, + self.template_course_user + ) + self.assertEqual(cloned_course.start_enroll_time, + self.template_course.start_enroll_time + ) + self.assertEqual(cloned_course.end_enroll_time, + self.template_course.end_enroll_time + ) + + # get duplicate quiz associated with duplicate course + cloned_quiz = cloned_course.quiz_set.all()[0] + + self.assertEqual(cloned_quiz.start_date_time, + self.template_quiz.start_date_time + ) + self.assertEqual(cloned_quiz.end_date_time, + self.template_quiz.end_date_time + ) + self.assertEqual(cloned_quiz.duration, + self.template_quiz.duration + ) + self.assertEqual(cloned_quiz.active, + self.template_quiz.active + ) + self.assertEqual(cloned_quiz.attempts_allowed, + self.template_quiz.attempts_allowed + ) + self.assertEqual(cloned_quiz.time_between_attempts, + self.template_quiz.time_between_attempts + ) + self.assertEqual(cloned_quiz.description, + 'Copy Of template quiz 1' + ) + self.assertEqual(cloned_quiz.pass_criteria, + self.template_quiz.pass_criteria + ) + self.assertEqual(cloned_quiz.language, + self.template_quiz.language + ) + self.assertEqual(cloned_quiz.course, + cloned_course + ) + self.assertEqual(cloned_quiz.instructions, + self.template_quiz.instructions + ) + + # Get duplicate questionpaper associated with duplicate quiz + cloned_qp = cloned_quiz.questionpaper_set.all()[0] + + self.assertEqual(cloned_qp.quiz, cloned_quiz) + self.assertEqual(cloned_qp.total_marks, + self.template_question_paper.total_marks + ) + self.assertEqual(cloned_qp.shuffle_questions, + self.template_question_paper.shuffle_questions + ) + + for q in cloned_qp.fixed_questions.all(): + self.assertIn(q, self.template_question_paper.fixed_questions.all()) + + # get second duplicate quiz associated with duplicate course + cloned_quiz = cloned_course.quiz_set.all()[1] + + self.assertEqual(cloned_quiz.start_date_time, + self.template_quiz2.start_date_time + ) + self.assertEqual(cloned_quiz.end_date_time, + self.template_quiz2.end_date_time + ) + self.assertEqual(cloned_quiz.duration, + self.template_quiz2.duration + ) + self.assertEqual(cloned_quiz.active, + self.template_quiz2.active + ) + self.assertEqual(cloned_quiz.attempts_allowed, + self.template_quiz2.attempts_allowed + ) + self.assertEqual(cloned_quiz.time_between_attempts, + self.template_quiz2.time_between_attempts + ) + self.assertEqual(cloned_quiz.description, + 'Copy Of template quiz 2' + ) + self.assertEqual(cloned_quiz.pass_criteria, + self.template_quiz2.pass_criteria + ) + self.assertEqual(cloned_quiz.language, + self.template_quiz2.language + ) + self.assertEqual(cloned_quiz.course, + cloned_course + ) + self.assertEqual(cloned_quiz.instructions, + self.template_quiz2.instructions + ) + + # Get second duplicate questionpaper associated with duplicate quiz + cloned_qp = cloned_quiz.questionpaper_set.all()[0] + + self.assertEqual(cloned_qp.quiz, cloned_quiz) + self.assertEqual(cloned_qp.total_marks, + self.template_question_paper2.total_marks + ) + self.assertEqual(cloned_qp.shuffle_questions, + self.template_question_paper2.shuffle_questions + ) + + for q in cloned_qp.fixed_questions.all(): + self.assertIn(q, self.template_question_paper2.fixed_questions.all()) + + def test_is_creator(self): """ Test is_creator method of Course""" self.assertTrue(self.course.is_creator(self.creator)) diff --git a/yaksh/urls.py b/yaksh/urls.py index 20ce918..b02b797 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -59,6 +59,8 @@ urlpatterns = [ views.show_statistics), url(r'^manage/monitor/download_csv/(?P<questionpaper_id>\d+)/$', views.download_csv), + url(r'^manage/duplicate_course/(?P<course_id>\d+)/$', views.duplicate_course, + name='duplicate_course'), url(r'manage/courses/$', views.courses, name='courses'), url(r'manage/add_course/$', views.add_course, name='add_course'), url(r'manage/edit_course/(?P<course_id>\d+)$', views.add_course, name='edit_course'), diff --git a/yaksh/views.py b/yaksh/views.py index 35df9f9..6a4325e 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1597,3 +1597,18 @@ def download_assignment_file(request, quiz_id, question_id=None, user_id=None): ) response.write(zipfile_name.read()) return response + +@login_required +def duplicate_course(request, course_id): + user = request.user + course = get_object_or_404(Course, pk=course_id) + if not is_moderator(user): + raise Http404('You are not allowed to view this page!') + + if course.is_teacher(user) or course.is_creator(user): + course.create_duplicate_course(user) + else: + msg = 'You do not have permissions to clone this course, please contact your '\ + 'instructor/administrator.' + return complete(request, msg, attempt_num=None, questionpaper_id=None) + return my_redirect('/exam/manage/courses/') |