diff options
-rw-r--r-- | yaksh/forms.py | 2 | ||||
-rw-r--r-- | yaksh/models.py | 13 | ||||
-rw-r--r-- | yaksh/static/yaksh/js/show_toc.js | 123 | ||||
-rw-r--r-- | yaksh/templates/base.html | 5 | ||||
-rw-r--r-- | yaksh/templates/yaksh/show_lesson_quiz.html | 131 | ||||
-rw-r--r-- | yaksh/templates/yaksh/show_video.html | 88 | ||||
-rw-r--r-- | yaksh/urls.py | 4 | ||||
-rw-r--r-- | yaksh/views.py | 51 |
8 files changed, 359 insertions, 58 deletions
diff --git a/yaksh/forms.py b/yaksh/forms.py index ba8b7d5..440a535 100644 --- a/yaksh/forms.py +++ b/yaksh/forms.py @@ -704,7 +704,7 @@ class VideoQuizForm(forms.ModelForm): {'class': form_input_class, 'placeholder': 'Points'} ) self.fields['type'].widget.attrs.update( - {'class': form_input_class} + {'class': form_input_class, 'readonly': True} ) self.fields['type'].initial = question_type self.fields['description'].widget.attrs.update( diff --git a/yaksh/models.py b/yaksh/models.py index 9ba4afd..851e5c6 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -2762,9 +2762,9 @@ class TableOfContents(models.Model): def get_toc_text(self): if self.content == 1: - content_name = Topic.objects.get(id=self.object_id).name + content_name = self.content_object.name else: - content_name = Question.objects.get(id=self.object_id).summary + content_name = self.content_object.summary return content_name def __str__(self): @@ -2779,6 +2779,15 @@ class Topic(models.Model): return f"{self.name}" +class VideoQuizAnswer(models.Model): + toc = models.ForeignKey(TableOfContents, on_delete=models.CASCADE) + student = models.ForeignKey(User, on_delete=models.CASCADE) + answer = models.ForeignKey(Answer, on_delete=models.CASCADE) + + def __str__(self): + return f"Lesson answer of {self.toc} by {self.user.get_full_name()}" + + class MicroManager(models.Model): manager = models.ForeignKey(User, on_delete=models.CASCADE, related_name='micromanaging', null=True) diff --git a/yaksh/static/yaksh/js/show_toc.js b/yaksh/static/yaksh/js/show_toc.js new file mode 100644 index 0000000..121d9e3 --- /dev/null +++ b/yaksh/static/yaksh/js/show_toc.js @@ -0,0 +1,123 @@ +$(document).ready(function() { + $('#sidebarCollapse').on('click', function () { + $('#sidebar').toggleClass('active'); + }); + player = new Plyr('#player'); + var totalSeconds; + store_video_time(contents_by_time); + var time_arr_length = video_time.length; + player.on('timeupdate', event => { + if (time_arr_length > 0 && player.currentTime >= video_time[loc]) { + var content = contents_by_time[loc]; + loc += 1; + if(content.content != 1) { + player.pause(); + url = $("#toc_"+content.id).val(); + ajax_call(url, "GET"); + } + } + }); +}); + +function store_video_time(contents) { + for (var j = 0; j < contents.length; j++) + video_time.push(get_time_in_seconds(contents[j].time)); +} + +function get_time_in_seconds(time) { + var time = time.split(":"); + var hh = parseInt(time[0]); + var mm = parseInt(time[1]); + var ss = parseInt(time[2]); + return hh * 3600 + mm * 60 + ss; +} + +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 = ""; + Object.keys(err).forEach(function(key) { + var value = err[key]; + err_msg = err_msg + key + " : " + value[0].message + "\n"; + }); + alert(err_msg); +} + +function show_question(data) { + $("#dialog").html(data); + $("#dialog").dialog({ + width: 600, + height: 500, + }); + $("#submit-quiz-form").submit(function(e) { + e.preventDefault(); + lock_screen(); + var csrf = document.getElementById("submit-quiz-form").elements[0].value; + ajax_call($(this).attr("action"), $(this).attr("method"), $(this).serialize(), csrf); + }); +} + +function select_toc(element) { + var toc_id = element.getAttribute("data-toc"); + var toc_time = $("#toc_time_"+toc_id).val(); + player.currentTime = get_time_in_seconds(toc_time); + url = $("#toc_"+toc_id).val(); + ajax_call(url, "GET"); +} + +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) { + lock_screen(); + $.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.data) { + show_question(msg.data); + } else { + $("#dialog").dialog("close"); + } + 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; + } + } + } + }); +}
\ No newline at end of file diff --git a/yaksh/templates/base.html b/yaksh/templates/base.html index 7ce653d..53edbee 100644 --- a/yaksh/templates/base.html +++ b/yaksh/templates/base.html @@ -55,9 +55,8 @@ <div id="ontop"> <div id="state"> Checking...<img src="{% static 'yaksh/images/check_answer.gif' %}"/> - </div></div> - - + </div> + </div> {% block nav %} {% endblock %} diff --git a/yaksh/templates/yaksh/show_lesson_quiz.html b/yaksh/templates/yaksh/show_lesson_quiz.html new file mode 100644 index 0000000..2d5184e --- /dev/null +++ b/yaksh/templates/yaksh/show_lesson_quiz.html @@ -0,0 +1,131 @@ +{% load custom_filters %} +<script type="text/javascript"> + function user_arranged_options() { + var temp_array = [] + var add_array = document.getElementById("arrange_order"); + var ans_array = order_array.children().get() + var answer_is = $.each(ans_array, function(index, value) { + temp_array.push(value.id); + }); + add_array.value = temp_array + } +</script> +<form id="submit-quiz-form" method="POST" action="{% url 'yaksh:submit_marker_quiz' course_id toc.id %}"> + {% csrf_token %} + <div class="card"> + <div class="card-header"> + <div> + <div> + <h2>{{ question.summary }}</h2> + </div> + <div> + {% if question.language == "other" %} + <small class="text text-muted"><strong>Topic:</strong> <span class="badge badge-primary">{{question.topic}}</small></span> + {% else %} + <small class="textx text-muted"><strong>Language:</strong> <span class="badge badge-primary">{{question.language}}</span></small> + {% endif %} + {% if question.type == "mcq" %} + <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">SINGLE CORRECT CHOICE</span></small> + {% elif question.type == "mcc" %} + <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">MULTIPLE CORRECT CHOICES</span></small> + {% elif question.type == "integer" %} + <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">FILL IN THE BLANKS WITH INTEGER ANSWER</span></small> + {% elif question.type == "string" %} + <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">FILL IN THE BLANKS WITH STRING ANSWER</span></small> + {% if testcase.string_check == "lower" %} + <br>(CASE INSENSITIVE) + {% else %} + <br>(CASE SENSITIVE) + {% endif %} + {% elif question.type == "float" %} + <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">FILL IN THE BLANKS WITH FLOAT ANSWER</span></small> + {% elif question.type == "arrange" %} + <small class="text text-muted"><strong>Type:</strong> <span class="badge badge-primary">ARRANGE THE OPTIONS IN CORRECT ORDER</span></small> + {% endif %} + <span class="badge badge-info pull-right"> + <small><strong>Marks: {{ question.points }}</strong></small> + </span> + </div> + </div> + </div> + <div class="card-body"> + <div> + {{ question.description|safe }} + </div> + <br> + {% if question.type == "mcq" %} + <!-- Single correct choice type Question --> + {% for test_case in test_cases %} + {% if last_attempt and last_attempt|to_int == test_case.id %} + <input name="answer" type="radio" value="{{ test_case.id }}" checked /> + {{ test_case.options|safe }} <br/> + {% else %} + <input name="answer" type="radio" value="{{ test_case.id }}" /> + {{ test_case.options|safe }} <br/> + {% endif %} + {% endfor %} + {% endif %} + + {% if question.type == "integer" %} + <!-- Integer type Question --> + Enter Integer:<br/> + <input autofocus class="form-control" name="answer" type="number" id="integer" value="{{ last_attempt|to_integer }}" /> + <br><br> + {% endif %} + + {% if question.type == "string" %} + <!-- String type question --> + Enter Text:<br/> + <textarea autofocus name="answer" id="string" class="form-control" style="width: 100%">{{ last_attempt|to_str }}</textarea> + <br/><br/> + {% endif %} + + {% if question.type == "float" %} + <!-- Float type question --> + Enter Decimal Value :<br/> + <input autofocus class="form-control" name="answer" type="number" step="any" id="float" value="{{ last_attempt|to_float }}" /> + <br/><br/> + {% endif %} + + {% if question.type == "mcc" %} + <!-- MCC type question --> + {% for test_case in test_cases %} + {% if last_attempt and test_case.id|safe in last_attempt|safe %} + <input name="answer" type="checkbox" value="{{ test_case.id }}" checked/> + {{ test_case.options| safe }} + <br> + {% else %} + <input name="answer" type="checkbox" value="{{ test_case.id }}"> + {{ test_case.options| safe }} + <br> + {% endif %} + {% endfor %} + {% endif %} + + {% if question.type == "arrange" %} + <!-- Arrange options type question --> + {% if last_attempt %} + {% get_answer_for_arrange_options last_attempt question as test_cases %} + {% endif %} + <input name="answer" type="hidden" id='arrange_order'/> + <div class="list-group"> + <ol class="arrange"> + {% for test_case in test_cases %} + <li class="list-group-item" id={{test_case.id}}> + {{test_case.options| safe }}</li> {% endfor %} + </ol> + </div> + <script type="text/javascript"> + var arrange = $("ol.arrange"); + var order_array = $(arrange).sortable(['serialize']); + </script> + {% endif %} + {% if question.type == "mcq" or question.type == "mcc" or question.type == "integer" or question.type == "float" or question.type == "string" %} + <br><button class="btn btn-success" type="submit" name="check" id="check">Submit + </button> + {% elif question.type == "arrange" %} + <br><button class="btn btn-success" type="submit" name="check" id="check" onClick="return user_arranged_options();">Submit</button> + {% endif %} + </div> + </div> +</form>
\ No newline at end of file diff --git a/yaksh/templates/yaksh/show_video.html b/yaksh/templates/yaksh/show_video.html index 4c54518..8f8bbb2 100644 --- a/yaksh/templates/yaksh/show_video.html +++ b/yaksh/templates/yaksh/show_video.html @@ -4,11 +4,22 @@ {% block title %} {{ learning_module.name }} {% endblock %} {% block script %} -<script type="text/javascript" src="{% static 'yaksh/js/lesson.js' %}"> +<script type="text/javascript"> + var player; + var contents_by_time = JSON.parse('{{ contents_by_time|safe }}'); + var loc = 0; + var video_time = []; +</script> +<script type="text/javascript" src="{% static 'yaksh/js/jquery-ui.js' %}"></script> +<script type="text/javascript" src="{% static 'yaksh/js/show_toc.js' %}"> </script> +<script src="{% static 'yaksh/js/jquery-sortable.js' %}"></script> <script type="text/javascript" src="{% static 'yaksh/js/mathjax/MathJax.js' %}?config=TeX-MML-AM_CHTML"> </script> {% endblock %} +{% block css %} +<link rel="stylesheet" href="{% static 'yaksh/css/jquery-ui/jquery-ui.css' %}"> +{% endblock %} {% block main %} <div class="wrapper"> <!-- Sidebar --> @@ -143,10 +154,6 @@ <div id="player" data-plyr-provider="{{video.1}}" data-plyr-embed-id="{{video.0}}"></div> {% endif %} {% endwith %} - <br> - <a href="{% url 'yaksh:next_unit' course.id learning_module.id current_unit.id %}" class="btn btn-info btn-lg" > - Next <i class="fa fa-step-forward"></i> - </a> {% endif %} </div> </div> @@ -164,14 +171,17 @@ {% with content.get_toc_text as toc_name %} <tr> <td> - {{ toc_name }} + <a href="#" onclick="select_toc(this);" data-toc="{{content.id}}" data-content-type="{{content.content}}">{{ toc_name }}</a> </td> <td> {{content.get_content_display}} </td> - <td> + <td id="toc_time_{{content.id}}"> {{content.time}} </td> + <td> + <input type="hidden" id="toc_{{content.id}}" value="{% url 'yaksh:get_marker_quiz' course.id content.id %}" data-content="{{content.content}}"/> + </td> </tr> {% endwith %} {% empty %} @@ -185,48 +195,40 @@ </div> </div> <div class="col-md-8"> - <hr> - <h3><strong>Lesson Description</strong></h3> - <hr> - <div class="col" style="width: 100%"> - {{lesson.html_data|safe}} - </div> - <br> - {% if not lesson.video_path %} - <a href="{% url 'yaksh:next_unit' course.id learning_module.id current_unit.id %}" class="btn btn-info btn-lg" > - Next <i class="fa fa-step-forward"></i> - </a> - <br> - {% endif %} - <div class="col-md-7"> - {% with lesson.get_files as lesson_files %} - {% if lesson_files %} - <div class="card"> - <div class="card-header"> - Files for this lesson - </div> - <div class="card-body"> - {% for f in lesson_files %} - <a href="{{f.file.url}}" class="list-group-item"> - {{forloop.counter}}.{{ f.file.name|file_title }} - </a> - {% endfor %} + <div class="card-body"> + <a href="{% url 'yaksh:next_unit' course.id learning_module.id current_unit.id %}" class="btn btn-info btn-lg" > + Next <i class="fa fa-step-forward"></i> + </a> + <hr> + <h3><strong>Lesson Description</strong></h3> + <hr> + <div class="col" style="width: 100%"> + {{lesson.html_data|safe}} + </div> + <div class="col-md-7"> + {% with lesson.get_files as lesson_files %} + {% if lesson_files %} + <div class="card"> + <div class="card-header"> + Files for this lesson + </div> + <div class="card-body"> + {% for f in lesson_files %} + <a href="{{f.file.url}}" class="list-group-item"> + {{forloop.counter}}.{{ f.file.name|file_title }} + </a> + {% endfor %} + </div> </div> - </div> - {% endif %} - {% endwith %} + {% endif %} + {% endwith %} + </div> </div> </div> </div> {% endif %} </div> </div> -<script type="text/javascript"> - $(document).ready(function () { - $('#sidebarCollapse').on('click', function () { - $('#sidebar').toggleClass('active'); - }); - }); -</script> +<div id="dialog"></div> {% endblock %} diff --git a/yaksh/urls.py b/yaksh/urls.py index e4f81e1..2b9a04f 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -259,4 +259,8 @@ urlpatterns = [ name='edit_marker_quiz'), path('manage/remove/lesson/toc/<int:course_id>/<int:toc_id>', views.delete_toc, name='delete_toc'), + path('get/marker/quiz/<int:course_id>/<int:toc_id>', views.get_marker_quiz, + name='get_marker_quiz'), + path('submit/marker/quiz/<int:course_id>/<int:toc_id>', + views.submit_marker_quiz, name='submit_marker_quiz'), ] diff --git a/yaksh/views.py b/yaksh/views.py index 652e08c..6f8aa2f 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -38,11 +38,7 @@ from yaksh.models import ( StdIOBasedTestCase, StringTestCase, TestCase, User, get_model_class, FIXTURES_DIR_PATH, MOD_GROUP_NAME, Lesson, LessonFile, LearningUnit, LearningModule, CourseStatus, question_types, Post, Comment, -<<<<<<< HEAD - Topic, TableOfContents -======= - MicroManager ->>>>>>> 23bf46ac2e262ceb373388196962a0bec048c6cb + Topic, TableOfContents, MicroManager ) from yaksh.forms import ( UserRegisterForm, UserLoginForm, QuizForm, QuestionForm, @@ -2759,6 +2755,9 @@ def show_lesson(request, lesson_id, module_id, course_id): _update_unit_status(course_id, user, learn_unit) toc = TableOfContents.objects.filter( course_id=course_id, lesson_id=lesson_id + ).order_by("time") + contents_by_time = json.dumps( + list(toc.values("id", "content", "time")) ) all_modules = course.get_learning_modules() if learn_unit.has_prerequisite(): @@ -2768,7 +2767,8 @@ 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, 'toc': toc} + 'learning_module': learn_module, 'toc': toc, + 'contents_by_time': contents_by_time} return my_render_to_response(request, 'yaksh/show_video.html', context) @@ -3504,7 +3504,6 @@ def hide_comment(request, course_id, uuid): @login_required @email_verified -<<<<<<< HEAD def add_marker(request, course_id, lesson_id): user = request.user course = get_object_or_404(Course, pk=course_id) @@ -3645,7 +3644,7 @@ def add_topic(request, content_type, course_id, lesson_id, toc_id=None, toc.save() status_code = 200 context['success'] = True - context['message'] = 'Added topic successfully' + context['message'] = 'Saved topic successfully' else: status_code = 400 context['success'] = False @@ -3709,7 +3708,7 @@ def add_marker_quiz(request, content_type, course_id, lesson_id, toc.save() status_code = 200 context['success'] = True - context['message'] = 'Added question successfully' + context['message'] = 'Saved question successfully' context['content_type'] = content_type else: status_code = 400 @@ -3802,3 +3801,37 @@ def extend_time(request, paper_id): messages.info(request, msg) return my_redirect('/exam/manage/monitor/{0}/{1}/'.format( anspaper.question_paper.quiz.id, course.id)) + + +@login_required +@email_verified +def get_marker_quiz(request, course_id, toc_id): + user = request.user + course = get_object_or_404(Course, pk=course_id) + if not course.is_student(user): + raise Http404("You are not allowed to view this page") + toc = get_object_or_404(TableOfContents, pk=toc_id) + question = toc.content_object + template_context = { + "question": question, "course_id": course_id, "toc": toc, + "test_cases": question.get_test_cases() + } + data = loader.render_to_string( + "yaksh/show_lesson_quiz.html", context=template_context, + request=request + ) + context = {"data": data, "success": True} + return JsonResponse(context) + + +@login_required +@email_verified +def submit_marker_quiz(request, course_id, toc_id): + user = request.user + course = get_object_or_404(Course, pk=course_id) + if not course.is_student(user): + raise Http404("You are not allowed to view this page") + toc = get_object_or_404(TableOfContents, pk=toc_id) + print(request.POST) + context = {"success": True} + return JsonResponse(context) |