tests.py 10.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
"""
Unit tests for instructor dashboard

Based on (and depends on) unit tests for courseware.

Notes for running by hand:

django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/instructor
"""

11 12 13 14 15 16 17
import courseware.tests.tests as ct

import json

from nose import SkipTest
from mock import patch, Mock

18 19
from override_settings import override_settings

20 21 22
# Need access to internal func to put users in the right group
from django.contrib.auth.models import Group

23
from django.core.urlresolvers import reverse
Brian Wilson committed
24
from django_comment_client.models import Role, FORUM_ROLE_ADMINISTRATOR, \
25
    FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA, FORUM_ROLE_STUDENT
Brian Wilson committed
26
from django_comment_client.utils import has_forum_access
27 28

from courseware.access import _course_staff_group_name
Brian Wilson committed
29
import courseware.tests.tests as ct
30
from xmodule.modulestore.django import modulestore
31 32
import xmodule.modulestore.django

Brian Wilson committed
33

34 35 36 37 38 39 40 41 42
@override_settings(MODULESTORE=ct.TEST_DATA_XML_MODULESTORE)
class TestInstructorDashboardGradeDownloadCSV(ct.PageLoader):
    '''
    Check for download of csv
    '''

    def setUp(self):
        xmodule.modulestore.django._MODULESTORES = {}

43 44
        self.full = modulestore().get_course("edX/full/6.002_Spring_2012")
        self.toy = modulestore().get_course("edX/toy/2012_Fall")
45 46 47 48 49 50 51 52 53 54

        # Create two accounts
        self.student = 'view@test.com'
        self.instructor = 'view2@test.com'
        self.password = 'foo'
        self.create_account('u1', self.student, self.password)
        self.create_account('u2', self.instructor, self.password)
        self.activate_user(self.student)
        self.activate_user(self.instructor)

55 56 57 58 59 60
        def make_instructor(course):
            group_name = _course_staff_group_name(course.location)
            g = Group.objects.create(name=group_name)
            g.user_set.add(ct.user(self.instructor))

        make_instructor(self.toy)
61 62 63 64 65 66 67 68 69

        self.logout()
        self.login(self.instructor, self.password)
        self.enroll(self.toy)


    def test_download_grades_csv(self):
        course = self.toy
        url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
70 71 72
        msg = "url = {0}\n".format(url)
        response = self.client.post(url, {'action': 'Download CSV of all student grades for this course'})
        msg += "instructor dashboard download csv grades: response = '{0}'\n".format(response)
73

74
        self.assertEqual(response['Content-Type'],'text/csv',msg)
75

76 77 78
        cdisp = response['Content-Disposition']
        msg += "Content-Disposition = '%s'\n" % cdisp
        self.assertEqual(cdisp, 'attachment; filename=grades_{0}.csv'.format(course.id), msg)
79

80
        body = response.content.replace('\r','')
81
        msg += "body = '{0}'\n".format(body)
82

83 84
        # All the not-actually-in-the-course hw and labs come from the
        # default grading policy string in graders.py
85 86
        expected_body = '''"ID","Username","Full Name","edX email","External email","HW 01","HW 02","HW 03","HW 04","HW 05","HW 06","HW 07","HW 08","HW 09","HW 10","HW 11","HW 12","HW Avg","Lab 01","Lab 02","Lab 03","Lab 04","Lab 05","Lab 06","Lab 07","Lab 08","Lab 09","Lab 10","Lab 11","Lab 12","Lab Avg","Midterm 01","Midterm Avg","Final 01","Final Avg"
"2","u2","Fred Weasley","view2@test.com","","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"
87
'''
88

89
        self.assertEqual(body, expected_body, msg)
90 91


