summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--yaksh/forms.py5
-rw-r--r--yaksh/models.py15
-rw-r--r--yaksh/templates/yaksh/addteacher.html77
-rw-r--r--yaksh/templates/yaksh/courses.html41
-rw-r--r--yaksh/templates/yaksh/user_data.html2
-rw-r--r--yaksh/tests.py25
-rw-r--r--yaksh/urls.py6
-rw-r--r--yaksh/views.py142
8 files changed, 290 insertions, 23 deletions
diff --git a/yaksh/forms.py b/yaksh/forms.py
index 681eca0..16f82fb 100644
--- a/yaksh/forms.py
+++ b/yaksh/forms.py
@@ -6,7 +6,7 @@ from django.contrib.auth.models import User
from taggit.managers import TaggableManager
from taggit.forms import TagField
from django.forms.models import inlineformset_factory
-
+from django.db.models import Q
from string import letters, punctuation, digits
import datetime
@@ -138,7 +138,8 @@ class QuizForm(forms.ModelForm):
queryset=Quiz.objects.filter(course__creator=user))
self.fields['prerequisite'].required = False
self.fields['course'] = forms.ModelChoiceField(
- queryset=Course.objects.filter(creator=user))
+ queryset=Course.objects.filter(Q(creator=user)|
+ Q(teachers=user)).distinct())
class Meta:
model = Quiz
diff --git a/yaksh/models.py b/yaksh/models.py
index 9b92791..a4ea6c3 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -23,7 +23,6 @@ class Profile(models.Model):
department = models.CharField(max_length=64)
position = models.CharField(max_length=64)
-
languages = (
("python", "Python"),
("bash", "Bash"),
@@ -71,6 +70,8 @@ class Course(models.Model):
requests = models.ManyToManyField(User, related_name='requests')
rejected = models.ManyToManyField(User, related_name='rejected')
created_on = models.DateTimeField(auto_now_add=True)
+ teachers = models.ManyToManyField(User, related_name='teachers')
+
def request(self, *users):
self.requests.add(*users)
@@ -104,6 +105,9 @@ class Course(models.Model):
def is_creator(self, user):
return self.creator == user
+ def is_teacher(self, user):
+ return True if user in self.teachers.all() else False
+
def is_self_enroll(self):
return True if self.enrollment == enrollment_methods[1][0] else False
@@ -116,6 +120,15 @@ class Course(models.Model):
def deactivate(self):
self.active = False
+ def add_teachers(self, *teachers):
+ self.teachers.add(*teachers)
+
+ def get_teachers(self):
+ return self.teachers.all()
+
+ def remove_teachers(self, *teachers):
+ self.teachers.remove(*teachers)
+
def __unicode__(self):
return self.name
diff --git a/yaksh/templates/yaksh/addteacher.html b/yaksh/templates/yaksh/addteacher.html
new file mode 100644
index 0000000..7e04f71
--- /dev/null
+++ b/yaksh/templates/yaksh/addteacher.html
@@ -0,0 +1,77 @@
+{% extends "manage.html" %}
+
+{% block title %} Add teacher {% 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 %}
+<center><h3>Add Teachers for this course</h3><br></center>
+<center><h3>Search teacher with username, firstname, lastname, email</h3><br></center>
+<div align="center">
+ <form action="{{ URL_ROOT }}/exam/manage/searchteacher/{{ course.id }}/" method="post">
+ {% csrf_token %}
+ Search Teacher: <input type="text" name="uname" style="height: 25px; padding: 0px"><br><br>
+ <center><button class="btn" type="submit">Search</button>
+ <button class="btn" type="button" name="button" onClick='location.replace("{{URL_ROOT}}/exam/manage/courses");'>Cancel</button> </center></form>
+</div>
+<br><br>
+<form action="{{ URL_ROOT }}/exam/manage/addteacher/{{ course.id }}/" method="post">
+{% csrf_token %}
+{% if success == True %}
+ {% if teachers|length == 0 %}
+ <center><h3>No results found</h3></center>
+ {% else %}
+ <center><b><u>Search Results</u></b></center><br>
+ <center><b>Search results does not include teachers already added</b></center><br>
+ <table>
+ <th></th>
+ <th>Username</th>
+ <th>First Name</th>
+ <th>Last Name</th>
+ <th>Email</th>
+ <th>Institute</th>
+ <th>Department</th>
+ <th>Position</th>
+ {% for teacher in teachers %}
+ {% if teacher not in course.get_teachers %}
+ <tr>
+ <td><input type="checkbox" name="check" value="{{ teacher.id }}"></td>
+ <td>{{ teacher.username }}</td>
+ <td>{{ teacher.first_name }}</td>
+ <td>{{ teacher.last_name }}</td>
+ <td>{{ teacher.email }}</td>
+ <td>{{ teacher.profile.institute }}</td>
+ <td>{{ teacher.profile.department }}</td>
+ <td>{{ teacher.profile.position }}</td>
+ </tr>
+ {% endif %}
+ {% endfor %}
+ </table>
+ <button class="btn" type="submit">Add Selected</button>
+ {% endif %}
+{% endif %}
+</form>
+{% if status == True %}
+<div class="row">
+ <div class="span6 offset4 wrap">
+ <center><b><u>Teacher(s) Added</u></b></center><br>
+ {% if teachers_added %}
+ {% for teacher in teachers_added %}
+ <div class="well">
+ <div class="row">
+ <div class="span3" style="width: auto;">
+ <h5>{{ teacher.get_full_name }}</h5>
+ </div>
+ </div>
+ </div>
+ {% endfor %}
+ {% else %}
+ <center><b>No Teacher(s) Added</b></center>
+ {% endif %}
+ </div>
+</div>
+{% endif %}
+{% endblock %}
diff --git a/yaksh/templates/yaksh/courses.html b/yaksh/templates/yaksh/courses.html
index f8f8273..06c848c 100644
--- a/yaksh/templates/yaksh/courses.html
+++ b/yaksh/templates/yaksh/courses.html
@@ -9,10 +9,15 @@
{% endblock %}
{% block manage %}
+<a href="{{URL_ROOT}}/exam/manage/allotted_course/">View Allotted Courses</a><br>
{% if not courses %}
- <center><h5> You have not created any courses </h5></center>
+ <center><h4> No new Courses added </h4></center>
{% else %}
+<center><h3> Course(s) Added</h3></center>
{% for course in courses %}
+ {% if user != course.creator %}
+ <h4> {{course.creator.get_full_name}} added you to this course</h4>
+ {% endif %}
<div class="row show-grid">
<div class="span14">
<div class="row">
@@ -26,8 +31,39 @@
{% endif %}
</p>
<a href="{{URL_ROOT}}/exam/manage/course_detail/{{course.id}}/">{{ course.name }}</a>
- </br>
+ </br></br>
+ {% if user == course.creator %}
+ <div class="row">
+ <div class="span6 wrap">
+ <center><b><u>Teacher(s) Added to {{ course }}</u></b></center>
+ {% if course.get_teachers %}
+ <div align="left">
+ <form action="{{URL_ROOT}}/exam/manage/remove_teachers/{{ course.id }}/" method="post">
+ {% csrf_token %}
+ {% for teacher in course.get_teachers %}
+ <div class="well">
+ <div class="row">
+ <div class="span3" style="width: auto;">
+ <input type="checkbox" name="remove" value="{{ teacher.id }}">&nbsp;{{ teacher.get_full_name }}
+ </div>
+ </div>
+ </div>
+ {% endfor %}
+ <button class="btn success" type="submit">Remove Selected</button>
+ </div>
+ {% else %}
+ <center><b>No Teacher(s) Added</b></center>
+ {% endif %}
+ </form>
+ </div>
+ </div>
+ {% endif %}
+ </div>
+ {% if user == course.creator %}
+ <div class="span6">
+ <p><b><a href="{{URL_ROOT}}/exam/manage/searchteacher/{{course.id}}/">Add Teacher</a></b></p>
</div>
+ {% endif %}
<div class="span6">
<p><b><u>Quiz(zes)</u></b></p>
{% if course.get_quizzes %}
@@ -43,7 +79,6 @@
</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 %}
diff --git a/yaksh/templates/yaksh/user_data.html b/yaksh/templates/yaksh/user_data.html
index 3350763..22be3ed 100644
--- a/yaksh/templates/yaksh/user_data.html
+++ b/yaksh/templates/yaksh/user_data.html
@@ -82,7 +82,7 @@ User IP address: {{ paper.user_ip }}
<a href="{{URL_ROOT}}/exam/manage/monitor/">Monitor quiz</a>
{% else %}
{% with data.papers.0 as paper %}
-<a href="{{URL_ROOT}}/exam/manage/monitor/{{paper.quiz.id}}/">Monitor quiz</a>
+<a href="{{URL_ROOT}}/exam/manage/monitor/{{paper.question_paper.id}}/">Monitor quiz</a>
{% endwith %}
{% endif %}
diff --git a/yaksh/tests.py b/yaksh/tests.py
index d6d1d91..cc3100e 100644
--- a/yaksh/tests.py
+++ b/yaksh/tests.py
@@ -3,7 +3,7 @@ from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
QuestionSet, AnswerPaper, Answer, TestCase, Course
import json
from datetime import datetime, timedelta
-
+from django.contrib.auth.models import Group
def setUpModule():
# create user profile
@@ -55,19 +55,19 @@ def tearDownModule():
###############################################################################
class ProfileTestCases(unittest.TestCase):
def setUp(self):
- self.user = User.objects.get(pk=1)
+ self.user1 = User.objects.get(pk=1)
self.profile = Profile.objects.get(pk=1)
+ self.user2 = User.objects.get(pk=3)
def test_user_profile(self):
""" Test user profile"""
- self.assertEqual(self.user.username, 'demo_user')
+ self.assertEqual(self.user1.username, 'demo_user')
self.assertEqual(self.profile.user.username, 'demo_user')
self.assertEqual(int(self.profile.roll_number), 1)
self.assertEqual(self.profile.institute, 'IIT')
self.assertEqual(self.profile.department, 'Chemical')
self.assertEqual(self.profile.position, 'Student')
-
###############################################################################
class QuestionTestCases(unittest.TestCase):
def setUp(self):
@@ -437,3 +437,20 @@ class CourseTestCases(unittest.TestCase):
def test_get_quizzes(self):
""" Test get_quizzes method of Courses"""
self.assertSequenceEqual(self.course.get_quizzes(), [self.quiz1, self.quiz2])
+
+ def test_add_teachers(self):
+ """ Test to add teachers to a course"""
+ self.course.add_teachers(self.student1, self.student2)
+ self.assertSequenceEqual(self.course.get_teachers(), [self.student1, self.student2])
+
+ def test_remove_teachers(self):
+ """ Test to remove teachers from a course"""
+ self.course.add_teachers(self.student1, self.student2)
+ self.course.remove_teachers(self.student1)
+ self.assertSequenceEqual(self.course.get_teachers(), [self.student2])
+
+ def test_is_teacher(self):
+ """ Test to check if user is teacher"""
+ self.course.add_teachers(self.student2)
+ result = self.course.is_teacher(self.student2)
+ self.assertTrue(result)
diff --git a/yaksh/urls.py b/yaksh/urls.py
index 471a36b..ea1922d 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -84,5 +84,9 @@ urlpatterns += [
url(r'manage/enroll/rejected/(?P<course_id>\d+)/$',
views.enroll, {'was_rejected': True}),
url(r'manage/enrolled/reject/(?P<course_id>\d+)/$',
- views.reject, {'was_enrolled': True})
+ views.reject, {'was_enrolled': True}),
+ url(r'^manage/searchteacher/(?P<course_id>\d+)/$', views.search_teacher),
+ url(r'^manage/addteacher/(?P<course_id>\d+)/$', views.add_teacher),
+ url(r'^manage/allotted_course/$', views.allotted_courses),
+ url(r'^manage/remove_teachers/(?P<course_id>\d+)/$', views.remove_teachers)
]
diff --git a/yaksh/views.py b/yaksh/views.py
index 1709488..5cc9f99 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -11,9 +11,10 @@ from django.contrib.auth import login, logout, authenticate
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.template import RequestContext
from django.http import Http404
-from django.db.models import Sum, Max
+from django.db.models import Sum, Max, Q
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
+from django.contrib.auth.models import Group
from taggit.models import Tag
from itertools import chain
import json
@@ -69,6 +70,13 @@ def has_profile(user):
""" check if user has profile """
return True if hasattr(user, 'profile') else False
+def add_to_group(users):
+ """ add users to moderator group """
+ group = Group.objects.get(name="moderator")
+ for user in users:
+ if not is_moderator(user):
+ user.groups.add(group)
+
def index(request):
"""The start page.
"""
@@ -596,19 +604,26 @@ def self_enroll(request, course_id):
@login_required
def courses(request):
user = request.user
+ ci = RequestContext(request)
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})
+ return my_render_to_response('yaksh/courses.html', {'courses': courses},
+ context_instance=ci)
@login_required
def course_detail(request, course_id):
user = request.user
ci = RequestContext(request)
+
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)
+
+ course = get_object_or_404(Course, pk=course_id)
+ if not course.is_creator(user) and not course.is_teacher(user):
+ raise Http404('This course does not belong to you')
+
return my_render_to_response('yaksh/course_detail.html', {'course': course},
context_instance=ci)
@@ -619,7 +634,11 @@ def enroll(request, course_id, user_id=None, was_rejected=False):
ci = RequestContext(request)
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)
+
+ course = get_object_or_404(Course, pk=course_id)
+ if not course.is_creator(user) and not course.is_teacher(user):
+ raise Http404('This course does not belong to you')
+
if request.method == 'POST':
enroll_ids = request.POST.getlist('check')
else:
@@ -638,7 +657,11 @@ def reject(request, course_id, user_id=None, was_enrolled=False):
ci = RequestContext(request)
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)
+
+ course = get_object_or_404(Course, pk=course_id)
+ if not course.is_creator(user) and not course.is_teacher(user):
+ raise Http404('This course does not belong to you')
+
if request.method == 'POST':
reject_ids = request.POST.getlist('check')
else:
@@ -656,7 +679,11 @@ 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)
+
+ course = get_object_or_404(Course, pk=course_id)
+ if not course.is_creator(user) and not course.is_teacher(user):
+ raise Http404('This course does not belong to you')
+
if course.active:
course.deactivate()
else:
@@ -701,7 +728,8 @@ 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.filter(quiz__course__creator=user)
+ q_paper = QuestionPaper.objects.filter(Q(quiz__course__creator=user)|
+ Q(quiz__course__teachers=user)).distinct()
context = {'papers': [],
'quiz': None,
'quizzes': q_paper}
@@ -709,8 +737,9 @@ def monitor(request, questionpaper_id=None):
context_instance=ci)
# quiz_id is not None.
try:
- q_paper = QuestionPaper.objects.get(id=questionpaper_id,
- quiz__course__creator=user)
+ q_paper = QuestionPaper.objects.filter(Q(quiz__course__creator=user)|
+ Q(quiz__course__teachers=user),
+ id=questionpaper_id).distinct()
except QuestionPaper.DoesNotExist:
papers = []
q_paper = None
@@ -833,7 +862,8 @@ def download_csv(request, questionpaper_id):
if not is_moderator(user):
raise Http404('You are not allowed to view this page!')
quiz = Quiz.objects.get(questionpaper=questionpaper_id)
- if quiz.course.creator != user:
+
+ if not quiz.course.is_creator(user) and not quiz.course.is_teacher(user):
raise Http404('The question paper does not belong to your course')
papers = AnswerPaper.objects.get_latest_attempts(questionpaper_id)
if not papers:
@@ -978,9 +1008,9 @@ def design_questionpaper(request):
@login_required
def view_profile(request):
""" view moderators and users profile """
-
user = request.user
ci = RequestContext(request)
+
context = {}
if has_profile(user):
return my_render_to_response('yaksh/view_profile.html', {'user':user})
@@ -992,6 +1022,7 @@ def view_profile(request):
return my_render_to_response('yaksh/editprofile.html', context,
context_instance=ci)
+
@login_required
def edit_profile(request):
""" edit profile details facility for moderator and students """
@@ -1025,3 +1056,92 @@ def edit_profile(request):
context['form'] = form
return my_render_to_response('yaksh/editprofile.html', context,
context_instance=ci)
+
+
+@login_required
+def search_teacher(request, course_id):
+ """ search teachers for the course """
+ user = request.user
+ ci = RequestContext(request)
+
+ if not is_moderator(user):
+ raise Http404('You are not allowed to view this page!')
+
+ context = {}
+ course = get_object_or_404(Course, creator=user, pk=course_id)
+ context['course'] = course
+
+ if request.method == 'POST':
+ u_name = request.POST.get('uname')
+ if len(u_name) == 0:
+ return my_render_to_response('yaksh/addteacher.html', context,
+ context_instance=ci)
+ else:
+ teachers = User.objects.filter(Q(username__icontains=u_name)|
+ Q(first_name__icontains=u_name)|Q(last_name__icontains=u_name)|
+ Q(email__icontains=u_name)).exclude(Q(id=user.id)|Q(is_superuser=1))
+ context['success'] = True
+ context['teachers'] = teachers
+ return my_render_to_response('yaksh/addteacher.html', context,
+ context_instance=ci)
+ else:
+ return my_render_to_response('yaksh/addteacher.html', context,
+ context_instance=ci)
+
+
+@login_required
+def add_teacher(request, course_id):
+ """ add teachers to the course """
+
+ user = request.user
+ ci = RequestContext(request)
+
+ if not is_moderator(user):
+ raise Http404('You are not allowed to view this page!')
+
+ context = {}
+ course = get_object_or_404(Course, creator=user, pk=course_id)
+ context['course'] = course
+
+ if request.method == 'POST':
+ teacher_ids = request.POST.getlist('check')
+ teachers = User.objects.filter(id__in=teacher_ids)
+ add_to_group(teachers)
+ course.add_teachers(*teachers)
+ context['status'] = True
+ context['teachers_added'] = teachers
+ return my_render_to_response('yaksh/addteacher.html', context,
+ context_instance=ci)
+ else:
+ return my_render_to_response('yaksh/addteacher.html', context,
+ context_instance=ci)
+
+
+@login_required
+def allotted_courses(request):
+ """ show courses allotted to a user """
+
+ user = request.user
+ ci = RequestContext(request)
+ if not is_moderator(user):
+ raise Http404('You are not allowed to view this page!')
+
+ courses = Course.objects.filter(teachers=user)
+ return my_render_to_response('yaksh/courses.html', {'courses': courses},
+ context_instance=ci)
+
+
+@login_required
+def remove_teachers(request, course_id):
+ """ remove user from a course """
+
+ 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 request.method == "POST":
+ teacher_ids = request.POST.getlist('remove')
+ teachers = User.objects.filter(id__in=teacher_ids)
+ course.remove_teachers(*teachers)
+ return my_redirect('/exam/manage/courses')