diff options
-rw-r--r-- | stats/models.py | 16 | ||||
-rw-r--r-- | stats/templates/view_lesson_tracking.html | 39 | ||||
-rw-r--r-- | yaksh/static/yaksh/js/show_toc.js | 26 | ||||
-rw-r--r-- | yaksh/templates/yaksh/course_added_modules.html | 34 | ||||
-rw-r--r-- | yaksh/templates/yaksh/courses.html | 20 | ||||
-rw-r--r-- | yaksh/templates/yaksh/show_lesson_statistics.html | 41 | ||||
-rw-r--r-- | yaksh/templates/yaksh/show_video.html | 2 | ||||
-rw-r--r-- | yaksh/templatetags/custom_filters.py | 11 |
8 files changed, 139 insertions, 50 deletions
diff --git a/stats/models.py b/stats/models.py index 0200a80..56c7f0d 100644 --- a/stats/models.py +++ b/stats/models.py @@ -1,7 +1,11 @@ +# Python Imports +import pandas as pd + # Django Imports from django.db import models from django.utils import timezone from django.contrib.auth.models import User +from django.db.models import F # Local Imports from yaksh.models import Course, Lesson @@ -93,6 +97,18 @@ class TrackLesson(models.Model): return str(timezone.timedelta(seconds=total_duration)) return self.get_current_time() + def get_no_of_vists(self): + lesson_logs = self.lessonlog_set.values("last_access_time").annotate( + visits=F('last_access_time') + ) + df = pd.DataFrame(lesson_logs) + visits = 1 + if not df.empty: + visits = df.groupby( + [df['visits'].dt.date] + ).first().count()['visits'] + return visits + def __str__(self): return (f"Track {self.lesson} in {self.course} " f"for {self.user.get_full_name()}") diff --git a/stats/templates/view_lesson_tracking.html b/stats/templates/view_lesson_tracking.html index ef5c9ae..d8d35c2 100644 --- a/stats/templates/view_lesson_tracking.html +++ b/stats/templates/view_lesson_tracking.html @@ -5,26 +5,29 @@ <script type="text/javascript" src="{% static 'yaksh/js/jquery.tablesorter.min.js' %}"> </script> <script type="text/javascript"> - 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; - } - $(document).ready(function() { - $("#stats-table").tablesorter({}); + $.tablesorter.addWidget({ + id: "numbering", + format: function(table) { + var c = table.config; + $("tr:visible", table.tBodies[0]).each(function(i) { + $(this).find('td').eq(0).text(i + 1); + }); + } + }); + $("#stats-table").tablesorter({ + headers: {0: { sorter: false }}, widgets: ['numbering'] + }); }); </script> {% endblock %} {% block content %} -<div class="container"> +<div class="container-fluid"> {% with objects.object_list as trackings %} <center> <h3>Statistics for {% with trackings|first as entry %} {{entry.lesson}} {% endwith %}</h3> </center> - <a class="btn btn-primary" href="{% url 'yaksh:lesson_statistics' course_id lesson_id %}"> + <a class="btn btn-primary" href="{% url 'yaksh:get_course_modules' course_id %}"> <i class="fa fa-arrow-left"></i> Back </a> <br><br> @@ -43,19 +46,29 @@ <th>Percentage Watched <i class="fa fa-sort"></i></th> <th>Watched Once Completely <i class="fa fa-sort"></i></th> <th>Total Time Spent <i class="fa fa-sort"></i></th> + <th>Total Visits <i class="fa fa-sort"></i></th> </tr> </thead> {% for track in trackings %} <tr> - <td>{{ forloop.counter0|add:objects.start_index }}</td> + <td>{{ forloop.counter0 }}</td> <td>{{track.user.get_full_name}}</td> <td>{{track.get_last_access_time}}</td> <td>{{track.creation_time}}</td> <td>{{track.get_current_time}}</td> <td>{{track.get_video_duration}}</td> <td>{{track.get_percentage_complete}}</td> - <td>{{track.get_watched}}</td> + <td> + {% with track.get_watched as watched %} + {% if watched %} + <span class="badge-pill badge-success">{{watched}}</span> + {% else %} + <span class="badge-pill badge-success">{{watched}}</span> + {% endif %} + {% endwith %} + </td> <td>{{track.time_spent}}</td> + <td>{{track.get_no_of_vists}}</td> </tr> {% endfor %} </table> diff --git a/yaksh/static/yaksh/js/show_toc.js b/yaksh/static/yaksh/js/show_toc.js index 914ab1c..2bedc6a 100644 --- a/yaksh/static/yaksh/js/show_toc.js +++ b/yaksh/static/yaksh/js/show_toc.js @@ -27,10 +27,8 @@ $(document).ready(function() { var total_duration; player.on('ready loadedmetadata', event => { total_duration = parseInt(player.duration); + store_tracker_time(total_duration); $("#video_duration").val(get_time_in_hrs(total_duration)); - if (total_duration > 0) { - start_tracker((total_duration * 1000) / 4, player); - } }); player.on('timeupdate', event => { @@ -44,10 +42,17 @@ $(document).ready(function() { } else { if(player.fullscreen.active) player.fullscreen.exit(); + player.pause() url = $("#toc_"+content.id).val(); ajax_call(url, "GET", screen_lock=true); } } + if(markers.length > 0 && current_time >= markers[track_count]) { + track_count++; + var csrf = document.getElementById("track-form").elements[0].value; + ajax_call($("#track-form").attr("action"), $("#track-form").attr("method"), + $("#track-form").serialize(), csrf, screen_lock=false); + } }); player.on('ended', event => { var csrf = document.getElementById("track-form").elements[0].value; @@ -57,16 +62,11 @@ $(document).ready(function() { }); }); - -function start_tracker(slice_duration, player) { - setTimeout(function run() { - if(player && player.playing) { - var csrf = document.getElementById("track-form").elements[0].value; - ajax_call($("#track-form").attr("action"), $("#track-form").attr("method"), - $("#track-form").serialize(), csrf, screen_lock=false); - } - setTimeout(run, slice_duration); - }, slice_duration); +function store_tracker_time(duration) { + marker = duration / 4; + for(var i = marker; i <= duration - marker; i = i + marker) { + markers.push(i); + } } function show_topic(description, override) { diff --git a/yaksh/templates/yaksh/course_added_modules.html b/yaksh/templates/yaksh/course_added_modules.html index d420b95..80d87c0 100644 --- a/yaksh/templates/yaksh/course_added_modules.html +++ b/yaksh/templates/yaksh/course_added_modules.html @@ -1,3 +1,4 @@ +{% load custom_filters %} {% if is_modules %} {% block pagetitle %} <center> <h3>Course Modules</h3> </center> {% endblock %} <a href="{% url 'yaksh:add_module' course.id %}" class="btn btn-primary btn-lg"> @@ -49,7 +50,8 @@ <table class="table table-responsive-sm"> {% for unit in units %} <tr> - <td> + <td>{{forloop.counter}}</td> + <td width="25%"> {% if unit.type == "quiz" %} {% if unit.quiz.is_exercise %} <a href="{% url 'yaksh:edit_exercise' course.id module.id unit.quiz.id %}"> @@ -69,12 +71,12 @@ {% if quiz.questionpaper_set.get.id %} <a href="{% url 'yaksh:designquestionpaper' course.id quiz.id quiz.questionpaper_set.get.id %}" class="btn btn-primary"> <i class="fa fa-edit"></i> - Edit Question Paper + Question Paper </a> {% else %} <a href="{% url 'yaksh:designquestionpaper' course.id quiz.id %}" class="btn btn-success"> <i class="fa fa-plus-circle"></i> - Add Question Paper + Question Paper </a> {% endif %} {% endwith %} @@ -95,6 +97,14 @@ </td> <td> {% if unit.type == "quiz" %} + ---- + {% else %} + {% get_lesson_views course.id unit.lesson.id as views %} + {{views.0}} views out of {{views.1}} + {% endif %} + </td> + <td> + {% if unit.type == "quiz" %} {% if unit.quiz.questionpaper_set.get.id %} <a href="{% url 'yaksh:show_statistics' unit.quiz.questionpaper_set.get.id course.id %}" class="btn btn-outline-primary"> <i class="fa fa-line-chart"></i> @@ -104,9 +114,23 @@ ---- {% endif %} {% else %} - <a href="{% url 'yaksh:lesson_statistics' course.id unit.lesson.id %}" class="btn btn-outline-primary"> - <i class="fa fa-line-chart"></i> Statistics + <div class="btn-group" role="group" aria-label="Button group with nested dropdown"> + <button type="button" class="btn btn-outline-primary"> + <i class="fa fa-line-chart"></i> + Statistics + </button> + <div class="btn-group" role="group"> + <button id="btnGroupDrop1" type="button" class="btn btn-outline-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button> + <div class="dropdown-menu" aria-labelledby="btnGroupDrop1" style=""> + <a class="dropdown-item" href="{% url 'stats:view_lesson_watch_stats' course.id unit.lesson.id %}"> + Video Views + </a> + <a class="dropdown-item" href="{% url 'yaksh:lesson_statistics' course.id unit.lesson.id %}"> + Quiz Submissions </a> + </div> + </div> + </div> {% endif %} </td> </tr> diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html index 151e1fb..38c106c 100644 --- a/yaksh/templates/yaksh/courses.html +++ b/yaksh/templates/yaksh/courses.html @@ -106,6 +106,17 @@ <div class="card border-primary"> <div class="card-header" style="height: 150px"> {{course.name}} + <div> + {% if user.id != course.creator.id %} + <span class="badge badge-pill badge-warning"> + Allotted Course + </span> + {% else %} + <span class="badge badge-pill badge-primary"> + Created Course + </span> + {% endif %} + </div> </div> <div class="card-body"> <div class="row"> @@ -119,15 +130,6 @@ </div> <hr> <div class="row"> - <div class="col-md-5"> - <span class="badge badge-pill badge-info"> - {% if user.id != course.creator.id %} - Allotted Course - {% else %} - Created Course - {% endif %} - </span> - </div> <div class="col-md-3"> {% if course.active %} <span class="badge badge-pill badge-success"> diff --git a/yaksh/templates/yaksh/show_lesson_statistics.html b/yaksh/templates/yaksh/show_lesson_statistics.html index 0c35e40..a7c2ebd 100644 --- a/yaksh/templates/yaksh/show_lesson_statistics.html +++ b/yaksh/templates/yaksh/show_lesson_statistics.html @@ -1,7 +1,31 @@ {% extends "manage.html" %} +{% load static %} {% load custom_filters %} {% block title %} Lesson Statistics {% endblock %} {% block pagetitle %} Statistics for {{lesson}} {% endblock %} +{% block script %} +<script type="text/javascript" src="{% static 'yaksh/js/jquery.tablesorter.min.js' %}"> +</script> +<script type="text/javascript"> + $(document).ready(function() { + $.tablesorter.addWidget({ + id: "numbering", + format: function(table) { + var c = table.config; + $("tr:visible", table.tBodies[0]).each(function(i) { + $(this).find('td').eq(0).text(i + 1); + }); + } + }); + $("#stats-table").tablesorter({ + headers: { + 0: { sorter: false } + }, + widgets: ['numbering'] + }); + }); +</script> +{% endblock %} {% block content %} <div class="container-fluid"> <br> @@ -11,11 +35,6 @@ <i class="fa fa-arrow-left"></i> Back </a> </div> - <div class="col-md-4"> - <a class="btn btn-outline-dark" href="{% url 'stats:view_lesson_watch_stats' course_id lesson.id %}"> - <i class="fa fa-line-chart"></i> Video Statistics - </a> - </div> </div> <br> {% if data %} @@ -161,16 +180,18 @@ <br><br> {% endif %} {% include "yaksh/paginator.html" %} - <table class="table table-responsive"> + <table class="table table-responsive" id="stats-table"> + <thead> <tr> <th>Sr No.</th> - <th>Student Name</th> - <th>Email</th> - <th>Latest Submission</th> + <th>Student Name <i class="fa fa-sort"></i></th> + <th>Email <i class="fa fa-sort"></i></th> + <th>Latest Submission <i class="fa fa-sort"></i></th> </tr> + </thead> {% for data in objects.object_list %} <tr> - <td>{{ forloop.counter0|add:objects.start_index }}</td> + <td>{{ forloop.counter }}</td> <td>{{data.student__first_name}} {{data.student__last_name}}</td> <td>{{data.student__email}}</td> {% get_answers data.toc_id data.student_id as user_answer %} diff --git a/yaksh/templates/yaksh/show_video.html b/yaksh/templates/yaksh/show_video.html index 0151f6b..b4f5628 100644 --- a/yaksh/templates/yaksh/show_video.html +++ b/yaksh/templates/yaksh/show_video.html @@ -9,6 +9,8 @@ var contents_by_time = JSON.parse('{{ contents_by_time|safe }}'); var loc = 0; var video_time = []; + var markers = []; + var track_count = 0; </script> <script type="text/javascript" src="{% static 'yaksh/js/show_toc.js' %}"> </script> diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py index a3cd3f1..91681c2 100644 --- a/yaksh/templatetags/custom_filters.py +++ b/yaksh/templatetags/custom_filters.py @@ -11,7 +11,11 @@ except ImportError: from pygments import highlight from pygments.lexers import get_lexer_by_name from pygments.formatters import HtmlFormatter + +# Local Imports from yaksh.models import User, Course, Quiz, TableOfContents, Lesson +from stats.models import TrackLesson + register = template.Library() @@ -200,3 +204,10 @@ def has_lesson_video(lesson_id): def get_tc_percent(tc_id, data): return data.get(str(tc_id), 0) + +@register.simple_tag +def get_lesson_views(course_id, lesson_id): + course = Course.objects.get(id=course_id) + return TrackLesson.objects.filter( + course_id=course_id, lesson_id=lesson_id + ).count(), course.students.count() |