Brian Wilson committed
92 93 94 95 96 97
FORUM_ROLES = [ FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA ]
FORUM_ADMIN_ACTION_SUFFIX = { FORUM_ROLE_ADMINISTRATOR : 'admin', FORUM_ROLE_MODERATOR : 'moderator', FORUM_ROLE_COMMUNITY_TA : 'community TA'}
FORUM_ADMIN_USER = { FORUM_ROLE_ADMINISTRATOR : 'forumadmin', FORUM_ROLE_MODERATOR : 'forummoderator', FORUM_ROLE_COMMUNITY_TA : 'forummoderator'}

def action_name(operation, rolename):
    if operation == 'List':
98
        return '{0} course forum {1}s'.format(operation, FORUM_ADMIN_ACTION_SUFFIX[rolename])
Brian Wilson committed
99
    else:
100
        return '{0} forum {1}'.format(operation, FORUM_ADMIN_ACTION_SUFFIX[rolename])
Brian Wilson committed
101

102 103


Brian Wilson committed
104 105 106 107 108
@override_settings(MODULESTORE=ct.TEST_DATA_XML_MODULESTORE)
class TestInstructorDashboardForumAdmin(ct.PageLoader):
    '''
    Check for change in forum admin role memberships
    '''
109

Brian Wilson committed
110 111 112 113 114
    def setUp(self):
        xmodule.modulestore.django._MODULESTORES = {}
        courses = modulestore().get_courses()


115 116
        self.course_id = "edX/toy/2012_Fall"
        self.toy = modulestore().get_course(self.course_id)
Brian Wilson committed
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134

        # Create two accounts
        self.student = 'view@test.com'
        self.instructor = 'view2@test.com'
        self.password = 'foo'
        self.create_account('u1', self.student, self.password)
        self.create_account('u2', self.instructor, self.password)
        self.activate_user(self.student)
        self.activate_user(self.instructor)

        group_name = _course_staff_group_name(self.toy.location)
        g = Group.objects.create(name=group_name)
        g.user_set.add(ct.user(self.instructor))

        self.logout()
        self.login(self.instructor, self.password)
        self.enroll(self.toy)

135 136


Brian Wilson committed
137 138 139 140 141 142
    def initialize_roles(self, course_id):
        self.admin_role = Role.objects.get_or_create(name=FORUM_ROLE_ADMINISTRATOR, course_id=course_id)[0]
        self.moderator_role = Role.objects.get_or_create(name=FORUM_ROLE_MODERATOR, course_id=course_id)[0]
        self.community_ta_role = Role.objects.get_or_create(name=FORUM_ROLE_COMMUNITY_TA, course_id=course_id)[0]

    def test_add_forum_admin_users_for_unknown_user(self):
Brian Wilson committed
143 144
        course = self.toy
        url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
Brian Wilson committed
145 146 147 148
        username = 'unknown'
        for action in ['Add', 'Remove']:
            for rolename in FORUM_ROLES:
                response = self.client.post(url, {'action': action_name(action, rolename), FORUM_ADMIN_USER[rolename]: username})
149
                self.assertTrue(response.content.find('Error: unknown username "{0}"'.format(username))>=0)
Brian Wilson committed
150 151 152 153 154 155 156 157

    def test_add_forum_admin_users_for_missing_roles(self):
        course = self.toy
        url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
        username = 'u1'
        for action in ['Add', 'Remove']:
            for rolename in FORUM_ROLES:
                response = self.client.post(url, {'action': action_name(action, rolename), FORUM_ADMIN_USER[rolename]: username})
158
                self.assertTrue(response.content.find('Error: unknown rolename "{0}"'.format(rolename))>=0)
Brian Wilson committed
159 160 161 162 163 164 165 166 167

    def test_remove_forum_admin_users_for_missing_users(self):
        course = self.toy
        self.initialize_roles(course.id)
        url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
        username = 'u1'
        action = 'Remove'
        for rolename in FORUM_ROLES:
            response = self.client.post(url, {'action': action_name(action, rolename), FORUM_ADMIN_USER[rolename]: username})
