summaryrefslogtreecommitdiff
path: root/yaksh
diff options
context:
space:
mode:
Diffstat (limited to 'yaksh')
-rw-r--r--yaksh/models.py182
-rw-r--r--yaksh/templates/yaksh/complete.html14
-rw-r--r--yaksh/templates/yaksh/intro.html30
-rw-r--r--yaksh/templates/yaksh/question.html45
-rw-r--r--yaksh/templates/yaksh/quit.html44
-rw-r--r--yaksh/templates/yaksh/quizzes_user.html19
-rw-r--r--yaksh/templates/yaksh/results_user.html9
-rw-r--r--yaksh/tests.py179
-rw-r--r--yaksh/urls.py9
-rw-r--r--yaksh/views.py553
10 files changed, 362 insertions, 722 deletions
diff --git a/yaksh/models.py b/yaksh/models.py
index 7bd0a9e..30257ef 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -1,4 +1,4 @@
-import datetime
+from datetime import datetime, timedelta
import json
from random import sample, shuffle
from itertools import islice, cycle
@@ -70,7 +70,7 @@ class Course(models.Model):
students = models.ManyToManyField(User, related_name='students')
requests = models.ManyToManyField(User, related_name='requests')
rejected = models.ManyToManyField(User, related_name='rejected')
- created_on = models.DateTimeField(default=datetime.datetime.now())
+ created_on = models.DateTimeField(default=datetime.now())
def request(self, *users):
self.requests.add(*users)
@@ -227,6 +227,10 @@ class Answer(models.Model):
###############################################################################
+class QuizManager(models.Manager):
+ def get_active_quizzes(self):
+ return self.filter(active=True)
+###############################################################################
class Quiz(models.Model):
"""A quiz that students will participate in. One can think of this
as the "examination" event.
@@ -236,12 +240,12 @@ class Quiz(models.Model):
# The start date of the quiz.
start_date_time = models.DateTimeField("Start Date and Time of the quiz",
- default=datetime.datetime.now(),
+ default=datetime.now(),
null=True)
# The end date and time of the quiz
end_date_time = models.DateTimeField("End Date and Time of the quiz",
- default=datetime.datetime(2199, 1, 1, 0, 0, 0, 0),
+ default=datetime(2199, 1, 1, 0, 0, 0, 0),
null=True)
# This is always in minutes.
@@ -269,9 +273,18 @@ class Quiz(models.Model):
time_between_attempts = models.IntegerField("Number of Days",\
choices=days_between_attempts)
+ objects = QuizManager()
+
class Meta:
verbose_name_plural = "Quizzes"
+
+ def is_expired(self):
+ return not self.start_date_time <= datetime.now() < self.end_date_time
+
+ def has_prerequisite(self):
+ return True if self.prerequisite else False
+
def __unicode__(self):
desc = self.description or 'Quiz'
return '%s: on %s for %d minutes' % (desc, self.start_date_time,
@@ -318,18 +331,36 @@ class QuestionPaper(models.Model):
def make_answerpaper(self, user, ip, attempt_num):
"""Creates an answer paper for the user to attempt the quiz"""
ans_paper = AnswerPaper(user=user, user_ip=ip, attempt_number=attempt_num)
- ans_paper.start_time = datetime.datetime.now()
+ ans_paper.start_time = datetime.now()
ans_paper.end_time = ans_paper.start_time \
- + datetime.timedelta(minutes=self.quiz.duration)
+ + timedelta(minutes=self.quiz.duration)
ans_paper.question_paper = self
- questions = self._get_questions_for_answerpaper()
- question_ids = [str(x.id) for x in questions]
- if self.shuffle_questions:
- shuffle(question_ids)
- ans_paper.questions = "|".join(question_ids)
ans_paper.save()
+ questions = self._get_questions_for_answerpaper()
+ ans_paper.questions.add(*questions)
+ ans_paper.questions_unanswered.add(*questions)
return ans_paper
+ def is_questionpaper_passed(self, user):
+ return AnswerPaper.objects.filter(question_paper=self, user=user,
+ passed = True).exists()
+
+ def is_attempt_allowed(self, user):
+ attempts = AnswerPaper.objects.get_total_attempt(questionpaper=self,
+ user=user)
+ return attempts != self.quiz.attempts_allowed
+
+ def can_attempt_now(self, user):
+ if self.is_attempt_allowed(user):
+ last_attempt = AnswerPaper.objects.get_user_last_attempt(user=user,
+ questionpaper=self)
+ if last_attempt:
+ time_lag = (datetime.today() - last_attempt.start_time).days
+ return time_lag >= self.quiz.time_between_attempts
+ else:
+ return True
+ else:
+ return False
###############################################################################
class QuestionSet(models.Model):
@@ -358,10 +389,13 @@ class AnswerPaperManager(models.Manager):
''' Return a dict of question id as key and count as value'''
papers = self.filter(question_paper_id=questionpaper_id,
attempt_number=attempt_number, status=status)
+ all_questions = list()
questions = list()
for paper in papers:
- questions += paper.get_questions()
- return Counter(map(int, questions))
+ all_questions += paper.get_questions()
+ for question in all_questions:
+ questions.append(question.id)
+ return Counter(questions)
def get_all_questions_answered(self, questionpaper_id, attempt_number,
status='completed'):
@@ -372,8 +406,8 @@ class AnswerPaperManager(models.Manager):
for paper in papers:
for question in filter(None, paper.get_questions_answered()):
if paper.is_answer_correct(question):
- questions_answered.append(question)
- return Counter(map(int, questions_answered))
+ questions_answered.append(question.id)
+ return Counter(questions_answered)
def get_attempt_numbers(self, questionpaper_id, status='completed'):
''' Return list of attempt numbers'''
@@ -431,6 +465,17 @@ class AnswerPaperManager(models.Manager):
def _get_latest_attempt(self, answerpapers, user_id):
return answerpapers.filter(user_id=user_id).order_by('-attempt_number')[0]
+ def get_user_last_attempt(self, questionpaper, user):
+ attempts = self.filter(question_paper=questionpaper,
+ user=user).order_by('-attempt_number')
+ if attempts:
+ return attempts[0]
+
+ 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()
###############################################################################
class AnswerPaper(models.Model):
@@ -439,9 +484,7 @@ class AnswerPaper(models.Model):
# The user taking this question paper.
user = models.ForeignKey(User)
- # All questions that remain to be attempted for a particular Student
- # (a list of ids separated by '|')
- questions = models.CharField(max_length=128)
+ questions = models.ManyToManyField(Question, related_name='questions')
# The Quiz to which this question paper is attached to.
question_paper = models.ForeignKey(QuestionPaper)
@@ -458,8 +501,13 @@ class AnswerPaper(models.Model):
# User's IP which is logged.
user_ip = models.CharField(max_length=15)
- # The questions successfully answered (a list of ids separated by '|')
- questions_answered = models.CharField(max_length=128)
+ # The questions unanswered
+ questions_unanswered = models.ManyToManyField(Question,
+ related_name='questions_unanswered')
+
+ # The questions answered
+ questions_answered = models.ManyToManyField(Question,
+ related_name='questions_answered')
# All the submitted answers.
answers = models.ManyToManyField(Answer)
@@ -484,66 +532,37 @@ class AnswerPaper(models.Model):
def current_question(self):
"""Returns the current active question to display."""
- qu = self.get_unanswered_questions()
- if len(qu) > 0:
- return qu[0]
- else:
- return ''
+ if self.questions_unanswered.all():
+ return self.questions_unanswered.all()[0]
def questions_left(self):
"""Returns the number of questions left."""
- qu = self.get_unanswered_questions()
- return len(qu)
-
- def get_unanswered_questions(self):
- """Returns the list of unanswered questions."""
- qa = self.questions_answered.split('|')
- qs = self.questions.split('|')
- qu = [q for q in qs if q not in qa]
- return qu
+ return self.questions_unanswered.count()
def completed_question(self, question_id):
"""
Adds the completed question to the list of answered
questions and returns the next question.
"""
- qa = self.questions_answered
- if len(qa) > 0:
- self.questions_answered = '|'.join([qa, str(question_id)])
- else:
- self.questions_answered = str(question_id)
- self.save()
+ self.questions_answered.add(question_id)
+ self.questions_unanswered.remove(question_id)
- return self.skip(question_id)
+ return self.current_question()
def skip(self, question_id):
"""
Skips the current question and returns the next sequentially
available question.
"""
- qu = self.get_unanswered_questions()
- qs = self.questions.split('|')
-
- if len(qu) == 0:
- return ''
-
- try:
- q_index = qs.index(unicode(question_id))
- except ValueError:
- return qs[0]
-
- start = q_index + 1
- stop = q_index + 1 + len(qs)
- q_list = islice(cycle(qs), start, stop)
- for next_q in q_list:
- if next_q in qu:
- return next_q
-
- return qs[0]
+ questions = self.questions_unanswered.all()
+ question_cycle = cycle(questions)
+ for question in question_cycle:
+ if question.id==int(question_id):
+ return question_cycle.next()
def time_left(self):
"""Return the time remaining for the user in seconds."""
- dt = datetime.datetime.now() - self.start_time.replace(tzinfo=None)
+ dt = datetime.now() - self.start_time.replace(tzinfo=None)
try:
secs = dt.total_seconds()
except AttributeError:
@@ -553,25 +572,22 @@ class AnswerPaper(models.Model):
remain = max(total - secs, 0)
return int(remain)
- def get_answered_str(self):
- """Returns the answered questions, sorted and as a nice string."""
- qa = self.questions_answered.split('|')
- answered = ', '.join(sorted(qa))
- return answered if answered else 'None'
-
- def update_marks_obtained(self):
+ def _update_marks_obtained(self):
"""Updates the total marks earned by student for this paper."""
marks = sum([x.marks for x in self.answers.filter(marks__gt=0.0)])
- self.marks_obtained = marks
+ if not marks:
+ self.marks_obtained = 0
+ else:
+ self.marks_obtained = marks
- def update_percent(self):
+ def _update_percent(self):
"""Updates the percent gained by the student for this paper."""
total_marks = self.question_paper.total_marks
if self.marks_obtained is not None:
percent = self.marks_obtained/self.question_paper.total_marks*100
self.percent = round(percent, 2)
- def update_passed(self):
+ def _update_passed(self):
"""
Checks whether student passed or failed, as per the quiz
passing criteria.
@@ -582,10 +598,18 @@ class AnswerPaper(models.Model):
else:
self.passed = False
- def update_status(self, state):
+ def _update_status(self, state):
""" Sets status as inprogress or completed """
self.status = state
+ def update_marks(self, state='completed'):
+ self._update_marks_obtained()
+ self._update_percent()
+ self._update_passed()
+ self._update_status(state)
+ self.end_time = datetime.now()
+ self.save()
+
def get_question_answers(self):
"""
Return a dictionary with keys as questions and a list of the
@@ -601,18 +625,24 @@ class AnswerPaper(models.Model):
return q_a
def get_questions(self):
- ''' Return a list of questions'''
- return self.questions.split('|')
+ return self.questions.all()
def get_questions_answered(self):
- ''' Return a list of questions answered'''
- return self.questions_answered.split('|')
+ return self.questions_answered.all()
+
+ def get_questions_unanswered(self):
+ return self.questions_unanswered.all()
def is_answer_correct(self, question_id):
''' Return marks of a question answered'''
return self.answers.filter(question_id=question_id,
correct=True).exists()
+ def is_attempt_inprogress(self):
+ if self.status == 'inprogress':
+ return self.time_left()> 0
+
+
def __unicode__(self):
u = self.user
return u'Question paper for {0} {1}'.format(u.first_name, u.last_name)
diff --git a/yaksh/templates/yaksh/complete.html b/yaksh/templates/yaksh/complete.html
index 08abe76..07cbf3a 100644
--- a/yaksh/templates/yaksh/complete.html
+++ b/yaksh/templates/yaksh/complete.html
@@ -5,27 +5,27 @@
{% block pagetitle %}Online Test{% endblock %}
{% block content %}
{% csrf_token %}
- {% if submitted or unattempted %}
+ {% if paper.questions_answered.all or paper.questions_unanswered.all %}
<br><center><table class="bordered-table zebra-striped span8"
style="text-align:left;">
<tr><td><b>Submitted Questions</b></td>
<td>
- {% if submitted %}
- {{ submitted|join:", " }}
+ {% if paper.questions_answered.all %}
+ {{ paper.questions_answered.all|join:", " }}
{% else %}
<p><b>No Questions have been Submitted</b></p>
{% endif %}
</td></tr>
<tr><td><b>Unattempted Questions</b></td>
<td>
- {% if unattempted %}
- {{ unattempted|join:", " }}
+ {% if paper.questions_unanswered.all %}
+ {{ paper.questions_unanswered.all|join:", " }}
{% else %}
<p><b>All Questions have been Submitted</b></p>
{% endif %}
</td></tr>
- </table></center>
- {% endif %}
+ </table></center>
+ {% endif %}
<center><h2> Good bye! </h2></center>
<center><h4> {{message}} </h4></center>
<br><center><h4>You may now close the browser.</h4></center><br>
diff --git a/yaksh/templates/yaksh/intro.html b/yaksh/templates/yaksh/intro.html
index 2542795..1ed82e2 100644
--- a/yaksh/templates/yaksh/intro.html
+++ b/yaksh/templates/yaksh/intro.html
@@ -5,21 +5,19 @@
{% block formtitle %}Important instructions & rules {% endblock %}
{% block content %}
- {% if enable_quiz_time or disable_quiz_time %}
- {% if quiz_expired %}
- <div class="alert alert-error">
- This Quiz has expired. You can no longer attempt this Quiz.
+ {% if questionpaper.quiz.is_expired %}
+ <div class="alert alert-error">
+ This Quiz has expired. You can no longer attempt this Quiz.
+ <br/>
+ </div>
+ {% else %}
+ <div class="alert">
+ You can attempt this Quiz at any time between {{ questionpaper.quiz.start_date_time }} GMT and {{ questionpaper.quiz.end_date_time }} GMT
<br/>
- </div>
- {% else %}
- <div class="alert">
- You can attempt this Quiz at any time between {{ enable_quiz_time }} GMT and {{ disable_quiz_time }} GMT
- <br/>
- You are not allowed to attempt the Quiz before or after this duration
- <br/>
- </div>
- {% endif %}
- {% endif %}
+ You are not allowed to attempt the Quiz before or after this duration
+ <br/>
+ </div>
+ {% endif %}
<p> Welcome <strong>{{user.first_name.title}} {{user.last_name.title}}</strong>, to the programming quiz! </p>
<p>
This examination system has been developed with the intention of making you
@@ -44,8 +42,8 @@
{% csrf_token %}
<center><button class="btn" name="home">Home</button></center>
</form>
- {% if not quiz_expired %}
- <form action="{{URL_ROOT}}/exam/start/{{ attempt_num }}/{{ paper_id }}/" method="post" align="center">
+ {% if not questionpaper.quiz.is_expired %}
+ <form action="{{URL_ROOT}}/exam/start/{{ attempt_num }}/{{ questionpaper.id }}/" method="post" align="center">
{% csrf_token %}
<center><button class="btn" type="submit" name="start">Start Exam!</button></center>
</form>
diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html
index 0d1daee..0c48167 100644
--- a/yaksh/templates/yaksh/question.html
+++ b/yaksh/templates/yaksh/question.html
@@ -15,7 +15,7 @@
<script src="{{ URL_ROOT }}/static/yaksh/js/bootstrap-modal.js"></script>
<script>
-var time_left = {{ time_left }}
+var time_left = {{ paper.time_left }}
function getTimeRemaining(endtime){
var t = Date.parse(endtime) - Date.parse(new Date());
@@ -90,11 +90,11 @@ function call_skip(url)
form.action = url
form.submit();
}
- {% if question.type == 'code' and success == 'True'%}
- {% if to_attempt|length != 0 %}
+ {% if question.type == 'code' and success == 'True'%}
+ {% if paper.questions_left %}
window.setTimeout(function()
{
- {% for qid, num in questions.items %}
+ {% for qid in paper.questions.all %}
location.href="{{ URL_ROOT }}/exam/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/"
{% endfor %}
}, 1000);
@@ -117,10 +117,10 @@ function call_skip(url)
<div class="container">
<h3 class="brand"><strong>Online Test</h3></strong>
<ul>
- <li><h5><a> Hi {{user.first_name.title}} {{user.last_name.title}} </a></h5></li>
+ <li><h5><a> Hi {{ paper.user.first_name.title}} {{ paper.user.last_name.title}} </a></h5></li>
</ul><br>
<div class=time-div id="time_left"></div>
- <h5 class=td1-class>You have {{ paper.questions_left }} question(s) left in {{ quiz_name }}</h5>
+ <h5>You have {{ paper.questions_left }} question(s) left in {{ paper.question_paper.quiz.description }}</h5>
</div>
</div>
</div>
@@ -128,20 +128,21 @@ function call_skip(url)
<div class="sidebar">
<p>Question Navigator </p>
<div class="pagination">
- <ul>
- {% for qid, num in questions.items %}
- {% if qid.id|slugify in to_attempt %}
- {% if qid.id|slugify == question.id|slugify %}
- <li class="active"><a href="#" data-toggle="tooltip" title="{{ qid.description }}" onclick="call_skip('{{ URL_ROOT }}/exam/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')">{{ num }}</a></li>
- {% else %}
- <li><a href="#" data-toggle="tooltip" title="{{ qid.description }}" onclick="call_skip('{{ URL_ROOT }}/exam/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')">{{ num }}</a></li>
- {% endif %}
- {% endif %}
- {% if qid.id|slugify in submitted %}
- <li class="disabled"><a href="#">{{ num }}</a></li>
- {% endif %}
- {% endfor %}
- </ul>
+ <ul>
+ {% for qid in paper.questions.all %}
+ {% if qid in paper.questions_unanswered.all %}
+ {% if qid.id == question.id %}
+ {{ q.id}}
+ <li class="active"><a href="#" data-toggle="tooltip" title="{{ qid.description }}" onclick="call_skip('{{ URL_ROOT }}/exam/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')">{{ forloop.counter }}</a></li>
+ {% else %}
+ <li><a href="#" data-toggle="tooltip" title="{{ qid.description }}" onclick="call_skip('{{ URL_ROOT }}/exam/{{ qid.id }}/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/')">{{ forloop.counter }}</a></li>
+ {% endif %}
+ {% endif %}
+ {% if qid in paper.questions_answered.all %}
+ <li class="disabled"><a href="#">{{ forloop.counter }}</a></li>
+ {% endif %}
+ {% endfor %}
+ </ul>
</div>
</div>
</div>
@@ -191,7 +192,7 @@ function call_skip(url)
<script type="text/javascript">
addLineNumbers('answer');
- </script>
+ </script>
<script>addLineNumbers('snippet');</script>
{% endif %}
@@ -202,7 +203,7 @@ function call_skip(url)
{% else %}
<button class="btn" type="submit" name="check" id="check" onClick="submitCode();">Check Answer</button>&nbsp;&nbsp;
{% endif %}
- {% if to_attempt|length != 1 %}
+ {% if paper.unanswered.all|length != 1 %}
<button class="btn" type="submit" name="skip" id="skip">Attempt Later</button>
{% endif %}
</form>
diff --git a/yaksh/templates/yaksh/quit.html b/yaksh/templates/yaksh/quit.html
index 91bce64..7be8ad5 100644
--- a/yaksh/templates/yaksh/quit.html
+++ b/yaksh/templates/yaksh/quit.html
@@ -3,33 +3,31 @@
{% block title %}Quit exam {% endblock %}
{% block pagetitle %}Online Test {% endblock %}
{% block content %}
- {% if submitted or unattempted %}
- <br><center><table class="bordered-table zebra-striped span8"
- style="text-align:left;">
- <tr><td><b>Submitted Questions</b></td>
- <td>
- {% if submitted %}
- {{ submitted|join:", " }}
- {% else %}
- <p><b>No Questions have been Submitted</b></p>
- {% endif %}
- </td></tr>
- <tr><td><b>Unattempted Questions</b></td>
- <td>
- {% if unattempted %}
- {{ unattempted|join:", " }}
- {% else %}
- <p><b>All Questions have been Submitted</b></p>
- {% endif %}
- </td></tr>
- </table></center>
- {% endif %}
+ <br><center><table class="bordered-table zebra-striped span8"
+ style="text-align:left;">
+ <tr><td><b>Submitted Questions</b></td>
+ <td>
+ {% if paper.questions_answered.all %}
+ {{ paper.questions_answered.all|join:", " }}
+ {% else %}
+ <p><b>No Questions have been Submitted</b></p>
+ {% endif %}
+ </td></tr>
+ <tr><td><b>Unattempted Questions</b></td>
+ <td>
+ {% if paper.questions_unanswered.all %}
+ {{ paper.questions_unanswered.all|join:", " }}
+ {% else %}
+ <p><b>All Questions have been Submitted</b></p>
+ {% endif %}
+ </td></tr>
+ </table></center>
<center><h4>Your current answers are saved.</h4></center>
<center><h4> Are you sure you wish to quit the exam?</h4></center>
<center><h4> Be sure, as you won't be able to restart this exam.</h4></center>
- <form action="{{URL_ROOT}}/exam/complete/{{ attempt_num }}/{{ id }}/" method="post">
+ <form action="{{URL_ROOT}}/exam/complete/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/" method="post">
{% csrf_token %}
- <center><button class="btn" type="submit" name="yes">Yes!</button>&nbsp;<button class="btn" type="button" name="no" onClick="window.location='{{ URL_ROOT }}/exam/start/{{ attempt_num }}/{{ id }}/'">No!</button></center>
+ <center><button class="btn" type="submit" name="yes">Yes!</button>&nbsp;<button class="btn" type="button" name="no" onClick="window.location='{{ URL_ROOT }}/exam/start/{{ paper.attempt_number }}/{{ paper.question_paper.id }}/'">No!</button></center>
</form>
{% endblock content %}
diff --git a/yaksh/templates/yaksh/quizzes_user.html b/yaksh/templates/yaksh/quizzes_user.html
index a800e68..69cf3ba 100644
--- a/yaksh/templates/yaksh/quizzes_user.html
+++ b/yaksh/templates/yaksh/quizzes_user.html
@@ -21,7 +21,7 @@
<div class="span14">
<div class="row">
<div class="span6">
- <h4><b><u> {{ course.name }} by {{ course.creator}}</u></b></h4>
+ <h4><b><u> {{ course.name }} by {{ course.creator }}</u></b></h4>
</div>
<div class="span6">
{% if user in course.requests.all %} <span class="label warning">Request Pending </span>
@@ -49,23 +49,24 @@
<table>
<th>Quiz</th>
<th>Pre requisite quiz</th>
- {% for paper in quizzes %}
- {% if paper.quiz.course_id == course.id %}
+ {% for quiz in quizzes %}
+ {% if quiz.course_id == course.id %}
<tr>
- {% if paper in unexpired_quizzes %}
+ {% if not quiz.is_expired %}
<td>
- <a href="{{ URL_ROOT }}/exam/intro/{{paper.id}}">{{ paper.quiz.description }}</a><br>
+ <a href="{{ URL_ROOT }}/exam/intro/{{quiz.questionpaper_set.get.id}}">{{ quiz.description }}</a><br>
</td>
{% else %}
<td>
- {{ paper.quiz.description }} <span class="label important">Expired</span><br>
+ <a href="{{ URL_ROOT }}/exam/intro/{{quiz.questionpaper_set.get.id}}">{{ quiz.description }}</a><br>
+ {{ quiz.description }} <span class="label important">INACTIVE</span><br>
</td>
{% endif %}
<td>
- {% if paper.quiz.prerequisite %}
- You have to pass {{ paper.quiz.prerequisite.description }} for taking {{ paper.quiz.description }}
+ {% if quiz.prerequisite %}
+ You have to pass {{ quiz.prerequisite.description }} for taking {{ paper.quiz.description }}
{% else %}
- No pre requisites for {{ paper.quiz.description }}
+ No pre requisites for {{ quiz.description }}
{% endif %}
</td>
</tr>
diff --git a/yaksh/templates/yaksh/results_user.html b/yaksh/templates/yaksh/results_user.html
index 0f35c0d..3a6450d 100644
--- a/yaksh/templates/yaksh/results_user.html
+++ b/yaksh/templates/yaksh/results_user.html
@@ -17,10 +17,11 @@
<th>Percentage
{% for paper in papers %}
<tr>
- {% for i in paper %}
- <td>{{ i }}
- {% endfor %}
- <br>
+ <td>{{ paper.question_paper.quiz.description }}</td>
+ <td>{{ paper.marks_obtained }}</td>
+ <td>{{ paper.question_paper.total_marks }}</td>
+ <td>{{ paper.percent }}</td>
+ </tr>
{% endfor %}
</table></center>
</form>
diff --git a/yaksh/tests.py b/yaksh/tests.py
index 848df74..bdc9584 100644
--- a/yaksh/tests.py
+++ b/yaksh/tests.py
@@ -1,7 +1,8 @@
from django.utils import unittest
from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
QuestionSet, AnswerPaper, Answer, TestCase, Course
-import datetime, json
+import json
+from datetime import datetime, timedelta
def setUpModule():
@@ -29,14 +30,22 @@ def setUpModule():
Question.objects.create(summary='Q%d' % (i), points=1)
# create a quiz
- Quiz.objects.create(start_date_time=datetime.datetime(2015, 10, 9, 10, 8, 15, 0),
+ quiz = Quiz.objects.create(start_date_time=datetime(2015, 10, 9, 10, 8, 15, 0),
+ end_date_time=datetime(2199, 10, 9, 10, 8, 15, 0),
+ duration=30, active=True,
+ attempts_allowed=1, time_between_attempts=0,
+ description='demo quiz', pass_criteria=0,
+ language='Python', prerequisite=None,
+ course=course)
+
+ Quiz.objects.create(start_date_time=datetime(2014, 10, 9, 10, 8, 15, 0),
+ end_date_time=datetime(2015, 10, 9, 10, 8, 15, 0),
duration=30, active=False,
attempts_allowed=-1, time_between_attempts=0,
description='demo quiz', pass_criteria=40,
- language='Python', prerequisite=None,
+ language='Python', prerequisite=quiz,
course=course)
-
def tearDownModule():
User.objects.all().delete()
Question.objects.all().delete()
@@ -133,20 +142,34 @@ class TestCaseTestCases(unittest.TestCase):
###############################################################################
class QuizTestCases(unittest.TestCase):
def setUp(self):
- self.quiz = Quiz.objects.get(pk=1)
+ self.quiz1 = Quiz.objects.get(pk=1)
+ self.quiz2 = Quiz.objects.get(pk=2)
def test_quiz(self):
""" Test Quiz"""
- self.assertEqual((self.quiz.start_date_time).strftime('%Y-%m-%d'),
+ self.assertEqual((self.quiz1.start_date_time).strftime('%Y-%m-%d'),
'2015-10-09')
- self.assertEqual((self.quiz.start_date_time).strftime('%H:%M:%S'),
+ self.assertEqual((self.quiz1.start_date_time).strftime('%H:%M:%S'),
'10:08:15')
- self.assertEqual(self.quiz.duration, 30)
- self.assertTrue(self.quiz.active is False)
- self.assertEqual(self.quiz.description, 'demo quiz')
- self.assertEqual(self.quiz.language, 'Python')
- self.assertEqual(self.quiz.pass_criteria, 40)
- self.assertEqual(self.quiz.prerequisite, None)
+ self.assertEqual(self.quiz1.duration, 30)
+ self.assertTrue(self.quiz1.active)
+ self.assertEqual(self.quiz1.description, 'demo quiz')
+ self.assertEqual(self.quiz1.language, 'Python')
+ self.assertEqual(self.quiz1.pass_criteria, 0)
+ self.assertEqual(self.quiz1.prerequisite, None)
+
+ def test_is_expired(self):
+ self.assertFalse(self.quiz1.is_expired())
+ self.assertTrue(self.quiz2.is_expired())
+
+ def test_has_prerequisite(self):
+ self.assertFalse(self.quiz1.has_prerequisite())
+ self.assertTrue(self.quiz2.has_prerequisite())
+
+ def test_get_active_quizzes(self):
+ quizzes = Quiz.objects.get_active_quizzes()
+ for quiz in quizzes:
+ self.assertTrue(quiz.active)
###############################################################################
@@ -212,6 +235,8 @@ class QuestionPaperTestCases(unittest.TestCase):
""" Test get_random_questions() method of Question Paper"""
random_questions_set_1 = self.question_set_1.get_random_questions()
random_questions_set_2 = self.question_set_2.get_random_questions()
+ total_random_questions = len(random_questions_set_1 + random_questions_set_2)
+ self.assertEqual(total_random_questions, 5)
# To check whether random questions are from random_question_set
questions_set_1 = set(self.question_set_1.questions.all())
@@ -227,20 +252,6 @@ class QuestionPaperTestCases(unittest.TestCase):
except AssertionError:
self.assertTrue(random_questions_set_1 != random_questions_set_2)
- def test_get_questions_for_answerpaper(self):
- """ Test get_questions_for_answerpaper() method of Question Paper"""
- questions = self.question_paper._get_questions_for_answerpaper()
- fixed = list(self.question_paper.fixed_questions.all())
- question_set = self.question_paper.random_questions.all()
- total_random_questions = 0
- available_questions = []
- for qs in question_set:
- total_random_questions += qs.num_questions
- available_questions += qs.questions.all()
- self.assertEqual(total_random_questions, 5)
- self.assertEqual(len(available_questions), 8)
- self.assertEqual(len(questions), 7)
-
def test_make_answerpaper(self):
""" Test make_answerpaper() method of Question Paper"""
already_attempted = self.attempted_papers.count()
@@ -248,11 +259,18 @@ class QuestionPaperTestCases(unittest.TestCase):
answerpaper = self.question_paper.make_answerpaper(self.user, self.ip,
attempt_num)
self.assertIsInstance(answerpaper, AnswerPaper)
- paper_questions = set((answerpaper.questions).split('|'))
+ paper_questions = answerpaper.questions.all()
self.assertEqual(len(paper_questions), 7)
- fixed = {'4', '6'}
- boolean = fixed.intersection(paper_questions) == fixed
- self.assertTrue(boolean)
+ fixed_questions = set(self.question_paper.fixed_questions.all())
+ self.assertTrue(fixed_questions.issubset(set(paper_questions)))
+ # test is_questionpaper_passed()
+ answerpaper.passed = True
+ answerpaper.save()
+ self.assertTrue(self.question_paper.is_questionpaper_passed(self.user))
+ # test is_attempt_allowed()
+ self.assertFalse(self.question_paper.is_attempt_allowed(self.user))
+ # test can_attempt_now(self):
+ self.assertFalse(self.question_paper.can_attempt_now(self.user))
###############################################################################
@@ -265,21 +283,24 @@ class AnswerPaperTestCases(unittest.TestCase):
self.quiz = Quiz.objects.get(pk=1)
self.question_paper = QuestionPaper(quiz=self.quiz, total_marks=3)
self.question_paper.save()
+ self.questions = Question.objects.filter(id__in=[1,2,3])
+ self.start_time = datetime.now()
+ self.end_time = self.start_time + timedelta(minutes=20)
# create answerpaper
self.answerpaper = AnswerPaper(user=self.user,
- questions='1|2|3',
question_paper=self.question_paper,
- start_time='2014-06-13 12:20:19.791297',
- end_time='2014-06-13 12:50:19.791297',
+ start_time=self.start_time,
+ end_time=self.end_time,
user_ip=self.ip)
- self.answerpaper.questions_answered = '1'
self.attempted_papers = AnswerPaper.objects.filter(question_paper=self.question_paper,
user=self.user)
already_attempted = self.attempted_papers.count()
self.answerpaper.attempt_number = already_attempted + 1
self.answerpaper.save()
-
+ self.answerpaper.questions.add(*self.questions)
+ self.answerpaper.questions_unanswered.add(*self.questions)
+ self.answerpaper.save()
# answers for the Answer Paper
self.answer_right = Answer(question=Question.objects.get(id=1),
answer="Demo answer", correct=True, marks=1)
@@ -294,57 +315,48 @@ class AnswerPaperTestCases(unittest.TestCase):
""" Test Answer Paper"""
self.assertEqual(self.answerpaper.user.username, 'demo_user')
self.assertEqual(self.answerpaper.user_ip, self.ip)
- questions = self.answerpaper.questions
- num_questions = len(questions.split('|'))
- self.assertEqual(questions, '1|2|3')
+ questions = self.answerpaper.get_questions()
+ num_questions = len(questions)
+ self.assertSequenceEqual(list(questions), list(self.questions))
self.assertEqual(num_questions, 3)
self.assertEqual(self.answerpaper.question_paper, self.question_paper)
- self.assertEqual(self.answerpaper.start_time,
- '2014-06-13 12:20:19.791297')
- self.assertEqual(self.answerpaper.end_time,
- '2014-06-13 12:50:19.791297')
+ self.assertEqual(self.answerpaper.start_time, self.start_time)
self.assertEqual(self.answerpaper.status, 'inprogress')
- def test_current_question(self):
- """ Test current_question() method of Answer Paper"""
+ def test_questions(self):
+ # Test questions_left() method of Answer Paper
+ self.assertEqual(self.answerpaper.questions_left(), 3)
+ # Test current_question() method of Answer Paper
current_question = self.answerpaper.current_question()
- self.assertEqual(current_question, '2')
-
- def test_completed_question(self):
- """ Test completed_question() method of Answer Paper"""
+ self.assertEqual(current_question.id, 1)
+ # Test completed_question() method of Answer Paper
question = self.answerpaper.completed_question(1)
self.assertEqual(self.answerpaper.questions_left(), 2)
-
- def test_questions_left(self):
- """ Test questions_left() method of Answer Paper"""
- self.assertEqual(self.answerpaper.questions_left(), 2)
-
- def test_skip(self):
- """ Test skip() method of Answer Paper"""
+ # Test skip() method of Answer Paper
current_question = self.answerpaper.current_question()
- next_question_id = self.answerpaper.skip(current_question)
+ self.assertEqual(current_question.id, 2)
+ next_question_id = self.answerpaper.skip(current_question.id)
self.assertTrue(next_question_id is not None)
- self.assertEqual(next_question_id, '3')
-
- def test_answered_str(self):
- """ Test answered_str() method of Answer Paper"""
- answered_question = self.answerpaper.get_answered_str()
- self.assertEqual(answered_question, '1')
-
- def test_update_marks_obtained(self):
- """ Test get_marks_obtained() method of Answer Paper"""
- self.answerpaper.update_marks_obtained()
+ self.assertEqual(next_question_id.id, 3)
+ questions_answered = self.answerpaper.get_questions_answered()
+ self.assertEqual(questions_answered.count(), 1)
+ self.assertSequenceEqual(questions_answered, [self.questions[0]])
+ questions_unanswered = self.answerpaper.get_questions_unanswered()
+ self.assertEqual(questions_unanswered.count(), 2)
+ self.assertSequenceEqual(questions_unanswered,
+ [self.questions[1], self.questions[2]])
+
+ def test_update_marks(self):
+ """ Test update_marks method of AnswerPaper"""
+ self.answerpaper.update_marks('inprogress')
+ self.assertEqual(self.answerpaper.status, 'inprogress')
+ self.assertTrue(self.answerpaper.is_attempt_inprogress())
+ self.answerpaper.update_marks()
+ self.assertEqual(self.answerpaper.status, 'completed')
self.assertEqual(self.answerpaper.marks_obtained, 1.0)
-
- def test_update_percent(self):
- """ Test update_percent() method of Answerpaper"""
- self.answerpaper.update_percent()
self.assertEqual(self.answerpaper.percent, 33.33)
-
- def test_update_passed(self):
- """ Test update_passed method of AnswerPaper"""
- self.answerpaper.update_passed()
- self.assertFalse(self.answerpaper.passed)
+ self.assertTrue(self.answerpaper.passed)
+ self.assertFalse(self.answerpaper.is_attempt_inprogress())
def test_get_question_answer(self):
""" Test get_question_answer() method of Answer Paper"""
@@ -354,13 +366,9 @@ class AnswerPaperTestCases(unittest.TestCase):
self.assertTrue(first_answer.correct)
self.assertEqual(len(answered), 2)
- def test_update_status(self):
- """ Test update_status method of Answer Paper"""
- self.answerpaper.update_status('inprogress')
- self.assertEqual(self.answerpaper.status, 'inprogress')
- self.answerpaper.update_status('completed')
- self.assertEqual(self.answerpaper.status, 'completed')
-
+ def test_is_answer_correct(self):
+ self.assertTrue(self.answerpaper.is_answer_correct(self.questions[0]))
+ self.assertFalse(self.answerpaper.is_answer_correct(self.questions[1]))
###############################################################################
class CourseTestCases(unittest.TestCase):
@@ -369,7 +377,8 @@ class CourseTestCases(unittest.TestCase):
self.creator = User.objects.get(pk=1)
self.student1 = User.objects.get(pk=2)
self.student2 = User.objects.get(pk=3)
- self.quiz = Quiz.objects.get(pk=1)
+ self.quiz1 = Quiz.objects.get(pk=1)
+ self.quiz2 = Quiz.objects.get(pk=2)
def test_is_creator(self):
""" Test is_creator method of Course"""
@@ -422,4 +431,4 @@ class CourseTestCases(unittest.TestCase):
def test_get_quizzes(self):
""" Test get_quizzes method of Courses"""
- self.assertSequenceEqual(self.course.get_quizzes(), [self.quiz])
+ self.assertSequenceEqual(self.course.get_quizzes(), [self.quiz1, self.quiz2])
diff --git a/yaksh/urls.py b/yaksh/urls.py
index be33051..53c7b8a 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -22,11 +22,10 @@ urlpatterns += patterns('yaksh.views',
url(r'^complete/(?P<attempt_num>\d+)/(?P<questionpaper_id>\d+)/$',\
'complete'),
url(r'^register/$', 'user_register'),
- url(r'^(?P<q_id>\d+)/$', 'question'),
url(r'^(?P<q_id>\d+)/check/$', 'check'),
url(r'^(?P<q_id>\d+)/check/(?P<attempt_num>\d+)/(?P<questionpaper_id>\d+)/$',\
'check'),
- url(r'^intro/$', 'start'),
+ url(r'^intro/$', 'intro'),
url(r'^(?P<q_id>\d+)/(?P<attempt_num>\d+)/(?P<questionpaper_id>\d+)/$', 'show_question'),
url(r'^enroll_request/(?P<course_id>\d+)/$', 'enroll_request'),
url(r'^self_enroll/(?P<course_id>\d+)/$', 'self_enroll'),
@@ -55,12 +54,6 @@ urlpatterns += patterns('yaksh.views',
url(r'^manage/designquestionpaper/$', 'design_questionpaper'),
url(r'^manage/designquestionpaper/(?P<questionpaper_id>\d+)/$',\
'design_questionpaper'),
- url(r'^manage/designquestionpaper/automatic/(?P<questionpaper_id>\d+)/$',\
- 'automatic_questionpaper'),
- url(r'^manage/designquestionpaper/automatic$', 'automatic_questionpaper'),
- url(r'^manage/designquestionpaper/manual$', 'manual_questionpaper'),
- url(r'^manage/designquestionpaper/manual/(?P<questionpaper_id>\d+)/$',\
- 'manual_questionpaper'),
url(r'^manage/statistics/question/(?P<questionpaper_id>\d+)/$',
'show_statistics'),
url(r'^manage/statistics/question/(?P<questionpaper_id>\d+)/(?P<attempt_number>\d+)/$',
diff --git a/yaksh/views.py b/yaksh/views.py
index 03f4f61..314814d 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -47,12 +47,6 @@ def my_render_to_response(template, context=None, **kwargs):
return render_to_response(template, context, **kwargs)
-def gen_key(no_of_chars):
- """Generate a random key of the number of characters."""
- allowed_chars = string.digits+string.uppercase
- return ''.join([random.choice(allowed_chars) for i in range(no_of_chars)])
-
-
def get_user_dir(user):
"""Return the output directory for the user."""
@@ -72,43 +66,6 @@ def is_moderator(user):
return True
-def fetch_questions(request):
- """Fetch questions from database based on the given search conditions &
- tags"""
- set1 = set()
- set2 = set()
- first_tag = request.POST.get('first_tag')
- first_condition = request.POST.get('first_condition')
- second_tag = request.POST.get('second_tag')
- second_condition = request.POST.get('second_condition')
- third_tag = request.POST.get('third_tag')
- question1 = set(Question.objects.filter(tags__name__in=[first_tag]))
- question2 = set(Question.objects.filter(tags__name__in=[second_tag]))
- question3 = set(Question.objects.filter(tags__name__in=[third_tag]))
- if first_condition == 'and':
- set1 = question1.intersection(question2)
- if second_condition == 'and':
- set2 = set1.intersection(question3)
- else:
- set2 = set1.union(question3)
- else:
- set1 = question1.union(question2)
- if second_condition == 'and':
- set2 = set1.intersection(question3)
- else:
- set2 = set1.union(question3)
- return set2
-
-
-def _update_marks(answer_paper, state='completed'):
- answer_paper.update_marks_obtained()
- answer_paper.update_percent()
- answer_paper.update_passed()
- answer_paper.update_status(state)
- answer_paper.end_time = datetime.datetime.now()
- answer_paper.save()
-
-
def index(request):
"""The start page.
"""
@@ -151,135 +108,56 @@ def user_register(request):
def quizlist_user(request):
"""Show All Quizzes that is available to logged-in user."""
user = request.user
- avail_quizzes = list(QuestionPaper.objects.filter(quiz__active=True))
+ avail_quizzes = Quiz.objects.get_active_quizzes()
user_answerpapers = AnswerPaper.objects.filter(user=user)
- pre_requisites = []
- enabled_quizzes = []
- disabled_quizzes = []
- unexpired_quizzes = []
-
courses = Course.objects.filter(active=True)
- for paper in avail_quizzes:
- quiz_enable_time = paper.quiz.start_date_time
- quiz_disable_time = paper.quiz.end_date_time
- if quiz_enable_time <= datetime.datetime.now() <= quiz_disable_time:
- unexpired_quizzes.append(paper)
-
- cannot_attempt = True if 'cannot_attempt' in request.GET else False
- quizzes_taken = None if user_answerpapers.count() == 0 else user_answerpapers
-
- context = {'cannot_attempt': cannot_attempt,
- 'quizzes': avail_quizzes,
+ context = { 'quizzes': avail_quizzes,
'user': user,
- 'quizzes_taken': quizzes_taken,
- 'unexpired_quizzes': unexpired_quizzes,
- 'courses': courses
+ 'courses': courses,
+ 'quizzes_taken': user_answerpapers,
}
return my_render_to_response("yaksh/quizzes_user.html", context)
@login_required
-def intro(request, questionpaper_id):
+def intro(request, questionpaper_id=None):
"""Show introduction page before quiz starts"""
user = request.user
ci = RequestContext(request)
+ if questionpaper_id is None:
+ return my_redirect('/exam/quizzes/')
quest_paper = QuestionPaper.objects.get(id=questionpaper_id)
- if not quest_paper.quiz.course.is_enrolled(user):
+ if not quest_paper.quiz.course.is_enrolled(user) :
raise Http404('You are not allowed to view this page!')
attempt_number = quest_paper.quiz.attempts_allowed
- time_lag = quest_paper.quiz.time_between_attempts
- quiz_enable_time = quest_paper.quiz.start_date_time
- quiz_disable_time = quest_paper.quiz.end_date_time
-
- quiz_expired = False if quiz_enable_time <= datetime.datetime.now() \
- <= quiz_disable_time else True
-
- if quest_paper.quiz.prerequisite:
- try:
- pre_quest = QuestionPaper.objects.get(
- quiz=quest_paper.quiz.prerequisite)
- answer_papers = AnswerPaper.objects.filter(
- question_paper=pre_quest, user=user)
- answer_papers_failed = AnswerPaper.objects.filter(
- question_paper=pre_quest, user=user, passed=False)
- if answer_papers.count() == answer_papers_failed.count():
- context = {'user': user, 'cannot_attempt': True}
- return my_redirect("/exam/quizzes/?cannot_attempt=True")
- except:
- context = {'user': user, 'cannot_attempt': True}
- return my_redirect("/exam/quizzes/?cannot_attempt=True")
-
- attempted_papers = AnswerPaper.objects.filter(question_paper=quest_paper,
- user=user)
- already_attempted = attempted_papers.count()
- inprogress, previous_attempt, next_attempt = _check_previous_attempt(attempted_papers,
- already_attempted,
- attempt_number)
-
- if previous_attempt:
- if inprogress:
- return show_question(request,
- previous_attempt.current_question(),
- previous_attempt.attempt_number,
- previous_attempt.question_paper.id)
- days_after_attempt = (datetime.datetime.today() - \
- previous_attempt.start_time).days
-
- if next_attempt:
- if days_after_attempt >= time_lag:
- context = {'user': user,
- 'paper_id': questionpaper_id,
- 'attempt_num': already_attempted + 1,
- 'enable_quiz_time': quiz_enable_time,
- 'disable_quiz_time': quiz_disable_time,
- 'quiz_expired': quiz_expired
- }
- return my_render_to_response('yaksh/intro.html', context,
- context_instance=ci)
- else:
- return my_redirect("/exam/quizzes/")
-
- else:
- context = {'user': user,
- 'paper_id': questionpaper_id,
- 'attempt_num': already_attempted + 1,
- 'enable_quiz_time': quiz_enable_time,
- 'disable_quiz_time': quiz_disable_time,
- 'quiz_expired': quiz_expired
- }
+ if quest_paper.quiz.has_prerequisite():
+ pre_quest = QuestionPaper.objects.get(quiz=quest_paper.quiz.prerequisite)
+ if not pre_quest.is_questionpaper_passed(user):
+ return quizlist_user(request)
+
+ last_attempt = AnswerPaper.objects.get_user_last_attempt(
+ questionpaper=quest_paper, user=user)
+ if last_attempt and last_attempt.is_attempt_inprogress():
+ return show_question(request, last_attempt.current_question().id,
+ last_attempt.attempt_number,
+ last_attempt.question_paper.id)
+
+ attempt_number = 1 if not last_attempt else last_attempt.attempt_number +1
+ if quest_paper.is_attempt_allowed(user) and quest_paper.can_attempt_now(user):
+ context = {'user': user, 'questionpaper': quest_paper,
+ 'attempt_num': attempt_number}
return my_render_to_response('yaksh/intro.html', context,
context_instance=ci)
+ return my_redirect("/exam/quizzes/")
-def _check_previous_attempt(attempted_papers, already_attempted, attempt_number):
- next_attempt = False if already_attempted == attempt_number else True
- if already_attempted == 0:
- return False, None, next_attempt
- else:
- previous_attempt = attempted_papers[already_attempted-1]
- if previous_attempt.status == 'inprogress':
- if previous_attempt.time_left() > 0:
- return True, previous_attempt, next_attempt
- else:
- return False, previous_attempt, next_attempt
- else:
- return False, previous_attempt, next_attempt
-
@login_required
def results_user(request):
"""Show list of Results of Quizzes that is taken by logged-in user."""
user = request.user
- papers = AnswerPaper.objects.filter(user=user)
- quiz_marks = []
- for paper in papers:
- marks_obtained = paper.marks_obtained
- max_marks = paper.question_paper.total_marks
- percentage = round((marks_obtained/max_marks)*100, 2)
- temp = paper.question_paper.quiz.description, marks_obtained,\
- max_marks, percentage
- quiz_marks.append(temp)
- context = {'papers': quiz_marks}
+ papers = AnswerPaper.objects.get_user_answerpapers(user)
+ context = {'papers': papers}
return my_render_to_response("yaksh/results_user.html", context)
@@ -592,174 +470,6 @@ def show_all_questionpapers(request, questionpaper_id=None):
@login_required
-def automatic_questionpaper(request, questionpaper_id=None):
- """Generate automatic question paper for a particular quiz"""
-
- user = request.user
- ci = RequestContext(request)
- if not user.is_authenticated() or not is_moderator(user):
- raise Http404('You are not allowed to view this page!')
-
- if questionpaper_id is None:
- if request.method == "POST":
- if request.POST.get('save') == 'save':
- quiz = Quiz.objects.order_by("-id")[0]
- quest_paper = QuestionPaper()
- questions = request.POST.getlist('questions')
- tot_marks = 0
- for quest in questions:
- q = Question.objects.get(id=quest)
- tot_marks += q.points
- quest_paper.quiz = quiz
- quest_paper.total_marks = tot_marks
- quest_paper.save()
- for quest in questions:
- q = Question.objects.get(id=quest)
- quest_paper.fixed_questions.add(q)
- return my_redirect('/exam/manage/showquiz')
- else:
- no_questions = int(request.POST.get('num_questions'))
- fetched_questions = fetch_questions(request)
- n = len(fetched_questions)
- msg = ''
- if (no_questions < n):
- i = n - no_questions
- for i in range(0, i):
- fetched_questions.pop()
- elif (no_questions > n):
- msg = 'The given Criteria does not satisfy the number\
- of Questions...'
- tags = Tag.objects.all()
- context = {'data': {'questions': fetched_questions,
- 'tags': tags,
- 'msg': msg}}
- return my_render_to_response(
- 'yaksh/automatic_questionpaper.html', context,
- context_instance=ci)
- else:
- tags = Tag.objects.all()
- context = {'data': {'tags': tags}}
- return my_render_to_response('yaksh/automatic_questionpaper.html',
- context, context_instance=ci)
-
- else:
- if request.method == "POST":
- if request.POST.get('save') == 'save':
- quest_paper = QuestionPaper.objects.get(id=questionpaper_id)
- questions = request.POST.getlist('questions')
- tot_marks = quest_paper.total_marks
- for quest in questions:
- q = Question.objects.get(id=quest)
- tot_marks += q.points
- quest_paper.total_marks = tot_marks
- quest_paper.save()
- for quest in questions:
- q = Question.objects.get(id=quest)
- quest_paper.questions.add(q)
- return my_redirect('/yaksh/manage/showquiz')
- else:
- no_questions = int(request.POST.get('num_questions'))
- fetched_questions = fetch_questions(request)
- n = len(fetched_questions)
- msg = ''
- if(no_questions < n):
- i = n - no_questions
- for i in range(0, i):
- fetched_questions.pop()
- elif(no_questions > n):
- msg = 'The given Criteria does not satisfy the number of \
- Questions...'
- tags = Tag.objects.all()
- context = {'data': {'questions': fetched_questions,
- 'tags': tags,
- 'msg': msg}}
- return my_render_to_response(
- 'yaksh/automatic_questionpaper.html', context,
- context_instance=ci)
- else:
- tags = Tag.objects.all()
- context = {'data': {'tags': tags}}
- return my_render_to_response('yaksh/automatic_questionpaper.html',
- context, context_instance=ci)
-
-
-@login_required
-def manual_questionpaper(request, questionpaper_id=None):
- user = request.user
- ci = RequestContext(request)
- if not user.is_authenticated() or not is_moderator(user):
- raise Http404('You are not allowed to view this page!')
-
- if questionpaper_id is None:
- if request.method == "POST":
- if request.POST.get('save') == 'save':
- questions = request.POST.getlist('questions')
- quest_paper = QuestionPaper()
- quiz = Quiz.objects.order_by("-id")[0]
- tot_marks = 0
- for quest in questions:
- q = Question.objects.get(id=quest)
- tot_marks += q.points
- quest_paper.quiz = quiz
- quest_paper.total_marks = tot_marks
- quest_paper.save()
- for i in questions:
- q = Question.objects.get(id=i)
- quest_paper.questions.add(q)
- return my_redirect('/exam/manage/showquiz')
- else:
- fetched_questions = fetch_questions(request)
- n = len(fetched_questions)
- msg = ''
- if (n == 0):
- msg = 'No matching Question found...'
- tags = Tag.objects.all()
- context = {'data': {'questions': fetched_questions,
- 'tags': tags, 'msg': msg}}
- return my_render_to_response('yaksh/manual_questionpaper.html',
- context,
- context_instance=ci)
- else:
- tags = Tag.objects.all()
- context = {'data': {'tags': tags}}
- return my_render_to_response('yaksh/manual_questionpaper.html',
- context, context_instance=ci)
-
- else:
- if request.method == "POST":
- if request.POST.get('save') == 'save':
- quest_paper = QuestionPaper.objects.get(id=questionpaper_id)
- questions = request.POST.getlist('questions')
- tot_marks = quest_paper.total_marks
- for quest in questions:
- q = Question.objects.get(id=quest)
- tot_marks += q.points
- quest_paper.total_marks = tot_marks
- quest_paper.save()
- for i in questions:
- q = Question.objects.get(id=i)
- quest_paper.questions.add(q)
- return my_redirect('/exam/manage/showquiz')
- else:
- fetched_questions = fetch_questions(request)
- n = len(fetched_questions)
- msg = ''
- if (n == 0):
- msg = 'No matching Question found...'
- tags = Tag.objects.all()
- context = {'data': {'questions': fetched_questions,
- 'tags': tags, 'msg': msg}}
- return my_render_to_response('yaksh/manual_questionpaper.html',
- context,
- context_instance=ci)
- else:
- tags = Tag.objects.all()
- context = {'data': {'tags': tags}}
- return my_render_to_response('yaksh/manual_questionpaper.html',
- context, context_instance=ci)
-
-
-@login_required
def prof_manage(request):
"""Take credentials of the user with professor/moderator
rights/permissions and log in."""
@@ -814,11 +524,9 @@ def start(request, attempt_num=None, questionpaper_id=None):
"""Check the user cedentials and if any quiz is available,
start the exam."""
user = request.user
- if questionpaper_id is None:
+ if questionpaper_id is None or attempt_num is None:
return my_redirect('/exam/quizzes/')
try:
- """Right now the app is designed so there is only one active quiz
- at a particular time."""
questionpaper = QuestionPaper.objects.get(id=questionpaper_id)
except QuestionPaper.DoesNotExist:
msg = 'Quiz not found, please contact your '\
@@ -831,11 +539,10 @@ def start(request, attempt_num=None, questionpaper_id=None):
try:
old_paper = AnswerPaper.objects.get(
question_paper=questionpaper, user=user, attempt_number=attempt_num)
- q = old_paper.current_question()
+ q = old_paper.current_question().id
return show_question(request, q, attempt_num, questionpaper_id)
except AnswerPaper.DoesNotExist:
ip = request.META['REMOTE_ADDR']
- key = gen_key(10)
try:
profile = user.get_profile()
except Profile.DoesNotExist:
@@ -845,91 +552,39 @@ def start(request, attempt_num=None, questionpaper_id=None):
new_paper = questionpaper.make_answerpaper(user, ip, attempt_num)
# Make user directory.
user_dir = get_user_dir(user)
- return start(request, attempt_num, questionpaper_id)
-
-
-def get_questions(paper):
- '''
- Takes answerpaper as an argument. Returns the total questions as
- ordered dictionary, the questions yet to attempt and the questions
- attempted
- '''
- to_attempt = []
- submitted = []
- all_questions = []
- questions = {}
- if paper.questions:
- all_questions = (paper.questions).split('|')
- if paper.questions_answered:
- q_answered = (paper.questions_answered).split('|')
- q_answered.sort()
- submitted = q_answered
- if paper.get_unanswered_questions():
- q_unanswered = paper.get_unanswered_questions()
- q_unanswered.sort()
- to_attempt = q_unanswered
- question = Question.objects.filter(id__in=all_questions)
- for index, value in enumerate(question, 1):
- questions[value] = index
- questions = collections.OrderedDict(sorted(questions.items(), key=lambda x:x[1]))
- return questions, to_attempt, submitted
+ return show_question(request, new_paper.current_question().id, attempt_num, questionpaper_id)
@login_required
-def question(request, q_id, attempt_num, questionpaper_id, success_msg=None):
- """Check the credentials of the user and start the exam."""
-
+def show_question(request, q_id, attempt_num, questionpaper_id, success_msg=None):
+ """Show a question if possible."""
user = request.user
- if not user.is_authenticated():
- return my_redirect('/exam/login/')
- q = get_object_or_404(Question, pk=q_id)
try:
q_paper = QuestionPaper.objects.get(id=questionpaper_id)
paper = AnswerPaper.objects.get(
user=request.user, attempt_number=attempt_num, question_paper=q_paper)
except AnswerPaper.DoesNotExist:
return my_redirect('/exam/start/')
- if not paper.question_paper.quiz.active:
- reason = 'The quiz has been deactivated!'
- return complete(request, reason, attempt_num, questionpaper_id)
- time_left = paper.time_left()
- if time_left == 0:
- return complete(request, attempt_num, questionpaper_id, reason='Your time is up!')
- quiz_name = paper.question_paper.quiz.description
- questions, to_attempt, submitted = get_questions(paper)
- if success_msg is None:
- context = {'question': q, 'questions': questions, 'paper': paper,
- 'user': user, 'quiz_name': quiz_name, 'time_left': time_left,
- 'to_attempt': to_attempt, 'submitted': submitted}
- else:
- context = {'question': q, 'questions': questions, 'paper': paper,
- 'user': user, 'quiz_name': quiz_name, 'time_left': time_left,
- 'success_msg': success_msg, 'to_attempt': to_attempt,
- 'submitted': submitted}
- if q.type == 'code':
- skipped_answer = paper.answers.filter(question=q, skipped=True)
- if skipped_answer:
- context['last_attempt'] = skipped_answer[0].answer
- ci = RequestContext(request)
- return my_render_to_response('yaksh/question.html', context,
- context_instance=ci)
-
-
-@login_required
-def show_question(request, q_id, attempt_num, questionpaper_id, success_msg=None):
- """Show a question if possible."""
- user = request.user
- q_paper = QuestionPaper.objects.get(id=questionpaper_id)
- paper = AnswerPaper.objects.get(user=request.user, attempt_number=attempt_num,
- question_paper=q_paper)
- if not user.is_authenticated():
- return my_redirect('/exam/login/')
- if len(q_id) == 0:
+ if not q_id:
msg = 'Congratulations! You have successfully completed the quiz.'
return complete(request, msg, attempt_num, questionpaper_id)
else:
- return question(request, q_id, attempt_num, questionpaper_id, success_msg)
-
+ q = get_object_or_404(Question, pk=q_id)
+ if not paper.question_paper.quiz.active:
+ reason = 'The quiz has been deactivated!'
+ return complete(request, reason, attempt_num, questionpaper_id)
+ time_left = paper.time_left()
+ if time_left == 0:
+ reason='Your time is up!'
+ return complete(request, reason, attempt_num, questionpaper_id)
+ context = {'question': q, 'paper': paper}
+ if q.type == 'code':
+ skipped_answer = paper.answers.filter(question=q, skipped=True)
+ if skipped_answer:
+ context['last_attempt'] = skipped_answer[0].answer
+ ci = RequestContext(request)
+ return my_render_to_response('yaksh/question.html', context,
+ context_instance=ci)
def _save_skipped_answer(old_skipped, user_answer, paper, question):
@@ -947,7 +602,7 @@ def _save_skipped_answer(old_skipped, user_answer, paper, question):
skipped_answer.save()
paper.answers.add(skipped_answer)
-
+@login_required
def check(request, q_id, attempt_num=None, questionpaper_id=None):
"""Checks the answers of the user for particular question"""
user = request.user
@@ -955,13 +610,10 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
paper = get_object_or_404(AnswerPaper, user=request.user, attempt_number=attempt_num,
question_paper=q_paper)
- if q_id in paper.questions_answered:
- next_q = paper.skip(q_id)
+ if q_id in paper.questions_answered.all():
+ next_q = paper.skip(q_id).id
return show_question(request, next_q, attempt_num, questionpaper_id)
- if not user.is_authenticated():
- return my_redirect('/exam/login/')
-
question = get_object_or_404(Question, pk=q_id)
test_cases = TestCase.objects.filter(question=question)
@@ -974,7 +626,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
if question.type == 'code':
old_skipped = paper.answers.filter(question=question, skipped=True)
_save_skipped_answer(old_skipped, user_code, paper, question)
- next_q = paper.skip(q_id)
+ next_q = paper.skip(q_id).id if paper.skip(q_id) else q_id
return show_question(request, next_q, attempt_num, questionpaper_id)
# Add the answer submitted, regardless of it being correct or not.
@@ -995,7 +647,6 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
else:
user_code = request.POST.get('answer')
user_answer = snippet_code + "\n" + user_code if snippet_code else user_code
-
new_answer = Answer(question=question, answer=user_answer,
correct=False)
new_answer.save()
@@ -1017,60 +668,45 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
new_answer.error = result.get('error')
new_answer.save()
- _update_marks(paper, 'inprogress')
- time_left = paper.time_left()
+ paper.update_marks('inprogress')
+ if paper.time_left() <= 0:
+ reason = 'Your time is up!'
+ return complete(request, reason, attempt_num, questionpaper_id)
+ if not paper.question_paper.quiz.active:
+ reason = 'The quiz has been deactivated!'
+ return complete(request, reason, attempt_num, questionpaper_id)
if not result.get('success'): # Should only happen for non-mcq questions.
- if time_left <= 0:
- reason = 'Your time is up!'
- return complete(request, reason, attempt_num, questionpaper_id)
- if not paper.question_paper.quiz.active:
- reason = 'The quiz has been deactivated!'
- return complete(request, reason, attempt_num, questionpaper_id)
- questions, to_attempt, submitted = get_questions(paper)
old_answer = paper.answers.filter(question=question, skipped=True)
if old_answer:
old_answer[0].answer = user_code
old_answer[0].save()
context = {'question': question, 'error_message': result.get('error'),
- 'paper': paper, 'last_attempt': user_code,
- 'quiz_name': paper.question_paper.quiz.description,
- 'time_left': time_left, 'questions': questions,
- 'to_attempt': to_attempt, 'submitted': submitted}
+ 'paper': paper}
ci = RequestContext(request)
return my_render_to_response('yaksh/question.html', context,
context_instance=ci)
else:
- if time_left <= 0:
- reason = 'Your time is up!'
- return complete(request, reason, attempt_num, questionpaper_id)
# Display the same question if user_answer is None
- elif not user_answer:
- msg = "Please submit a valid option or code"
- time_left = paper.time_left()
- questions, to_attempt, submitted = get_questions(paper)
- context = {'question': question, 'paper': paper,
- 'quiz_name': paper.question_paper.quiz.description,
- 'time_left': time_left, 'questions': questions,
- 'to_attempt': to_attempt, 'submitted': submitted,
- 'error_message': msg}
+ if not user_answer:
ci = RequestContext(request)
-
+ msg = "Please submit a valid option or code"
+ context = {'question': question, 'error_message': msg,
+ 'paper': paper}
elif question.type == 'code' and user_answer:
msg = "Correct Output"
success = "True"
paper.completed_question(question.id)
- time_left = paper.time_left()
- questions, to_attempt, submitted = get_questions(paper)
context = {'question': question, 'paper': paper,
- 'quiz_name': paper.question_paper.quiz.description,
- 'time_left': time_left, 'questions': questions,
- 'to_attempt': to_attempt, 'submitted': submitted,
'error_message': msg, 'success': success}
ci = RequestContext(request)
+ return my_render_to_response('yaksh/question.html', context,
+ context_instance=ci)
else:
next_q = paper.completed_question(question.id)
+ if next_q:
+ next_q = next_q.id
return show_question(request, next_q, attempt_num,
questionpaper_id, success_msg)
@@ -1108,34 +744,13 @@ def validate_answer(user, user_answer, question, json_data=None):
correct = True
return correct, result
-def get_question_labels(request, attempt_num=None, questionpaper_id=None):
- """Get the question number show in template for corresponding
- question id."""
- unattempted_questions = []
- submitted_questions = []
- try:
- q_paper = QuestionPaper.objects.get(id=questionpaper_id)
- paper = AnswerPaper.objects.get(
- user=request.user, attempt_number=attempt_num, question_paper=q_paper)
- except AnswerPaper.DoesNotExist:
- return my_redirect('/exam/start/')
- questions, to_attempt, submitted = get_questions(paper)
- for q_id, question_label in questions.items():
- if q_id in to_attempt:
- unattempted_questions.append(question_label)
- else:
- submitted_questions.append(question_label)
- unattempted_questions.sort()
- submitted_questions.sort()
- return unattempted_questions, submitted_questions
-def quit(request, attempt_num=None, questionpaper_id=None):
+def quit(request, reason=None, attempt_num=None, questionpaper_id=None):
"""Show the quit page when the user logs out."""
- unattempted_questions, submitted_questions = get_question_labels(request,
- attempt_num, questionpaper_id)
- context = {'id': questionpaper_id, 'attempt_num': attempt_num,
- 'unattempted': unattempted_questions,
- 'submitted': submitted_questions}
+ paper = AnswerPaper.objects.get(user=request.user,
+ attempt_number=attempt_num,
+ question_paper=questionpaper_id)
+ context = {'paper': paper, 'message': reason}
return my_render_to_response('yaksh/quit.html', context,
context_instance=RequestContext(request))
@@ -1150,27 +765,20 @@ def complete(request, reason=None, attempt_num=None, questionpaper_id=None):
context = {'message': message}
return my_render_to_response('yaksh/complete.html', context)
else:
- unattempted_questions, submitted_questions = get_question_labels(request,
- attempt_num, questionpaper_id)
q_paper = QuestionPaper.objects.get(id=questionpaper_id)
paper = AnswerPaper.objects.get(user=user, question_paper=q_paper,
attempt_number=attempt_num)
- _update_marks(paper)
- obt_marks = paper.marks_obtained
- tot_marks = paper.question_paper.total_marks
- if obt_marks == paper.question_paper.total_marks:
+ paper.update_marks()
+ if paper.percent == 100:
context = {'message': "Hurray ! You did an excellent job.\
you answered all the questions correctly.\
You have been logged out successfully,\
Thank You !",
- 'unattempted': unattempted_questions,
- 'submitted': submitted_questions}
+ 'paper': paper}
return my_render_to_response('yaksh/complete.html', context)
else:
message = reason or "You are successfully logged out"
- context = {'message': message,
- 'unattempted': unattempted_questions,
- 'submitted': submitted_questions}
+ context = {'message': message, 'paper': paper}
return my_render_to_response('yaksh/complete.html', context)
no = False
message = reason or 'The quiz has been completed. Thank you.'
@@ -1589,7 +1197,7 @@ def download_csv(request, questionpaper_id):
'total_marks',
'percentage',
'questions',
- 'questions_answererd',
+ 'questions_answered',
'status'
]
writer.writerow(header)
@@ -1602,7 +1210,8 @@ def download_csv(request, questionpaper_id):
paper.marks_obtained,
paper.question_paper.total_marks,
paper.percent,
- paper.questions, paper.questions_answered,
+ paper.questions.all(),
+ paper.questions_answered.all(),
paper.status
]
writer.writerow(row)