summaryrefslogtreecommitdiff
path: root/yaksh
diff options
context:
space:
mode:
Diffstat (limited to 'yaksh')
-rw-r--r--yaksh/forms.py2
-rw-r--r--yaksh/models.py13
-rw-r--r--yaksh/static/yaksh/js/show_toc.js123
-rw-r--r--yaksh/templates/base.html5
-rw-r--r--yaksh/templates/yaksh/show_lesson_quiz.html131
-rw-r--r--yaksh/templates/yaksh/show_video.html88
-rw-r--r--yaksh/urls.py4
-rw-r--r--yaksh/views.py51
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&nbsp;<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&nbsp;<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&nbsp;<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)