168
            self.assertTrue(response.content.find('Error: user "{0}" does not have rolename "{1}"'.format(username, rolename))>=0)
Brian Wilson committed
169 170 171 172 173

    def test_add_and_remove_forum_admin_users(self):
        course = self.toy
        self.initialize_roles(course.id)
        url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
174
        username = 'u2'
Brian Wilson committed
175 176
        for rolename in FORUM_ROLES:
            response = self.client.post(url, {'action': action_name('Add', rolename), FORUM_ADMIN_USER[rolename]: username})
177
            self.assertTrue(response.content.find('Added "{0}" to "{1}" forum role = "{2}"'.format(username, course.id, rolename))>=0)
Brian Wilson committed
178 179
            self.assertTrue(has_forum_access(username, course.id, rolename))
            response = self.client.post(url, {'action': action_name('Remove', rolename), FORUM_ADMIN_USER[rolename]: username})
180
            self.assertTrue(response.content.find('Removed "{0}" from "{1}" forum role = "{2}"'.format(username, course.id, rolename))>=0)
Brian Wilson committed
181 182
            self.assertFalse(has_forum_access(username, course.id, rolename))

183
    def test_add_and_read_forum_admin_users(self):
Brian Wilson committed
184 185 186
        course = self.toy
        self.initialize_roles(course.id)
        url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
187
        username = 'u2'
Brian Wilson committed
188 189 190 191
        for rolename in FORUM_ROLES:
            # perform an add, and follow with a second identical add:
            self.client.post(url, {'action': action_name('Add', rolename), FORUM_ADMIN_USER[rolename]: username})
            response = self.client.post(url, {'action': action_name('Add', rolename), FORUM_ADMIN_USER[rolename]: username})
192
            self.assertTrue(response.content.find('Error: user "{0}" already has rolename "{1}", cannot add'.format(username, rolename))>=0)
Brian Wilson committed
193 194
            self.assertTrue(has_forum_access(username, course.id, rolename))

195 196 197 198 199 200 201
    def test_add_nonstaff_forum_admin_users(self):
        course = self.toy
        self.initialize_roles(course.id)
        url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
        username = 'u1'
        rolename = FORUM_ROLE_ADMINISTRATOR
        response = self.client.post(url, {'action': action_name('Add', rolename), FORUM_ADMIN_USER[rolename]: username})
202
        self.assertTrue(response.content.find('Error: user "{0}" should first be added as staff'.format(username))>=0)
203

Brian Wilson committed
204 205 206 207
    def test_list_forum_admin_users(self):
        course = self.toy
        self.initialize_roles(course.id)
        url = reverse('instructor_dashboard', kwargs={'course_id': course.id})
208
        username = 'u2'
209
        added_roles = [FORUM_ROLE_STUDENT]  # u2 is already added as a student to the discussion forums
210
        self.assertTrue(has_forum_access(username, course.id, 'Student'))
Brian Wilson committed
211 212
        for rolename in FORUM_ROLES:
            response = self.client.post(url, {'action': action_name('Add', rolename), FORUM_ADMIN_USER[rolename]: username})
213
            self.assertTrue(has_forum_access(username, course.id, rolename))
Brian Wilson committed
214 215
            response = self.client.post(url, {'action': action_name('List', rolename), FORUM_ADMIN_USER[rolename]: username})
            for header in ['Username', 'Full name', 'Roles']:
216 217
                self.assertTrue(response.content.find('<th>{0}</th>'.format(header))>0)
            self.assertTrue(response.content.find('<td>{0}</td>'.format(username))>=0)
Brian Wilson committed
218 219 220 221
            # concatenate all roles for user, in sorted order:
            added_roles.append(rolename)
            added_roles.sort()
            roles = ', '.join(added_roles)
222
            self.assertTrue(response.content.find('<td>{0}</td>'.format(roles))>=0, 'not finding roles "{0}"'.format(roles))
223 224