summaryrefslogtreecommitdiff
path: root/yaksh
diff options
context:
space:
mode:
Diffstat (limited to 'yaksh')
-rw-r--r--yaksh/models.py64
-rw-r--r--yaksh/templates/yaksh/complete.html3
-rw-r--r--yaksh/templates/yaksh/courses.html21
-rw-r--r--yaksh/test_views.py104
-rw-r--r--yaksh/urls.py4
-rw-r--r--yaksh/views.py40
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/')