diff options
author | Prabhu Ramachandran | 2016-03-09 15:19:21 +0530 |
---|---|---|
committer | Prabhu Ramachandran | 2016-03-09 15:19:21 +0530 |
commit | 4874eb1e66c12269fa75849048afd2c9f129d5e3 (patch) | |
tree | 444f167c4bf4516b84e0cfeb6e60e921ae8519f8 | |
parent | 2bff22d61d5d4d6f038c5b368ec437c6bda1dfbe (diff) | |
parent | c974da07d46709d3aa3640ba69c89d1a287ff3c1 (diff) | |
download | online_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.py | 17 | ||||
-rw-r--r-- | yaksh/models.py | 68 | ||||
-rw-r--r-- | yaksh/static/yaksh/css/course.css | 18 | ||||
-rw-r--r-- | yaksh/static/yaksh/css/question_quiz.css | 3 | ||||
-rw-r--r-- | yaksh/templates/manage.html | 6 | ||||
-rw-r--r-- | yaksh/templates/yaksh/add_course.html | 21 | ||||
-rw-r--r-- | yaksh/templates/yaksh/add_quiz.html | 2 | ||||
-rw-r--r-- | yaksh/templates/yaksh/course_detail.html | 59 | ||||
-rw-r--r-- | yaksh/templates/yaksh/courses.html | 50 | ||||
-rw-r--r-- | yaksh/templates/yaksh/quizzes_user.html | 153 | ||||
-rw-r--r-- | yaksh/tests.py | 105 | ||||
-rw-r--r-- | yaksh/urls.py | 12 | ||||
-rw-r--r-- | yaksh/views.py | 129 |
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() |