summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--yaksh/forms.py2
-rw-r--r--yaksh/models.py62
-rw-r--r--yaksh/static/yaksh/js/course.js75
-rw-r--r--yaksh/templates/yaksh/add_exercise.html46
-rw-r--r--yaksh/templates/yaksh/add_quiz.html43
-rw-r--r--yaksh/templates/yaksh/course_detail.html99
-rw-r--r--yaksh/templates/yaksh/register.html3
-rw-r--r--yaksh/templates/yaksh/show_video.html19
-rw-r--r--yaksh/templatetags/custom_filters.py36
-rw-r--r--yaksh/test_models.py42
-rw-r--r--yaksh/test_views.py257
-rw-r--r--yaksh/urls.py6
-rw-r--r--yaksh/views.py108
13 files changed, 676 insertions, 122 deletions
diff --git a/yaksh/forms.py b/yaksh/forms.py
index 8399bc9..9fd2eaa 100644
--- a/yaksh/forms.py
+++ b/yaksh/forms.py
@@ -176,7 +176,7 @@ class UserLoginForm(forms.Form):
class ExerciseForm(forms.ModelForm):
class Meta:
model = Quiz
- fields = ['description', 'view_answerpaper']
+ fields = ['description', 'view_answerpaper', 'active']
class QuizForm(forms.ModelForm):
diff --git a/yaksh/models.py b/yaksh/models.py
index fea8fda..9a406c3 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -1,4 +1,4 @@
-from __future__ import unicode_literals
+from __future__ import unicode_literals, division
from datetime import datetime, timedelta
import json
import random
@@ -464,12 +464,16 @@ class LearningModule(models.Model):
ordered_units = learning_module.learning_unit.order_by("order")
status_list = [unit.get_completion_status(user, course)
for unit in ordered_units]
- if all([status == "completed" for status in status_list]):
- return "completed"
+
+ if not status_list:
+ default_status = "no units"
+ elif all([status == "completed" for status in status_list]):
+ default_status = "completed"
elif "inprogress" in status_list:
- return "inprogress"
+ default_status = "inprogress"
else:
- return "not attempted"
+ default_status = "not attempted"
+ return default_status
def is_prerequisite_passed(self, user, course):
""" Check if prerequisite module is completed """
@@ -492,6 +496,17 @@ class LearningModule(models.Model):
def has_prerequisite(self):
return self.check_prerequisite
+ def get_module_complete_percent(self, course, user):
+ units = self.get_learning_units()
+ if not units:
+ percent = 0.0
+ else:
+ status_list = [unit.get_completion_status(user, course)
+ for unit in units]
+ count = status_list.count("completed")
+ percent = round((count / len(units)) * 100)
+ return percent
+
def __str__(self):
return self.name
@@ -692,6 +707,17 @@ class Course(models.Model):
next_index = 0
return modules.get(id=module_ids[next_index])
+ def percent_completed(self, user):
+ modules = self.get_learning_modules()
+ if not modules:
+ percent = 0.0
+ else:
+ status_list = [module.get_module_complete_percent(self, user)
+ for module in modules]
+ count = sum(status_list)
+ percent = round((count / len(modules)))
+ return percent
+
def __str__(self):
return self.name
@@ -1181,13 +1207,14 @@ class QuestionPaper(models.Model):
return ans_paper
- def _is_attempt_allowed(self, user):
+ def _is_attempt_allowed(self, user, course_id):
attempts = AnswerPaper.objects.get_total_attempt(questionpaper=self,
- user=user)
+ user=user,
+ course_id=course_id)
return attempts != self.quiz.attempts_allowed
def can_attempt_now(self, user, course_id):
- if self._is_attempt_allowed(user):
+ if self._is_attempt_allowed(user, course_id):
last_attempt = AnswerPaper.objects.get_user_last_attempt(
user=user, questionpaper=self, course_id=course_id
)
@@ -1265,10 +1292,11 @@ class QuestionSet(models.Model):
###############################################################################
class AnswerPaperManager(models.Manager):
- def get_all_questions(self, questionpaper_id, attempt_number,
+ def get_all_questions(self, questionpaper_id, attempt_number, course_id,
status='completed'):
''' Return a dict of question id as key and count as value'''
papers = self.filter(question_paper_id=questionpaper_id,
+ course_id=course_id,
attempt_number=attempt_number, status=status)
all_questions = list()
questions = list()
@@ -1279,9 +1307,10 @@ class AnswerPaperManager(models.Manager):
return Counter(questions)
def get_all_questions_answered(self, questionpaper_id, attempt_number,
- status='completed'):
+ course_id, status='completed'):
''' Return a dict of answered question id as key and count as value'''
papers = self.filter(question_paper_id=questionpaper_id,
+ course_id=course_id,
attempt_number=attempt_number, status=status)
questions_answered = list()
for paper in papers:
@@ -1319,15 +1348,17 @@ class AnswerPaperManager(models.Manager):
).count()
def get_question_statistics(self, questionpaper_id, attempt_number,
- status='completed'):
+ course_id, status='completed'):
''' Return dict with question object as key and list as value
The list contains two value, first the number of times a question
was answered correctly, and second the number of times a question
appeared in a quiz'''
question_stats = {}
questions_answered = self.get_all_questions_answered(questionpaper_id,
- attempt_number)
- questions = self.get_all_questions(questionpaper_id, attempt_number)
+ attempt_number,
+ course_id)
+ questions = self.get_all_questions(questionpaper_id, attempt_number,
+ course_id)
all_questions = Question.objects.filter(
id__in=set(questions),
active=True
@@ -1376,8 +1407,9 @@ class AnswerPaperManager(models.Manager):
def get_user_answerpapers(self, user):
return self.filter(user=user)
- def get_total_attempt(self, questionpaper, user):
- return self.filter(question_paper=questionpaper, user=user).count()
+ def get_total_attempt(self, questionpaper, user, course_id):
+ return self.filter(question_paper=questionpaper, user=user,
+ course_id=course_id).count()
def get_users_for_questionpaper(self, questionpaper_id, course_id):
return self._get_answerpapers_for_quiz(questionpaper_id, course_id,
diff --git a/yaksh/static/yaksh/js/course.js b/yaksh/static/yaksh/js/course.js
index f0d03e2..1c64a3e 100644
--- a/yaksh/static/yaksh/js/course.js
+++ b/yaksh/static/yaksh/js/course.js
@@ -66,4 +66,79 @@ $("#send_mail").click(function(){
return status;
});
+// Download course status as csv
+function exportTableToCSV($table, filename) {
+ var $headers = $table.find('tr:has(th)')
+ ,$rows = $table.find('tr:has(td)')
+
+ // Temporary delimiter characters unlikely to be typed by keyboard
+ // This is to avoid accidentally splitting the actual contents
+ ,tmpColDelim = String.fromCharCode(11) // vertical tab character
+ ,tmpRowDelim = String.fromCharCode(0) // null character
+
+ // actual delimiter characters for CSV format
+ ,colDelim = '","'
+ ,rowDelim = '"\r\n"';
+
+ // Grab text from table into CSV formatted string
+ var csv = '"';
+ csv += formatRows($headers.map(grabRow));
+ csv += rowDelim;
+ csv += formatRows($rows.map(grabRow)) + '"';
+
+ // Data URI
+ var csvData = 'data:application/csv;charset=utf-8,' + encodeURIComponent(csv);
+
+ // For IE (tested 10+)
+ if (window.navigator.msSaveOrOpenBlob) {
+ var blob = new Blob([decodeURIComponent(encodeURI(csv))], {
+ type: "text/csv;charset=utf-8;"
+ });
+ navigator.msSaveBlob(blob, filename);
+ } else {
+ $(this)
+ .attr({
+ 'download': filename,'href': csvData
+ });
+ }
+
+ function formatRows(rows){
+ return rows.get().join(tmpRowDelim)
+ .split(tmpRowDelim).join(rowDelim)
+ .split(tmpColDelim).join(colDelim);
+ }
+ // Grab and format a row from the table
+ function grabRow(i,row){
+ var $row = $(row);
+ var $cols = $row.find('td');
+ if(!$cols.length) $cols = $row.find('th');
+
+ return $cols.map(grabCol)
+ .get().join(tmpColDelim);
+ }
+ // Grab and format a column from the table
+ function grabCol(j,col){
+ var $col = $(col),
+ $text = $col.text();
+
+ return $text.replace('"', '""').replace("View Unit Status", '').replace("View Units", ""); // escape double quotes
+
+ }
+}
+
+
+$("#export").click(function (event) {
+ var outputFile = $("#course_name").val().replace(" ", "_") + '.csv';
+
+ exportTableToCSV.apply(this, [$('#course_table'), outputFile]);
});
+
+// Table sorter for course details
+$("table").tablesorter({});
+
+});
+
+function view_status(unit){
+ title_list = $(unit).attr("title").split("/");
+ $(unit).attr("title", title_list.join("\n"));
+}
diff --git a/yaksh/templates/yaksh/add_exercise.html b/yaksh/templates/yaksh/add_exercise.html
index dac35d4..77e3ee8 100644
--- a/yaksh/templates/yaksh/add_exercise.html
+++ b/yaksh/templates/yaksh/add_exercise.html
@@ -14,17 +14,41 @@
{% block content %}
<form name=frm id=frm action="" method="post" >
- {% csrf_token %}
- <center>
- <table class="span1 table">
- {{ form.as_table }}
- </table>
- <br/><br/>
- </center>
+ {% csrf_token %}
+ <center>
+ <table class="span1 table">
+ {{ form.as_table }}
+ </table>
+ <br/><br/>
+ </center>
+ <center><button class="btn" type="submit" id="submit" name="save_exercise"> Save
+ </button>
- <center><button class="btn" type="submit" id="submit" name="questionpaper"> Save
- </button>
-
- <button class="btn" type="button" name="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/courses/");'>Cancel</button> </center>
+ <button class="btn" type="button" name="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/courses/");'>Cancel</button> </center>
</form>
+{% if exercise and course_id %}
+ {% if exercise.questionpaper_set.get.id %}
+ <center>
+ <h4>You can check the quiz by attempting it in the following modes:</h4>
+ <a href="{{URL_ROOT}}/exam/manage/designquestionpaper/{{ exercise.id }}/{{exercise.questionpaper_set.get.id}}/{{course_id}}" class="btn btn-primary">View Question Paper</a>
+ <button class="btn" type="button" name="button" onClick='usermode("{{URL_ROOT}}/exam/manage/usermode/{{exercise.id}}/{{course_id}}/");'>User Mode</button>
+
+ <button class="btn" type="button" name="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/godmode/{{exercise.id}}/{{course_id}}/");'>
+ God Mode</button>
+ <a data-toggle="collapse" data-target="#help">
+ <span class="glyphicon glyphicon-info-sign">Help</span></a>
+ <div id="help" class="collapse">
+ <br/>
+ <ul>
+ <li><b>User Mode:</b> Attempt quiz the way normal users will attempt i.e. -
+ <ul>
+ <li><i>Quiz will have the same duration as that of the original quiz.</li>
+ <li>Quiz won't start if the course is inactive or the quiz time has expired.</li>
+ <li>You will be notified about quiz prerequisites.(You can still attempt the quiz though)</i></li>
+ </ul>
+ </p>
+ <li> <b>God Mode:</b> Attempt quiz without any time or eligibilty constraints.</p>
+ </div>
+ {% endif %}
+{% endif %}
{% endblock %}
diff --git a/yaksh/templates/yaksh/add_quiz.html b/yaksh/templates/yaksh/add_quiz.html
index d3705e3..684f804 100644
--- a/yaksh/templates/yaksh/add_quiz.html
+++ b/yaksh/templates/yaksh/add_quiz.html
@@ -35,31 +35,34 @@
</form>
<br>
-{% if quiz_id and course_id %}
+{% if quiz and course_id %}
+ {% if quiz.questionpaper_set.get.id %}
<center>
<h4>You can check the quiz by attempting it in the following modes:</h4>
- <button class="btn" type="button" name="button" onClick='usermode("{{URL_ROOT}}/exam/manage/usermode/{{quiz_id}}/{{course_id}}/");'>User Mode</button>
-
- <button class="btn" type="button" name="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/godmode/{{quiz_id}}/{{course_id}}/");'>
- God Mode</button>
+ <a href="{{URL_ROOT}}/exam/manage/designquestionpaper/{{ quiz.id }}/{{quiz.questionpaper_set.get.id}}/{{course_id}}" class="btn btn-primary">View Question Paper</a>
+ <button class="btn" type="button" name="button" onClick='usermode("{{URL_ROOT}}/exam/manage/usermode/{{quiz.id}}/{{course_id}}/");'>User Mode</button>
+
+ <button class="btn" type="button" name="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/godmode/{{quiz.id}}/{{course_id}}/");'>
+ God Mode</button>
<a data-toggle="collapse" data-target="#help">
<span class="glyphicon glyphicon-info-sign">Help</span></a>
- <div id="help" class="collapse">
- <br/>
- <ul>
- <li><b>User Mode:</b> Attempt quiz the way normal users will attempt i.e. -
- <ul>
- <li><i>Quiz will have the same duration as that of the original quiz.</li>
- <li>Quiz won't start if the course is inactive or the quiz time has expired.</li>
- <li>You will be notified about quiz prerequisites.(You can still attempt the quiz though)</i></li>
- </ul>
- </p>
- <li> <b>God Mode:</b> Attempt quiz without any time or eligibilty constraints.</p>
- </div>
- {% endif %}
- <style type="text/css">
+ <div id="help" class="collapse">
+ <br/>
+ <ul>
+ <li><b>User Mode:</b> Attempt quiz the way normal users will attempt i.e. -
+ <ul>
+ <li><i>Quiz will have the same duration as that of the original quiz.</li>
+ <li>Quiz won't start if the course is inactive or the quiz time has expired.</li>
+ <li>You will be notified about quiz prerequisites.(You can still attempt the quiz though)</i></li>
+ </ul>
+ </p>
+ <li> <b>God Mode:</b> Attempt quiz without any time or eligibilty constraints.</p>
+ </div>
+ {% endif %}
+{% endif %}
+<style type="text/css">
#rendered_text{
width: 550px;
}
- </style>
+</style>
{% endblock %}
diff --git a/yaksh/templates/yaksh/course_detail.html b/yaksh/templates/yaksh/course_detail.html
index cf0ab18..a5d10a7 100644
--- a/yaksh/templates/yaksh/course_detail.html
+++ b/yaksh/templates/yaksh/course_detail.html
@@ -1,5 +1,5 @@
{% extends "manage.html" %}
-
+{% load custom_filters %}
{% block title %} Course Details {% endblock title %}
<div class="col-md-9 col-md-offset-2 main">
@@ -10,6 +10,7 @@
<script language="JavaScript" type="text/javascript" src="{{ URL_ROOT }}/static/yaksh/js/course.js"></script>
<script type="text/javascript" src="{{ URL_ROOT }}/static/yaksh/js/tinymce/js/tinymce/tinymce.min.js"></script>
<script src="{{ URL_ROOT }}/static/yaksh/js/jquery-ui.js"></script>
+<script src="{{ URL_ROOT }}/static/yaksh/js/jquery.tablesorter.min.js"></script>
{% endblock %}
{% block css %}
<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/jquery-ui/jquery-ui.css">
@@ -20,7 +21,7 @@
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
- {% if state == 'mail'%}
+ {% if state == 'mail' or state == 'course_status' %}
<li><a href="{{URL_ROOT}}/exam/manage/course_detail/{{course.id}}/">
Go to Course Details</a></li>
{% else %}
@@ -35,6 +36,10 @@
<a href="{{URL_ROOT}}/exam/manage/send_mail/{{ course.id }}/">
Send Mail</a>
</li>
+ <li>
+ <a href="{{URL_ROOT}}/exam/manage/course_status/{{ course.id }}/">
+ View Course Status</a>
+ </li>
</ul>
</div>
</div>
@@ -84,7 +89,8 @@
<div id="reject">
<form action="{{URL_ROOT}}/exam/manage/send_mail/{{ course.id }}/" method="post" id="send_mail_form">
{% csrf_token %}
- <table class="table table-striped">
+ <table id="mail_table" class="tablesorter table table-striped" data-sortlist="[1,0]">
+ <thead>
<th></th>
<th></th>
<th>Full Name</th>
@@ -92,6 +98,8 @@
<th>Roll Number</th>
<th>Institute</th>
<th>Department</th>
+ </thead>
+ <tbody>
{% for enrolled in course.get_enrolled %}
<tr>
<td><input type="checkbox" name="check" value="{{ enrolled.id }}"></td>
@@ -103,6 +111,7 @@
<td> {{enrolled.profile.department}}</td>
</tr>
{% endfor %}
+ </tbody>
</table>
<br>
<textarea name="subject" id="subject" placeholder="Email Subject" cols="50"></textarea>
@@ -116,7 +125,72 @@
{% endif %}
</form>
</div>
+ {% elif state == "course_status" %}
+ <div class="course_data">
+ <input type="hidden" id="course_name" value="{{course.name}}">
+ <a href="#" class="btn btn-info" id="export">Export to CSV</a>
+ <center><h2>Course Status</h2></center>
+ <table class="tablesorter table table-bordered" id="course_table" data-sortlist="[0,0]">
+ <thead>
+ <tr>
+ <th>Sr No.</th>
+ <th>Students</th>
+ <th>Total</th>
+ <th colspan="{{modules|length}}">Modules</th>
+ </tr>
+ <tr>
+ <th scope="row"></th>
+ <th></th>
+ <th></th>
+ {% if modules %}
+ {% for module in modules %}
+ <th>
+ {{module.name}}
+ <br>
+ ({{module.get_learning_units|length}} Units)
+ <br>
+ <a data-target="tooltip" title="{% for unit in module.get_learning_units %}{% if unit.type == 'quiz' %}{{unit.quiz.description}}{% else %}{{unit.lesson.name}}{% endif %} / {% endfor %}" id="unit_status{{module.id}}" onmouseover="view_status('#unit_status{{module.id}}')">
+ View Units</a>
+ </th>
+ {% endfor %}
+ {% else %}
+ <th></th>
+ {% endif %}
+ </tr>
+ </thead>
+ <tbody>
+ {% for student in students %}
+ <tr>
+ <td width="5%">
+ {{forloop.counter}}.
+ </td>
+ <td>
+ {{ student.get_full_name|title }}
+ </td>
+ <td>
+ {% course_completion_percent course student as c_percent %}
+ {{c_percent}} %
+ </td>
+ {% if modules %}
+ {% for module in modules %}
+ <td>
+ {% module_completion_percent course module student as m_percent %}
+ {{m_percent}} %
+ <br>
+ <a data-target="tooltip" title="{% for unit in module.get_learning_units %}{% if unit.type == 'quiz' %}{{unit.quiz.description}}{% else %}{{unit.lesson.name}}{% endif %} - {% get_unit_status course module unit student as status %}{{status|title}} / {% endfor %}" id="unit_status{{module.id}}{{student.id}}" onmouseover="view_status('#unit_status{{module.id}}{{student.id}}')">
+ View Unit Status</a>
+ </td>
+ {% endfor %}
+ {% else %}
+ <td>-------</td>
+ {% endif %}
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ </div>
{% else %}
+ <div id="students_enrollment">
<div id="student-requests">
<center><b><u>Requests</u></b></center><br>
{% if course.get_requests %}
@@ -124,7 +198,8 @@
<div id="enroll-all">
<form action="{{URL_ROOT}}/exam/manage/enroll/{{ course.id }}/" method="post">
{% csrf_token %}
- <table class="table table-striped">
+ <table id="requested_table" class="tablesorter table table-striped" data-sortlist="[1,0]">
+ <thead>
<th></th>
<th></th>
<th>Full Name</th>
@@ -133,6 +208,8 @@
<th>Institute</th>
<th>Department</th>
<th>Enroll/Reject</th>
+ </thead>
+ <tbody>
{% for request in course.get_requests %}
<tr>
<td><input type="checkbox" name="check" value="{{ request.id }}"></td>
@@ -152,6 +229,7 @@
</td>
</tr>
{% endfor %}
+ </tbody>
</table>
<button class="btn btn-success" type="submit" name='enroll' value='enroll'>Enroll Selected</button>
</div>
@@ -166,7 +244,8 @@
<div id="reject">
<form action="{{URL_ROOT}}/exam/manage/enrolled/reject/{{ course.id }}/" method="post" id="reject-form">
{% csrf_token %}
- <table class="table table-striped">
+ <table id="enrolled_table" class="tablesorter table table-striped" data-sortlist="[1,0]">
+ <thead>
<th></th>
<th></th>
<th>Full Name</th>
@@ -175,6 +254,8 @@
<th>Institute</th>
<th>Department</th>
<th>Reject</th>
+ </thead>
+ <tbody>
{% for enrolled in course.get_enrolled %}
<tr>
<td><input type="checkbox" name="check" value="{{ enrolled.id }}"></td>
@@ -190,6 +271,7 @@
</td>
</tr>
{% endfor %}
+ </tbody>
</table>
<button class="btn btn-danger" type="submit" name='reject' value='reject'>
Reject Selected</button>
@@ -205,7 +287,8 @@
<div id="enroll">
<form action="{{URL_ROOT}}/exam/manage/enroll/rejected/{{ course.id }}/" method="post">
{% csrf_token %}
- <table class="table table-striped">
+ <table id="rejected_table" class="tablesorter table table-striped" data-sortlist="[1,0]">
+ <thead>
<th></th>
<th></th>
<th>Full Name</th>
@@ -214,6 +297,8 @@
<th>Institute</th>
<th>Department</th>
<th>Enroll</th>
+ </thead>
+ <tbody>
{% for rejected in course.get_rejected %}
<tr>
<td><input type="checkbox" name="check" value="{{ rejected.id }}"></td>
@@ -230,6 +315,7 @@
</td>
</tr>
{% endfor %}
+ </tbody>
</table>
<br>
<button class="btn btn-success" type="submit" name='enroll' value='enroll'>
@@ -238,6 +324,7 @@
{% endif %}
</form>
</div>
+ </div>
{% endif %}
</div>
</div>
diff --git a/yaksh/templates/yaksh/register.html b/yaksh/templates/yaksh/register.html
index 7cf15a6..13cd248 100644
--- a/yaksh/templates/yaksh/register.html
+++ b/yaksh/templates/yaksh/register.html
@@ -12,7 +12,8 @@
<table class="table">
{{ form.as_table }}
</table></center>
- <center><button class="btn btn-primary" type="submit">Register</button>&nbsp;&nbsp;<button class="btn btn-primary" type="reset">Cancel</button></center>
+ <center><button class="btn btn-primary" type="submit">Register</button>&nbsp;&nbsp;
+ <a href="{{URL_ROOT}}/exam" class="btn btn-danger">Cancel</a></center>
</form>
{% endblock content %}
diff --git a/yaksh/templates/yaksh/show_video.html b/yaksh/templates/yaksh/show_video.html
index f4b59ac..17f9d86 100644
--- a/yaksh/templates/yaksh/show_video.html
+++ b/yaksh/templates/yaksh/show_video.html
@@ -64,6 +64,7 @@
{% if learning_module.html_data%}
<hr>
{% endif %}
+ {% if learning_module.get_learning_units %}
<center><h4>Following are the units in this modules</h4></center>
<table class="table">
<tr>
@@ -95,13 +96,23 @@
</tr>
{% endfor %}
</table>
+ {% else %}
+ <center><h3>No Lessons/Quizzes Found</h3></center>
+ {% endif %}
</div>
</div>
<div style="text-align: center;">
- <a href="{{ URL_ROOT }}/exam/next_unit/{{course.id}}/{{learning_module.id}}/{{first_unit.id}}/1" class="btn btn-info">Start
- <span class="glyphicon glyphicon-chevron-right">
- </span>
- </a>
+ {% if first_unit %}
+ <a href="{{ URL_ROOT }}/exam/next_unit/{{course.id}}/{{learning_module.id}}/{{first_unit.id}}/1" class="btn btn-info">Start
+ <span class="glyphicon glyphicon-chevron-right">
+ </span>
+ </a>
+ {% else %}
+ <a href="{{ URL_ROOT }}/exam/next_unit/{{course.id}}/{{learning_module.id}}" class="btn btn-info">Next
+ <span class="glyphicon glyphicon-chevron-right">
+ </span>
+ </a>
+ {% endif %}
</div>
{% else %}
<div class="panel panel-default" style="border: none; box-shadow: none;">
diff --git a/yaksh/templatetags/custom_filters.py b/yaksh/templatetags/custom_filters.py
index 6ddd213..3c2c6fd 100644
--- a/yaksh/templatetags/custom_filters.py
+++ b/yaksh/templatetags/custom_filters.py
@@ -8,43 +8,57 @@ except ImportError:
register = template.Library()
+
@stringfilter
@register.filter(name='escape_quotes')
def escape_quotes(value):
- value = value.decode("utf-8")
- escape_single_quotes = value.replace("'", "\\'")
- escape_single_and_double_quotes = escape_single_quotes.replace('"', '\\"')
+ value = value.decode("utf-8")
+ escape_single_quotes = value.replace("'", "\\'")
+ escape_single_and_double_quotes = escape_single_quotes.replace('"', '\\"')
+
+ return escape_single_and_double_quotes
- return escape_single_and_double_quotes
@register.assignment_tag(name="completed")
def completed(answerpaper):
- return answerpaper.filter(status="completed").count()
+ return answerpaper.filter(status="completed").count()
+
@register.assignment_tag(name="inprogress")
def inprogress(answerpaper):
- return answerpaper.filter(status="inprogress").count()
+ return answerpaper.filter(status="inprogress").count()
+
@register.filter(name='zip')
def zip_longest_out(a, b):
- return zip_longest(a, b)
+ return zip_longest(a, b)
@register.filter(name="file_title")
def file_title(name):
- return os.path.basename(name)
+ return os.path.basename(name)
@register.simple_tag
def get_unit_status(course, module, unit, user):
- return course.get_unit_completion_status(module, user, unit)
+ return course.get_unit_completion_status(module, user, unit)
@register.simple_tag
def get_module_status(user, module, course):
- return module.get_status(user, course)
+ return module.get_status(user, course)
@register.simple_tag
def get_course_details(course):
- return course.get_quiz_details()
+ return course.get_quiz_details()
+
+
+@register.simple_tag
+def module_completion_percent(course, module, user):
+ return module.get_module_complete_percent(course, user)
+
+
+@register.simple_tag
+def course_completion_percent(course, user):
+ return course.percent_completed(user)
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index 0cd76bc..df8372c 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -2,7 +2,7 @@ import unittest
from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\
StdIOBasedTestCase, FileUpload, McqTestCase, AssignmentUpload,\
- LearningModule, LearningUnit, Lesson, LessonFile
+ LearningModule, LearningUnit, Lesson, LessonFile, CourseStatus
from yaksh.code_server import(ServerPool,
get_result as get_result_from_code_server
)
@@ -204,6 +204,26 @@ class LearningModuleTestCases(unittest.TestCase):
# Then
self.assertEqual(status, module_status)
+ def test_module_completion_percent(self):
+ # for module without learning units
+ percent = self.learning_module_two.get_module_complete_percent(
+ self.course, self.student
+ )
+ self.assertEqual(percent, 0)
+
+ # for module with learning units
+ lesson = Lesson.objects.get(name='L1')
+ self.completed_unit = LearningUnit.objects.get(lesson=lesson)
+
+ course_status = CourseStatus.objects.create(
+ course=self.course, user=self.student)
+ course_status.completed_units.add(self.completed_unit)
+
+ percent = self.learning_module.get_module_complete_percent(
+ self.course, self.student
+ )
+ self.assertEqual(percent, 50)
+
class LearningUnitTestCases(unittest.TestCase):
def setUp(self):
@@ -1585,6 +1605,26 @@ class CourseTestCases(unittest.TestCase):
"""Test to check enrollment is closed for open course"""
self.assertFalse(self.enroll_request_course.is_active_enrollment())
+ def test_course_complete_percent(self):
+ # for course with no modules
+ self.no_module_course = Course.objects.create(
+ name="test_course", creator=self.creator, enrollment="open")
+ percent = self.course.percent_completed(self.student1)
+ self.assertEqual(percent, 0)
+
+ # for course with module but zero percent completed
+ percent = self.course.percent_completed(self.student1)
+ self.assertEqual(percent, 0)
+
+ # Add completed unit to course status and check percent
+ lesson = Lesson.objects.get(name='L1')
+ self.completed_unit = LearningUnit.objects.get(lesson=lesson)
+
+ course_status = CourseStatus.objects.create(
+ course=self.course, user=self.student1)
+ course_status.completed_units.add(self.completed_unit)
+ updated_percent = self.course.percent_completed(self.student1)
+ self.assertEqual(updated_percent, 25)
###############################################################################
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index 71d6f80..9be8d13 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -1230,6 +1230,7 @@ class TestAddQuiz(TestCase):
kwargs={'quiz_id': self.exercise.id}),
data={
'description': 'updated demo exercise',
+ 'active': True
}
)
@@ -1257,6 +1258,7 @@ class TestAddQuiz(TestCase):
response = self.client.post(reverse('yaksh:add_exercise'),
data={
'description': "Demo Exercise",
+ 'active': True
}
)
quiz_list = Quiz.objects.all().order_by('-id')
@@ -1916,6 +1918,25 @@ class TestAddCourse(TestCase):
timezone='UTC'
)
+ # Create a teacher
+ self.teacher_plaintext_pass = 'demo_teacher'
+ self.teacher = User.objects.create_user(
+ username='demo_teacher',
+ password=self.teacher_plaintext_pass,
+ first_name='first_name',
+ last_name='last_name',
+ email='demo@test.com'
+ )
+
+ Profile.objects.create(
+ user=self.teacher,
+ roll_number=10,
+ institute='IIT',
+ department='Chemical',
+ position='Moderator',
+ timezone='UTC'
+ )
+
# Create Student
self.student_plaintext_pass = 'demo_student'
self.student = User.objects.create_user(
@@ -1928,10 +1949,13 @@ class TestAddCourse(TestCase):
# Add to moderator group
self.mod_group.user_set.add(self.user)
+ self.mod_group.user_set.add(self.teacher)
self.course = Course.objects.create(name="Python Course",
enrollment="Enroll Request", creator=self.user)
+ self.course.teachers.add(self.teacher)
+
self.pre_req_quiz = Quiz.objects.create(
start_date_time=datetime(2014, 2, 1, 5, 8, 15, 0, tzone),
end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzone),
@@ -2021,6 +2045,33 @@ class TestAddCourse(TestCase):
self.assertRedirects(response, '/exam/manage/courses',
target_status_code=301)
+ def test_add_course_teacher_cannot_be_creator(self):
+ """
+ Teacher editing the course should not become creator
+ """
+ self.client.login(
+ username=self.teacher.username,
+ password=self.teacher_plaintext_pass
+ )
+
+ response = self.client.post(reverse('yaksh:edit_course',
+ kwargs={"course_id": self.course.id}),
+ data={'name': 'Teacher_course',
+ 'active': True,
+ 'enrollment': 'open',
+ 'start_enroll_time': '2016-01-10 09:00:15',
+ 'end_enroll_time': '2016-01-15 09:00:15',
+ }
+ )
+ updated_course = Course.objects.get(id=self.course.id)
+ self.assertEqual(updated_course.name, 'Teacher_course')
+ self.assertEqual(updated_course.enrollment, 'open')
+ self.assertEqual(updated_course.active, True)
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(updated_course.creator, self.user)
+ self.assertRedirects(response, '/exam/manage/courses',
+ target_status_code=301)
+
class TestCourseDetail(TestCase):
def setUp(self):
@@ -2089,6 +2140,11 @@ class TestCourseDetail(TestCase):
self.user1_course = Course.objects.create(name="Python Course",
enrollment="Enroll Request", creator=self.user1)
+ self.learning_module = LearningModule.objects.create(
+ name="test module", description="test description module",
+ html_data="test html description module", creator=self.user1,
+ order=1)
+ self.user1_course.learning_module.add(self.learning_module)
def tearDown(self):
self.client.logout()
@@ -2475,6 +2531,30 @@ class TestCourseDetail(TestCase):
self.assertEqual(response.get('Content-Disposition'),
'attachment; filename="sample_user_upload"')
+ def test_view_course_status(self):
+ """ Test to view course status """
+ self.client.login(
+ username=self.student.username,
+ password=self.student_plaintext_pass
+ )
+
+ # Denies student to view course status
+ response = self.client.get(reverse('yaksh:course_status',
+ kwargs={'course_id': self.user1_course.id}))
+ self.assertEqual(response.status_code, 404)
+
+ # Moderator Login
+ self.client.login(
+ username=self.user1.username,
+ password=self.user1_plaintext_pass
+ )
+ response = self.client.get(reverse('yaksh:course_status',
+ kwargs={'course_id': self.user1_course.id}))
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context['state'], "course_status")
+ self.assertEqual(response.context['course'], self.user1_course)
+ self.assertEqual(response.context['modules'][0], self.learning_module)
+
class TestEnrollRequest(TestCase):
def setUp(self):
@@ -3960,8 +4040,27 @@ class TestQuestionPaper(TestCase):
timezone='UTC'
)
+ self.teacher_plaintext_pass = 'demo_teacher'
+ self.teacher = User.objects.create_user(
+ username='demo_teacher',
+ password=self.teacher_plaintext_pass,
+ first_name='first_name',
+ last_name='last_name',
+ email='demo@test.com'
+ )
+
+ Profile.objects.create(
+ user=self.teacher,
+ roll_number=10,
+ institute='IIT',
+ department='Chemical',
+ position='Moderator',
+ timezone='UTC'
+ )
+
# Add to moderator group
self.mod_group.user_set.add(self.user)
+ self.mod_group.user_set.add(self.teacher)
self.course = Course.objects.create(
name="Python Course",
@@ -3976,6 +4075,15 @@ class TestQuestionPaper(TestCase):
creator=self.user
)
+ self.demo_quiz = Quiz.objects.create(
+ start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
+ end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzone),
+ duration=30, active=True, instructions="Demo Instructions",
+ attempts_allowed=-1, time_between_attempts=0,
+ description='demo quiz 2', pass_criteria=40,
+ creator=self.user
+ )
+
self.learning_unit = LearningUnit.objects.create(
order=1, type="quiz", quiz=self.quiz)
self.learning_module = LearningModule.objects.create(
@@ -3997,8 +4105,6 @@ class TestQuestionPaper(TestCase):
)
self.mcq_based_testcase.save()
- ordered_questions = str(self.question_mcq.id)
-
# Mcc Question
self.question_mcc = Question.objects.create(
summary="Test_mcc_question", description="Test MCC",
@@ -4012,8 +4118,6 @@ class TestQuestionPaper(TestCase):
)
self.mcc_based_testcase.save()
- ordered_questions = ordered_questions + str(self.question_mcc.id)
-
# Integer Question
self.question_int = Question.objects.create(
summary="Test_mcc_question", description="Test MCC",
@@ -4026,8 +4130,6 @@ class TestQuestionPaper(TestCase):
)
self.int_based_testcase.save()
- ordered_questions = ordered_questions + str(self.question_int.id)
-
# String Question
self.question_str = Question.objects.create(
summary="Test_mcc_question", description="Test MCC",
@@ -4054,17 +4156,19 @@ class TestQuestionPaper(TestCase):
)
self.float_based_testcase.save()
- ordered_questions = ordered_questions + str(self.question_float.id)
-
- questions_list = [self.question_mcq, self.question_mcc,
- self.question_int, self.question_str,
- self.question_float]
-
+ self.questions_list = [self.question_mcq, self.question_mcc,
+ self.question_int, self.question_str,
+ self.question_float]
+ questions_order = ",".join([
+ str(self.question_mcq.id), str(self.question_mcc.id),
+ str(self.question_int.id), str(self.question_str.id),
+ str(self.question_float.id)
+ ])
self.question_paper = QuestionPaper.objects.create(
quiz=self.quiz,
- total_marks=5.0, fixed_question_order=ordered_questions
+ total_marks=5.0, fixed_question_order=questions_order
)
- self.question_paper.fixed_questions.add(*questions_list)
+ self.question_paper.fixed_questions.add(*self.questions_list)
self.answerpaper = AnswerPaper.objects.create(
user=self.user, question_paper=self.question_paper,
attempt_number=1,
@@ -4073,12 +4177,14 @@ class TestQuestionPaper(TestCase):
user_ip="127.0.0.1", status="inprogress", passed=False,
percent=0, marks_obtained=0, course=self.course
)
- self.answerpaper.questions.add(*questions_list)
+ self.answerpaper.questions.add(*self.questions_list)
def tearDown(self):
self.client.logout()
self.user.delete()
+ self.teacher.delete()
self.quiz.delete()
+ self.demo_quiz.delete()
self.course.delete()
self.answerpaper.delete()
self.question_mcq.delete()
@@ -4352,6 +4458,58 @@ class TestQuestionPaper(TestCase):
wrong_answer_paper = AnswerPaper.objects.get(id=self.answerpaper.id)
self.assertEqual(wrong_answer_paper.marks_obtained, 0)
+ def test_design_questionpaper(self):
+ """ Test design Question Paper """
+
+ # Should fail if Question paper is not the one which is associated
+ # with a quiz
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+
+ response = self.client.get(
+ reverse('yaksh:designquestionpaper',
+ kwargs={"quiz_id": self.demo_quiz.id,
+ "questionpaper_id": self.question_paper.id}))
+ self.assertEqual(response.status_code, 404)
+
+ self.client.login(
+ username=self.teacher.username,
+ password=self.teacher_plaintext_pass
+ )
+
+ # Should not allow teacher to view question paper
+ response = self.client.get(
+ reverse('yaksh:designquestionpaper',
+ kwargs={"quiz_id": self.quiz.id,
+ "questionpaper_id": self.question_paper.id}))
+
+ self.assertEqual(response.status_code, 404)
+
+ # Should not allow teacher to view question paper
+ response = self.client.get(
+ reverse('yaksh:designquestionpaper',
+ kwargs={"quiz_id": self.quiz.id,
+ "course_id": self.course.id,
+ "questionpaper_id": self.question_paper.id}))
+
+ self.assertEqual(response.status_code, 404)
+
+ # Should allow course teacher to view question paper
+ # Add teacher to the course
+ self.course.teachers.add(self.teacher)
+ response = self.client.get(
+ reverse('yaksh:designquestionpaper',
+ kwargs={"quiz_id": self.quiz.id,
+ "course_id": self.course.id,
+ "questionpaper_id": self.question_paper.id}))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'yaksh/design_questionpaper.html')
+ self.assertEqual(response.context['fixed_questions'],
+ self.questions_list)
+ self.assertEqual(response.context['qpaper'], self.question_paper)
+
class TestLearningModule(TestCase):
def setUp(self):
@@ -4446,6 +4604,7 @@ class TestLearningModule(TestCase):
check_prerequisite=False, creator=self.user)
self.course.teachers.add(self.teacher)
self.course.learning_module.add(self.learning_module)
+ self.course.learning_module.add(self.learning_module1)
self.expected_url = "/exam/manage/courses/all_learning_module/"
@@ -4710,13 +4869,35 @@ class TestLearningModule(TestCase):
self.assertEqual(response.context["current_unit"].id,
self.learning_unit1.id)
+ # Go to next module with empty module
+ response = self.client.get(
+ reverse('yaksh:next_unit',
+ kwargs={"module_id": self.learning_module1.id,
+ "course_id": self.course.id}),
+ follow=True)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["state"], "module")
+ self.assertEqual(response.context["learning_module"].id,
+ self.learning_module.id)
+
+ # Go to next module from last unit of previous unit
+ response = self.client.get(
+ reverse('yaksh:next_unit',
+ kwargs={"module_id": self.learning_module.id,
+ "course_id": self.course.id,
+ "current_unit_id": self.learning_unit1.id}),
+ follow=True)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["state"], "module")
+ self.assertEqual(response.context["learning_module"].id,
+ self.learning_module1.id)
+
class TestLessons(TestCase):
def setUp(self):
self.client = Client()
self.mod_group = Group.objects.create(name='moderator')
- tzone = pytz.timezone('UTC')
# Create Moderator with profile
self.user_plaintext_pass = 'demo'
self.user = User.objects.create_user(
@@ -4764,25 +4945,30 @@ class TestLessons(TestCase):
name="Python Course",
enrollment="Open Enrollment", creator=self.user)
- self.quiz = Quiz.objects.create(
- start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0, tzone),
- end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzone),
- duration=30, active=True, instructions="Demo Instructions",
- attempts_allowed=-1, time_between_attempts=0,
- description='demo quiz', pass_criteria=40,
- creator=self.user
- )
-
self.lesson = Lesson.objects.create(
name="test lesson", description="test description",
creator=self.user)
+ self.lesson2 = Lesson.objects.create(
+ name="test lesson2", description="test description2",
+ creator=self.user)
self.learning_unit = LearningUnit.objects.create(
- order=0, type="lesson", lesson=self.lesson)
+ order=0, type="lesson", lesson=self.lesson
+ )
+ self.learning_unit2 = LearningUnit.objects.create(
+ order=0, type="lesson", lesson=self.lesson2
+ )
self.learning_module = LearningModule.objects.create(
order=0, name="test module", description="module",
- check_prerequisite=False, creator=self.user)
+ check_prerequisite=False, creator=self.user
+ )
+ self.learning_module2 = LearningModule.objects.create(
+ order=1, name="test module 2", description="module 2",
+ check_prerequisite=True, creator=self.user
+ )
self.learning_module.learning_unit.add(self.learning_unit.id)
- self.course.learning_module.add(self.learning_module.id)
+ self.learning_module2.learning_unit.add(self.learning_unit2.id)
+ self.course.learning_module.add(*[
+ self.learning_module.id, self.learning_module2.id])
self.course.teachers.add(self.teacher.id)
self.expected_url = "/exam/manage/courses/"
@@ -4791,11 +4977,13 @@ class TestLessons(TestCase):
self.user.delete()
self.student.delete()
self.teacher.delete()
- self.quiz.delete()
self.course.delete()
self.learning_unit.delete()
+ self.learning_unit2.delete()
self.learning_module.delete()
+ self.learning_module2.delete()
self.lesson.delete()
+ self.lesson2.delete()
def test_edit_lesson_denies_non_moderator(self):
""" Student should not be allowed to edit lesson """
@@ -4880,6 +5068,17 @@ class TestLessons(TestCase):
self.assertEqual(response.context["state"], "lesson")
self.assertEqual(response.context["current_unit"], self.learning_unit)
+ # Check unit module prerequisite completion status
+ response = self.client.get(
+ reverse('yaksh:show_lesson',
+ kwargs={"lesson_id": self.lesson2.id,
+ "module_id": self.learning_module2.id,
+ "course_id": self.course.id}))
+ err_msg = "You have not completed the module previous to {0}".format(
+ self.learning_module2.name)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["msg"], err_msg)
+
def test_show_all_lessons(self):
""" Moderator should be able to see all created lessons"""
self.client.login(
diff --git a/yaksh/urls.py b/yaksh/urls.py
index b4bbb41..08c2091 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -40,6 +40,8 @@ urlpatterns = [
views.view_module, name='view_module'),
url(r'^next_unit/(?P<course_id>\d+)/(?P<module_id>\d+)/(?P<current_unit_id>\d+)/$',
views.get_next_unit, name='next_unit'),
+ url(r'^next_unit/(?P<course_id>\d+)/(?P<module_id>\d+)/$',
+ views.get_next_unit, name='next_unit'),
url(r'^next_unit/(?P<course_id>\d+)/(?P<module_id>\d+)/(?P<current_unit_id>\d+)/(?P<first_unit>\d+)/$',
views.get_next_unit, name='next_unit'),
url(r'^course_modules/(?P<course_id>\d+)/$',
@@ -76,6 +78,8 @@ urlpatterns = [
name='design_questionpaper'),
url(r'^manage/designquestionpaper/(?P<quiz_id>\d+)/(?P<questionpaper_id>\d+)/$',
views.design_questionpaper, name='designquestionpaper'),
+ url(r'^manage/designquestionpaper/(?P<quiz_id>\d+)/(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$',
+ views.design_questionpaper, name='designquestionpaper'),
url(r'^manage/statistics/question/(?P<questionpaper_id>\d+)/(?P<course_id>\d+)/$',
views.show_statistics, name="show_statistics"),
url(r'^manage/statistics/question/(?P<questionpaper_id>\d+)/(?P<attempt_number>\d+)/(?P<course_id>\d+)/$',
@@ -166,4 +170,6 @@ urlpatterns = [
views.design_course, name="design_course"),
url(r'^manage/courses/designcourse/(?P<course_id>\d+)/$',
views.design_course, name="design_course"),
+ url(r'^manage/course_status/(?P<course_id>\d+)/$',
+ views.course_status, name="course_status"),
]
diff --git a/yaksh/views.py b/yaksh/views.py
index 49249ca..2bc5dfe 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -320,10 +320,9 @@ def add_quiz(request, quiz_id=None, course_id=None):
return my_redirect("/exam/manage/courses/")
else:
- quiz = Quiz.objects.get(id=quiz_id) if quiz_id else None
form = QuizForm(instance=quiz)
- context["quiz_id"] = quiz_id
context["course_id"] = course_id
+ context["quiz"] = quiz
context["form"] = form
return my_render_to_response(
'yaksh/add_quiz.html', context, context_instance=ci
@@ -346,7 +345,7 @@ def add_exercise(request, quiz_id=None, course_id=None):
if course_id:
course = get_object_or_404(Course, pk=course_id)
if not course.is_creator(user) and not course.is_teacher(user):
- raise Http404('This quiz does not belong to you')
+ raise Http404('This Course does not belong to you')
context = {}
if request.method == "POST":
@@ -370,9 +369,8 @@ def add_exercise(request, quiz_id=None, course_id=None):
return my_redirect("/exam/manage/courses/")
else:
- quiz = Quiz.objects.get(id=quiz_id) if quiz_id else None
form = ExerciseForm(instance=quiz)
- context["quiz_id"] = quiz_id
+ context["exercise"] = quiz
context["course_id"] = course_id
context["form"] = form
return my_render_to_response(
@@ -477,6 +475,16 @@ def start(request, questionpaper_id=None, attempt_num=None, course_id=None,
learning_module = course.learning_module.get(id=module_id)
learning_unit = learning_module.learning_unit.get(quiz=quest_paper.quiz.id)
+ # unit module prerequiste check
+ if learning_module.has_prerequisite():
+ if not learning_module.is_prerequisite_passed(user, course):
+ msg = "You have not completed the module previous to {0}".format(
+ learning_module.name)
+ return course_modules(request, course_id, msg)
+
+ # update course status with current unit
+ _update_unit_status(course_id, user, learning_unit)
+
# is user enrolled in the course
if not course.is_enrolled(user):
msg = 'You are not enrolled in {0} course'.format(course.name)
@@ -931,14 +939,14 @@ def add_course(request, course_id=None):
raise Http404("You are not allowed to view this course")
else:
course = None
-
if not is_moderator(user):
raise Http404('You are not allowed to view this page')
if request.method == 'POST':
form = CourseForm(request.POST, instance=course)
if form.is_valid():
new_course = form.save(commit=False)
- new_course.creator = user
+ if course_id is None:
+ new_course.creator = user
new_course.save()
return my_redirect('/exam/manage/courses')
else:
@@ -1157,7 +1165,7 @@ def show_statistics(request, questionpaper_id, attempt_number=None,
course_id):
return my_redirect('/exam/manage/')
question_stats = AnswerPaper.objects.get_question_statistics(
- questionpaper_id, attempt_number
+ questionpaper_id, attempt_number, course_id
)
context = {'question_stats': question_stats, 'quiz': quiz,
'questionpaper_id': questionpaper_id,
@@ -1291,14 +1299,21 @@ def _remove_already_present(questionpaper_id, questions):
@login_required
@email_verified
-def design_questionpaper(request, quiz_id, questionpaper_id=None):
+def design_questionpaper(request, quiz_id, questionpaper_id=None,
+ course_id=None):
user = request.user
if not is_moderator(user):
raise Http404('You are not allowed to view this page!')
- quiz = Quiz.objects.get(id=quiz_id)
- if not quiz.creator == user:
- raise Http404('This course does not belong to you')
+ if quiz_id:
+ quiz = get_object_or_404(Quiz, pk=quiz_id)
+ if quiz.creator != user and not course_id:
+ raise Http404('This quiz does not belong to you')
+ if course_id:
+ course = get_object_or_404(Course, pk=course_id)
+ if not course.is_creator(user) and not course.is_teacher(user):
+ raise Http404('This Course does not belong to you')
+
filter_form = QuestionFilterForm(user=user)
questions = None
marks = None
@@ -1306,7 +1321,8 @@ def design_questionpaper(request, quiz_id, questionpaper_id=None):
if questionpaper_id is None:
question_paper = QuestionPaper.objects.get_or_create(quiz_id=quiz_id)[0]
else:
- question_paper = get_object_or_404(QuestionPaper, id=questionpaper_id)
+ question_paper = get_object_or_404(QuestionPaper, id=questionpaper_id,
+ quiz_id=quiz_id)
qpaper_form = QuestionPaperForm(instance=question_paper)
if request.method == 'POST':
@@ -1523,9 +1539,9 @@ def download_quiz_csv(request, course_id, quiz_id):
questions = question_paper.get_question_bank()
answerpapers = AnswerPaper.objects.filter(question_paper=question_paper,
- attempt_number=attempt_number)
+ attempt_number=attempt_number, course_id=course_id)
if not answerpapers:
- return monitor(request, quiz_id)
+ return monitor(request, quiz_id, course_id)
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="{0}-{1}-attempt{2}.csv"'.format(
course.name.replace('.', ''), quiz.description.replace('.', ''),
@@ -2332,6 +2348,15 @@ def show_lesson(request, lesson_id, module_id, course_id):
learn_module = course.learning_module.get(id=module_id)
learn_unit = learn_module.learning_unit.get(lesson_id=lesson_id)
learning_units = learn_module.get_learning_units()
+ if learn_module.has_prerequisite():
+ if not learn_module.is_prerequisite_passed(user, course):
+ msg = "You have not completed the module previous to {0}".format(
+ learn_module.name)
+ return view_module(request, module_id, course_id, msg)
+
+ # update course status with current unit
+ _update_unit_status(course_id, user, learn_unit)
+
all_modules = course.get_learning_modules()
if learn_unit.has_prerequisite():
if not learn_unit.is_prerequisite_passed(user, learn_module, course):
@@ -2508,7 +2533,7 @@ def preview_html_text(request):
@login_required
@email_verified
-def get_next_unit(request, course_id, module_id, current_unit_id,
+def get_next_unit(request, course_id, module_id, current_unit_id=None,
first_unit=None):
user = request.user
course = Course.objects.prefetch_related("learning_module").get(
@@ -2517,8 +2542,14 @@ def get_next_unit(request, course_id, module_id, current_unit_id,
raise Http404('You are not enrolled for this course!')
learning_module = course.learning_module.prefetch_related(
"learning_unit").get(id=module_id)
- current_learning_unit = learning_module.learning_unit.get(
- id=current_unit_id)
+
+ if current_unit_id:
+ current_learning_unit = learning_module.learning_unit.get(
+ id=current_unit_id)
+ else:
+ next_module = course.next_module(learning_module.id)
+ return my_redirect("/exam/quizzes/view_module/{0}/{1}".format(
+ next_module.id, course_id))
if first_unit:
next_unit = current_learning_unit
@@ -2547,9 +2578,6 @@ def get_next_unit(request, course_id, module_id, current_unit_id,
return my_redirect("/exam/quizzes/view_module/{0}/{1}/".format(
next_module.id, course.id))
- # make next available unit as current unit
- course_status.current_unit = next_unit
- course_status.save()
if next_unit.type == "quiz":
return my_redirect("/exam/start/{0}/{1}/{2}".format(
next_unit.quiz.questionpaper_set.get().id, module_id, course_id))
@@ -2635,13 +2663,14 @@ def view_module(request, module_id, course_id, msg=None):
all_modules = course.get_learning_modules()
if learning_module.has_prerequisite():
if not learning_module.is_prerequisite_passed(user, course):
- msg = "You have not completed the previous learning module"
+ msg = "You have not completed the module previous to {0}".format(
+ learning_module.name)
return course_modules(request, course_id, msg)
learning_units = learning_module.get_learning_units()
context['learning_units'] = learning_units
context['learning_module'] = learning_module
- context['first_unit'] = learning_units[0]
+ context['first_unit'] = learning_units.first()
context['all_modules'] = all_modules
context['user'] = user
context['course'] = course
@@ -2666,3 +2695,36 @@ def course_modules(request, course_id, msg=None):
context = {"course": course, "learning_modules": learning_modules,
"user": user, "msg": msg}
return my_render_to_response('yaksh/course_modules.html', context)
+
+
+@login_required
+@email_verified
+def course_status(request, course_id):
+ user = request.user
+ if not is_moderator(user):
+ raise Http404('You are not allowed to view this page!')
+ course = get_object_or_404(Course, pk=course_id)
+ if not course.is_creator(user) and not course.is_teacher(user):
+ raise Http404('This course does not belong to you')
+ students = course.get_only_students()
+ context = {
+ 'course': course, 'students': students,
+ 'state': 'course_status', 'modules': course.get_learning_modules()
+ }
+ return my_render_to_response('yaksh/course_detail.html', context)
+
+
+def _update_unit_status(course_id, user, unit):
+ """ Update course status with current unit """
+ course_status = CourseStatus.objects.filter(
+ user=user, course_id=course_id,
+ )
+ if not course_status.exists():
+ course_status = CourseStatus.objects.create(
+ user=user, course_id=course_id
+ )
+ else:
+ course_status = course_status.first()
+ # make next available unit as current unit
+ course_status.current_unit = unit
+ course_status.save()