diff options
-rw-r--r-- | yaksh/forms.py | 5 | ||||
-rw-r--r-- | yaksh/models.py | 15 | ||||
-rw-r--r-- | yaksh/templates/yaksh/addteacher.html | 77 | ||||
-rw-r--r-- | yaksh/templates/yaksh/courses.html | 41 | ||||
-rw-r--r-- | yaksh/templates/yaksh/user_data.html | 2 | ||||
-rw-r--r-- | yaksh/tests.py | 25 | ||||
-rw-r--r-- | yaksh/urls.py | 6 | ||||
-rw-r--r-- | yaksh/views.py | 142 |
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 }}"> {{ 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') |