summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKing2018-07-13 01:44:00 -0700
committerGitHub2018-07-13 01:44:00 -0700
commitf0f4a882a796319f766ff67cd3f8133a04054dfd (patch)
treea5c8a3ab617107743864f9faacad341d0e74da00
parent661c9d82bb680e745cc6b498131a0793b954c436 (diff)
parent0af47ee9292132ab472e3e0bbae617d77437ff72 (diff)
downloadonline_test-f0f4a882a796319f766ff67cd3f8133a04054dfd.tar.gz
online_test-f0f4a882a796319f766ff67cd3f8133a04054dfd.tar.bz2
online_test-f0f4a882a796319f766ff67cd3f8133a04054dfd.zip
Merge pull request #480 from ankitjavalkar/toggle-mod
[Role based implementation] Allow moderator to switch between student and moderator roles
-rw-r--r--yaksh/evaluator_tests/test_simple_question_types.py4
-rw-r--r--yaksh/live_server_tests/load_test.py12
-rw-r--r--yaksh/management/commands/create_moderator.py20
-rw-r--r--yaksh/models.py34
-rw-r--r--yaksh/templates/yaksh/moderator_dashboard.html5
-rw-r--r--yaksh/templates/yaksh/quizzes_user.html7
-rw-r--r--yaksh/templatetags/test_custom_filters.py5
-rw-r--r--yaksh/test_models.py14
-rw-r--r--yaksh/test_views.py320
-rw-r--r--yaksh/urls.py1
-rw-r--r--yaksh/views.py54
11 files changed, 418 insertions, 58 deletions
diff --git a/yaksh/evaluator_tests/test_simple_question_types.py b/yaksh/evaluator_tests/test_simple_question_types.py
index f7a6cf6..5edd545 100644
--- a/yaksh/evaluator_tests/test_simple_question_types.py
+++ b/yaksh/evaluator_tests/test_simple_question_types.py
@@ -1,6 +1,7 @@
import unittest
from datetime import datetime, timedelta
from django.utils import timezone
+from django.contrib.auth.models import Group
from textwrap import dedent
import pytz
from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
@@ -8,7 +9,9 @@ from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
StringTestCase, McqTestCase, ArrangeTestCase
+
def setUpModule():
+ mod_group = Group.objects.create(name='moderator')
# Create user profile
# Create User 1
user = User.objects.create_user(username='demo_user_100',
@@ -46,6 +49,7 @@ def setUpModule():
def tearDownModule():
User.objects.filter(username__in=["demo_user_100", "demo_user_101"])\
.delete()
+ Group.objects.all().delete()
class IntegerQuestionTestCases(unittest.TestCase):
diff --git a/yaksh/live_server_tests/load_test.py b/yaksh/live_server_tests/load_test.py
index 520bebe..e5ac068 100644
--- a/yaksh/live_server_tests/load_test.py
+++ b/yaksh/live_server_tests/load_test.py
@@ -1,8 +1,10 @@
from threading import Thread
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
+from django.contrib.auth.models import User, Group, Permission
+from django.contrib.contenttypes.models import ContentType
# Local imports
-from yaksh.models import User, Profile, Course
+from yaksh.models import User, Profile, Course, create_group
from yaksh.code_server import ServerPool
from yaksh import settings
from .selenium_test import SeleniumTest
@@ -26,6 +28,10 @@ class YakshSeleniumTests(StaticLiveServerTestCase):
cls.code_server_thread = t = Thread(target=code_server_pool.run)
t.start()
+ app_label = 'yaksh'
+ group_name = 'moderator'
+ cls.group = create_group(group_name, app_label)
+
cls.demo_student = User.objects.create_user(
username='demo_student',
password='demo_student',
@@ -45,7 +51,8 @@ class YakshSeleniumTests(StaticLiveServerTestCase):
cls.demo_mod_profile = Profile.objects.create(
user=cls.demo_mod,
roll_number=0, institute='IIT',
- department='Chemical', position='Moderator'
+ department='Chemical', position='Moderator',
+ is_moderator=True
)
course_obj = Course()
@@ -61,6 +68,7 @@ class YakshSeleniumTests(StaticLiveServerTestCase):
cls.demo_mod.delete()
cls.demo_mod_profile.delete()
cls.demo_course.delete()
+ cls.group.delete()
cls.code_server_pool.stop()
cls.code_server_thread.join()
diff --git a/yaksh/management/commands/create_moderator.py b/yaksh/management/commands/create_moderator.py
index 86489d5..96276b5 100644
--- a/yaksh/management/commands/create_moderator.py
+++ b/yaksh/management/commands/create_moderator.py
@@ -7,7 +7,9 @@
# django imports
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User, Group, Permission
-from django.contrib.contenttypes.models import ContentType
+
+# local imports
+from yaksh.models import create_group
class Command(BaseCommand):
@@ -19,19 +21,9 @@ class Command(BaseCommand):
def handle(self, *args, **options):
app_label = 'yaksh'
-
- try:
- group = Group.objects.get(name='moderator')
- except Group.DoesNotExist:
- group = Group(name='moderator')
- group.save()
- # Get the models for the given app
- content_types = ContentType.objects.filter(app_label=app_label)
- # Get list of permissions for the models
- permission_list = Permission.objects.filter(
- content_type__in=content_types)
- group.permissions.add(*permission_list)
- group.save()
+ group_name = 'moderator'
+ group = create_group(group_name, app_label)
+ if group and isinstance(group, Group):
self.stdout.write('Moderator group added successfully')
if options['usernames']:
diff --git a/yaksh/models.py b/yaksh/models.py
index 1eca721..152289f 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -8,7 +8,7 @@ from ruamel.yaml.comments import CommentedMap
from random import sample
from collections import Counter
from django.db import models
-from django.contrib.auth.models import User
+from django.contrib.auth.models import User, Group, Permission
from django.contrib.contenttypes.models import ContentType
from taggit.managers import TaggableManager
from django.utils import timezone
@@ -92,6 +92,8 @@ test_status = (
FIXTURES_DIR_PATH = os.path.join(settings.BASE_DIR, 'yaksh', 'fixtures')
+MOD_GROUP_NAME = 'moderator'
+
def get_assignment_dir(instance, filename):
upload_dir = instance.question_paper.quiz.description.replace(" ", "_")
@@ -135,6 +137,21 @@ def get_file_dir(instance, filename):
upload_dir = instance.name.replace(" ", "_")
return os.sep.join((upload_dir, filename))
+def create_group(group_name, app_label):
+ try:
+ group = Group.objects.get(name=group_name)
+ except Group.DoesNotExist:
+ group = Group(name=group_name)
+ group.save()
+ # Get the models for the given app
+ content_types = ContentType.objects.filter(app_label=app_label)
+ # Get list of permissions for the models
+ permission_list = Permission.objects.filter(
+ content_type__in=content_types)
+ group.permissions.add(*permission_list)
+ group.save()
+ return group
+
###############################################################################
class CourseManager(models.Manager):
@@ -982,6 +999,7 @@ class Profile(models.Model):
institute = models.CharField(max_length=128)
department = models.CharField(max_length=64)
position = models.CharField(max_length=64)
+ is_moderator = models.BooleanField(default=False)
timezone = models.CharField(
max_length=64,
default=pytz.utc.zone,
@@ -1000,6 +1018,20 @@ class Profile(models.Model):
os.chmod(user_dir, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
return user_dir
+ def _toggle_moderator_group(self, group_name):
+ group = Group.objects.get(name=group_name)
+ if self.is_moderator:
+ self.user.groups.add(group)
+ else:
+ self.user.groups.remove(group)
+
+ def save(self, *args, **kwargs):
+ if self.pk is not None:
+ old_profile = Profile.objects.get(pk=self.pk)
+ if old_profile.is_moderator != self.is_moderator:
+ self._toggle_moderator_group(group_name=MOD_GROUP_NAME)
+ super(Profile, self).save(*args, **kwargs)
+
def __str__(self):
return '%s' % (self.user.get_full_name())
diff --git a/yaksh/templates/yaksh/moderator_dashboard.html b/yaksh/templates/yaksh/moderator_dashboard.html
index 17a4924..c848074 100644
--- a/yaksh/templates/yaksh/moderator_dashboard.html
+++ b/yaksh/templates/yaksh/moderator_dashboard.html
@@ -8,7 +8,10 @@
{% block content %}
-<center><h4>List of quizzes! Click on the given links to have a look at answer papers for a quiz.</h4></center>
+<center><h4 class="pull-left">List of quizzes! Click on the given links to have a look at answer papers for a quiz.</h4></center>
+<a href="{{URL_ROOT}}/exam/toggle_moderator/" class="btn btn-primary pull-right">
+ Switch To Student
+</a>
<table class="table table-bordered">
<th>Courses</th>
<th>Quizzes</th>
diff --git a/yaksh/templates/yaksh/quizzes_user.html b/yaksh/templates/yaksh/quizzes_user.html
index 78cdc48..68044da 100644
--- a/yaksh/templates/yaksh/quizzes_user.html
+++ b/yaksh/templates/yaksh/quizzes_user.html
@@ -7,6 +7,13 @@
<center>{{ msg }}</center>
</div>
{% endif %}
+ {% if user.profile.is_moderator %}
+ <div class="row">
+ <a href="{{URL_ROOT}}/exam/toggle_moderator/" class="btn btn-primary pull-right" style="margin-top: 20px; margin-bottom: 20px;">
+ Switch To Moderator
+ </a>
+ </div>
+ {% endif %}
{% if 'Enrolled Courses' not in title%}
<div class="row well">
<form action="{{ URL_ROOT }}/exam/quizzes/" method="post" id="custom-search-form" class="form-search form-horizontal">
diff --git a/yaksh/templatetags/test_custom_filters.py b/yaksh/templatetags/test_custom_filters.py
index e8d1d61..eb1f0fb 100644
--- a/yaksh/templatetags/test_custom_filters.py
+++ b/yaksh/templatetags/test_custom_filters.py
@@ -1,6 +1,7 @@
import unittest
from datetime import datetime, timedelta
from django.utils import timezone
+from django.contrib.auth.models import Group
import pytz
# local imports
@@ -15,6 +16,8 @@ from yaksh.templatetags.custom_filters import (completed, inprogress,
def setUpModule():
+ mod_group = Group.objects.create(name='moderator')
+
# Create user profile
teacher = User.objects.create_user(
username='teacher2000', password='demo',
@@ -52,7 +55,7 @@ def setUpModule():
def tearDownModule():
User.objects.get(username="teacher2000").delete()
-
+ Group.objects.all().delete()
class CustomFiltersTestCases(unittest.TestCase):
diff --git a/yaksh/test_models.py b/yaksh/test_models.py
index 403dfb4..14d5197 100644
--- a/yaksh/test_models.py
+++ b/yaksh/test_models.py
@@ -1,9 +1,10 @@
import unittest
+from django.contrib.auth.models import Group
from yaksh.models import User, Profile, Question, Quiz, QuestionPaper,\
QuestionSet, AnswerPaper, Answer, Course, StandardTestCase,\
StdIOBasedTestCase, FileUpload, McqTestCase, AssignmentUpload,\
LearningModule, LearningUnit, Lesson, LessonFile, CourseStatus, \
- TestCaseOrder
+ TestCaseOrder, create_group
from yaksh.code_server import (
ServerPool, get_result as get_result_from_code_server
)
@@ -24,6 +25,8 @@ from yaksh import settings
def setUpModule():
+ mod_group = Group.objects.create(name='moderator')
+
# create user profile
user = User.objects.create_user(username='creator',
password='demo',
@@ -111,7 +114,16 @@ def tearDownModule():
LearningUnit.objects.all().delete()
LearningModule.objects.all().delete()
AnswerPaper.objects.all().delete()
+ Group.objects.all().delete()
+
+###############################################################################
+class GlobalMethodsTestCases(unittest.TestCase):
+ def test_create_group_when_group_exists(self):
+ self.assertEqual(
+ create_group('moderator', 'yaksh'),
+ Group.objects.get(name='moderator')
+ )
###############################################################################
class LessonTestCases(unittest.TestCase):
diff --git a/yaksh/test_views.py b/yaksh/test_views.py
index 06a4fa3..899ed31 100644
--- a/yaksh/test_views.py
+++ b/yaksh/test_views.py
@@ -14,6 +14,7 @@ from django.contrib.auth import authenticate
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test import Client
+from django.http import Http404
from django.utils import timezone
from django.core import mail
from django.conf import settings
@@ -27,15 +28,18 @@ from yaksh.models import (
FloatTestCase, FIXTURES_DIR_PATH, LearningModule, LearningUnit, Lesson,
LessonFile, CourseStatus, dict_to_yaml
)
+from yaksh.views import add_as_moderator
from yaksh.decorators import user_has_profile
class TestUserRegistration(TestCase):
def setUp(self):
self.client = Client()
+ self.mod_group = Group.objects.create(name='moderator')
def tearDown(self):
self.registered_user.delete()
+ self.mod_group.delete()
def test_register_user_post(self):
self.client.post(
@@ -63,6 +67,7 @@ class TestUserRegistration(TestCase):
class TestProfile(TestCase):
def setUp(self):
self.client = Client()
+ self.mod_group = Group.objects.create(name='moderator')
# Create User without profile
self.user1_plaintext_pass = 'demo1'
@@ -95,6 +100,7 @@ class TestProfile(TestCase):
self.client.logout()
self.user1.delete()
self.user2.delete()
+ self.mod_group.delete()
def test_user_has_profile_for_user_without_profile(self):
"""
@@ -302,6 +308,7 @@ class TestProfile(TestCase):
class TestStudentDashboard(TestCase):
def setUp(self):
self.client = Client()
+ self.mod_group = Group.objects.create(name='moderator')
# student
self.student_plaintext_pass = 'student'
@@ -365,6 +372,7 @@ class TestStudentDashboard(TestCase):
self.client.logout()
self.user.delete()
self.course.delete()
+ self.mod_group.delete()
def test_student_dashboard_denies_anonymous_user(self):
"""
@@ -467,7 +475,7 @@ class TestMonitor(TestCase):
password=self.user_plaintext_pass,
first_name='first_name',
last_name='last_name',
- email='demo@test.com'
+ email='demo@test.com',
)
Profile.objects.create(
@@ -476,7 +484,8 @@ class TestMonitor(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create Student
@@ -561,6 +570,7 @@ class TestMonitor(TestCase):
self.new_answer.delete()
self.learning_module.delete()
self.learning_unit.delete()
+ self.mod_group.delete()
def test_monitor_denies_student(self):
"""
@@ -661,7 +671,8 @@ class TestGradeUser(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create Student
@@ -877,7 +888,8 @@ class TestDownloadAssignment(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Add to moderator group
@@ -957,6 +969,7 @@ class TestDownloadAssignment(TestCase):
self.course.delete()
self.learning_module.delete()
self.learning_unit.delete()
+ self.mod_group.delete()
dir_name = self.quiz.description.replace(" ", "_")
file_path = os.sep.join((settings.MEDIA_ROOT, dir_name))
if os.path.exists(file_path):
@@ -1052,7 +1065,8 @@ class TestAddQuiz(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create Student
@@ -1096,6 +1110,7 @@ class TestAddQuiz(TestCase):
self.quiz.delete()
self.exercise.delete()
self.course.delete()
+ self.mod_group.delete()
def test_add_quiz_denies_anonymous(self):
"""
@@ -1330,6 +1345,141 @@ class TestAddQuiz(TestCase):
self.assertEqual(response.context['quizzes'][0], self.quiz)
self.assertTemplateUsed(response, "yaksh/courses.html")
+class TestAddAsModerator(TestCase):
+ def setUp(self):
+ self.client = Client()
+ self.mod_group = Group.objects.create(name='moderator')
+ # Create Moderator with profile
+ self.user_plaintext_pass = 'demo'
+ self.user = User.objects.create_user(
+ username='demo_user',
+ password=self.user_plaintext_pass,
+ first_name='first_name',
+ last_name='last_name',
+ email='demo@test.com'
+ )
+
+ Profile.objects.create(
+ user=self.user,
+ roll_number=10,
+ institute='IIT',
+ department='Chemical',
+ position='Moderator',
+ timezone='UTC',
+ is_moderator=True
+ )
+
+ self.course = Course.objects.create(
+ name="Python Course",
+ enrollment="Enroll Request", creator=self.user
+ )
+
+ self.mod_group.delete()
+
+ def tearDown(self):
+ self.client.logout()
+ self.user.delete()
+
+ def test_add_as_moderator_group_does_not_exist(self):
+ """
+ If group does not exist return 404
+ """
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+
+ response = self.client.get(
+ reverse('yaksh:add_teacher',
+ kwargs={'course_id': self.course.id}
+ ),
+ follow=True
+ )
+ self.assertEqual(response.status_code, 404)
+ with self.assertRaises(Http404):
+ add_as_moderator(self.user, 'moderator')
+
+class TestToggleModerator(TestCase):
+ def setUp(self):
+ self.client = Client()
+
+ self.mod_group = Group.objects.create(name='moderator')
+
+ # Create Moderator with profile
+ self.user_plaintext_pass = 'demo'
+ self.user = User.objects.create_user(
+ username='demo_user',
+ password=self.user_plaintext_pass,
+ first_name='first_name',
+ last_name='last_name',
+ email='demo@test.com'
+ )
+
+ Profile.objects.create(
+ user=self.user,
+ roll_number=10,
+ institute='IIT',
+ department='Chemical',
+ position='Moderator',
+ timezone='UTC',
+ is_moderator=True
+ )
+
+ # Create Student
+ self.student_plaintext_pass = 'demo_student'
+ self.student = User.objects.create_user(
+ username='demo_student',
+ password=self.student_plaintext_pass,
+ first_name='student_first_name',
+ last_name='student_last_name',
+ email='demo_student@test.com'
+ )
+
+ Profile.objects.create(
+ user=self.student,
+ roll_number=10,
+ institute='IIT',
+ department='Chemical',
+ position='Student',
+ timezone='UTC',
+ )
+
+ # Add to moderator group
+ self.mod_group.user_set.add(self.user)
+
+ self.course = Course.objects.create(
+ name="Python Course",
+ enrollment="Enroll Request", creator=self.user
+ )
+
+ def tearDown(self):
+ self.client.logout()
+ self.user.delete()
+ self.student.delete()
+ self.course.delete()
+ self.mod_group.delete()
+
+ def test_toggle_for_moderator(self):
+ self.client.login(
+ username=self.user.username,
+ password=self.user_plaintext_pass
+ )
+ response = self.client.get(
+ reverse('yaksh:toggle_moderator')
+ )
+ self.assertEqual(response.status_code, 302)
+ self.assertEquals(self.user.groups.all().count(), 0)
+
+ def test_toggle_for_student(self):
+ self.client.login(
+ username=self.student.username,
+ password=self.student_plaintext_pass
+ )
+ response = self.client.get(
+ reverse('yaksh:toggle_moderator')
+ )
+
+ self.assertEqual(response.status_code, 404)
class TestAddTeacher(TestCase):
def setUp(self):
@@ -1338,6 +1488,16 @@ class TestAddTeacher(TestCase):
self.mod_group = Group.objects.create(name='moderator')
tzone = pytz.timezone('UTC')
+ # Create User with no profile
+ self.user_no_profile_plaintext_pass = 'demo_no_profile'
+ self.user_no_profile = User.objects.create_user(
+ username='demo_user_no_profile',
+ password=self.user_no_profile_plaintext_pass,
+ first_name='first_name_no_profile',
+ last_name='last_name_no_profile',
+ email='demo_no_profile@test.com'
+ )
+
# Create Moderator with profile
self.user_plaintext_pass = 'demo'
self.user = User.objects.create_user(
@@ -1354,7 +1514,8 @@ class TestAddTeacher(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create Student
@@ -1400,6 +1561,24 @@ class TestAddTeacher(TestCase):
self.quiz.delete()
self.pre_req_quiz.delete()
self.course.delete()
+ self.mod_group.delete()
+
+ def test_add_teacher_denies_no_profile(self):
+ """
+ If not moderator redirect to login page
+ """
+ self.client.login(
+ username=self.user_no_profile.username,
+ password=self.user_no_profile_plaintext_pass
+ )
+
+ response = self.client.get(
+ reverse('yaksh:add_teacher',
+ kwargs={'course_id': self.course.id}
+ ),
+ follow=True
+ )
+ self.assertEqual(response.status_code, 404)
def test_add_teacher_denies_anonymous(self):
"""
@@ -1565,6 +1744,7 @@ class TestRemoveTeacher(TestCase):
self.quiz.delete()
self.pre_req_quiz.delete()
self.course.delete()
+ self.mod_group.delete()
def test_remove_teacher_denies_anonymous(self):
"""
@@ -1668,7 +1848,8 @@ class TestCourses(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
self.user2_plaintext_pass = 'demo2'
@@ -1686,7 +1867,8 @@ class TestCourses(TestCase):
institute='IIT',
department='Aeronautical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create Student
@@ -1716,6 +1898,16 @@ class TestCourses(TestCase):
email='demo_teacher@test.com'
)
+ Profile.objects.create(
+ user=self.teacher,
+ roll_number=10,
+ institute='IIT',
+ department='Aeronautical',
+ position='Moderator',
+ timezone='UTC',
+ is_moderator=True
+ )
+
# Add to moderator group
self.mod_group.user_set.add(self.user1)
self.mod_group.user_set.add(self.user2)
@@ -1771,6 +1963,7 @@ class TestCourses(TestCase):
self.user2.delete()
self.student.delete()
self.teacher.delete()
+ self.mod_group.delete()
def test_courses_denies_anonymous(self):
"""
@@ -2087,7 +2280,8 @@ class TestAddCourse(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create a teacher
@@ -2106,7 +2300,8 @@ class TestAddCourse(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create Student
@@ -2154,6 +2349,7 @@ class TestAddCourse(TestCase):
self.quiz.delete()
self.pre_req_quiz.delete()
self.course.delete()
+ self.mod_group.delete()
def test_add_course_denies_anonymous(self):
"""
@@ -2266,7 +2462,8 @@ class TestCourseDetail(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
self.user2_plaintext_pass = 'demo2'
@@ -2284,7 +2481,8 @@ class TestCourseDetail(TestCase):
institute='IIT',
department='Aeronautical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create Student
@@ -2331,6 +2529,7 @@ class TestCourseDetail(TestCase):
self.user2.delete()
self.student.delete()
self.user1_course.delete()
+ self.mod_group.delete()
def test_upload_users_with_correct_csv(self):
# Given
@@ -2849,7 +3048,8 @@ class TestEnrollRequest(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
self.user2_plaintext_pass = 'demo2'
@@ -2867,7 +3067,8 @@ class TestEnrollRequest(TestCase):
institute='IIT',
department='Aeronautical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create Student
@@ -2895,6 +3096,7 @@ class TestEnrollRequest(TestCase):
self.user2.delete()
self.student.delete()
self.course.delete()
+ self.mod_group.delete()
def test_enroll_request_denies_anonymous(self):
"""
@@ -3118,7 +3320,8 @@ class TestSelfEnroll(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
self.user2_plaintext_pass = 'demo2'
@@ -3136,7 +3339,8 @@ class TestSelfEnroll(TestCase):
institute='IIT',
department='Aeronautical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create Student
@@ -3164,6 +3368,7 @@ class TestSelfEnroll(TestCase):
self.user2.delete()
self.student.delete()
self.course.delete()
+ self.mod_group.delete()
def test_self_enroll_denies_anonymous(self):
response = self.client.get(
@@ -3228,7 +3433,8 @@ class TestGrader(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
self.user2_plaintext_pass = 'demo2'
@@ -3246,7 +3452,8 @@ class TestGrader(TestCase):
institute='IIT',
department='Aeronautical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create Student
@@ -3295,6 +3502,7 @@ class TestGrader(TestCase):
Quiz.objects.all().delete()
QuestionPaper.objects.all().delete()
AnswerPaper.objects.all().delete()
+ self.mod_group.delete()
def test_grader_denies_anonymous(self):
# Given
@@ -3444,6 +3652,8 @@ class TestGrader(TestCase):
class TestPasswordReset(TestCase):
def setUp(self):
+ self.mod_group = Group.objects.create(name='moderator')
+
# Create User with profile
self.user1_plaintext_pass = 'demo1'
self.user1 = User.objects.create_user(
@@ -3465,6 +3675,7 @@ class TestPasswordReset(TestCase):
def tearDown(self):
self.user1.delete()
+ self.mod_group.delete()
def test_password_reset_post(self):
"""
@@ -3553,7 +3764,8 @@ class TestModeratorDashboard(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
self.mod_no_profile_plaintext_pass = 'demo2'
@@ -3654,6 +3866,7 @@ class TestModeratorDashboard(TestCase):
self.question_paper.delete()
self.answerpaper.delete()
self.new_answer.delete()
+ self.mod_group.delete()
def test_moderator_dashboard_denies_student(self):
"""
@@ -3740,6 +3953,8 @@ class TestUserLogin(TestCase):
def setUp(self):
self.client = Client()
+ self.mod_group = Group.objects.create(name='moderator')
+
# Create Moderator with profile
self.user1_plaintext_pass = 'demo1'
self.user1 = User.objects.create_user(
@@ -3763,6 +3978,7 @@ class TestUserLogin(TestCase):
self.client.logout()
settings.IS_DEVELOPMENT = True
self.user1.delete()
+ self.mod_group.delete()
def test_successful_user_login(self):
"""
@@ -3801,7 +4017,7 @@ class TestUserLogin(TestCase):
self.assertTemplateUsed(response, "yaksh/activation_status.html")
-class TestDownloadcsv(TestCase):
+class TestDownloadCsv(TestCase):
def setUp(self):
self.client = Client()
tzone = pytz.timezone("utc")
@@ -3841,7 +4057,8 @@ class TestDownloadcsv(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
self.mod_group.user_set.add(self.user)
self.course = Course.objects.create(
@@ -3895,6 +4112,7 @@ class TestDownloadcsv(TestCase):
self.student.delete()
self.quiz.delete()
self.course.delete()
+ self.mod_group.delete()
def test_download_csv_denies_student(self):
"""
@@ -4036,7 +4254,8 @@ class TestShowQuestions(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
self.mod_group.user_set.add(self.user)
self.question = Question.objects.create(
@@ -4080,6 +4299,13 @@ class TestShowQuestions(TestCase):
yaml_question_2.encode("utf-8")
)
+ def tearDown(self):
+ self.client.logout()
+ User.objects.all().delete()
+ Profile.objects.all().delete()
+ Question.objects.all().delete()
+ Group.objects.all().delete()
+
def test_show_questions_denies_student(self):
"""
Check show questions denies student
@@ -4352,7 +4578,8 @@ class TestShowStatistics(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create Student
@@ -4429,6 +4656,7 @@ class TestShowStatistics(TestCase):
self.question.delete()
self.question_paper.delete()
self.new_answer.delete()
+ self.mod_group.delete()
def test_show_statistics_denies_student(self):
"""
@@ -4514,7 +4742,8 @@ class TestQuestionPaper(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
self.student_plaintext_pass = 'demo'
@@ -4568,7 +4797,8 @@ class TestQuestionPaper(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Add to moderator group
@@ -4739,6 +4969,7 @@ class TestQuestionPaper(TestCase):
self.question_paper.delete()
self.learning_module.delete()
self.learning_unit.delete()
+ self.mod_group.delete()
def test_preview_questionpaper_correct(self):
self.client.login(
@@ -5262,7 +5493,8 @@ class TestLearningModule(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create a student
@@ -5282,7 +5514,17 @@ class TestLearningModule(TestCase):
password=self.teacher_plaintext_pass,
first_name='first_name',
last_name='last_name',
- email='demo@student.com'
+ email='demo@teacher.com',
+ )
+
+ Profile.objects.create(
+ user=self.teacher,
+ roll_number=10,
+ institute='IIT',
+ department='Chemical',
+ position='Moderator',
+ timezone='UTC',
+ is_moderator=True
)
# Add to moderator group
@@ -5345,6 +5587,7 @@ class TestLearningModule(TestCase):
self.course.delete()
self.learning_unit.delete()
self.learning_module.delete()
+ self.mod_group.delete()
def test_add_new_module_denies_non_moderator(self):
self.client.login(
@@ -5643,7 +5886,8 @@ class TestLessons(TestCase):
institute='IIT',
department='Chemical',
position='Moderator',
- timezone='UTC'
+ timezone='UTC',
+ is_moderator=True
)
# Create a student
@@ -5656,6 +5900,15 @@ class TestLessons(TestCase):
email='demo@student.com'
)
+ Profile.objects.create(
+ user=self.student,
+ roll_number=10,
+ institute='IIT',
+ department='Chemical',
+ position='student',
+ timezone='UTC'
+ )
+
# Create a teacher to add to the course
self.teacher_plaintext_pass = 'demo_teacher'
self.teacher = User.objects.create_user(
@@ -5666,6 +5919,16 @@ class TestLessons(TestCase):
email='demo@student.com'
)
+ Profile.objects.create(
+ user=self.teacher,
+ roll_number=10,
+ institute='IIT',
+ department='Chemical',
+ position='Moderator',
+ timezone='UTC',
+ is_moderator=True
+ )
+
# Add to moderator group
self.mod_group.user_set.add(self.user)
self.mod_group.user_set.add(self.teacher)
@@ -5713,6 +5976,7 @@ class TestLessons(TestCase):
self.learning_module2.delete()
self.lesson.delete()
self.lesson2.delete()
+ self.mod_group.delete()
def test_edit_lesson_denies_non_moderator(self):
""" Student should not be allowed to edit lesson """
diff --git a/yaksh/urls.py b/yaksh/urls.py
index 6e6b8c1..8f3401b 100644
--- a/yaksh/urls.py
+++ b/yaksh/urls.py
@@ -8,6 +8,7 @@ urlpatterns = [
url(r'^update_email/$', views.update_email, name="update_email"),
url(r'^activate/(?P<key>.+)$', views.activate_user, name="activate"),
url(r'^new_activation/$', views.new_activation, name='new_activation'),
+ url(r'^toggle_moderator/$', views.toggle_moderator_role, name='toggle_moderator'),
url(r'^quizzes/$', views.quizlist_user, name='quizlist_user'),
url(r'^quizzes/(?P<enrolled>\w+)/$', views.quizlist_user,
name='quizlist_user'),
diff --git a/yaksh/views.py b/yaksh/views.py
index c6b1184..ecd7efd 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -30,7 +30,7 @@ from yaksh.code_server import get_result as get_result_from_code_server
from yaksh.models import (
Answer, AnswerPaper, AssignmentUpload, Course, FileUpload, Profile,
QuestionPaper, QuestionSet, Quiz, Question, TestCase, User,
- FIXTURES_DIR_PATH, Lesson, LessonFile, LearningUnit, LearningModule,
+ FIXTURES_DIR_PATH, MOD_GROUP_NAME, Lesson, LessonFile, LearningUnit, LearningModule,
CourseStatus
)
from yaksh.forms import (
@@ -63,18 +63,27 @@ def my_render_to_response(request, template, context=None, **kwargs):
return render(request, template, context, **kwargs)
-def is_moderator(user):
+def is_moderator(user, group_name=MOD_GROUP_NAME):
"""Check if the user is having moderator rights"""
- if user.groups.filter(name='moderator').exists():
- return True
+ try:
+ group = Group.objects.get(name=group_name)
+ return user.profile.is_moderator and user in group.user_set.all()
+ except Profile.DoesNotExist:
+ return False
+ except Group.DoesNotExist:
+ return False
-def add_to_group(users):
+def add_as_moderator(users, group_name=MOD_GROUP_NAME):
""" add users to moderator group """
- group = Group.objects.get(name="moderator")
+ try:
+ group = Group.objects.get(name=group_name)
+ except Group.DoesNotExist:
+ raise Http404('The Group {0} does not exist.'.format(group_name))
for user in users:
if not is_moderator(user):
- user.groups.add(group)
+ user.profile.is_moderator = True
+ user.profile.save()
CSV_FIELDS = ['name', 'username', 'roll_number', 'institute', 'department',
@@ -163,8 +172,10 @@ def quizlist_user(request, enrolled=None, msg=None):
)
title = 'All Courses'
- context = {'user': user, 'courses': courses, 'title': title,
- 'msg': msg}
+ context = {
+ 'user': user, 'courses': courses,
+ 'title': title, 'msg': msg
+ }
return my_render_to_response(request, "yaksh/quizzes_user.html", context)
@@ -1757,6 +1768,29 @@ def search_teacher(request, course_id):
@login_required
@email_verified
+def toggle_moderator_role(request):
+ """ Allow moderator to switch to student and back """
+
+ user = request.user
+
+ try:
+ group = Group.objects.get(name='moderator')
+ except Group.DoesNotExist:
+ raise Http404('The Moderator group does not exist')
+
+ if not user.profile.is_moderator:
+ raise Http404('You are not allowed to view this page!')
+
+ if user not in group.user_set.all():
+ group.user_set.add(user)
+ else:
+ group.user_set.remove(user)
+
+ return my_redirect('/exam/')
+
+
+@login_required
+@email_verified
def add_teacher(request, course_id):
""" add teachers to the course """
@@ -1775,7 +1809,7 @@ def add_teacher(request, course_id):
if request.method == 'POST':
teacher_ids = request.POST.getlist('check')
teachers = User.objects.filter(id__in=teacher_ids)
- add_to_group(teachers)
+ add_as_moderator(teachers)
course.add_teachers(*teachers)
context['status'] = True
context['teachers_added'] = teachers