diff options
Diffstat (limited to 'yaksh')
-rw-r--r-- | yaksh/models.py | 64 | ||||
-rw-r--r-- | yaksh/templates/yaksh/complete.html | 3 | ||||
-rw-r--r-- | yaksh/templates/yaksh/courses.html | 21 | ||||
-rw-r--r-- | yaksh/test_views.py | 104 | ||||
-rw-r--r-- | yaksh/urls.py | 4 | ||||
-rw-r--r-- | yaksh/views.py | 40 |
6 files changed, 202 insertions, 34 deletions
diff --git a/yaksh/models.py b/yaksh/models.py index f065190..252bde6 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -163,6 +163,23 @@ class Lesson(models.Model): def get_files(self): return LessonFile.objects.filter(lesson=self) + def _create_lesson_copy(self, user): + lesson_files = self.get_files() + new_lesson = self + new_lesson.id = None + new_lesson.name = "Copy of {0}".format(self.name) + new_lesson.creator = user + new_lesson.save() + for _file in lesson_files: + file_name = os.path.basename(_file.file.name) + if os.path.exists(_file.file.path): + lesson_file = open(_file.file.path, "rb") + django_file = File(lesson_file) + lesson_file_obj = LessonFile() + lesson_file_obj.lesson = new_lesson + lesson_file_obj.file.save(file_name, django_file, save=True) + return new_lesson + ############################################################################# class LessonFile(models.Model): @@ -366,6 +383,17 @@ class Quiz(models.Model): course=course, passed=False ).values_list("user", flat=True).distinct().count() + def _create_quiz_copy(self, user): + question_papers = self.questionpaper_set.all() + new_quiz = self + new_quiz.id = None + new_quiz.description = "Copy of {0}".format(self.description) + new_quiz.creator = user + new_quiz.save() + for qp in question_papers: + qp._create_duplicate_questionpaper(new_quiz) + return new_quiz + def __str__(self): desc = self.description or 'Quiz' return '%s: on %s for %d minutes' % (desc, self.start_date_time, @@ -416,6 +444,17 @@ class LearningUnit(models.Model): success = False return success + def _create_unit_copy(self, user): + if self.type == "quiz": + new_quiz = self.quiz._create_quiz_copy(user) + new_unit = LearningUnit.objects.create( + order=self.order, type="quiz", quiz=new_quiz) + else: + new_lesson = self.lesson._create_lesson_copy(user) + new_unit = LearningUnit.objects.create( + order=self.order, type="lesson", lesson=new_lesson) + return new_unit + ############################################################################### class LearningModule(models.Model): @@ -511,6 +550,18 @@ class LearningModule(models.Model): percent = round((count / len(units)) * 100) return percent + def _create_module_copy(self, user, module_name): + learning_units = self.learning_unit.order_by("order") + new_module = self + new_module.id = None + new_module.name = module_name + new_module.creator = user + new_module.save() + for unit in learning_units: + new_unit = unit._create_unit_copy(user) + new_module.learning_unit.add(new_unit) + return new_module + def __str__(self): return self.name @@ -571,6 +622,15 @@ class Course(models.Model): return new_course + def create_shallow_copy(self, user): + learning_modules = self.learning_module.order_by("order") + copy_course_name = "Copy Of {0}".format(self.name) + new_course = self._create_duplicate_instance(user, copy_course_name) + for module in learning_modules: + copy_module_name = "Copy of {0}".format(module.name) + new_module = module._create_module_copy(user, copy_module_name) + new_course.learning_module.add(new_module) + def request(self, *users): self.requests.add(*users) @@ -1144,8 +1204,8 @@ class QuestionPaper(models.Model): return questions def _create_duplicate_questionpaper(self, quiz): - new_questionpaper = QuestionPaper.objects.create(quiz=quiz, - shuffle_questions=self.shuffle_questions, + new_questionpaper = QuestionPaper.objects.create( + quiz=quiz, shuffle_questions=self.shuffle_questions, total_marks=self.total_marks, fixed_question_order=self.fixed_question_order ) diff --git a/yaksh/templates/yaksh/complete.html b/yaksh/templates/yaksh/complete.html index 3d6cadc..0881bfe 100644 --- a/yaksh/templates/yaksh/complete.html +++ b/yaksh/templates/yaksh/complete.html @@ -33,9 +33,6 @@ width="80" alt="YAKSH"></img>{% endblock %} <center><h3>{{message}}</h3></center> <center> <br> - {% if not module_id %} - <br><center><h4>You may now close the browser.</h4></center><br> - {% endif %} {% if module_id and not user == "moderator" %} {% if first_unit %} <a href="{{URL_ROOT}}/exam/next_unit/{{course_id}}/{{module_id}}/{{learning_unit.id}}/1" class="btn btn-info" id="Next"> Next diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html index bc96bf5..9c00957 100644 --- a/yaksh/templates/yaksh/courses.html +++ b/yaksh/templates/yaksh/courses.html @@ -4,6 +4,7 @@ {% block script %} <script> $(document).ready(function(){ + $('[data-toggle="tooltip"]').tooltip(); $("#created_courses").toggle(); $("#link_created_courses").click(function() { if ($("#allotted_courses").is(":visible")){ @@ -24,6 +25,14 @@ }); </script> {% endblock %} +{% block css %} +<style> + .test + .tooltip.top > .tooltip-inner { + padding: 15px; + font-size: 12px; + } +</style> +{% endblock %} {% block content %} <div class="row"> <div class="col-sm-3 col-md-2 sidebar"> @@ -99,7 +108,8 @@ <br><br> <ul> <li> - <a href="{{URL_ROOT}}/exam/manage/courses/designcourse/{{course.id}}/">Design Course + <a href="{{URL_ROOT}}/exam/manage/courses/designcourse/{{course.id}}/" data-toggle="tooltip" title="Add/Remove/Change course modules" data-placement="top"> + Design Course </a> </li> <br> @@ -123,7 +133,12 @@ </li> <br> <li> - <a href="{{URL_ROOT}}/exam/manage/duplicate_course/{{ course.id }}/"> + <a class="test" href="{{URL_ROOT}}/exam/manage/duplicate_course/copy/{{ course.id }}/" data-toggle="tooltip" title="Creates a new copy and link modules of selected course to the copy" data-placement="top"> + Copy Course</a> + </li> + <br> + <li> + <a class="test" href="{{URL_ROOT}}/exam/manage/duplicate_course/clone/{{ course.id }}/" data-toggle="tooltip" title="Creates Copy of selected Course as well as its Modules, Lessons/Quizzes" data-placement="top"> Clone Course</a> </li> </ul> @@ -259,7 +274,7 @@ </li> <br> <li> - <a href="{{URL_ROOT}}/exam/manage/duplicate_course/{{ course.id }}/"> + <a class="test" href="{{URL_ROOT}}/exam/manage/duplicate_course/clone/{{ course.id }}/" data-toggle="tooltip" title="Creates Copy of selected Course as well as its Modules, Lessons/Quizzes" data-placement="top"> Clone Course</a> </li> </ul> diff --git a/yaksh/test_views.py b/yaksh/test_views.py index 3b27338..fb33114 100644 --- a/yaksh/test_views.py +++ b/yaksh/test_views.py @@ -20,6 +20,7 @@ from django.utils import timezone from django.core import mail from django.conf import settings from django.core.files.uploadedfile import SimpleUploadedFile +from django.core.files import File from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\ QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\ @@ -1668,13 +1669,38 @@ class TestCourses(TestCase): order=0, name="test module", description="module", check_prerequisite=False, creator=self.teacher) - self.user1_course = Course.objects.create(name="Python Course", + self.user1_course = Course.objects.create( + name="Python Course", enrollment="Enroll Request", creator=self.user1) + # Create Learning Module for Python Course + self.learning_module1 = LearningModule.objects.create( + order=0, name="demo module", description="module", + check_prerequisite=False, creator=self.user1) + + self.quiz = Quiz.objects.create( + time_between_attempts=0, description='demo quiz', + creator=self.user1) + self.question_paper = QuestionPaper.objects.create( + quiz=self.quiz, total_marks=1.0) + self.lesson = Lesson.objects.create( + name="demo lesson", description="test description", + creator=self.user1) + + self.lesson_unit = LearningUnit.objects.create( + order=1, type="lesson", lesson=self.lesson) + self.quiz_unit = LearningUnit.objects.create( + order=2, type="quiz", quiz=self.quiz) + + # Add units to module + self.learning_module1.learning_unit.add(self.lesson_unit) + self.learning_module1.learning_unit.add(self.quiz_unit) + # Add teacher to user1 course self.user1_course.teachers.add(self.teacher) - self.user2_course = Course.objects.create(name="Java Course", + self.user2_course = Course.objects.create( + name="Java Course", enrollment="Enroll Request", creator=self.user2) self.user2_course.learning_module.add(self.learning_module) @@ -1683,10 +1709,7 @@ class TestCourses(TestCase): self.user1.delete() self.user2.delete() self.student.delete() - self.user1_course.delete() - self.user2_course.delete() self.teacher.delete() - self.learning_module.delete() def test_courses_denies_anonymous(self): """ @@ -1837,7 +1860,7 @@ class TestCourses(TestCase): self.learning_module) def test_duplicate_course(self): - """ Test To clone/duplicate course """ + """ Test To clone/duplicate course and link modules""" # Student Login self.client.login( @@ -1847,7 +1870,8 @@ class TestCourses(TestCase): response = self.client.get( reverse('yaksh:duplicate_course', - kwargs={"course_id": self.user2_course.id}), + kwargs={"course_id": self.user2_course.id, + "copy_type": "copy"}), follow=True ) self.assertEqual(response.status_code, 404) @@ -1861,7 +1885,8 @@ class TestCourses(TestCase): # Denies teacher not added in the course response = self.client.get( reverse('yaksh:duplicate_course', - kwargs={"course_id": self.user2_course.id}), + kwargs={"course_id": self.user2_course.id, + "copy_type": "copy"}), follow=True ) err_msg = "You do not have permissions" @@ -1878,7 +1903,8 @@ class TestCourses(TestCase): # Allows creator to duplicate the course response = self.client.get( reverse('yaksh:duplicate_course', - kwargs={"course_id": self.user2_course.id}), + kwargs={"course_id": self.user2_course.id, + "copy_type": "copy"}), follow=True ) @@ -1891,6 +1917,66 @@ class TestCourses(TestCase): self.assertEqual(courses.last().get_learning_modules()[0].id, self.user2_course.get_learning_modules()[0].id) + # Test clone/duplicate courses and create copies of modules and units + + # Teacher Login + # Given + # Add files to a lesson + lesson_file = SimpleUploadedFile("file1.txt", b"Test") + django_file = File(lesson_file) + lesson_file_obj = LessonFile() + lesson_file_obj.lesson = self.lesson + lesson_file_obj.file.save(lesson_file.name, django_file, save=True) + + # Add module to Python Course + self.user1_course.learning_module.add(self.learning_module1) + self.client.login( + username=self.teacher.username, + password=self.teacher_plaintext_pass + ) + response = self.client.get( + reverse('yaksh:duplicate_course', + kwargs={"course_id": self.user1_course.id, + "copy_type": "clone"}), + follow=True + ) + + # When + courses = Course.objects.filter( + creator=self.teacher).order_by("id") + module = courses.last().get_learning_modules()[0] + units = module.get_learning_units() + cloned_lesson = units[0].lesson + cloned_quiz = units[1].quiz + expected_lesson_files = cloned_lesson.get_files() + actual_lesson_files = self.lesson.get_files() + cloned_qp = cloned_quiz.questionpaper_set.get() + self.all_files = LessonFile.objects.filter( + lesson_id__in=[self.lesson.id, cloned_lesson.id]) + + # Then + self.assertEqual(response.status_code, 200) + self.assertEqual(courses.last().creator, self.teacher) + self.assertEqual(courses.last().name, "Copy Of Python Course") + self.assertEqual(module.name, "Copy of demo module") + self.assertEqual(module.creator, self.teacher) + self.assertEqual(module.order, 0) + self.assertEqual(len(units), 2) + self.assertEqual(cloned_lesson.name, "Copy of demo lesson") + self.assertEqual(cloned_lesson.creator, self.teacher) + self.assertEqual(cloned_quiz.description, "Copy of demo quiz") + self.assertEqual(cloned_quiz.creator, self.teacher) + self.assertEqual(cloned_qp.__str__(), + "Question Paper for Copy of demo quiz") + self.assertEqual(os.path.basename(expected_lesson_files[0].file.name), + os.path.basename(actual_lesson_files[0].file.name)) + + for lesson_file in self.all_files: + file_path = lesson_file.file.path + if os.path.exists(file_path): + os.remove(file_path) + shutil.rmtree(os.path.dirname(file_path)) + class TestAddCourse(TestCase): def setUp(self): diff --git a/yaksh/urls.py b/yaksh/urls.py index 08c2091..b1e6249 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -86,8 +86,8 @@ urlpatterns = [ views.show_statistics, name="show_statistics"), url(r'^manage/download_quiz_csv/(?P<course_id>\d+)/(?P<quiz_id>\d+)/$', views.download_quiz_csv, name="download_quiz_csv"), - url(r'^manage/duplicate_course/(?P<course_id>\d+)/$', views.duplicate_course, - name='duplicate_course'), + url(r'^manage/duplicate_course/(?P<copy_type>copy|clone)/(?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 011b417..62a448c 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -390,8 +390,8 @@ def prof_manage(request, msg=None): return my_redirect('/exam/login') if not is_moderator(user): return my_redirect('/exam/') - courses = Course.objects.filter(creator=user, is_trial=False) - + courses = Course.objects.filter(Q(creator=user) | Q(teachers=user), + is_trial=False) trial_paper = AnswerPaper.objects.filter( user=user, question_paper__quiz__is_trial=True, course__is_trial=True @@ -399,17 +399,18 @@ def prof_manage(request, msg=None): if request.method == "POST": delete_paper = request.POST.getlist('delete_paper') for answerpaper_id in delete_paper: - answerpaper = AnswerPaper.objects.get(id=answerpaper_id) - qpaper = answerpaper.question_paper - answerpaper.course.remove_trial_modules() - answerpaper.course.delete() - if qpaper.quiz.is_trial: - qpaper.quiz.delete() - else: - if qpaper.answerpaper_set.count() == 1: + answerpaper = AnswerPaper.objects.filter(id=answerpaper_id) + if answerpaper.exists(): + qpaper = answerpaper.first().question_paper + answerpaper.first().course.remove_trial_modules() + answerpaper.first().course.delete() + if qpaper.quiz.is_trial: qpaper.quiz.delete() else: - answerpaper.delete() + if qpaper.answerpaper_set.count() == 1: + qpaper.quiz.delete() + else: + answerpaper.delete() context = {'user': user, 'courses': courses, 'trial_paper': trial_paper, 'msg': msg } @@ -2245,17 +2246,26 @@ def download_sample_csv(request): @login_required @email_verified -def duplicate_course(request, course_id): +def duplicate_course(request, copy_type, course_id): user = request.user course = Course.objects.get(id=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) + if copy_type == "copy": + # Link all the modules from current course to copied course + course.create_duplicate_course(user) + else: + # Create new entries of modules, lessons/quizzes + # from current course to copied course + course.create_shallow_copy(user) else: - msg = 'You do not have permissions to clone this course, please contact your '\ - 'instructor/administrator.' + msg = dedent( + '''\ + You do not have permissions to clone {0} course, please contact + your instructor/administrator.'''.format(course.name) + ) return complete(request, msg, attempt_num=None, questionpaper_id=None) return my_redirect('/exam/manage/courses/') |