summaryrefslogtreecommitdiff
path: root/yaksh
diff options
context:
space:
mode:
Diffstat (limited to 'yaksh')
-rw-r--r--yaksh/forms.py2
-rw-r--r--yaksh/live_server_tests/selenium_test.py2
-rw-r--r--yaksh/models.py52
-rw-r--r--yaksh/templates/404.html8
-rw-r--r--yaksh/templates/yaksh/complete.html7
-rw-r--r--yaksh/templates/yaksh/courses.html10
-rw-r--r--yaksh/templates/yaksh/design_questionpaper.html9
-rw-r--r--yaksh/test_models.py123
-rw-r--r--yaksh/test_views.py24
-rw-r--r--yaksh/views.py18
10 files changed, 215 insertions, 40 deletions
diff --git a/yaksh/forms.py b/yaksh/forms.py
index 9fd2eaa..258a1ee 100644
--- a/yaksh/forms.py
+++ b/yaksh/forms.py
@@ -339,4 +339,4 @@ class LearningModuleForm(forms.ModelForm):
class Meta:
model = LearningModule
- fields = ['name', 'description']
+ fields = ['name', 'description', 'active']
diff --git a/yaksh/live_server_tests/selenium_test.py b/yaksh/live_server_tests/selenium_test.py
index 5635add..6351f9e 100644
--- a/yaksh/live_server_tests/selenium_test.py
+++ b/yaksh/live_server_tests/selenium_test.py
@@ -152,7 +152,7 @@ class SeleniumTest():
def close_quiz(self):
quit_link_elem = WebDriverWait(self.driver, 5).until(
- EC.presence_of_element_located((By.ID, "home"))
+ EC.presence_of_element_located((By.ID, "Next"))
)
quit_link_elem.click()
diff --git a/yaksh/models.py b/yaksh/models.py
index d5b2923..252bde6 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -78,7 +78,6 @@ string_check_type = (
attempts = [(i, i) for i in range(1, 6)]
attempts.append((-1, 'Infinite'))
-days_between_attempts = [(j, j) for j in range(401)]
test_status = (
('inprogress', 'Inprogress'),
@@ -155,6 +154,9 @@ class Lesson(models.Model):
# Creator of the lesson
creator = models.ForeignKey(User)
+ # Activate/Deactivate Lesson
+ active = models.BooleanField(default=True)
+
def __str__(self):
return "{0}".format(self.name)
@@ -311,8 +313,8 @@ class Quiz(models.Model):
# Number of attempts for the quiz
attempts_allowed = models.IntegerField(default=1, choices=attempts)
- time_between_attempts = models.IntegerField(
- "Number of Days", choices=days_between_attempts
+ time_between_attempts = models.FloatField(
+ "Time Between Quiz Attempts in hours"
)
is_trial = models.BooleanField(default=False)
@@ -352,20 +354,32 @@ class Quiz(models.Model):
return demo_quiz
def get_total_students(self, course):
+ try:
+ qp = self.questionpaper_set.get().id
+ except QuestionPaper.DoesNotExist:
+ qp = None
return AnswerPaper.objects.filter(
- question_paper=self.questionpaper_set.get().id,
+ question_paper=qp,
course=course
).values_list("user", flat=True).distinct().count()
def get_passed_students(self, course):
+ try:
+ qp = self.questionpaper_set.get().id
+ except QuestionPaper.DoesNotExist:
+ qp = None
return AnswerPaper.objects.filter(
- question_paper=self.questionpaper_set.get().id,
+ question_paper=qp,
course=course, passed=True
).values_list("user", flat=True).distinct().count()
def get_failed_students(self, course):
+ try:
+ qp = self.questionpaper_set.get().id
+ except QuestionPaper.DoesNotExist:
+ qp = None
return AnswerPaper.objects.filter(
- question_paper=self.questionpaper_set.get().id,
+ question_paper=qp,
course=course, passed=False
).values_list("user", flat=True).distinct().count()
@@ -453,6 +467,7 @@ class LearningModule(models.Model):
creator = models.ForeignKey(User, related_name="module_creator")
check_prerequisite = models.BooleanField(default=True)
html_data = models.TextField(null=True, blank=True)
+ active = models.BooleanField(default=True)
is_trial = models.BooleanField(default=False)
def get_quiz_units(self):
@@ -486,7 +501,7 @@ class LearningModule(models.Model):
return ordered_units.get(id=ordered_units_ids[next_index])
def get_status(self, user, course):
- """ Get module status if it completed, inprogress or not attempted"""
+ """ Get module status if completed, inprogress or not attempted"""
learning_module = course.learning_module.prefetch_related(
"learning_unit").get(id=self.id)
ordered_units = learning_module.learning_unit.order_by("order")
@@ -497,10 +512,10 @@ class LearningModule(models.Model):
default_status = "no units"
elif all([status == "completed" for status in status_list]):
default_status = "completed"
- elif "inprogress" in status_list:
- default_status = "inprogress"
- else:
+ elif all([status == "not attempted" for status in status_list]):
default_status = "not attempted"
+ else:
+ default_status = "inprogress"
return default_status
def is_prerequisite_passed(self, user, course):
@@ -1221,7 +1236,7 @@ class QuestionPaper(models.Model):
return all_questions
def make_answerpaper(self, user, ip, attempt_num, course_id):
- """Creates an answer paper for the user to attempt the quiz"""
+ """Creates an answer paper for the user to attempt the quiz"""
try:
ans_paper = AnswerPaper.objects.get(user=user,
attempt_number=attempt_num,
@@ -1246,14 +1261,6 @@ class QuestionPaper(models.Model):
ans_paper.questions_order = ",".join(question_ids)
ans_paper.save()
ans_paper.questions_unanswered.add(*questions)
- except AnswerPaper.MultipleObjectsReturned:
- ans_paper = AnswerPaper.objects.get(user=user,
- attempt_number=attempt_num,
- question_paper=self,
- course_id=course_id
- ).order_by('-id')
- ans_paper = ans_paper[0]
-
return ans_paper
def _is_attempt_allowed(self, user, course_id):
@@ -1268,7 +1275,7 @@ class QuestionPaper(models.Model):
user=user, questionpaper=self, course_id=course_id
)
if last_attempt:
- time_lag = (timezone.now() - last_attempt.start_time).days
+ time_lag = (timezone.now() - last_attempt.start_time).total_seconds() / 3600
return time_lag >= self.quiz.time_between_attempts
else:
return True
@@ -1563,6 +1570,11 @@ class AnswerPaper(models.Model):
objects = AnswerPaperManager()
+ class Meta:
+ unique_together = ('user', 'question_paper',
+ 'attempt_number', "course"
+ )
+
def get_per_question_score(self, question_id):
if question_id not in self.get_questions().values_list('id', flat=True):
return 'NA'
diff --git a/yaksh/templates/404.html b/yaksh/templates/404.html
index e9d99de..d4777f2 100644
--- a/yaksh/templates/404.html
+++ b/yaksh/templates/404.html
@@ -1,7 +1,13 @@
{% extends "base.html" %}
+{% block pagetitle %} <h2>Yaksh</h2> {% endblock %}
+
{% block content %}
+<center>
It seems that you have encountered an error
-Type of Error - {{ exception }}
+<br>
Please contact your administrator
+<br><br>
+<div class="alert alert-danger">Error Message:- {{exception}}</div>
+</center>
{% endblock %}
diff --git a/yaksh/templates/yaksh/complete.html b/yaksh/templates/yaksh/complete.html
index 19013ca..0881bfe 100644
--- a/yaksh/templates/yaksh/complete.html
+++ b/yaksh/templates/yaksh/complete.html
@@ -33,19 +33,20 @@ width="80" alt="YAKSH"></img>{% endblock %}
<center><h3>{{message}}</h3></center>
<center>
<br>
- <a href="{{URL_ROOT}}/exam/" id="home" class="btn btn-success"> Home </a>
{% if module_id and not user == "moderator" %}
{% if first_unit %}
- <a href="{{URL_ROOT}}/exam/next_unit/{{course_id}}/{{module_id}}/{{learning_unit.id}}/1" class="btn btn-info"> Next
+ <a href="{{URL_ROOT}}/exam/next_unit/{{course_id}}/{{module_id}}/{{learning_unit.id}}/1" class="btn btn-info" id="Next"> Next
<span class="glyphicon glyphicon-chevron-right">
</span>
</a>
{% else %}
- <a href="{{URL_ROOT}}/exam/next_unit/{{course_id}}/{{module_id}}/{{learning_unit.id}}" class="btn btn-info"> Next
+ <a href="{{URL_ROOT}}/exam/next_unit/{{course_id}}/{{module_id}}/{{learning_unit.id}}" class="btn btn-info" id="Next"> Next
<span class="glyphicon glyphicon-chevron-right">
</span>
</a>
{% endif %}
+ {% else %}
+ <a href="{{URL_ROOT}}/exam/" id="home" class="btn btn-success"> Home </a>
{% endif %}
</center>
{% endblock content %}
diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html
index afd53d7..9c00957 100644
--- a/yaksh/templates/yaksh/courses.html
+++ b/yaksh/templates/yaksh/courses.html
@@ -412,6 +412,11 @@
<ul class="list-group">
<a href="{{URL_ROOT}}/exam/manage/courses/edit_lesson/{{lesson.id}}/">
{{ lesson.name }}</a>
+ {% if lesson.active %}
+ <span class="label label-success">Active</span>
+ {% else %}
+ <span class="label label-danger">Closed</span>
+ {% endif %}
</ul>
</td>
{% endfor %} <!-- end for lessons -->
@@ -446,6 +451,11 @@
<td>
<a href="{{URL_ROOT}}/exam/manage/courses/add_module/{{module.id}}/">
{{ module.name }}</a>
+ {% if module.active %}
+ <span class="label label-success">Active</span>
+ {% else %}
+ <span class="label label-danger">Closed</span>
+ {% endif %}
</td>
<td>
<a href="{{URL_ROOT}}/exam/manage/courses/designmodule/{{module.id}}">
diff --git a/yaksh/templates/yaksh/design_questionpaper.html b/yaksh/templates/yaksh/design_questionpaper.html
index 829e27f..1656e2b 100644
--- a/yaksh/templates/yaksh/design_questionpaper.html
+++ b/yaksh/templates/yaksh/design_questionpaper.html
@@ -23,8 +23,13 @@ select
{% block content %}
<input type=hidden id="url_root" value={{ URL_ROOT }}>
-<form action="{{ URL_ROOT }}/exam/manage/designquestionpaper/{{ qpaper.quiz.id }}/{{ qpaper.id }}/" method="POST" id="design_q">
-<input class ="btn primary small" type="submit" name="back" id="back" value="Cancel">
+{% if course_id %}
+ <form action="{{ URL_ROOT }}/exam/manage/designquestionpaper/{{ qpaper.quiz.id }}/{{ qpaper.id }}/{{course_id}}/" method="POST" id="design_q">
+ <a href="{{URL_ROOT}}/exam/manage/courses" class="btn btn-danger">Cancel</a>
+{% else %}
+ <form action="{{ URL_ROOT }}/exam/manage/designquestionpaper/{{ qpaper.quiz.id }}/{{ qpaper.id }}/" method="POST" id="design_q">
+ <a href="{{URL_ROOT}}/exam/manage/courses/all_quizzes" class="btn btn-danger">Cancel</a>
+{% endif %}
{% csrf_token %}
<input type=hidden name="is_active" id="is_active" value="{{ state }}">
<center><b>Manual mode to design the {{lang}} Question Paper</center><br>
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index 7086a1e..cd4279b 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -12,6 +12,7 @@ from datetime import datetime, timedelta
from django.utils import timezone
import pytz
from django.contrib.auth.models import Group
+from django.db import IntegrityError
from django.core.files import File
from django.forms.models import model_to_dict
from textwrap import dedent
@@ -99,6 +100,7 @@ def setUpModule():
course.students.add(course_user)
course.save()
LessonFile.objects.create(lesson=lesson)
+ CourseStatus.objects.create(course=course, user=course_user)
def tearDownModule():
@@ -136,6 +138,13 @@ class LearningModuleTestCases(unittest.TestCase):
self.quiz = Quiz.objects.get(description='demo quiz 1')
self.lesson = Lesson.objects.get(name='L1')
self.course = Course.objects.get(name='Python Course')
+ self.course_status = CourseStatus.objects.get(
+ course=self.course, user=self.student)
+
+ def tearDown(self):
+ # Remove unit from course status completed units
+ self.course_status.completed_units.remove(self.learning_unit_one)
+ self.course_status.completed_units.remove(self.learning_unit_two)
def test_learning_module(self):
self.assertEqual(self.learning_module.description, 'module one')
@@ -196,7 +205,7 @@ class LearningModuleTestCases(unittest.TestCase):
# Then
self.assertEqual(unit, next_unit)
- def test_get_status(self):
+ def test_get_module_status(self):
# Given
module_status = 'not attempted'
# When
@@ -204,6 +213,29 @@ class LearningModuleTestCases(unittest.TestCase):
# Then
self.assertEqual(status, module_status)
+ # Module in progress
+
+ # Given
+ self.course_status.completed_units.add(self.learning_unit_one)
+ # When
+ status = self.learning_module.get_status(self.student, self.course)
+ # Then
+ self.assertEqual("inprogress", status)
+
+ # Module is completed
+
+ # Given
+ self.course_status.completed_units.add(self.learning_unit_two)
+ # When
+ status = self.learning_module.get_status(self.student, self.course)
+ # Then
+ self.assertEqual("completed", status)
+
+ # Module with no units
+ self.course.learning_module.add(self.learning_module_two)
+ status = self.learning_module_two.get_status(self.student, self.course)
+ self.assertEqual("no units", status)
+
def test_module_completion_percent(self):
# for module without learning units
percent = self.learning_module_two.get_module_complete_percent(
@@ -212,17 +244,12 @@ class LearningModuleTestCases(unittest.TestCase):
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)
-
+ self.course_status.completed_units.add(self.learning_unit_one)
+ self.course_status.completed_units.add(self.learning_unit_two)
percent = self.learning_module.get_module_complete_percent(
self.course, self.student
)
- self.assertEqual(percent, 50)
+ self.assertEqual(percent, 100)
class LearningUnitTestCases(unittest.TestCase):
@@ -449,10 +476,73 @@ class QuizTestCases(unittest.TestCase):
self.course = Course.objects.get(name="Python Course")
self.creator = User.objects.get(username="creator")
self.teacher = User.objects.get(username="demo_user2")
+ self.student1 = User.objects.get(username='demo_user3')
+ self.student2 = User.objects.get(username='demo_user4')
self.quiz1 = Quiz.objects.get(description='demo quiz 1')
self.quiz2 = Quiz.objects.get(description='demo quiz 2')
+ self.quiz3 = Quiz.objects.create(
+ start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc),
+ end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc),
+ duration=30, active=True,
+ attempts_allowed=1, time_between_attempts=0,
+ description='demo quiz 3', pass_criteria=0,
+ instructions="Demo Instructions"
+ )
+ self.question_paper3 = QuestionPaper.objects.create(quiz=self.quiz3)
+ self.quiz4 = Quiz.objects.create(
+ start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc),
+ end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc),
+ duration=30, active=True,
+ attempts_allowed=1, time_between_attempts=0,
+ description='demo quiz 4', pass_criteria=0,
+ instructions="Demo Instructions"
+ )
+ self.answerpaper1 = AnswerPaper.objects.create(
+ user=self.student1,
+ question_paper=self.question_paper3,
+ course=self.course,
+ attempt_number=1,
+ start_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc),
+ end_time=datetime(2015, 10, 9, 10, 28, 15, 0, tzinfo=pytz.utc),
+ passed=True
+ )
+ self.answerpaper2 = AnswerPaper.objects.create(
+ user=self.student2,
+ question_paper=self.question_paper3,
+ course=self.course,
+ attempt_number=1,
+ start_time=datetime(2015, 10, 9, 10, 8, 15, 0, tzinfo=pytz.utc),
+ end_time=datetime(2015, 10, 9, 10, 28, 15, 0, tzinfo=pytz.utc),
+ passed=False
+ )
self.trial_course = Course.objects.create_trial_course(self.creator)
+ def tearDown(self):
+ self.answerpaper1.delete()
+ self.answerpaper2.delete()
+ self.trial_course.delete()
+ self.quiz3.delete()
+ self.quiz4.delete()
+ self.question_paper3.delete()
+
+ def test_get_total_students(self):
+ self.assertEqual(self.quiz3.get_total_students(self.course), 2)
+
+ def test_get_total_students_without_questionpaper(self):
+ self.assertEqual(self.quiz4.get_total_students(self.course), 0)
+
+ def test_get_passed_students(self):
+ self.assertEqual(self.quiz3.get_passed_students(self.course), 1)
+
+ def test_get_passed_students_without_questionpaper(self):
+ self.assertEqual(self.quiz4.get_passed_students(self.course), 0)
+
+ def test_get_failed_students(self):
+ self.assertEqual(self.quiz3.get_failed_students(self.course), 1)
+
+ def test_get_failed_students_without_questionpaper(self):
+ self.assertEqual(self.quiz4.get_failed_students(self.course), 0)
+
def test_quiz(self):
""" Test Quiz"""
self.assertEqual((self.quiz1.start_date_time).strftime('%Y-%m-%d'),
@@ -530,7 +620,6 @@ class QuizTestCases(unittest.TestCase):
self.assertTrue(self.quiz1.view_answerpaper)
-
###############################################################################
class QuestionPaperTestCases(unittest.TestCase):
@classmethod
@@ -773,7 +862,8 @@ class AnswerPaperTestCases(unittest.TestCase):
question_paper=self.question_paper,
start_time=self.start_time,
end_time=self.end_time,
- user_ip=self.ip
+ user_ip=self.ip,
+ course=self.course
)
self.attempted_papers = AnswerPaper.objects.filter(
question_paper=self.question_paper,
@@ -1313,6 +1403,17 @@ class AnswerPaperTestCases(unittest.TestCase):
self.assertEqual(self.user2_answerpaper2.current_question(),
self.question1)
+ def test_duplicate_attempt_answerpaper(self):
+ with self.assertRaises(IntegrityError):
+ new_answerpaper = AnswerPaper.objects.create(
+ user=self.answerpaper.user,
+ question_paper=self.answerpaper.question_paper,
+ attempt_number=self.answerpaper.attempt_number,
+ start_time=self.answerpaper.start_time,
+ end_time=self.answerpaper.end_time,
+ course=self.answerpaper.course
+ )
+
###############################################################################
class CourseTestCases(unittest.TestCase):
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index 04aff8b..fb33114 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -5165,6 +5165,30 @@ class TestLessons(TestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context["msg"], err_msg)
+ # Check if lesson is active
+ self.lesson.active = False
+ self.lesson.save()
+ response = self.client.get(
+ reverse('yaksh:show_lesson',
+ kwargs={"lesson_id": self.lesson.id,
+ "module_id": self.learning_module.id,
+ "course_id": self.course.id}))
+ err_msg = "{0} is not active".format(self.lesson.name)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context["msg"], err_msg)
+
+ # Check if module is active
+ self.learning_module2.active = False
+ self.learning_module2.save()
+ 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 = "{0} is not active".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/views.py b/yaksh/views.py
index 1055bda..62a448c 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -476,6 +476,10 @@ 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 active status
+ if not learning_module.active:
+ return view_module(request, module_id, course_id)
+
# unit module prerequiste check
if learning_module.has_prerequisite():
if not learning_module.is_prerequisite_passed(user, course):
@@ -1393,7 +1397,8 @@ def design_questionpaper(request, quiz_id, questionpaper_id=None,
'questions': questions,
'fixed_questions': fixed_questions,
'state': state,
- 'random_sets': random_sets
+ 'random_sets': random_sets,
+ 'course_id': course_id
}
return my_render_to_response(
'yaksh/design_questionpaper.html',
@@ -2358,6 +2363,13 @@ 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 not learn_module.active:
+ return view_module(request, module_id, course_id)
+
+ if not learn_unit.lesson.active:
+ msg = "{0} is not active".format(learn_unit.lesson.name)
+ return view_module(request, module_id, course_id, msg)
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(
@@ -2670,6 +2682,10 @@ def view_module(request, module_id, course_id, msg=None):
msg = "{0} is either expired or not active".format(course.name)
return course_modules(request, course_id, msg)
learning_module = course.learning_module.get(id=module_id)
+
+ if not learning_module.active:
+ msg = "{0} is not active".format(learning_module.name)
+ return course_modules(request, course_id, msg)
all_modules = course.get_learning_modules()
if learning_module.has_prerequisite():
if not learning_module.is_prerequisite_passed(user, course):