summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPrabhu Ramachandran2016-03-09 15:19:21 +0530
committerPrabhu Ramachandran2016-03-09 15:19:21 +0530
commit4874eb1e66c12269fa75849048afd2c9f129d5e3 (patch)
tree444f167c4bf4516b84e0cfeb6e60e921ae8519f8
parent2bff22d61d5d4d6f038c5b368ec437c6bda1dfbe (diff)
parentc974da07d46709d3aa3640ba69c89d1a287ff3c1 (diff)
downloadonline_test-4874eb1e66c12269fa75849048afd2c9f129d5e3.tar.gz
online_test-4874eb1e66c12269fa75849048afd2c9f129d5e3.tar.bz2
online_test-4874eb1e66c12269fa75849048afd2c9f129d5e3.zip
Merge pull request #82 from prathamesh920/courses
Courses
-rw-r--r--yaksh/forms.py17
-rw-r--r--yaksh/models.py68
-rw-r--r--yaksh/static/yaksh/css/course.css18
-rw-r--r--yaksh/static/yaksh/css/question_quiz.css3
-rw-r--r--yaksh/templates/manage.html6
-rw-r--r--yaksh/templates/yaksh/add_course.html21
-rw-r--r--yaksh/templates/yaksh/add_quiz.html2
-rw-r--r--yaksh/templates/yaksh/course_detail.html59
-rw-r--r--yaksh/templates/yaksh/courses.html50
-rw-r--r--yaksh/templates/yaksh/quizzes_user.html153
-rw-r--r--yaksh/tests.py105
-rw-r--r--yaksh/urls.py12
-rw-r--r--yaksh/views.py129
13 files changed, 549 insertions, 94 deletions
diff --git a/yaksh/forms.py b/yaksh/forms.py
index de40419..1af02f7 100644
--- a/yaksh/forms.py
+++ b/yaksh/forms.py
@@ -1,5 +1,5 @@
from django import forms
-from yaksh.models import Profile, Quiz, Question, TestCase
+from yaksh.models import Profile, Quiz, Question, TestCase, Course
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
@@ -132,12 +132,15 @@ class QuizForm(forms.Form):
It has the related fields and functions required."""
def __init__(self, *args, **kwargs):
+ user = kwargs.pop('user')
super(QuizForm, self).__init__(*args, **kwargs)
quizzes = [('', 'Select a prerequisite quiz')]
- quizzes = quizzes + \
- list(Quiz.objects.values_list('id', 'description'))
+ quizzes += list(Quiz.objects.filter(
+ course__creator=user).values_list('id', 'description'))
self.fields['prerequisite'] = forms.CharField(required=False,
widget=forms.Select(choices=quizzes))
+ self.fields['course'] = forms.ModelChoiceField(
+ queryset=Course.objects.filter(creator=user))
start_date = forms.DateField(initial=datetime.date.today(), required=False)
start_time = forms.TimeField(initial=datetime.datetime.now().time(), required=False)
@@ -156,6 +159,7 @@ class QuizForm(forms.Form):
help_text='Will be in days')
def save(self):
+ course = self.cleaned_data["course"]
start_date = self.cleaned_data["start_date"]
start_time = self.cleaned_data["start_time"]
end_date = self.cleaned_data["end_date"]
@@ -169,6 +173,7 @@ class QuizForm(forms.Form):
attempts_allowed = self.cleaned_data["attempts_allowed"]
time_between_attempts = self.cleaned_data["time_between_attempts"]
new_quiz = Quiz()
+ new_quiz.course = course
new_quiz.start_date_time = datetime.datetime.combine(start_date,
start_time)
new_quiz.end_date_time = datetime.datetime.combine(end_date,
@@ -265,3 +270,9 @@ class QuestionFilterForm(forms.Form):
TestCaseFormSet = inlineformset_factory(Question, TestCase,\
can_order=False, can_delete=False, extra=1)
+
+
+class CourseForm(forms.ModelForm):
+ class Meta:
+ model = Course
+ fields = ['name', 'active', 'enrollment']
diff --git a/yaksh/models.py b/yaksh/models.py
index 8415930..1bbd035 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -40,6 +40,12 @@ question_types = (
("code", "Code"),
("upload", "Assignment Upload"),
)
+
+enrollment_methods = (
+ ("default", "Enroll Request"),
+ ("open", "Open Course"),
+ )
+
attempts = [(i, i) for i in range(1, 6)]
attempts.append((-1, 'Infinite'))
days_between_attempts = ((j, j) for j in range(401))
@@ -55,6 +61,66 @@ def get_assignment_dir(instance, filename):
###############################################################################
+class Course(models.Model):
+ """ Course for students"""
+ name = models.CharField(max_length=128)
+ enrollment = models.CharField(max_length=32, choices=enrollment_methods)
+ active = models.BooleanField(default=True)
+ creator = models.ForeignKey(User, related_name='creator')
+ 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())
+
+ def request(self, *users):
+ self.requests.add(*users)
+
+ def get_requests(self):
+ return self.requests.all()
+
+ def enroll(self, was_rejected, *users):
+ self.students.add(*users)
+ if not was_rejected:
+ self.requests.remove(*users)
+ else:
+ self.rejected.remove(*users)
+
+ def get_enrolled(self):
+ return self.students.all()
+
+ def reject(self, was_enrolled, *users):
+ self.rejected.add(*users)
+ if not was_enrolled:
+ self.requests.remove(*users)
+ else:
+ self.students.remove(*users)
+
+ def get_rejected(self):
+ return self.rejected.all()
+
+ def is_enrolled(self, user):
+ return user in self.students.all()
+
+ def is_creator(self, user):
+ return self.creator == user
+
+ def is_self_enroll(self):
+ return True if self.enrollment == enrollment_methods[1][0] else False
+
+ def get_quizzes(self):
+ return self.quiz_set.all()
+
+ def activate(self):
+ self.active = True
+
+ def deactivate(self):
+ self.active = False
+
+ def __unicode__(self):
+ return self.name
+
+
+###############################################################################
class Question(models.Model):
"""Question for a quiz."""
@@ -166,6 +232,8 @@ class Quiz(models.Model):
as the "examination" event.
"""
+ course = models.ForeignKey(Course)
+
# The start date of the quiz.
start_date_time = models.DateTimeField("Start Date and Time of the quiz",
default=datetime.datetime.now(),
diff --git a/yaksh/static/yaksh/css/course.css b/yaksh/static/yaksh/css/course.css
new file mode 100644
index 0000000..580a95c
--- /dev/null
+++ b/yaksh/static/yaksh/css/course.css
@@ -0,0 +1,18 @@
+.show-grid [class*="span"] {
+ background: none repeat scroll 0% 0% #EEE;
+ text-align: center;
+ border-radius: 10px;
+ min-height: 30px;
+ line-height: 30px;
+ padding: 5px;
+}
+
+.well{
+ padding: 4px;
+}
+
+.wrap{
+ border: 1px solid #333;
+ padding: 5px;
+ background: none repeat scroll 0% 0% #E0DADA;
+}
diff --git a/yaksh/static/yaksh/css/question_quiz.css b/yaksh/static/yaksh/css/question_quiz.css
index ee249d4..26dc42b 100644
--- a/yaksh/static/yaksh/css/question_quiz.css
+++ b/yaksh/static/yaksh/css/question_quiz.css
@@ -22,3 +22,6 @@ table th, table td
height : 30px;
width : 100px;
}
+input, textarea, select, .uneditable-input {
+ height: 30px;
+}
diff --git a/yaksh/templates/manage.html b/yaksh/templates/manage.html
index ca2ac65..11e2187 100644
--- a/yaksh/templates/manage.html
+++ b/yaksh/templates/manage.html
@@ -27,9 +27,9 @@
<a href="{{ URL_ROOT }}/exam/manage/"><h3 class="brand"><strong>Online Test</h3></strong></a>
<ul>
<li><a href="{{ URL_ROOT }}/exam/manage/questions">Questions</a></li>
- <li><a href="{{ URL_ROOT }}/exam/manage/showquiz">Quizzes</a></li>
<li><a href="{{ URL_ROOT }}/exam/manage/gradeuser">Grade User</a></li>
<li><a href="{{ URL_ROOT }}/exam/manage/monitor">Monitor</a></li>
+<li><a href="{{ URL_ROOT }}/exam/manage/courses">Courses</a></li>
</ul>
<ul style="float:right;">
<li><strong><a style='cursor:pointer' onClick='location.replace("{{URL_ROOT}}/exam/complete/");'>Log out</a></strong></li>
@@ -72,8 +72,8 @@
<hr>
<center>
<h4>Moderator's Dashboard!</h4>
- <h5>Click on the button given below to add a new quiz.</h5>
- <button class="btn" type="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/addquiz");'>Add New Quiz</button>
+ <h5>Click on the button given below to add a new course.</h5>
+ <button class="btn" type="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/add_course");'>Add New Course</button>
</center>
{% endblock %}
</div>
diff --git a/yaksh/templates/yaksh/add_course.html b/yaksh/templates/yaksh/add_course.html
new file mode 100644
index 0000000..0afa27a
--- /dev/null
+++ b/yaksh/templates/yaksh/add_course.html
@@ -0,0 +1,21 @@
+{% extends "manage.html" %}
+
+{% block subtitle %}Add Course{% endblock %}
+
+{% block css %}
+<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/question_quiz.css" type="text/css" />
+{% endblock %}
+
+{% block manage %}
+<form name=frm id=frm action="" method="post" >
+ {% csrf_token %}
+ <center>
+ <table class=span1>
+ {{ form.as_table }}
+ </table>
+ </center>
+
+ <center><button class="btn primary" type="submit" id="submit" name="questionpaper">Add Course</button>
+ <button class="btn primary" type="button" name="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/");'>Cancel</button> </center>
+</form>
+{% endblock %}
diff --git a/yaksh/templates/yaksh/add_quiz.html b/yaksh/templates/yaksh/add_quiz.html
index e78023d..c47d1f7 100644
--- a/yaksh/templates/yaksh/add_quiz.html
+++ b/yaksh/templates/yaksh/add_quiz.html
@@ -20,6 +20,6 @@
</center>
<center><button class="btn" type="submit" id="submit" name="questionpaper">Design Question Paper</button>
- <button class="btn" type="button" name="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/showquiz/");'>Cancel</button> </center>
+ <button class="btn" type="button" name="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/courses/");'>Cancel</button> </center>
</form>
{% endblock %}
diff --git a/yaksh/templates/yaksh/course_detail.html b/yaksh/templates/yaksh/course_detail.html
new file mode 100644
index 0000000..ed56585
--- /dev/null
+++ b/yaksh/templates/yaksh/course_detail.html
@@ -0,0 +1,59 @@
+{% extends "manage.html" %}
+
+{% block title %} Course {% endblock title %}
+
+{% block subtitle %} {{ course.name }} {% endblock %}
+
+{% block css %}
+<link rel="stylesheet" media="all" type="text/css" href="{{ URL_ROOT }}/static/yaksh/css/course.css" />
+{% endblock %}
+
+{% block manage %}
+<div class="row">
+ <div class="span6 wrap">
+ <center><b><u>Requests</u></b></center><br>
+ {% for request in course.get_requests %}
+ <div class="well">
+ <div class="row">
+ <div class="span3" style="padding-top:10px">
+ {{ request.username }}
+ </div>
+ <a class="btn success" href="{{URL_ROOT}}/exam/manage/enroll/{{ course.id }}/{{ request.id }}/"> Enroll </a> <a class="btn danger" href="{{URL_ROOT}}/exam/manage/reject/{{ course.id }}/{{ request.id }}/"> Reject </a>
+ </div>
+ </div>
+ {% endfor %}
+ </div>
+ <div class="span6 wrap">
+ <center><b><u>Rejected</u></b></center><br>
+ {% for rejected in course.get_rejected %}
+ <div class="well">
+ <div class="row">
+ <div class="span4" style="padding-top:10px">
+ {{ rejected.username }}
+ </div>
+ <a class="btn success" href="{{URL_ROOT}}/exam/manage/enroll/rejected/{{ course.id }}/{{ rejected.id }}/"> Enroll </a>
+ </div>
+ </div>
+ {% endfor %}
+ </div>
+</div>
+<br>
+<div class="row">
+ <div class="span6 offset4 wrap">
+ <center><b><u>Enrolled</u></b></center><br>
+ {% for enrolled in course.get_enrolled %}
+ <div class="well">
+ <div class="row">
+ <div class="span4" style="padding-top:10px">
+ {{ enrolled.username }}
+ </div>
+ <a class="btn danger" href="{{URL_ROOT}}/exam/manage/enrolled/reject/{{ course.id }}/{{ enrolled.id }}/"> Reject </a>
+ </div>
+ </div>
+ {% endfor %}
+ </div>
+</div>
+<div class="pull-right">
+ <a class="btn primary" href="{{URL_ROOT}}/exam/manage/toggle_status/{{ course.id }}/">{% if course.active %}Deactivate Course {% else %} Activate Course {% endif %}</a>
+</div>
+{% endblock %}
diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html
new file mode 100644
index 0000000..f8f8273
--- /dev/null
+++ b/yaksh/templates/yaksh/courses.html
@@ -0,0 +1,50 @@
+{% extends "manage.html" %}
+
+{% block title %} Courses {% endblock title %}
+
+{% block subtitle %} Courses {% endblock %}
+
+{% block css %}
+<link rel="stylesheet" media="all" type="text/css" href="{{ URL_ROOT }}/static/yaksh/css/course.css" />
+{% endblock %}
+
+{% block manage %}
+{% if not courses %}
+ <center><h5> You have not created any courses </h5></center>
+{% else %}
+ {% for course in courses %}
+ <div class="row show-grid">
+ <div class="span14">
+ <div class="row">
+ <div class="span6">
+ <p>
+ <b><u>Course</u></b>
+ {% if course.active %}
+ <span class="label success">Active</span>
+ {% else %}
+ <span class="label important">Closed</span>
+ {% endif %}
+ </p>
+ <a href="{{URL_ROOT}}/exam/manage/course_detail/{{course.id}}/">{{ course.name }}</a>
+ </br>
+ </div>
+ <div class="span6">
+ <p><b><u>Quiz(zes)</u></b></p>
+ {% if course.get_quizzes %}
+ {% for quiz in course.get_quizzes %}
+ <a href="{{URL_ROOT}}/exam/manage/addquiz/{{quiz.id}}/">{{ quiz.description }}</a><br>
+ {% endfor %}
+ {% else %}
+ <p><b>No quiz </b></p>
+ {% endif %}
+ </div>
+ </div>
+ </div>
+ </div>
+ <br><br>
+ {% endfor %}
+
+ <button class="btn primary" type="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/add_course");'>Add New Course</button>
+ <button class="btn primary" type="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/addquiz");'>Add New Quiz</button>
+{% endif %}
+{% endblock %}
diff --git a/yaksh/templates/yaksh/quizzes_user.html b/yaksh/templates/yaksh/quizzes_user.html
index 1dcd20d..a800e68 100644
--- a/yaksh/templates/yaksh/quizzes_user.html
+++ b/yaksh/templates/yaksh/quizzes_user.html
@@ -4,7 +4,7 @@
{% block subtitle %}Hello {{ user.first_name }}, welcome to your dashboard !{% endblock %}
{% block css %}
-<link rel="stylesheet" href="{{ URL_ROOT }}/static/yaksh/css/question_quiz.css" type="text/css" />
+<link rel="stylesheet" media="all" type="text/css" href="{{ URL_ROOT }}/static/yaksh/css/course.css" />
{% endblock %}
{% block script %}
@@ -14,73 +14,106 @@
{% block manage %}
- {% if cannot_attempt %}
- <p>You have not passed the prerequisite & hence you cannot take the quiz.</p>
- {% endif %}
- <h4>List of quizzes availbale for you</h4>
- {% if not quizzes %}
- <h5>No active quizzes for you</h5>
- {% endif %}
- <table>
- <th>Quiz</th>
- <th>Pre requisite quiz</th>
- {% for paper in quizzes %}
- <tr>
- {% if paper in unexpired_quizzes %}
- <td>
- <a href="{{ URL_ROOT }}/exam/intro/{{paper.id}}">{{ paper.quiz.description }}</a><br>
- </td>
- {% else %}
- <td>
- {{ paper.quiz.description }} <span class="label important">Expired</span><br>
- </td>
+
+<center><b><u>Available Courses</u></b></center><br>
+{% for course in courses %}
+<div class="row show-grid">
+ <div class="span14">
+ <div class="row">
+ <div class="span6">
+ <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>
+ {% elif user in course.rejected.all %}<span class="label important">Request Rejected</span>
+ {% elif user in course.students.all %}<span class="label notice">Enrolled</span>
+ {% else %}
+ {% if course.is_self_enroll %}
+ <a class="btn success" href="{{ URL_ROOT }}/exam/self_enroll/{{ course.id }}">Enroll</a>
+ {% else %}
+ <a class="btn success" href="{{ URL_ROOT }}/exam/enroll_request/{{ course.id }}">Enroll Request</a>
+ {% endif %}
+ {% endif %}
+ </div>
+ </div>
+
+ <div class="row">
+ {% if user in course.students.all %}
+ {% if cannot_attempt %}
+ <p>You have not passed the prerequisite & hence you cannot take the quiz.</p>
+ {% endif %}
+ <h4>List of quizzes availbale for you</h4>
+ {% if not quizzes %}
+ <h5>No active quizzes for you</h5>
+ {% endif %}
+ <table>
+ <th>Quiz</th>
+ <th>Pre requisite quiz</th>
+ {% for paper in quizzes %}
+ {% if paper.quiz.course_id == course.id %}
+ <tr>
+ {% if paper in unexpired_quizzes %}
+ <td>
+ <a href="{{ URL_ROOT }}/exam/intro/{{paper.id}}">{{ paper.quiz.description }}</a><br>
+ </td>
+ {% else %}
+ <td>
+ {{ paper.quiz.description }} <span class="label important">Expired</span><br>
+ </td>
+ {% endif %}
+ <td>
+ {% if paper.quiz.prerequisite %}
+ You have to pass {{ paper.quiz.prerequisite.description }} for taking {{ paper.quiz.description }}
+ {% else %}
+ No pre requisites for {{ paper.quiz.description }}
+ {% endif %}
+ </td>
+ </tr>
+ {% endif %}
+ {% endfor %}
+ </table>
{% endif %}
+ </div>
+ </div>
+</div><!--/row show-grid-->
+ </br>
+{% endfor %}
+<hr>
+<h4>List of quizzes taken by you so far</h4>
+{% if quizzes_taken %}
+ <table class="bordered-table zebra-striped">
+ <th>Quiz</th>
+ <th>Result</th>
+ <th>Marks Obtained</th>
+ <th>Total Marks</th>
+ <th>Percentage</th>
+ {% for paper in quizzes_taken %}
+ <tr>
<td>
- {% if paper.quiz.prerequisite %}
- You have to pass {{ paper.quiz.prerequisite.description }} for taking {{ paper.quiz.description }}
+ {{ paper.question_paper.quiz.description }}
+ </td>
+ <td>
+ {% if paper.passed %}
+ <p>Pass</p>
{% else %}
- No pre requisites for {{ paper.quiz.description }}
+ <p>Fail</p>
{% endif %}
</td>
+ <td>
+ {{ paper.marks_obtained }}
+ </td>
+ <td>
+ {{ paper.question_paper.total_marks }}
+ </td>
+ <td>
+ {{ paper.percent }}
+ </td>
</tr>
{% endfor %}
</table>
- <hr>
- <h4>List of quizzes taken by you so far</h4>
- {% if quizzes_taken %}
- <table class="bordered-table zebra-striped">
- <th>Quiz</th>
- <th>Result</th>
- <th>Marks Obtained</th>
- <th>Total Marks</th>
- <th>Percentage</th>
- {% for paper in quizzes_taken %}
- <tr>
- <td>
- {{ paper.question_paper.quiz.description }}
- </td>
- <td>
- {% if paper.passed %}
- <p>Pass</p>
- {% else %}
- <p>Fail</p>
- {% endif %}
- </td>
- <td>
- {{ paper.marks_obtained }}
- </td>
- <td>
- {{ paper.question_paper.total_marks }}
- </td>
- <td>
- {{ paper.percent }}
- </td>
- </tr>
- {% endfor %}
- </table>
- {% else %}
- <p>You have not taken any quiz yet !!</p>
- {% endif %}
+{% else %}
+ <p>You have not taken any quiz yet !!</p>
+{% endif %}
{% endblock %}
diff --git a/yaksh/tests.py b/yaksh/tests.py
index 9c67c02..848df74 100644
--- a/yaksh/tests.py
+++ b/yaksh/tests.py
@@ -1,8 +1,9 @@
from django.utils import unittest
from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
- QuestionSet, AnswerPaper, Answer, TestCase
+ QuestionSet, AnswerPaper, Answer, TestCase, Course
import datetime, json
+
def setUpModule():
# create user profile
user = User.objects.create_user(username='demo_user',
@@ -13,6 +14,15 @@ def setUpModule():
email='demo@test.com')
Profile.objects.create(user=user, roll_number=1, institute='IIT',
department='Chemical', position='Student')
+ student = User.objects.create_user(username='demo_user3',
+ password='demo',
+ email='demo3@test.com')
+ Profile.objects.create(user=student, roll_number=3, institute='IIT',
+ department='Chemical', position='Student')
+
+ # create a course
+ course = Course.objects.create(name="Python Course",
+ enrollment="Enroll Request", creator=user)
# create 20 questions
for i in range(1, 21):
@@ -20,10 +30,11 @@ def setUpModule():
# create a quiz
Quiz.objects.create(start_date_time=datetime.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)
+ duration=30, active=False,
+ attempts_allowed=-1, time_between_attempts=0,
+ description='demo quiz', pass_criteria=40,
+ language='Python', prerequisite=None,
+ course=course)
def tearDownModule():
@@ -62,14 +73,14 @@ class QuestionTestCases(unittest.TestCase):
func_name='def myfunc', kw_args='a=10,b=11',
pos_args='12,13', expected_answer='15')
answer_data = { "test": "",
- "user_answer": "demo_answer",
- "test_parameter": [{"func_name": "def myfunc",
- "expected_answer": "15",
- "test_id": self.testcase.id,
- "pos_args": ["12", "13"],
- "kw_args": {"a": "10",
+ "user_answer": "demo_answer",
+ "test_parameter": [{"func_name": "def myfunc",
+ "expected_answer": "15",
+ "test_id": self.testcase.id,
+ "pos_args": ["12", "13"],
+ "kw_args": {"a": "10",
"b": "11"}
- }],
+ }],
"id": self.question.id,
"ref_code_path": "",
}
@@ -93,10 +104,9 @@ class QuestionTestCases(unittest.TestCase):
def test_consolidate_answer_data(self):
""" Test consolidate_answer_data function """
- result = self.question.consolidate_answer_data([self.testcase],
+ result = self.question.consolidate_answer_data([self.testcase],
self.user_answer)
self.assertEqual(result, self.answer_data_json)
-
###############################################################################
@@ -107,7 +117,7 @@ class TestCaseTestCases(unittest.TestCase):
description='Write a function', points=1.0,
snippet='def myfunc()')
self.question.save()
- self.testcase = TestCase(question=self.question,
+ self.testcase = TestCase(question=self.question,
func_name='def myfunc', kw_args='a=10,b=11',
pos_args='12,13', expected_answer='15')
@@ -188,7 +198,7 @@ class QuestionPaperTestCases(unittest.TestCase):
def test_questionpaper(self):
""" Test question paper"""
self.assertEqual(self.question_paper.quiz.description, 'demo quiz')
- self.assertEqual(list(self.question_paper.fixed_questions.all()),
+ self.assertSequenceEqual(self.question_paper.fixed_questions.all(),
[self.questions[3], self.questions[5]])
self.assertTrue(self.question_paper.shuffle_questions)
@@ -350,3 +360,66 @@ class AnswerPaperTestCases(unittest.TestCase):
self.assertEqual(self.answerpaper.status, 'inprogress')
self.answerpaper.update_status('completed')
self.assertEqual(self.answerpaper.status, 'completed')
+
+
+###############################################################################
+class CourseTestCases(unittest.TestCase):
+ def setUp(self):
+ self.course = Course.objects.get(pk=1)
+ 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)
+
+ def test_is_creator(self):
+ """ Test is_creator method of Course"""
+ self.assertTrue(self.course.is_creator(self.creator))
+
+ def test_is_self_enroll(self):
+ """ Test is_self_enroll method of Course"""
+ self.assertFalse(self.course.is_self_enroll())
+
+ def test_deactivate(self):
+ """ Test deactivate method of Course"""
+ self.course.deactivate()
+ self.assertFalse(self.course.active)
+
+ def test_activate(self):
+ """ Test activate method of Course"""
+ self.course.activate()
+ self.assertTrue(self.course.active)
+
+ def test_request(self):
+ """ Test request and get_requests methods of Course"""
+ self.course.request(self.student1, self.student2)
+ self.assertSequenceEqual(self.course.get_requests(),
+ [self.student1, self.student2])
+
+ def test_enroll_reject(self):
+ """ Test enroll, reject, get_enrolled and get_rejected methods"""
+ self.assertSequenceEqual(self.course.get_enrolled(), [])
+ was_rejected = False
+ self.course.enroll(was_rejected, self.student1)
+ self.assertSequenceEqual(self.course.get_enrolled(), [self.student1])
+
+ self.assertSequenceEqual(self.course.get_rejected(), [])
+ was_enrolled = False
+ self.course.reject(was_enrolled, self.student2)
+ self.assertSequenceEqual(self.course.get_rejected(), [self.student2])
+
+ was_rejected = True
+ self.course.enroll(was_rejected, self.student2)
+ self.assertSequenceEqual(self.course.get_enrolled(),
+ [self.student1, self.student2])
+ self.assertSequenceEqual(self.course.get_rejected(), [])
+
+ was_enrolled = True
+ self.course.reject(was_enrolled, self.student2)
+ self.assertSequenceEqual(self.course.get_rejected(), [self.student2])
+ self.assertSequenceEqual(self.course.get_enrolled(), [self.student1])
+
+ self.assertTrue(self.course.is_enrolled(self.student1))
+
+ def test_get_quizzes(self):
+ """ Test get_quizzes method of Courses"""
+ self.assertSequenceEqual(self.course.get_quizzes(), [self.quiz])
diff --git a/yaksh/urls.py b/yaksh/urls.py
index e6c2e15..ad0a925 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -19,6 +19,8 @@ urlpatterns = patterns('yaksh.views',
'check'),
url(r'^intro/$', 'start'),
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'),
url(r'^manage/$', 'prof_manage'),
url(r'^manage/addquestion/$', 'add_question'),
@@ -54,6 +56,16 @@ urlpatterns = patterns('yaksh.views',
'show_statistics'),
url(r'^manage/statistics/question/(?P<questionpaper_id>\d+)/(?P<attempt_number>\d+)/$',
'show_statistics'),
+ url(r'manage/courses/$', 'courses'),
+ url(r'manage/add_course/$', 'add_course'),
+ url(r'manage/course_detail/(?P<course_id>\d+)/$', 'course_detail'),
+ url(r'manage/enroll/(?P<course_id>\d+)/(?P<user_id>\d+)/$', 'enroll'),
+ url(r'manage/enroll/rejected/(?P<course_id>\d+)/(?P<user_id>\d+)/$',
+ 'enroll', {'was_rejected': True}),
+ url(r'manage/reject/(?P<course_id>\d+)/(?P<user_id>\d+)/$', 'reject'),
+ url(r'manage/enrolled/reject/(?P<course_id>\d+)/(?P<user_id>\d+)/$',
+ 'reject', {'was_enrolled': True}),
+ url(r'manage/toggle_status/(?P<course_id>\d+)/$', 'toggle_course_status'),
url(r'^ajax/questionpaper/(?P<query>.+)/$', 'ajax_questionpaper'),
url(r'^ajax/questions/filter/$', 'ajax_questions_filter'),
diff --git a/yaksh/views.py b/yaksh/views.py
index 6558427..9cd6fdb 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -17,11 +17,11 @@ from taggit.models import Tag
from itertools import chain
import json
# Local imports.
-from yaksh.models import Quiz, Question, QuestionPaper, QuestionSet
+from yaksh.models import Quiz, Question, QuestionPaper, QuestionSet, Course
from yaksh.models import Profile, Answer, AnswerPaper, User, TestCase
from yaksh.forms import UserRegisterForm, UserLoginForm, QuizForm,\
QuestionForm, RandomQuestionForm, TestCaseFormSet,\
- QuestionFilterForm
+ QuestionFilterForm, CourseForm
from yaksh.xmlrpc_clients import code_server
from settings import URL_ROOT
from yaksh.models import AssignmentUpload
@@ -157,6 +157,8 @@ def quizlist_user(request):
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
@@ -170,7 +172,8 @@ def quizlist_user(request):
'quizzes': avail_quizzes,
'user': user,
'quizzes_taken': quizzes_taken,
- 'unexpired_quizzes': unexpired_quizzes
+ 'unexpired_quizzes': unexpired_quizzes,
+ 'courses': courses
}
return my_render_to_response("yaksh/quizzes_user.html", context)
@@ -181,6 +184,8 @@ def intro(request, questionpaper_id):
user = request.user
ci = RequestContext(request)
quest_paper = QuestionPaper.objects.get(id=questionpaper_id)
+ 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
@@ -495,7 +500,7 @@ def add_quiz(request, quiz_id=None):
if not user.is_authenticated() or not is_moderator(user):
raise Http404('You are not allowed to view this page!')
if request.method == "POST":
- form = QuizForm(request.POST)
+ form = QuizForm(request.POST, user=user)
if form.is_valid():
data = form.cleaned_data
if quiz_id is None:
@@ -527,13 +532,13 @@ def add_quiz(request, quiz_id=None):
context_instance=ci)
else:
if quiz_id is None:
- form = QuizForm()
+ form = QuizForm(user=user)
return my_render_to_response('yaksh/add_quiz.html',
{'form': form},
context_instance=ci)
else:
d = Quiz.objects.get(id=quiz_id)
- form = QuizForm()
+ form = QuizForm(user=user)
form.initial['start_date'] = d.start_date_time.date()
form.initial['start_time'] = d.start_date_time.time()
form.initial['end_date'] = d.end_date_time.date()
@@ -759,7 +764,7 @@ def prof_manage(request):
rights/permissions and log in."""
user = request.user
if user.is_authenticated() and is_moderator(user):
- question_papers = QuestionPaper.objects.all()
+ question_papers = QuestionPaper.objects.filter(quiz__course__creator=user)
users_per_paper = []
for paper in question_papers:
answer_papers = AnswerPaper.objects.filter(question_paper=paper)
@@ -819,6 +824,9 @@ def start(request, attempt_num=None, questionpaper_id=None):
'instructor/administrator. Please login again thereafter.'
return complete(request, msg, attempt_num, questionpaper_id)
+ if not questionpaper.quiz.course.is_enrolled(user):
+ raise Http404('You are not allowed to view this page!')
+
try:
old_paper = AnswerPaper.objects.get(
question_paper=questionpaper, user=user, attempt_number=attempt_num)
@@ -941,7 +949,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None):
"""Checks the answers of the user for particular question"""
user = request.user
q_paper = QuestionPaper.objects.get(id=questionpaper_id)
- paper = AnswerPaper.objects.get(user=request.user, attempt_number=attempt_num,
+ paper = get_object_or_404(AnswerPaper, user=request.user, attempt_number=attempt_num,
question_paper=q_paper)
if q_id in paper.questions_answered:
@@ -1166,6 +1174,103 @@ def complete(request, reason=None, attempt_num=None, questionpaper_id=None):
@login_required
+def add_course(request):
+ user = request.user
+ ci = RequestContext(request)
+ if not is_moderator(user):
+ raise Http404('You are not allowed to view this page')
+ if request.method == 'POST':
+ form = CourseForm(request.POST)
+ if form.is_valid():
+ new_course = form.save(commit=False)
+ new_course.creator = user
+ new_course.save()
+ return my_render_to_response('manage.html', {'course': new_course})
+ else:
+ return my_render_to_response('yaksh/add_course.html',
+ {'form': form},
+ context_instance=ci)
+ else:
+ form = CourseForm()
+ return my_render_to_response('yaksh/add_course.html', {'form': form},
+ context_instance=ci)
+
+
+@login_required
+def enroll_request(request, course_id):
+ user = request.user
+ ci = RequestContext(request)
+ course = get_object_or_404(Course, pk=course_id)
+ course.request(user)
+ return my_redirect('/exam/manage/')
+
+
+@login_required
+def self_enroll(request, course_id):
+ user = request.user
+ ci = RequestContext(request)
+ course = get_object_or_404(Course, pk=course_id)
+ if course.is_self_enroll():
+ was_rejected = False
+ course.enroll(was_rejected, user)
+ return my_redirect('/exam/manage/')
+
+
+@login_required
+def courses(request):
+ user = request.user
+ if not is_moderator(user):
+ raise Http404('You are not allowed to view this page')
+ courses = Course.objects.filter(creator=user)
+ return my_render_to_response('yaksh/courses.html', {'courses': courses})
+
+
+@login_required
+def course_detail(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, creator=user, pk=course_id)
+ return my_render_to_response('yaksh/course_detail.html', {'course': course})
+
+
+@login_required
+def enroll(request, course_id, user_id, was_rejected=False):
+ user = request.user
+ if not is_moderator(user):
+ raise Http404('You are not allowed to view this page')
+ course = get_object_or_404(Course, creator=user, pk=course_id)
+ user = get_object_or_404(User, pk=user_id)
+ course.enroll(was_rejected, user)
+ return course_detail(request, course_id)
+
+
+@login_required
+def reject(request, course_id, user_id, was_enrolled=False):
+ user = request.user
+ if not is_moderator(user):
+ raise Http404('You are not allowed to view this page')
+ course = get_object_or_404(Course, creator=user, pk=course_id)
+ user = get_object_or_404(User, pk=user_id)
+ course.reject(was_enrolled, user)
+ return course_detail(request, course_id)
+
+
+@login_required
+def toggle_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, creator=user, pk=course_id)
+ if course.active:
+ course.deactivate()
+ else:
+ course.activate()
+ course.save()
+ return course_detail(request, course_id)
+
+
+@login_required
def show_statistics(request, questionpaper_id, attempt_number=None):
user = request.user
if not is_moderator(user):
@@ -1201,7 +1306,7 @@ def monitor(request, questionpaper_id=None):
raise Http404('You are not allowed to view this page!')
if questionpaper_id is None:
- q_paper = QuestionPaper.objects.all()
+ q_paper = QuestionPaper.objects.filter(quiz__course__creator=user)
context = {'papers': [],
'quiz': None,
'quizzes': q_paper}
@@ -1209,10 +1314,12 @@ def monitor(request, questionpaper_id=None):
context_instance=ci)
# quiz_id is not None.
try:
- q_paper = QuestionPaper.objects.get(id=questionpaper_id)
+ q_paper = QuestionPaper.objects.get(id=questionpaper_id,
+ quiz__course__creator=user)
except QuestionPaper.DoesNotExist:
papers = []
q_paper = None
+ latest_attempts = []
else:
latest_attempts = []
papers = AnswerPaper.objects.filter(question_paper=q_paper).order_by(
@@ -1301,7 +1408,7 @@ def show_all_quiz(request):
forms = []
for j in data:
d = Quiz.objects.get(id=j)
- form = QuizForm()
+ form = QuizForm(user=user)
form.initial['start_date'] = d.start_date_time.date()
form.initial['start_time'] = d.start_date_time.time()
form.initial['end_date'] = d.end_date_time.date()