summaryrefslogtreecommitdiff
path: root/yaksh
diff options
context:
space:
mode:
authoradityacp2020-09-12 11:44:01 +0530
committeradityacp2020-09-12 11:44:01 +0530
commitb1d2b88746fc670d7362f9b4d175d5e570f3ac77 (patch)
tree7225713597b09645ce7b75ffe44271691e3e28b0 /yaksh
parentbee8d54d8ae094db5e3c9c04c5e28fb5b2abb1df (diff)
downloadonline_test-b1d2b88746fc670d7362f9b4d175d5e570f3ac77.tar.gz
online_test-b1d2b88746fc670d7362f9b4d175d5e570f3ac77.tar.bz2
online_test-b1d2b88746fc670d7362f9b4d175d5e570f3ac77.zip
Mulitple changes
- Remove all alerts and add toast messages - Accept user submissions for the lesson quiz and evaluate - Initial lesson statistics
Diffstat (limited to 'yaksh')
-rw-r--r--yaksh/admin.py4
-rw-r--r--yaksh/forms.py7
-rw-r--r--yaksh/models.py20
-rw-r--r--yaksh/static/yaksh/css/custom.css28
-rw-r--r--yaksh/static/yaksh/css/toastr.min.css1
-rw-r--r--yaksh/static/yaksh/js/lesson.js64
-rw-r--r--yaksh/static/yaksh/js/show_toc.js78
-rw-r--r--yaksh/static/yaksh/js/toastr.min.js7
-rw-r--r--yaksh/templates/base.html19
-rw-r--r--yaksh/templates/yaksh/show_lesson_statistics.html5
-rw-r--r--yaksh/templates/yaksh/show_video.html11
-rw-r--r--yaksh/urls.py2
-rw-r--r--yaksh/views.py62
13 files changed, 244 insertions, 64 deletions
diff --git a/yaksh/admin.py b/yaksh/admin.py
index 6fb05d1..e98c7c5 100644
--- a/yaksh/admin.py
+++ b/yaksh/admin.py
@@ -2,7 +2,7 @@ from yaksh.models import Question, Quiz, QuestionPaper, Profile
from yaksh.models import (TestCase, StandardTestCase, StdIOBasedTestCase,
Course, AnswerPaper, CourseStatus, LearningModule,
Lesson, Post, Comment, Topic, TableOfContents,
- VideoQuizAnswer, Answer
+ LessonQuizAnswer, Answer
)
from django.contrib import admin
@@ -62,5 +62,5 @@ admin.site.register(Lesson, LessonAdmin)
admin.site.register(LearningModule, LearningModuleAdmin)
admin.site.register(Topic)
admin.site.register(TableOfContents)
-admin.site.register(VideoQuizAnswer)
+admin.site.register(LessonQuizAnswer)
admin.site.register(Answer)
diff --git a/yaksh/forms.py b/yaksh/forms.py
index 440a535..cc5daaf 100644
--- a/yaksh/forms.py
+++ b/yaksh/forms.py
@@ -655,10 +655,13 @@ class TopicForm(forms.ModelForm):
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'}
+ {'class': form_input_class, 'placeholder': 'Name'}
)
self.fields['timer'].widget.attrs.update(
- {'class': form_input_class, 'placeholder': 'Topic Time'}
+ {'class': form_input_class, 'placeholder': 'Time'}
+ )
+ self.fields['description'].widget.attrs.update(
+ {'class': form_input_class, 'placeholder': 'Description'}
)
self.fields['timer'].initial = time
diff --git a/yaksh/models.py b/yaksh/models.py
index 570c4c6..19f3302 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -40,7 +40,7 @@ from django.contrib.contenttypes.models import ContentType
from django.template import Context, Template
from django.conf import settings
from django.forms.models import model_to_dict
-
+from django.db.models import Count
# Local Imports
from yaksh.code_server import (
submit, get_result as get_result_from_code_server
@@ -2745,6 +2745,18 @@ class Comment(ForumBase):
self.post_field.title)
+class TOCManager(models.Manager):
+
+ def get_data(self, course_id, lesson_id):
+ toc = TableOfContents.objects.filter(
+ course_id=course_id, lesson_id=lesson_id, content__in=[2, 3, 4]
+ )
+ answers = LessonQuizAnswer.objects.select_related("toc").filter(
+ toc__course_id=course_id, toc__lesson_id=lesson_id
+ )
+ return answers
+
+
class TableOfContents(models.Model):
toc_types = ((1, "Topic"), (2, "Graded Quiz"), (3, "Exercise"), (4, "Poll"))
course = models.ForeignKey(Course, on_delete=models.CASCADE,
@@ -2757,6 +2769,8 @@ class TableOfContents(models.Model):
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
+ objects = TOCManager()
+
class Meta:
verbose_name_plural = "Table Of Contents"
@@ -2773,13 +2787,14 @@ class TableOfContents(models.Model):
class Topic(models.Model):
name = models.CharField(max_length=255)
+ description = models.TextField(null=True, blank=True)
content = GenericRelation(TableOfContents)
def __str__(self):
return f"{self.name}"
-class VideoQuizAnswer(models.Model):
+class LessonQuizAnswer(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)
@@ -2849,6 +2864,7 @@ class VideoQuizAnswer(models.Model):
if ans_status:
self.answer.marks = self.answer.question.points
self.answer.save()
+ return result
def __str__(self):
return f"Lesson answer of {self.toc} by {self.student.get_full_name()}"
diff --git a/yaksh/static/yaksh/css/custom.css b/yaksh/static/yaksh/css/custom.css
index c93b03c..9d4ab76 100644
--- a/yaksh/static/yaksh/css/custom.css
+++ b/yaksh/static/yaksh/css/custom.css
@@ -68,7 +68,7 @@ body, .dropdown-menu {
}
#sidebar.active {
- margin-left: -350px;
+ margin-left: -370px;
}
#sidebar .sidebar-header {
@@ -132,7 +132,6 @@ body, .dropdown-menu {
border: none;
}
-<<<<<<< HEAD
/* Simple MDE editor style */
.editor-toolbar.fullscreen, .editor-preview-side {
z-index: 2000 !important;
@@ -167,3 +166,28 @@ iframe {
display:block;
width:100%;
}
+
+#loader {
+ position: fixed;
+ display: none;
+ width: 100%;
+ height: 100%;
+ top:0;
+ bottom: 0;
+ left:0;
+ right: 0;
+ background-color: rgba(0,0,0,0.5);
+ z-index: 1001; /* 1001 coz sidebar is 1000. So will be on top of sidebar*/
+}
+
+#state1 {
+ position: absolute;
+ top: 40%;
+ left: 45%;
+ font-size: 30px;
+ color: white;
+}
+
+.toast-top-center {
+ padding-top: 5em;
+} \ No newline at end of file
diff --git a/yaksh/static/yaksh/css/toastr.min.css b/yaksh/static/yaksh/css/toastr.min.css
new file mode 100644
index 0000000..643135e
--- /dev/null
+++ b/yaksh/static/yaksh/css/toastr.min.css
@@ -0,0 +1 @@
+/* * Note that this is toastr v2.1.3, the "latest" version in url has no more maintenance, * please go to https://cdnjs.com/libraries/toastr.js and pick a certain version you want to use, * make sure you copy the url from the website since the url may change between versions. * */ .toast-title{font-weight:700}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#FFF}.toast-message a:hover{color:#CCC;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:700;color:#FFF;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80);line-height:1}.toast-close-button:focus,.toast-close-button:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}.rtl .toast-close-button{left:-.3em;float:left;right:.3em}button.toast-close-button{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999;pointer-events:none}#toast-container *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#toast-container>div{position:relative;pointer-events:auto;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#FFF;opacity:.8;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=80);filter:alpha(opacity=80)}#toast-container>div.rtl{direction:rtl;padding:15px 50px 15px 15px;background-position:right 15px center}#toast-container>div:hover{-moz-box-shadow:0 0 12px #000;-webkit-box-shadow:0 0 12px #000;box-shadow:0 0 12px #000;opacity:1;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);filter:alpha(opacity=100);cursor:pointer}#toast-container>.toast-info{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=)!important}#toast-container>.toast-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=)!important}#toast-container>.toast-success{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==)!important}#toast-container>.toast-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=)!important}#toast-container.toast-bottom-center>div,#toast-container.toast-top-center>div{width:300px;margin-left:auto;margin-right:auto}#toast-container.toast-bottom-full-width>div,#toast-container.toast-top-full-width>div{width:96%;margin-left:auto;margin-right:auto}.toast{background-color:#030303}.toast-success{background-color:#51A351}.toast-error{background-color:#BD362F}.toast-info{background-color:#2F96B4}.toast-warning{background-color:#F89406}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)}@media all and (max-width:240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width:241px) and (max-width:480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width:481px) and (max-width:768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}#toast-container>div.rtl{padding:15px 50px 15px 15px}} \ No newline at end of file
diff --git a/yaksh/static/yaksh/js/lesson.js b/yaksh/static/yaksh/js/lesson.js
index 0558bd0..60eff78 100644
--- a/yaksh/static/yaksh/js/lesson.js
+++ b/yaksh/static/yaksh/js/lesson.js
@@ -71,6 +71,7 @@ function add_topic() {
if (!$("#id_timer").val()) {
$("#id_timer").val($("#vtimer").val());
}
+ document.getElementById("id_timer").focus();
$("#topic-form").submit(function(e) {
e.preventDefault();
lock_screen();
@@ -83,6 +84,7 @@ function add_question() {
if (!$("#id_timer").val()) {
$("#id_timer").val($("#vtimer").val());
}
+ document.getElementById("id_timer").focus();
$("#question-form").submit(function(e) {
e.preventDefault();
lock_screen();
@@ -92,11 +94,11 @@ function add_question() {
}
function lock_screen() {
- document.getElementById("ontop").style.display = "block";
+ document.getElementById("loader").style.display = "block";
}
function unlock_screen() {
- document.getElementById("ontop").style.display = "none";
+ document.getElementById("loader").style.display = "none";
}
function show_error(error) {
@@ -105,9 +107,40 @@ function show_error(error) {
var value = err[key];
err_msg = err_msg + key + " : " + value[0].message + "\n";
});
- alert(err_msg);
+ show_message(err_msg, "error");
}
+function show_message(message, msg_type) {
+ toastr.options = {
+ "positionClass": "toast-top-center",
+ "timeOut": "1500",
+ "showDuration": "300",
+ }
+ switch(msg_type) {
+ case "info": {
+ toastr.info(message);
+ break;
+ }
+ case "error": {
+ toastr.error(message);
+ break;
+ }
+ case "warning": {
+ toastr.warning(message);
+ break;
+ }
+ case "success": {
+ toastr.success(message);
+ break;
+ }
+ default: {
+ toastr.info(message);
+ break;
+ }
+ }
+}
+
+
function show_toc(toc) {
$("#lesson-content").empty();
$("#toc").html(toc);
@@ -131,17 +164,22 @@ function ajax_call(url, method, data, 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();
+ 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) {
+ if (msg.success) {
+ show_message(msg.message, "success");
}
else {
- add_question();
+ show_message(msg.message, "warning");
}
}
- if (msg.toc) show_toc(msg.toc);
- if (msg.message) alert(msg.message)
},
error: function(xhr, data) {
unlock_screen();
@@ -152,15 +190,15 @@ function ajax_call(url, method, data, csrf) {
break;
}
case 500: {
- alert('500 status code! server error');
+ show_message('500 status code! server error', "error");
break;
}
case 404: {
- alert('404 status code! server error');
+ show_message('404 status code! server error', "error");
break;
}
default: {
- alert('Unable to perform action. Please try again');
+ show_message('Unable to perform action. Please try again', "error");
break;
}
}
diff --git a/yaksh/static/yaksh/js/show_toc.js b/yaksh/static/yaksh/js/show_toc.js
index f42346b..5fef923 100644
--- a/yaksh/static/yaksh/js/show_toc.js
+++ b/yaksh/static/yaksh/js/show_toc.js
@@ -12,6 +12,7 @@ $(document).ready(function() {
loc += 1;
if(content.content != 1) {
player.pause();
+ player.fullscreen.exit();
url = $("#toc_"+content.id).val();
ajax_call(url, "GET");
}
@@ -33,26 +34,23 @@ function get_time_in_seconds(time) {
}
function lock_screen() {
- document.getElementById("ontop").style.display = "block";
+ document.getElementById("loader").style.display = "block";
+ if ($("#check").is(":visible")) {
+ $("#check").attr("disabled", true);
+ }
}
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);
+ document.getElementById("loader").style.display = "none";
+ if ($("#check").is(":visible")) {
+ $("#check").attr("disabled", false);
+ }
}
function show_question(data) {
$("#dialog").html(data);
$("#dialog").dialog({
- width: 600,
+ width: 800,
height: 500,
});
$("#submit-quiz-form").submit(function(e) {
@@ -65,10 +63,13 @@ function show_question(data) {
function select_toc(element) {
var toc_id = element.getAttribute("data-toc");
+ var content_type = element.getAttribute("data-toc-type");
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");
+ if (content_type != 1) {
+ url = $("#toc_"+toc_id).val();
+ ajax_call(url, "GET");
+ }
}
function csrfSafeMethod(method) {
@@ -76,6 +77,36 @@ function csrfSafeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
+function show_message(message, msg_type) {
+ toastr.options = {
+ "positionClass": "toast-top-center",
+ "timeOut": "1500",
+ "showDuration": "300",
+ }
+ switch(msg_type) {
+ case "info": {
+ toastr.info(message);
+ break;
+ }
+ case "error": {
+ toastr.error(message);
+ break;
+ }
+ case "warning": {
+ toastr.warning(message);
+ break;
+ }
+ case "success": {
+ toastr.success(message);
+ break;
+ }
+ default: {
+ toastr.info(message);
+ break;
+ }
+ }
+}
+
function ajax_call(url, method, data, csrf) {
lock_screen();
$.ajax({
@@ -93,26 +124,33 @@ function ajax_call(url, method, data, csrf) {
if (msg.data) {
show_question(msg.data);
}
- if(msg.message) alert(msg.message);
+ if (msg.message) {
+ if (msg.success) {
+ $("#dialog").dialog("close");
+ show_message(msg.message, "success");
+ }
+ else {
+ show_message(msg.message, "warning");
+ }
+ }
},
error: function(xhr, data) {
unlock_screen();
switch(xhr.status) {
case 400: {
- err = JSON.parse(xhr.responseJSON.message);
- show_error(err);
+ show_message("400 status code! server error", "error");
break;
}
case 500: {
- alert('500 status code! server error');
+ show_message("500 status code! server error", "error");
break;
}
case 404: {
- alert('404 status code! server error');
+ show_message("404 status code! server error", "error");
break;
}
default: {
- alert('Unable to perform action. Please try again');
+ show_message('Unable to perform action. Please try again', "error");
break;
}
}
diff --git a/yaksh/static/yaksh/js/toastr.min.js b/yaksh/static/yaksh/js/toastr.min.js
new file mode 100644
index 0000000..4b5f34a
--- /dev/null
+++ b/yaksh/static/yaksh/js/toastr.min.js
@@ -0,0 +1,7 @@
+/*
+ * Note that this is toastr v2.1.3, the "latest" version in url has no more maintenance,
+ * please go to https://cdnjs.com/libraries/toastr.js and pick a certain version you want to use,
+ * make sure you copy the url from the website since the url may change between versions.
+ * */
+!function(e){e(["jquery"],function(e){return function(){function t(e,t,n){return g({type:O.error,iconClass:m().iconClasses.error,message:e,optionsOverride:n,title:t})}function n(t,n){return t||(t=m()),v=e("#"+t.containerId),v.length?v:(n&&(v=d(t)),v)}function o(e,t,n){return g({type:O.info,iconClass:m().iconClasses.info,message:e,optionsOverride:n,title:t})}function s(e){C=e}function i(e,t,n){return g({type:O.success,iconClass:m().iconClasses.success,message:e,optionsOverride:n,title:t})}function a(e,t,n){return g({type:O.warning,iconClass:m().iconClasses.warning,message:e,optionsOverride:n,title:t})}function r(e,t){var o=m();v||n(o),u(e,o,t)||l(o)}function c(t){var o=m();return v||n(o),t&&0===e(":focus",t).length?void h(t):void(v.children().length&&v.remove())}function l(t){for(var n=v.children(),o=n.length-1;o>=0;o--)u(e(n[o]),t)}function u(t,n,o){var s=!(!o||!o.force)&&o.force;return!(!t||!s&&0!==e(":focus",t).length)&&(t[n.hideMethod]({duration:n.hideDuration,easing:n.hideEasing,complete:function(){h(t)}}),!0)}function d(t){return v=e("<div/>").attr("id",t.containerId).addClass(t.positionClass),v.appendTo(e(t.target)),v}function p(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:void 0,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:void 0,closeMethod:!1,closeDuration:!1,closeEasing:!1,closeOnHover:!0,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",escapeHtml:!1,target:"body",closeHtml:'<button type="button">&times;</button>',closeClass:"toast-close-button",newestOnTop:!0,preventDuplicates:!1,progressBar:!1,progressClass:"toast-progress",rtl:!1}}function f(e){C&&C(e)}function g(t){function o(e){return null==e&&(e=""),e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/'/g,"&#39;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function s(){c(),u(),d(),p(),g(),C(),l(),i()}function i(){var e="";switch(t.iconClass){case"toast-success":case"toast-info":e="polite";break;default:e="assertive"}I.attr("aria-live",e)}function a(){E.closeOnHover&&I.hover(H,D),!E.onclick&&E.tapToDismiss&&I.click(b),E.closeButton&&j&&j.click(function(e){e.stopPropagation?e.stopPropagation():void 0!==e.cancelBubble&&e.cancelBubble!==!0&&(e.cancelBubble=!0),E.onCloseClick&&E.onCloseClick(e),b(!0)}),E.onclick&&I.click(function(e){E.onclick(e),b()})}function r(){I.hide(),I[E.showMethod]({duration:E.showDuration,easing:E.showEasing,complete:E.onShown}),E.timeOut>0&&(k=setTimeout(b,E.timeOut),F.maxHideTime=parseFloat(E.timeOut),F.hideEta=(new Date).getTime()+F.maxHideTime,E.progressBar&&(F.intervalId=setInterval(x,10)))}function c(){t.iconClass&&I.addClass(E.toastClass).addClass(y)}function l(){E.newestOnTop?v.prepend(I):v.append(I)}function u(){if(t.title){var e=t.title;E.escapeHtml&&(e=o(t.title)),M.append(e).addClass(E.titleClass),I.append(M)}}function d(){if(t.message){var e=t.message;E.escapeHtml&&(e=o(t.message)),B.append(e).addClass(E.messageClass),I.append(B)}}function p(){E.closeButton&&(j.addClass(E.closeClass).attr("role","button"),I.prepend(j))}function g(){E.progressBar&&(q.addClass(E.progressClass),I.prepend(q))}function C(){E.rtl&&I.addClass("rtl")}function O(e,t){if(e.preventDuplicates){if(t.message===w)return!0;w=t.message}return!1}function b(t){var n=t&&E.closeMethod!==!1?E.closeMethod:E.hideMethod,o=t&&E.closeDuration!==!1?E.closeDuration:E.hideDuration,s=t&&E.closeEasing!==!1?E.closeEasing:E.hideEasing;if(!e(":focus",I).length||t)return clearTimeout(F.intervalId),I[n]({duration:o,easing:s,complete:function(){h(I),clearTimeout(k),E.onHidden&&"hidden"!==P.state&&E.onHidden(),P.state="hidden",P.endTime=new Date,f(P)}})}function D(){(E.timeOut>0||E.extendedTimeOut>0)&&(k=setTimeout(b,E.extendedTimeOut),F.maxHideTime=parseFloat(E.extendedTimeOut),F.hideEta=(new Date).getTime()+F.maxHideTime)}function H(){clearTimeout(k),F.hideEta=0,I.stop(!0,!0)[E.showMethod]({duration:E.showDuration,easing:E.showEasing})}function x(){var e=(F.hideEta-(new Date).getTime())/F.maxHideTime*100;q.width(e+"%")}var E=m(),y=t.iconClass||E.iconClass;if("undefined"!=typeof t.optionsOverride&&(E=e.extend(E,t.optionsOverride),y=t.optionsOverride.iconClass||y),!O(E,t)){T++,v=n(E,!0);var k=null,I=e("<div/>"),M=e("<div/>"),B=e("<div/>"),q=e("<div/>"),j=e(E.closeHtml),F={intervalId:null,hideEta:null,maxHideTime:null},P={toastId:T,state:"visible",startTime:new Date,options:E,map:t};return s(),r(),a(),f(P),E.debug&&console&&console.log(P),I}}function m(){return e.extend({},p(),b.options)}function h(e){v||(v=n()),e.is(":visible")||(e.remove(),e=null,0===v.children().length&&(v.remove(),w=void 0))}var v,C,w,T=0,O={error:"error",info:"info",success:"success",warning:"warning"},b={clear:r,remove:c,error:t,getContainer:n,info:o,options:{},subscribe:s,success:i,version:"2.1.3",warning:a};return b}()})}("function"==typeof define&&define.amd?define:function(e,t){"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):window.toastr=t(window.jQuery)});
+//# sourceMappingURL=toastr.js.map
diff --git a/yaksh/templates/base.html b/yaksh/templates/base.html
index 53edbee..8bf7fbc 100644
--- a/yaksh/templates/base.html
+++ b/yaksh/templates/base.html
@@ -22,6 +22,7 @@
<link rel="stylesheet" href="{% static 'yaksh/css/ontop.css' %}" type="text/css" />
<link rel="stylesheet" href="{% static 'yaksh/css/plyr.css' %}" />
<link rel="stylesheet" type="text/css" href="{% static 'yaksh/css/simplemde.min.css' %}">
+ <link rel="stylesheet" href="{% static 'yaksh/css/toastr.min.css' %}" />">
{% block meta %}
@@ -42,6 +43,8 @@
<script src="{% static 'yaksh/js/plyr.js' %}"></script>
<script type="text/javascript" src="{% static 'yaksh/js/simplemde.min.js' %}">
</script>
+ <script type="text/javascript" src="{% static 'yaksh/js/toastr.min.js' %}">
+ </script>
<script>
new WOW().init();
@@ -57,6 +60,22 @@
Checking...<img src="{% static 'yaksh/images/check_answer.gif' %}"/>
</div>
</div>
+ <div id="loader">
+ <div id="state1">
+ <div class="spinner-grow text-success" role="status">
+ <span class="sr-only">Loading...</span>
+ </div>
+ <div class="spinner-grow text-danger" role="status">
+ <span class="sr-only">Loading...</span>
+ </div>
+ <div class="spinner-grow text-warning" role="status">
+ <span class="sr-only">Loading...</span>
+ </div>
+ <div class="spinner-grow text-primary" role="status">
+ <span class="sr-only">Loading...</span>
+ </div>
+ </div>
+ </div>
{% block nav %}
{% endblock %}
diff --git a/yaksh/templates/yaksh/show_lesson_statistics.html b/yaksh/templates/yaksh/show_lesson_statistics.html
new file mode 100644
index 0000000..36b1fbd
--- /dev/null
+++ b/yaksh/templates/yaksh/show_lesson_statistics.html
@@ -0,0 +1,5 @@
+{% extends "manage.html" %}
+{% block title %} Lesson Statistics {% endblock %}
+{% block content %}
+{{data}}
+{% endblock %} \ No newline at end of file
diff --git a/yaksh/templates/yaksh/show_video.html b/yaksh/templates/yaksh/show_video.html
index 8f8bbb2..f6e247c 100644
--- a/yaksh/templates/yaksh/show_video.html
+++ b/yaksh/templates/yaksh/show_video.html
@@ -90,11 +90,9 @@
<!-- Page Content -->
<div id="content">
-
<button type="button" id="sidebarCollapse" class="btn btn-outline-info">
<i class="fa fa-navicon fa-lg"></i>
</button>
-
<br><br>
<ol class="breadcrumb">
<li class="breadcrumb-item">
@@ -108,7 +106,6 @@
{% endif %}
</ol>
<br>
-
{% if msg %}
<center>
<div class="alert alert-dismissible alert-warning">
@@ -171,7 +168,9 @@
{% with content.get_toc_text as toc_name %}
<tr>
<td>
- <a href="#" onclick="select_toc(this);" data-toc="{{content.id}}" data-content-type="{{content.content}}">{{ toc_name }}</a>
+ <a href="#" onclick="select_toc(this);" data-toc="{{content.id}}" data-toc-type="{{content.content}}">
+ {{ toc_name }}
+ </a>
</td>
<td>
{{content.get_content_display}}
@@ -179,9 +178,7 @@
<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>
+ <input type="hidden" id="toc_{{content.id}}" value="{% url 'yaksh:get_marker_quiz' course.id content.id %}" data-content="{{content.content}}"/>
</tr>
{% endwith %}
{% empty %}
diff --git a/yaksh/urls.py b/yaksh/urls.py
index 2b9a04f..ada5829 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -263,4 +263,6 @@ urlpatterns = [
name='get_marker_quiz'),
path('submit/marker/quiz/<int:course_id>/<int:toc_id>',
views.submit_marker_quiz, name='submit_marker_quiz'),
+ path('manage/lesson/stats/<int:course_id>/<int:lesson_id>',
+ views.lessson_statistics, name='lessson_statistics'),
]
diff --git a/yaksh/views.py b/yaksh/views.py
index ff22a26..c817c51 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -38,7 +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,
- Topic, TableOfContents, VideoQuizAnswer, MicroManager
+ Topic, TableOfContents, LessonQuizAnswer, MicroManager
)
from yaksh.forms import (
UserRegisterForm, UserLoginForm, QuizForm, QuestionForm,
@@ -3772,6 +3772,7 @@ def delete_toc(request, course_id, toc_id):
get_object_or_404(Topic, pk=toc.object_id).delete()
else:
get_object_or_404(Question, id=toc.object_id).delete()
+ messages.success(request, "Content deleted successfully")
return redirect(redirect_url)
@@ -3839,12 +3840,12 @@ def submit_marker_quiz(request, course_id, toc_id):
try:
user_answer = int(request.POST.get('answer'))
except ValueError:
- msg = "Please enter an Integer Value"
+ user_answer = None
elif current_question.type == 'float':
try:
user_answer = float(request.POST.get('answer'))
except ValueError:
- msg = "Please enter a Float Value"
+ user_answer = None
elif current_question.type == 'string':
user_answer = str(request.POST.get('answer'))
elif current_question.type == 'mcc':
@@ -3853,30 +3854,59 @@ def submit_marker_quiz(request, course_id, toc_id):
user_answer_ids = request.POST.get('answer').split(',')
user_answer = [int(ids) for ids in user_answer_ids]
- def is_valid_answer(user_answer):
- success = True
- if current_question.type == "mcc" and not user_answer:
- success = False
- elif not str(user_answer):
- success = False
- return success
+ def is_valid_answer(answer):
+ status = True
+ if ((current_question.type == "mcc" or
+ current_question.type == "arrange") and not answer):
+ status = False
+ elif answer is None or not str(answer):
+ status = False
+ return status
if is_valid_answer(user_answer):
- if not VideoQuizAnswer.objects.filter(
- toc_id=toc_id, student_id=user.id).exists():
+ success = True
+ # check if graded quiz and already attempted
+ has_attempts = LessonQuizAnswer.objects.filter(
+ toc_id=toc_id, student_id=user.id).exists()
+ if ((toc.content == 2 and not has_attempts) or
+ toc.content == 3 or toc.content == 4):
answer = Answer.objects.create(
question_id=current_question.id, answer=user_answer,
correct=False, error=json.dumps([])
)
- lesson_ans = VideoQuizAnswer.objects.create(
+ lesson_ans = LessonQuizAnswer.objects.create(
toc_id=toc_id, student=user, answer=answer
)
- if toc.content == 2:
- lesson_ans.check_answer(user_answer)
msg = "Answer saved successfully"
+ # call check answer only for graded quiz and exercise
+ if toc.content == 3 or toc.content == 2:
+ result = lesson_ans.check_answer(user_answer)
+ # if exercise then show custom message
+ if toc.content == 3:
+ if result.get("success"):
+ msg = "You answered the question correctly"
+ else:
+ success = False
+ msg = "You have answered the question incorrectly. "\
+ "Please refer the lesson again"
else:
msg = "You have already submitted the answer"
else:
+ success = False
msg = "Please submit a valid answer"
- context = {"success": True, "message": msg}
+ context = {"success": success, "message": msg}
return JsonResponse(context)
+
+
+@login_required
+@email_verified
+def lessson_statistics(request, course_id, lesson_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 = TableOfContents.objects.get_data(course_id, lesson_id)
+ return render(request, 'yaksh/show_lesson_statistics.html', {
+ 'data': toc,
+ })