summaryrefslogtreecommitdiff
path: root/yaksh
diff options
context:
space:
mode:
authoradityacp2018-06-26 13:13:22 +0530
committeradityacp2018-06-26 13:13:22 +0530
commit20b692ea468a280e3edb4a9e7f97543b5499025d (patch)
treeaff5b76b2bb3f7b3507e9fe4841762310939273d /yaksh
parent4eb754c2e71922819de7390d1b4993a21763de3e (diff)
downloadonline_test-20b692ea468a280e3edb4a9e7f97543b5499025d.tar.gz
online_test-20b692ea468a280e3edb4a9e7f97543b5499025d.tar.bz2
online_test-20b692ea468a280e3edb4a9e7f97543b5499025d.zip
Changes in views, models, forms, urls, file_utils
- Add new view function to download course content - Add new attribute to Lesson model - Add new model methods to add course, module and lesson content - Add validation in forms to check for lesson video file format - Add functions in file_utils to add static files and templates to the zip file
Diffstat (limited to 'yaksh')
-rw-r--r--yaksh/file_utils.py26
-rw-r--r--yaksh/forms.py14
-rw-r--r--yaksh/models.py87
-rw-r--r--yaksh/urls.py6
-rw-r--r--yaksh/views.py37
5 files changed, 165 insertions, 5 deletions
diff --git a/yaksh/file_utils.py b/yaksh/file_utils.py
index 6c3fd5d..6b0340f 100644
--- a/yaksh/file_utils.py
+++ b/yaksh/file_utils.py
@@ -3,6 +3,7 @@ import os
import zipfile
import tempfile
import csv
+from django.template import Context, Template
def copy_files(file_paths):
@@ -66,3 +67,28 @@ def is_csv(document):
except (csv.Error, UnicodeDecodeError):
return False, None
return True, dialect
+
+
+def write_static_files_to_zip(zipfile, course_name, current_dir):
+ relative_folders = ["css", "js", "images"]
+ static_files = {"js": ["bootstrap.js", "bootstrap.min.js",
+ "jquery-1.9.1.min.js", "video.js"],
+ "css": ["bootstrap.css", "bootstrap.min.css",
+ "video-js.css"],
+ "images": ["yaksh_banner.png"]}
+ for folder in relative_folders:
+ folder_path = os.sep.join((current_dir, "static", "yaksh", folder))
+ for file in static_files[folder]:
+ file_path = os.sep.join((folder_path, file))
+ zipfile.write(file_path, os.sep.join((course_name, "static",
+ folder, file)))
+
+
+def write_templates_to_zip(zipfile, template_path, data, filename, filepath):
+ with open(template_path) as f:
+ template_data = f.read()
+ template = Template(template_data)
+ context = Context(data)
+ render = template.render(context)
+ zipfile.writestr(os.sep.join((filepath, "{0}.html".format(filename))),
+ render)
diff --git a/yaksh/forms.py b/yaksh/forms.py
index 41c9176..66076b1 100644
--- a/yaksh/forms.py
+++ b/yaksh/forms.py
@@ -324,7 +324,19 @@ class LessonForm(forms.ModelForm):
class Meta:
model = Lesson
- exclude = ['completed_lessons', 'creator', 'html_data']
+ exclude = ['creator', 'html_data']
+
+ def clean_video_file(self):
+ file = self.cleaned_data.get("video_file")
+ if file:
+ extension = file.name.split(".")[-1]
+ actual_extension = ["mp4", "ogv"]
+ if extension not in actual_extension:
+ raise forms.ValidationError(
+ "Please upload video files in {0} format".format(
+ ", ".join(actual_extension))
+ )
+ return file
class LessonFileForm(forms.Form):
diff --git a/yaksh/models.py b/yaksh/models.py
index 5d17dba..b97859d 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -26,7 +26,10 @@ import zipfile
import tempfile
from textwrap import dedent
from ast import literal_eval
-from .file_utils import extract_files, delete_files
+from .file_utils import (
+ extract_files, delete_files, write_templates_to_zip,
+ write_static_files_to_zip
+)
from yaksh.code_server import (
submit, get_result as get_result_from_code_server
)
@@ -124,7 +127,10 @@ def dict_to_yaml(dictionary):
def get_file_dir(instance, filename):
- upload_dir = instance.lesson.name.replace(" ", "_")
+ if isinstance(instance, LessonFile):
+ upload_dir = instance.lesson.name.replace(" ", "_")
+ else:
+ upload_dir = instance.name.replace(" ", "_")
return os.sep.join((upload_dir, filename))
@@ -159,6 +165,11 @@ class Lesson(models.Model):
# Activate/Deactivate Lesson
active = models.BooleanField(default=True)
+ # A video file
+ video_file = models.FileField(upload_to=get_file_dir, default=None,
+ null=True, blank=True
+ )
+
def __str__(self):
return "{0}".format(self.name)
@@ -182,6 +193,37 @@ class Lesson(models.Model):
lesson_file_obj.file.save(file_name, django_file, save=True)
return new_lesson
+ def remove_file(self):
+ if self.video_file:
+ file_path = self.video_file.path
+ if os.path.exists(file_path):
+ os.remove(file_path)
+
+ def _add_lesson_to_zip(self, module, course, zip_file, path):
+ lesson_name = self.name.replace(" ", "_")
+ course_name = course.name.replace(" ", "_")
+ module_name = module.name.replace(" ", "_")
+ sub_folder_name = os.sep.join((
+ course_name, module_name, lesson_name
+ ))
+ lesson_files = self.get_files()
+ if self.video_file:
+ video_file = os.sep.join((sub_folder_name, os.path.basename(
+ self.video_file.name)))
+ zip_file.write(self.video_file.path, video_file)
+ for lesson_file in lesson_files:
+ if os.path.exists(lesson_file.file.path):
+ filename = os.sep.join((sub_folder_name, os.path.basename(
+ lesson_file.file.name)))
+ zip_file.write(lesson_file.file.path, filename)
+ unit_file_path = os.sep.join((
+ path, "templates", "yaksh", "unit.html"
+ ))
+ lesson_data = {"course": course, "module": module,
+ "lesson": self, "lesson_files": lesson_files}
+ write_templates_to_zip(zip_file, unit_file_path, lesson_data,
+ lesson_name, sub_folder_name)
+
#############################################################################
class LessonFile(models.Model):
@@ -491,6 +533,10 @@ class LearningModule(models.Model):
return [unit.quiz for unit in self.learning_unit.filter(
type="quiz")]
+ def get_lesson_units(self):
+ return [unit.lesson for unit in self.learning_unit.filter(
+ type="lesson").order_by("order")]
+
def get_learning_units(self):
return self.learning_unit.order_by("order")
@@ -579,6 +625,20 @@ class LearningModule(models.Model):
new_module.learning_unit.add(new_unit)
return new_module
+ def _add_module_to_zip(self, course, zip_file, path):
+ module_name = self.name.replace(" ", "_")
+ course_name = course.name.replace(" ", "_")
+ folder_name = os.sep.join((course_name, module_name))
+ lessons = self.get_lesson_units()
+ for lesson in lessons:
+ lesson._add_lesson_to_zip(self, course, zip_file, path)
+ module_file_path = os.sep.join((
+ path, "templates", "yaksh", "module.html"
+ ))
+ module_data = {"course": course, "module": self, "lessons": lessons}
+ write_templates_to_zip(zip_file, module_file_path, module_data,
+ module_name, folder_name)
+
def __str__(self):
return self.name
@@ -819,6 +879,29 @@ class Course(models.Model):
percentage = 0
return percentage
+ def is_student(self, user):
+ return user in self.students.all()
+
+ def create_zip(self, zip_file, path):
+ course_name = self.name.replace(" ", "_")
+ modules = self.get_learning_modules()
+ file_path = os.sep.join((path, "templates", "yaksh", "index.html"))
+ write_static_files_to_zip(zip_file, course_name, path)
+ course_data = {"course": self, "modules": modules}
+ write_templates_to_zip(zip_file, file_path, course_data,
+ "index", course_name)
+ for module in modules:
+ module._add_module_to_zip(self, zip_file, path)
+
+ def has_lessons(self):
+ modules = self.get_learning_modules()
+ status = False
+ for module in modules:
+ if module.get_lesson_units():
+ status = True
+ break
+ return status
+
def __str__(self):
return self.name
diff --git a/yaksh/urls.py b/yaksh/urls.py
index 1e1def6..6e6b8c1 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -200,5 +200,9 @@ urlpatterns = [
url(r'^manage/preview_questionpaper/(?P<questionpaper_id>\d+)/$',
views.preview_questionpaper, name="preview_questionpaper"),
url(r'^manage/get_user_status/(?P<course_id>\d+)/(?P<student_id>\d+)/$',
- views.get_user_data, name="get_user_data")
+ views.get_user_data, name="get_user_data"),
+ url(r'^manage/courses/download_course/(?P<course_id>\d+)/$',
+ views.download_course, name="download_course"),
+ url(r'^download_course/(?P<course_id>\d+)/$',
+ views.download_course, name="download_course"),
]
diff --git a/yaksh/views.py b/yaksh/views.py
index 3341aca..c5c5be1 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -2297,9 +2297,16 @@ def edit_lesson(request, lesson_id=None, course_id=None):
context = {}
if request.method == "POST":
if "Save" in request.POST:
- lesson_form = LessonForm(request.POST, instance=lesson)
+ lesson_form = LessonForm(request.POST, request.FILES,
+ instance=lesson)
lesson_file_form = LessonFileForm(request.POST, request.FILES)
lessonfiles = request.FILES.getlist('Lesson_files')
+ clear = request.POST.get("video_file-clear")
+ video_file = request.FILES.get("video_file")
+ if (clear or video_file) and lesson:
+ # Remove previous video file if new file is uploaded or
+ # if clear is selected
+ lesson.remove_file()
if lesson_form.is_valid():
if lesson is None:
lesson_form.instance.creator = user
@@ -2314,6 +2321,7 @@ def edit_lesson(request, lesson_id=None, course_id=None):
return my_redirect(redirect_url)
else:
context['lesson_form'] = lesson_form
+ context['error'] = lesson_form["video_file"].errors
context['lesson_file_form'] = lesson_file_form
if 'Delete' in request.POST:
@@ -2808,3 +2816,30 @@ def get_user_data(request, course_id, student_id):
context = Context(data)
data = template.render(context)
return HttpResponse(json.dumps({"user_data": data}), **response_kwargs)
+
+
+@login_required
+@email_verified
+def download_course(request, course_id):
+ user = request.user
+ course = get_object_or_404(Course, pk=course_id)
+ if (not course.is_creator(user) and not course.is_teacher(user) and not
+ course.is_student(user)):
+ raise Http404("You are not allowed to download {0} course".format(
+ course.name))
+ if not course.has_lessons():
+ raise Http404("{0} course does not have any lessons".format(
+ course.name))
+ file_name = string_io()
+ current_dir = os.path.dirname(__file__)
+ course_name = course.name.replace(" ", "_")
+ zip_file = zipfile.ZipFile(file_name, "w")
+ course.create_zip(zip_file, current_dir)
+ zip_file.close()
+ file_name.seek(0)
+ response = HttpResponse(content_type='application/zip')
+ response['Content-Disposition'] = 'attachment; filename={0}.zip'.format(
+ course_name
+ )
+ response.write(file_name.read())
+ return response