diff options
-rw-r--r-- | yaksh/forms.py | 28 | ||||
-rw-r--r-- | yaksh/static/yaksh/js/lesson.js | 198 | ||||
-rw-r--r-- | yaksh/templates/yaksh/add_lesson.html | 6 | ||||
-rw-r--r-- | yaksh/templates/yaksh/add_topic.html | 2 | ||||
-rw-r--r-- | yaksh/templates/yaksh/add_video_quiz.html | 2 | ||||
-rw-r--r-- | yaksh/templates/yaksh/show_toc.html | 39 | ||||
-rw-r--r-- | yaksh/templatetags/custom_filters.py | 3 | ||||
-rw-r--r-- | yaksh/urls.py | 19 | ||||
-rw-r--r-- | yaksh/views.py | 115 |
9 files changed, 269 insertions, 143 deletions
diff --git a/yaksh/forms.py b/yaksh/forms.py index eedd809..ba8b7d5 100644 --- a/yaksh/forms.py +++ b/yaksh/forms.py @@ -652,6 +652,7 @@ class TopicForm(forms.ModelForm): timer = forms.CharField() def __init__(self, *args, **kwargs): + time = kwargs.pop("time") if "time" in kwargs else None super(TopicForm, self).__init__(*args, **kwargs) self.fields['name'].widget.attrs.update( {'class': form_input_class, 'placeholder': 'Topic Name'} @@ -659,11 +660,23 @@ class TopicForm(forms.ModelForm): self.fields['timer'].widget.attrs.update( {'class': form_input_class, 'placeholder': 'Topic Time'} ) + self.fields['timer'].initial = time class Meta: model = Topic fields = "__all__" + def clean_timer(self): + timer = self.cleaned_data.get("timer") + if timer: + try: + hh, mm, ss = timer.split(":") + except ValueError: + raise forms.ValidationError( + "Marker time should be in the format hh:mm:ss" + ) + return timer + class VideoQuizForm(forms.ModelForm): @@ -676,6 +689,7 @@ class VideoQuizForm(forms.ModelForm): question_type = kwargs.pop('question_type') else: question_type = "mcq" + time = kwargs.pop("time") if "time" in kwargs else None super(VideoQuizForm, self).__init__(*args, **kwargs) self.fields['summary'].widget.attrs.update( {'class': form_input_class, 'placeholder': 'Summary'} @@ -690,7 +704,7 @@ class VideoQuizForm(forms.ModelForm): {'class': form_input_class, 'placeholder': 'Points'} ) self.fields['type'].widget.attrs.update( - {'class': form_input_class, 'readonly': True} + {'class': form_input_class} ) self.fields['type'].initial = question_type self.fields['description'].widget.attrs.update( @@ -699,8 +713,20 @@ class VideoQuizForm(forms.ModelForm): self.fields['timer'].widget.attrs.update( {'class': form_input_class, 'placeholder': 'Quiz Time'} ) + self.fields['timer'].initial = time class Meta: model = Question fields = ['summary', 'description', 'points', 'language', 'type', 'topic'] + + def clean_timer(self): + timer = self.cleaned_data.get("timer") + if timer: + try: + hh, mm, ss = timer.split(":") + except ValueError: + raise forms.ValidationError( + "Marker time should be in the format hh:mm:ss" + ) + return timer
\ No newline at end of file diff --git a/yaksh/static/yaksh/js/lesson.js b/yaksh/static/yaksh/js/lesson.js index 38db7d2..92038c9 100644 --- a/yaksh/static/yaksh/js/lesson.js +++ b/yaksh/static/yaksh/js/lesson.js @@ -16,10 +16,6 @@ $(document).ready(function() { seconds = seconds < 10 ? "0" + seconds : seconds; timer.val(hours + ":" + minutes + ":" + seconds); }); - function csrfSafeMethod(method) { - // these HTTP methods do not require CSRF protection - return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); - } $("#vtimer").on("change keyup paste", function() { player.pause(); @@ -50,95 +46,7 @@ $(document).ready(function() { ajax_call($(this).attr("action"), $(this).attr("method"), $(this).serialize(), csrf); }); - function ajax_call(url, method, data, csrf) { - $.ajax({ - url: url, - timeout: 15000, - method: method, - data: data, - beforeSend: function(xhr, settings) { - if (!csrfSafeMethod(settings.type) && !this.crossDomain) { - xhr.setRequestHeader("X-CSRFToken", csrf); - } - }, - success: function(msg) { - unlock_screen(); - if (msg.success) { - if (msg.status) $("#lesson-content").html(msg.data); - if (msg.content_type === '1') { - add_topic(); - } - else { - add_question(); - } - } - if (msg.toc) show_toc(msg.toc); - if (msg.message) alert(msg.message) - }, - error: function(xhr, data) { - switch(xhr.status) { - case 400: { - unlock_screen(); - console.log(data.responseJSON); - break; - } - case 500: { - unlock_screen(); - alert('500 status code! server error'); - break; - } - case 404: { - unlock_screen(); - alert('404 status code! server error'); - break; - } - } - } - }); - } - - function add_topic() { - if (!$("#id_timer").val()) { - $("#id_timer").val($("#vtimer").val()); - } - $("#topic-form").submit(function(e) { - e.preventDefault(); - lock_screen(); - var csrf = document.getElementById("topic-form").elements[0].value; - ajax_call($(this).attr("action"), $(this).attr("method"), $(this).serialize(), csrf); - }); - } - - function add_question() { - if (!$("#id_timer").val()) { - $("#id_timer").val($("#vtimer").val()); - } - $("#question-form").submit(function(e) { - e.preventDefault(); - lock_screen(); - var csrf = document.getElementById("question-form").elements[0].value; - ajax_call($(this).attr("action"), $(this).attr("method"), $(this).serialize(), csrf); - }); - } - - function lock_screen() { - document.getElementById("ontop").style.display = "block"; - } - - function unlock_screen() { - document.getElementById("ontop").style.display = "none"; - } - - function show_error() { - - } - - function show_toc(toc) { - $("#lesson-content").empty(); - $("#toc").html(toc); - } - - $('#id_video_file').on('change',function(){ + $('#id_video_file').on('change',function() { //get the file name var files = []; for (var i = 0; i < $(this)[0].files.length; i++) { @@ -147,7 +55,7 @@ $(document).ready(function() { $(this).next('.custom-file-label').html(files.join(', ')); }); - $('#id_Lesson_files').on('change',function(){ + $('#id_Lesson_files').on('change',function() { //get the file name var files = []; for (var i = 0; i < $(this)[0].files.length; i++) { @@ -156,3 +64,105 @@ $(document).ready(function() { $(this).next('.custom-file-label').html(files.join(', ')); }); }); + + +function add_topic() { + if (!$("#id_timer").val()) { + $("#id_timer").val($("#vtimer").val()); + } + $("#topic-form").submit(function(e) { + e.preventDefault(); + lock_screen(); + var csrf = document.getElementById("topic-form").elements[0].value; + ajax_call($(this).attr("action"), $(this).attr("method"), $(this).serialize(), csrf); + }); +} + +function add_question() { + if (!$("#id_timer").val()) { + $("#id_timer").val($("#vtimer").val()); + } + $("#question-form").submit(function(e) { + e.preventDefault(); + lock_screen(); + var csrf = document.getElementById("question-form").elements[0].value; + ajax_call($(this).attr("action"), $(this).attr("method"), $(this).serialize(), csrf); + }); +} + +function lock_screen() { + document.getElementById("ontop").style.display = "block"; +} + +function unlock_screen() { + document.getElementById("ontop").style.display = "none"; +} + +function show_error(error) { + var err_msg = "\n"; + Object.keys(err).forEach(function(key) { + var value = err[key]; + err_msg = err_msg + key + " : " + value[0].message + "\n"; + }); + alert(err_msg); +} + +function show_toc(toc) { + $("#lesson-content").empty(); + $("#toc").html(toc); +} + +function csrfSafeMethod(method) { + // these HTTP methods do not require CSRF protection + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); +} + +function ajax_call(url, method, data, csrf) { + $.ajax({ + url: url, + timeout: 15000, + method: method, + data: data, + beforeSend: function(xhr, settings) { + if (!csrfSafeMethod(settings.type) && !this.crossDomain) { + xhr.setRequestHeader("X-CSRFToken", csrf); + } + }, + success: function(msg) { + unlock_screen(); + if (msg.success) { + if (msg.status) $("#lesson-content").html(msg.data); + if (parseInt(msg.content_type) === 1) { + add_topic(); + } + else { + add_question(); + } + } + if (msg.toc) show_toc(msg.toc); + if (msg.message) alert(msg.message) + }, + error: function(xhr, data) { + unlock_screen(); + switch(xhr.status) { + case 400: { + err = JSON.parse(xhr.responseJSON.message); + show_error(err); + break; + } + case 500: { + alert('500 status code! server error'); + break; + } + case 404: { + alert('404 status code! server error'); + break; + } + default: { + alert('Unable to perform action. Please try again'); + break; + } + } + } + }); +} diff --git a/yaksh/templates/yaksh/add_lesson.html b/yaksh/templates/yaksh/add_lesson.html index 62aa881..6018e54 100644 --- a/yaksh/templates/yaksh/add_lesson.html +++ b/yaksh/templates/yaksh/add_lesson.html @@ -158,7 +158,13 @@ <div class="card-body"> {% if lesson_form.instance and lesson_form.instance.video_path %} {% with lesson_form.instance.video_path|video_name as video %} + {% if video.1 == "others" %} + <video id="player" playsinline controls> + <source src="{{video.0}}" /> + </video> + {% else %} <div id="player" data-plyr-provider="{{video.1}}" data-plyr-embed-id="{{video.0}}"></div> + {% endif %} <br> <form action="{% url 'yaksh:add_marker' course_id lesson_form.instance.id %}" method="POST" id="marker-form" name="marker-form"> {% csrf_token %} diff --git a/yaksh/templates/yaksh/add_topic.html b/yaksh/templates/yaksh/add_topic.html index 8c8fdb3..52923e7 100644 --- a/yaksh/templates/yaksh/add_topic.html +++ b/yaksh/templates/yaksh/add_topic.html @@ -1,5 +1,5 @@ {% if topic_id %} -<form class="form-group" method="POST" action="{% url 'yaksh:edit_topic' content_type course_id lesson_id topic_id %}" id="topic-form"> +<form class="form-group" method="POST" action="{% url 'yaksh:edit_topic' content_type course_id lesson_id toc_id topic_id %}" id="topic-form"> {% else %} <form class="form-group" method="POST" action="{% url 'yaksh:add_topic' content_type course_id lesson_id %}" id="topic-form"> {% endif %} diff --git a/yaksh/templates/yaksh/add_video_quiz.html b/yaksh/templates/yaksh/add_video_quiz.html index cf59fcb..ad087bc 100644 --- a/yaksh/templates/yaksh/add_video_quiz.html +++ b/yaksh/templates/yaksh/add_video_quiz.html @@ -3,7 +3,7 @@ <script type="text/javascript" src="{% static 'yaksh/js/mathjax/MathJax.js' %}?config=TeX-MML-AM_CHTML"> </script> {% if question_id %} -<form class="form-group" method="POST" action="{% url 'yaksh:edit_marker_quiz' content_type course_id lesson_id question_id %}" id="question-form"> +<form class="form-group" method="POST" action="{% url 'yaksh:edit_marker_quiz' content_type course_id lesson_id toc_id question_id %}" id="question-form"> {% else %} <form class="form-group" method="POST" action="{% url 'yaksh:add_marker_quiz' content_type course_id lesson_id %}" id="question-form"> {% endif %} diff --git a/yaksh/templates/yaksh/show_toc.html b/yaksh/templates/yaksh/show_toc.html index 85bcf40..b263652 100644 --- a/yaksh/templates/yaksh/show_toc.html +++ b/yaksh/templates/yaksh/show_toc.html @@ -1,8 +1,9 @@ <table class="table table-responsive-sm"> {% for toc in contents %} + {% with toc.get_toc_text as toc_name %} <tr> <td> - {{ toc.get_toc_text }} + {{ toc_name }} </td> <td> {{toc.get_content_display}} @@ -12,18 +13,42 @@ </td> <td> {% if toc.content == 1 %} - <a href="#" class="btn btn-outline-primary" data-url="{% url 'yaksh:edit_topic' toc.content toc.course_id toc.lesson_id toc.object_id %}"> + <a href="#" class="btn btn-outline-info" data-url="{% url 'yaksh:edit_topic' toc.content toc.course_id toc.lesson_id toc.id toc.object_id %}" id="edit-toc" onclick="edit_toc(this)"> {% else %} - <a href="#" class="btn btn-outline-primary" data-url="{% url 'yaksh:edit_marker_quiz' toc.content toc.course_id toc.lesson_id toc.object_id %}"> + <a href="#" class="btn btn-outline-info" data-url="{% url 'yaksh:edit_marker_quiz' toc.content toc.course_id toc.lesson_id toc.id toc.object_id %}" id="edit-toc" onclick="edit_toc(this)"> {% endif %} - <i class="fa fa-edit"> Edit</i> + <i class="fa fa-edit"></i> Edit </a> </td> <td> - <a href="#" class="btn btn-outline-danger"> - <i class="fa fa-trash"> Delete</i> - </a> + <form action="{% url 'yaksh:delete_toc' toc.course_id toc.id %}" method="POST"> + {% csrf_token %} + <input type="hidden" name="redirect_url" id="redirect_url" class="hidden"> + <button class="btn btn-outline-danger" type="submit" onclick="return confirm('Are you sure you want to remove {{toc_name|capfirst}}?')"> + <i class="fa fa-trash"></i> Remove + </button> + </form> </td> </tr> + {% endwith %} +{% empty %} + <center> + <span class="badge badge-warning">No Table of contents added</span> + </center> {% endfor %} +<script type="text/javascript"> + $(document).ready(function() { + var divs = document.getElementsByClassName("hidden"); + for(var i = 0; i < divs.length; i++) + { + divs[i].value = window.location.pathname; + } + }); + + function edit_toc(obj) { + var url = obj.getAttribute("data-url"); + lock_screen(); + ajax_call(url, "GET"); + } +</script> </table>
\ No newline at end of file diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py index 712df09..81c7b71 100644 --- a/yaksh/templatetags/custom_filters.py +++ b/yaksh/templatetags/custom_filters.py @@ -133,6 +133,5 @@ def video_name(text): elif 'vimeo' in video.keys(): name, vformat = video.get('vimeo'), 'vimeo' else: - filename = os.path.basename(video.get("others")) - name, vformat = os.path.split(filename) + name, vformat = video.get('others'), 'others' return name, vformat diff --git a/yaksh/urls.py b/yaksh/urls.py index 2c462b3..d7fd410 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -241,18 +241,13 @@ urlpatterns = [ name='add_marker'), path('manage/add/lesson/topic/<int:content_type>/<int:course_id>/<int:lesson_id>', views.add_topic, name='add_topic'), - path('manage/edit/lesson/topic/<int:content_type>/<int:course_id>/<int:lesson_id>/<int:topic_id>', - views.add_topic, name='edit_topic'), + path('manage/edit/lesson/topic/<int:content_type>/<int:course_id>/<int:lesson_id>/' + '<int:toc_id>/<int:topic_id>', views.add_topic, name='edit_topic'), path('manage/add/lesson/quiz/<int:content_type>/<int:course_id>/<int:lesson_id>', views.add_marker_quiz, name='add_marker_quiz'), - path('manage/edit/lesson/quiz/<int:content_type>/<int:course_id>/<int:lesson_id>/<int:question_id>', - views.add_marker_quiz, name='edit_marker_quiz'), - path('manage/add/lesson/exercise/<int:content_type>/<int:course_id>/<int:lesson_id>', - views.add_marker_quiz, name='add_marker_quiz'), - path('manage/edit/lesson/exercise/<int:content_type>/<int:course_id>/<int:lesson_id>/<int:question_id>', - views.add_marker_quiz, name='edit_marker_quiz'), - path('manage/add/lesson/poll/<int:content_type>/<int:course_id>/<int:lesson_id>', - views.add_marker_quiz, name='add_marker_quiz'), - path('manage/edit/lesson/poll/<int:content_type>/<int:course_id>/<int:lesson_id>/<int:question_id>', - views.add_marker_quiz, name='edit_marker_quiz') + path('manage/edit/lesson/quiz/<int:content_type>/<int:course_id>/<int:lesson_id>/' + '<int:toc_id>/<int:question_id>', views.add_marker_quiz, + name='edit_marker_quiz'), + path('manage/remove/lesson/toc/<int:course_id>/<int:toc_id>', + views.delete_toc, name='delete_toc'), ] diff --git a/yaksh/views.py b/yaksh/views.py index db46d90..60d72e9 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -2686,6 +2686,9 @@ def show_lesson(request, lesson_id, module_id, course_id): # update course status with current unit _update_unit_status(course_id, user, learn_unit) + toc = TableOfContents.objects.filter( + course_id=course_id, lesson_id=lesson_id + ) all_modules = course.get_learning_modules() if learn_unit.has_prerequisite(): @@ -2695,7 +2698,7 @@ def show_lesson(request, lesson_id, module_id, course_id): context = {'lesson': learn_unit.lesson, 'user': user, 'course': course, 'state': "lesson", "all_modules": all_modules, 'learning_units': learning_units, "current_unit": learn_unit, - 'learning_module': learn_module} + 'learning_module': learn_module, 'toc': toc} return my_render_to_response(request, 'yaksh/show_video.html', context) @@ -3478,14 +3481,26 @@ def get_tc_formset(question_type, post=None, question=None): Question, tc, form=TestcaseForm, extra=1, fields="__all__", ) formset = TestcaseFormset( - post, initial=[{'type': tc_class}], instance=question + post, initial=[{'type': tc_class}], instance=question ) return formset, tc_class +def get_toc_contents(request, course_id, lesson_id): + contents = TableOfContents.objects.filter( + course_id=course_id, lesson_id=lesson_id + ) + data = loader.render_to_string( + "yaksh/show_toc.html", context={'contents': contents}, + request=request + ) + return data + + @login_required @email_verified -def add_topic(request, content_type, course_id, lesson_id, topic_id=None): +def add_topic(request, content_type, course_id, lesson_id, toc_id=None, + topic_id=None): user = request.user course = get_object_or_404(Course, pk=course_id) if (not is_moderator(user) or @@ -3495,25 +3510,26 @@ def add_topic(request, content_type, course_id, lesson_id, topic_id=None): topic = get_object_or_404(Topic, pk=topic_id) else: topic = None + if toc_id: + toc = get_object_or_404(TableOfContents, pk=toc_id) + else: + toc = None context = {} if request.method == "POST": form = TopicForm(request.POST, instance=topic) if form.is_valid(): form.save() + time = request.POST.get("timer") if not topic: TableOfContents.objects.create( content_object=form.instance, course_id=course_id, lesson_id=lesson_id, content=content_type, - time=request.POST.get("timer") - ) - contents = TableOfContents.objects.filter( - course_id=course_id, lesson_id=lesson_id - ) - data = loader.render_to_string( - "yaksh/show_toc.html", context={'contents': contents}, - request=request + time=time ) - context['toc'] = data + context['toc'] = get_toc_contents(request, course_id, lesson_id) + if toc: + toc.time = time + toc.save() status_code = 200 context['success'] = True context['message'] = 'Added topic successfully' @@ -3521,13 +3537,26 @@ def add_topic(request, content_type, course_id, lesson_id, topic_id=None): status_code = 400 context['success'] = False context['message'] = form.errors.as_json() + else: + form = TopicForm(instance=topic, time=toc.time) + template_context = {'form': form, 'course_id': course.id, + 'lesson_id': lesson_id, 'content_type': content_type, + 'topic_id': topic_id, 'toc_id': toc_id} + data = loader.render_to_string( + "yaksh/add_topic.html", context=template_context, request=request + ) + context['success'] = True + context['data'] = data + context['content_type'] = content_type + context['status'] = 1 + status_code = 200 return JsonResponse(context, status=status_code) @login_required @email_verified def add_marker_quiz(request, content_type, course_id, lesson_id, - question_id=None): + toc_id=None, question_id=None): user = request.user course = get_object_or_404(Course, pk=course_id) if (not is_moderator(user) or @@ -3537,32 +3566,34 @@ def add_marker_quiz(request, content_type, course_id, lesson_id, question = get_object_or_404(Question, pk=question_id) else: question = None + if toc_id: + toc = get_object_or_404(TableOfContents, pk=toc_id) + else: + toc = None context = {} if request.method == "POST": qform = VideoQuizForm(request.POST, instance=question) if qform.is_valid(): - qform.save(commit=False) - qform.instance.user = user + if not question_id: + qform.save(commit=False) + qform.instance.user = user qform.save() formset, tc_class = get_tc_formset( qform.instance.type, request.POST, qform.instance ) if formset.is_valid(): formset.save() + time = request.POST.get("timer") if not question: TableOfContents.objects.create( content_object=qform.instance, course_id=course_id, lesson_id=lesson_id, content=content_type, - time=request.POST.get("timer") + time=time ) - contents = TableOfContents.objects.filter( - course_id=course_id, lesson_id=lesson_id - ) - data = loader.render_to_string( - "yaksh/show_toc.html", context={'contents': contents}, - request=request - ) - context['toc'] = data + context['toc'] = get_toc_contents(request, course_id, lesson_id) + if toc: + toc.time = time + toc.save() status_code = 200 context['success'] = True context['message'] = 'Added question successfully' @@ -3570,9 +3601,43 @@ def add_marker_quiz(request, content_type, course_id, lesson_id, else: status_code = 400 context['success'] = False - context['message'] = formset.errors.as_json() + context['message'] = "Error in saving form" else: status_code = 400 context['success'] = False context['message'] = qform.errors.as_json() + else: + form = VideoQuizForm(instance=question, time=toc.time) + formset, tc_class = get_tc_formset(question.type, question=question) + template_context = { + 'form': form, 'course_id': course.id, 'lesson_id': lesson_id, + 'formset': formset, 'tc_class': tc_class, 'toc_id': toc_id, + 'content_type': content_type, 'question_id': question_id + } + data = loader.render_to_string( + "yaksh/add_video_quiz.html", context=template_context, + request=request + ) + context['success'] = True + context['data'] = data + context['content_type'] = content_type + context['status'] = 2 + status_code = 200 return JsonResponse(context, status=status_code) + + +@login_required +@email_verified +def delete_toc(request, course_id, toc_id): + user = request.user + course = get_object_or_404(Course, pk=course_id) + if (not is_moderator(user) or + not course.is_creator(user) or not course.is_creator(user)): + raise Http404("You are not allowed to view this page") + toc = get_object_or_404(TableOfContents, pk=toc_id) + redirect_url = request.POST.get("redirect_url") + if toc.content == 1: + get_object_or_404(Topic, pk=toc.object_id).delete() + else: + get_object_or_404(Question, id=toc.object_id).delete() + return redirect(redirect_url) |