Commit f77ebc82 by Miles Steele

add tests

parent a751702a
...@@ -8,8 +8,9 @@ from django.contrib.auth.models import User ...@@ -8,8 +8,9 @@ from django.contrib.auth.models import User
import xmodule.graders as xmgraders import xmodule.graders as xmgraders
AVAILABLE_STUDENT_FEATURES = ['username', 'first_name', 'last_name', 'is_staff', 'email'] STUDENT_FEATURES = ('username', 'first_name', 'last_name', 'is_staff', 'email')
AVAILABLE_PROFILE_FEATURES = ['name', 'language', 'location', 'year_of_birth', 'gender', 'level_of_education', 'mailing_address', 'goals'] PROFILE_FEATURES = ('name', 'language', 'location', 'year_of_birth', 'gender', 'level_of_education', 'mailing_address', 'goals')
AVAILABLE_FEATURES = STUDENT_FEATURES + PROFILE_FEATURES
def enrolled_students_profiles(course_id, features): def enrolled_students_profiles(course_id, features):
...@@ -19,10 +20,12 @@ def enrolled_students_profiles(course_id, features): ...@@ -19,10 +20,12 @@ def enrolled_students_profiles(course_id, features):
# enrollments = CourseEnrollment.objects.filter(course_id=course_id) # enrollments = CourseEnrollment.objects.filter(course_id=course_id)
# students = [enrollment.user for enrollment in enrollments] # students = [enrollment.user for enrollment in enrollments]
students = User.objects.filter(courseenrollment__course_id=course_id).order_by('username').select_related('profile') students = User.objects.filter(courseenrollment__course_id=course_id).order_by('username').select_related('profile')
print len(students)
print students
def extract_student(student): def extract_student(student):
student_features = [feature for feature in features if feature in AVAILABLE_STUDENT_FEATURES] student_features = [feature for feature in features if feature in STUDENT_FEATURES]
profile_features = [feature for feature in features if feature in AVAILABLE_PROFILE_FEATURES] profile_features = [feature for feature in features if feature in PROFILE_FEATURES]
student_dict = dict((feature, getattr(student, feature)) for feature in student_features) student_dict = dict((feature, getattr(student, feature)) for feature in student_features)
profile = student.profile profile = student.profile
...@@ -35,7 +38,7 @@ def enrolled_students_profiles(course_id, features): ...@@ -35,7 +38,7 @@ def enrolled_students_profiles(course_id, features):
def dump_grading_context(course): def dump_grading_context(course):
""" """
Dump information about course grading context (eg which problems are graded in what assignments) Render information about course grading context (eg which problems are graded in what assignments)
Useful for debugging grading_policy.json and policy.json Useful for debugging grading_policy.json and policy.json
Returns HTML string Returns HTML string
......
...@@ -27,30 +27,34 @@ def create_csv_response(filename, header, datarows): ...@@ -27,30 +27,34 @@ def create_csv_response(filename, header, datarows):
def format_dictlist(dictlist): def format_dictlist(dictlist):
""" """
Convert from [ Convert FROM [
{ {
'label1': 'value1,1', 'label1': 'value-1,1',
'label2': 'value2,1', 'label2': 'value-1,2',
'label3': 'value3,1', 'label3': 'value-1,3',
'label4': 'value4,1', 'label4': 'value-1,4',
}, },
{ {
'label1': 'value1,2', 'label1': 'value-2,1',
'label2': 'value2,2', 'label2': 'value-2,2',
'label3': 'value3,2', 'label3': 'value-2,3',
'label4': 'value4,2', 'label4': 'value-2,4',
} }
] ]
to { TO {
'header': ['label1', 'label2', 'label3', 'label4'], 'header': ['label1', 'label2', 'label3', 'label4'],
'datarows': ['value1,1', 'value2,1', 'value3,1', 'value4,1'], ['value1,2', 'value2,2', 'value3,2', 'value4,2'] 'datarows': [['value-1,1', 'value-1,2', 'value-1,3', 'value-1,4'],
['value-2,1', 'value-2,2', 'value-2,3', 'value-2,4']]
} }
Do not handle empty lists. Assumes all keys for input dicts are the same.
""" """
header = dictlist[0].keys() if len(dictlist) > 0:
header = dictlist[0].keys()
else:
header = []
def dict_to_entry(d): def dict_to_entry(d):
ordered = sorted(d.items(), key=lambda (k, v): header.index(k)) ordered = sorted(d.items(), key=lambda (k, v): header.index(k))
......
...@@ -14,7 +14,9 @@ def profile_distribution(course_id, feature): ...@@ -14,7 +14,9 @@ def profile_distribution(course_id, feature):
Retrieve distribution of students over a given feature. Retrieve distribution of students over a given feature.
feature is one of AVAILABLE_PROFILE_FEATURES. feature is one of AVAILABLE_PROFILE_FEATURES.
Returna dictionary {'type': 'SOME_TYPE', 'data': {'key': 'val'}} Returna dictionary {'type': 'SOME_TYPE', 'data': {'key': 'val'}, 'display_names': {'key': 'displaynameval'}}
display_names is only return for EASY_CHOICE type eatuers
note no_data instead of None to be compatible with the json spec.
data types e.g. data types e.g.
EASY_CHOICE - choices with a restricted domain, e.g. level_of_education EASY_CHOICE - choices with a restricted domain, e.g. level_of_education
OPEN_CHOICE - choices with a larger domain e.g. year_of_birth OPEN_CHOICE - choices with a larger domain e.g. year_of_birth
...@@ -23,18 +25,23 @@ def profile_distribution(course_id, feature): ...@@ -23,18 +25,23 @@ def profile_distribution(course_id, feature):
EASY_CHOICE_FEATURES = ['gender', 'level_of_education'] EASY_CHOICE_FEATURES = ['gender', 'level_of_education']
OPEN_CHOICE_FEATURES = ['year_of_birth'] OPEN_CHOICE_FEATURES = ['year_of_birth']
def raise_not_implemented():
raise NotImplementedError("feature requested not implemented but is advertised in AVAILABLE_PROFILE_FEATURES {}" .format(feature))
feature_results = {} feature_results = {}
if not feature in AVAILABLE_PROFILE_FEATURES: if not feature in AVAILABLE_PROFILE_FEATURES:
raise ValueError("unsupported feature requested for distribution '%s'" % feature) raise ValueError("unsupported feature requested for distribution '{}'".format(feature))
if feature in EASY_CHOICE_FEATURES: if feature in EASY_CHOICE_FEATURES:
if feature == 'gender': if feature == 'gender':
choices = [(short, full) for (short, full) in UserProfile.GENDER_CHOICES] + [(None, 'No Data')] raw_choices = UserProfile.GENDER_CHOICES
elif feature == 'level_of_education': elif feature == 'level_of_education':
choices = [(short, full) for (short, full) in UserProfile.LEVEL_OF_EDUCATION_CHOICES] + [(None, 'No Data')] raw_choices = UserProfile.LEVEL_OF_EDUCATION_CHOICES
else: else:
raise ValueError("feature request not implemented for feature %s" % feature) raise raise_not_implemented()
choices = [(short, full) for (short, full) in raw_choices] + [('no_data', 'No Data')]
data = {} data = {}
for (short, full) in choices: for (short, full) in choices:
...@@ -43,21 +50,30 @@ def profile_distribution(course_id, feature): ...@@ -43,21 +50,30 @@ def profile_distribution(course_id, feature):
elif feature == 'level_of_education': elif feature == 'level_of_education':
count = CourseEnrollment.objects.filter(course_id=course_id, user__profile__level_of_education=short).count() count = CourseEnrollment.objects.filter(course_id=course_id, user__profile__level_of_education=short).count()
else: else:
raise ValueError("feature request not implemented for feature %s" % feature) raise raise_not_implemented()
data[full] = count data[short] = count
feature_results['data'] = data feature_results['data'] = data
feature_results['type'] = 'EASY_CHOICE' feature_results['type'] = 'EASY_CHOICE'
feature_results['display_names'] = dict(choices)
elif feature in OPEN_CHOICE_FEATURES: elif feature in OPEN_CHOICE_FEATURES:
profiles = UserProfile.objects.filter(user__courseenrollment__course_id=course_id) profiles = UserProfile.objects.filter(user__courseenrollment__course_id=course_id)
query_distribution = profiles.values(feature).annotate(Count(feature)).order_by() query_distribution = profiles.values(feature).annotate(Count(feature)).order_by()
# query_distribution is of the form [{'attribute': 'value1', 'attribute__count': 4}, {'attribute': 'value2', 'attribute__count': 2}, ...] # query_distribution is of the form [{'featureval': 'value1', 'featureval__count': 4}, {'featureval': 'value2', 'featureval__count': 2}, ...]
distribution = dict((vald[feature], vald[feature + '__count']) for vald in query_distribution) distribution = dict((vald[feature], vald[feature + '__count']) for vald in query_distribution)
# distribution is of the form {'value1': 4, 'value2': 2, ...} # distribution is of the form {'value1': 4, 'value2': 2, ...}
# change none to no_data for valid json key
if None in distribution:
distribution['no_data'] = distribution.pop(None)
# django does not properly count NULL values, so the above will alwasy be 0.
# this correctly counts null values
distribution['no_data'] = profiles.filter(**{feature: None}).count()
feature_results['data'] = distribution feature_results['data'] = distribution
feature_results['type'] = 'OPEN_CHOICE' feature_results['type'] = 'OPEN_CHOICE'
else: else:
raise ValueError("feature requested for distribution has not been implemented but is advertised in AVAILABLE_PROFILE_FEATURES! '%s'" % feature) raise raise_not_implemented()
return feature_results return feature_results
from django.test import TestCase
from django.contrib.auth.models import User, Group
from student.models import CourseEnrollment
from xmodule.modulestore.tests.factories import CourseFactory
from student.tests.factories import UserFactory
from analytics.basic import enrolled_students_profiles, AVAILABLE_FEATURES, STUDENT_FEATURES, PROFILE_FEATURES
class TestAnalyticsBasic(TestCase):
'''Test basic analytics functions.'''
def setUp(self):
self.course_id = 'some/robot/course/id'
self.users = tuple(UserFactory() for _ in xrange(30))
self.ces = tuple(CourseEnrollment.objects.create(course_id=self.course_id, user=user) for user in self.users)
def test_enrolled_students_profiles_username(self):
self.assertIn('username', AVAILABLE_FEATURES)
userreports = enrolled_students_profiles(self.course_id, ['username'])
self.assertEqual(len(userreports), len(self.users))
for userreport in userreports:
self.assertEqual(userreport.keys(), ['username'])
self.assertIn(userreport['username'], [user.username for user in self.users])
def test_enrolled_students_profiles_keys(self):
query_features = ('username', 'name', 'email')
for feature in query_features:
self.assertIn(feature, AVAILABLE_FEATURES)
userreports = enrolled_students_profiles(self.course_id, query_features)
self.assertEqual(len(userreports), len(self.users))
for userreport in userreports:
self.assertEqual(set(userreport.keys()), set(query_features))
self.assertIn(userreport['username'], [user.username for user in self.users])
self.assertIn(userreport['email'], [user.email for user in self.users])
self.assertIn(userreport['name'], [user.profile.name for user in self.users])
def test_available_features(self):
self.assertEqual(len(AVAILABLE_FEATURES), len(STUDENT_FEATURES + PROFILE_FEATURES))
self.assertEqual(set(AVAILABLE_FEATURES), set(STUDENT_FEATURES + PROFILE_FEATURES))
from django.test import TestCase
from analytics.csvs import create_csv_response, format_dictlist
class TestAnalyticsCSVS(TestCase):
'''Test analytics rendering of csv files.'''
def test_create_csv_response_nodata(self):
header = ['Name', 'Email']
datarows = []
res = create_csv_response('robot.csv', header, datarows)
self.assertEqual(res['Content-Type'], 'text/csv')
self.assertEqual(res['Content-Disposition'], 'attachment; filename={0}'.format('robot.csv'))
self.assertEqual(res.content.strip(), '"Name","Email"')
def test_create_csv_response(self):
header = ['Name', 'Email']
datarows = [['Jim', 'jim@edy.org'], ['Jake', 'jake@edy.org'], ['Jeeves', 'jeeves@edy.org']]
res = create_csv_response('robot.csv', header, datarows)
self.assertEqual(res['Content-Type'], 'text/csv')
self.assertEqual(res['Content-Disposition'], 'attachment; filename={0}'.format('robot.csv'))
self.assertEqual(res.content.strip(), '"Name","Email"\r\n"Jim","jim@edy.org"\r\n"Jake","jake@edy.org"\r\n"Jeeves","jeeves@edy.org"')
def test_create_csv_response_empty(self):
header = []
datarows = []
res = create_csv_response('robot.csv', header, datarows)
self.assertEqual(res['Content-Type'], 'text/csv')
self.assertEqual(res['Content-Disposition'], 'attachment; filename={0}'.format('robot.csv'))
self.assertEqual(res.content.strip(), '')
def test_format_dictlist(self):
data_in = [
{
'label1': 'value-1,1',
'label2': 'value-1,2',
'label3': 'value-1,3',
'label4': 'value-1,4',
},
{
'label1': 'value-2,1',
'label2': 'value-2,2',
'label3': 'value-2,3',
'label4': 'value-2,4',
},
]
data_out = {
'header': ['label1', 'label2', 'label3', 'label4'],
'datarows': [['value-1,1', 'value-1,2', 'value-1,3', 'value-1,4'],
['value-2,1', 'value-2,2', 'value-2,3', 'value-2,4']],
}
self.assertEqual(format_dictlist(data_in), data_out)
def test_format_dictlist_empty(self):
self.assertEqual(format_dictlist([]), {
'header': [],
'datarows': [],
})
from django.test import TestCase
from nose.tools import raises
from django.contrib.auth.models import User, Group
from student.models import CourseEnrollment
from xmodule.modulestore.tests.factories import CourseFactory
from student.tests.factories import UserFactory
from analytics.distributions import profile_distribution, AVAILABLE_PROFILE_FEATURES
class TestAnalyticsDistributions(TestCase):
'''Test analytics distribution gathering.'''
def setUp(self):
self.course_id = 'some/robot/course/id'
self.users = tuple(UserFactory(
profile__gender=['m', 'f', 'o'][i % 3],
profile__year_of_birth=i + 1930
) for i in xrange(30))
self.ces = tuple(CourseEnrollment.objects.create(course_id=self.course_id, user=user) for user in self.users)
@raises(ValueError)
def test_profile_distribution_bad_feature(self):
feature = 'robot-not-a-real-feature'
self.assertNotIn(feature, AVAILABLE_PROFILE_FEATURES)
profile_distribution(self.course_id, feature)
@raises(NotImplementedError)
def test_profile_distribution_not_implemented_feature(self):
feature = 'ROBOT_DO_NOT_USE_FEATURE'
AVAILABLE_PROFILE_FEATURES.append(feature)
self.assertIn(feature, AVAILABLE_PROFILE_FEATURES)
profile_distribution(self.course_id, feature)
def test_profile_distribution_easy_choice(self):
feature = 'gender'
self.assertIn(feature, AVAILABLE_PROFILE_FEATURES)
distribution = profile_distribution(self.course_id, feature)
self.assertEqual(distribution['type'], 'EASY_CHOICE')
self.assertEqual(distribution['data']['no_data'], 0)
self.assertEqual(distribution['data']['m'], len(self.users) / 3)
self.assertEqual(distribution['display_names']['m'], 'Male')
def test_profile_distribution_open_choice(self):
feature = 'year_of_birth'
self.assertIn(feature, AVAILABLE_PROFILE_FEATURES)
distribution = profile_distribution(self.course_id, feature)
print distribution
self.assertEqual(distribution['type'], 'OPEN_CHOICE')
self.assertNotIn('display_names', distribution)
self.assertNotIn('no_data', distribution['data'])
self.assertEqual(distribution['data'][1930], 1)
class TestAnalyticsDistributionsNoData(TestCase):
'''Test analytics distribution gathering.'''
def setUp(self):
self.course_id = 'some/robot/course/id'
self.users = tuple(UserFactory(
profile__year_of_birth=i + 1930,
) for i in xrange(5))
self.nodata_users = tuple(UserFactory(
profile__year_of_birth=None,
) for _ in xrange(4))
self.users += self.nodata_users
self.ces = tuple(CourseEnrollment.objects.create(course_id=self.course_id, user=user) for user in self.users)
def test_profile_distribution_open_choice_nodata(self):
feature = 'year_of_birth'
self.assertIn(feature, AVAILABLE_PROFILE_FEATURES)
distribution = profile_distribution(self.course_id, feature)
print distribution
self.assertEqual(distribution['type'], 'OPEN_CHOICE')
self.assertNotIn('display_names', distribution)
self.assertIn('no_data', distribution['data'])
self.assertEqual(distribution['data']['no_data'], len(self.nodata_users))
...@@ -88,7 +88,5 @@ def update_forum_role_membership(course_id, user, rolename, mode): ...@@ -88,7 +88,5 @@ def update_forum_role_membership(course_id, user, rolename, mode):
role.users.add(user) role.users.add(user)
elif mode == 'revoke': elif mode == 'revoke':
role.users.remove(user) role.users.remove(user)
print "\n" * 5
print role.users.all()
else: else:
raise ValueError("unrecognized mode '{}'".format(mode)) raise ValueError("unrecognized mode '{}'".format(mode))
"""
Unit tests for instructor.enrollment methods.
"""
import json
from django.contrib.auth.models import Group, User
# from courseware.access import _course_staff_group_name
from courseware.models import StudentModule
from django.test import TestCase
from student.tests.factories import UserFactory
from student.models import CourseEnrollment, CourseEnrollmentAllowed
from instructor.enrollment import (enroll_emails, unenroll_emails,
split_input_list, reset_student_attempts)
class TestInstructorEnrollmentDB(TestCase):
'''Test instructor enrollment administration against database effects'''
def setUp(self):
self.course_id = 'robot:/a/fake/c::rse/id'
def test_split_input_list(self):
strings = []
lists = []
strings.append("Lorem@ipsum.dolor, sit@amet.consectetur\nadipiscing@elit.Aenean\r convallis@at.lacus\r, ut@lacinia.Sed")
lists.append(['Lorem@ipsum.dolor', 'sit@amet.consectetur', 'adipiscing@elit.Aenean', 'convallis@at.lacus', 'ut@lacinia.Sed'])
for (s, l) in zip(strings, lists):
self.assertEqual(split_input_list(s), l)
def test_enroll_emails_userexists_alreadyenrolled(self):
user = UserFactory()
ce = CourseEnrollment(course_id=self.course_id, user=user)
ce.save()
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user__email=user.email).count(), 1)
enroll_emails(self.course_id, [user.email])
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user__email=user.email).count(), 1)
def test_enroll_emails_userexists_succeedenrolling(self):
user = UserFactory()
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user__email=user.email).count(), 0)
enroll_emails(self.course_id, [user.email])
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user__email=user.email).count(), 1)
def test_enroll_emails_nouser_alreadyallowed(self):
email_without_user = 'test_enroll_emails_nouser_alreadyallowed@test.org'
self.assertEqual(User.objects.filter(email=email_without_user).count(), 0)
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user__email=email_without_user).count(), 0)
self.assertEqual(CourseEnrollmentAllowed.objects.filter(course_id=self.course_id, email=email_without_user).count(), 0)
cea = CourseEnrollmentAllowed(course_id=self.course_id, email=email_without_user, auto_enroll=False)
cea.save()
enroll_emails(self.course_id, [email_without_user])
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user__email=email_without_user).count(), 0)
self.assertEqual(CourseEnrollmentAllowed.objects.filter(course_id=self.course_id, email=email_without_user).count(), 1)
self.assertEqual(CourseEnrollmentAllowed.objects.get(course_id=self.course_id, email=email_without_user).auto_enroll, False)
def test_enroll_emails_nouser_suceedallow(self):
email_without_user = 'test_enroll_emails_nouser_suceedallow@test.org'
self.assertEqual(User.objects.filter(email=email_without_user).count(), 0)
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user__email=email_without_user).count(), 0)
self.assertEqual(CourseEnrollmentAllowed.objects.filter(course_id=self.course_id, email=email_without_user).count(), 0)
enroll_emails(self.course_id, [email_without_user])
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user__email=email_without_user).count(), 0)
self.assertEqual(CourseEnrollmentAllowed.objects.filter(course_id=self.course_id, email=email_without_user).count(), 1)
self.assertEqual(CourseEnrollmentAllowed.objects.get(course_id=self.course_id, email=email_without_user).auto_enroll, False)
def test_enroll_multiple(self):
user1 = UserFactory()
user2 = UserFactory()
user3 = UserFactory()
email_without_user1 = 'test_enroll_emails_nouser_suceedallow_1@test.org'
email_without_user2 = 'test_enroll_emails_nouser_suceedallow_2@test.org'
email_without_user3 = 'test_enroll_emails_nouser_suceedallow_3@test.org'
def test_db(auto_enroll):
for user in [user1, user2, user3]:
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user=user).count(), 1)
self.assertEqual(CourseEnrollmentAllowed.objects.filter(course_id=self.course_id, email=user.email).count(), 0)
for email in [email_without_user1, email_without_user2, email_without_user3]:
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user__email=email).count(), 0)
self.assertEqual(CourseEnrollmentAllowed.objects.filter(course_id=self.course_id, email=email).count(), 1)
self.assertEqual(CourseEnrollmentAllowed.objects.get(course_id=self.course_id, email=email).auto_enroll, auto_enroll)
enroll_emails(self.course_id, [user1.email, user2.email, user3.email, email_without_user1, email_without_user2, email_without_user3], auto_enroll=True)
test_db(True)
enroll_emails(self.course_id, [user1.email, user2.email, user3.email, email_without_user1, email_without_user2, email_without_user3], auto_enroll=False)
test_db(False)
def test_unenroll_alreadyallowed(self):
email_without_user = 'test_unenroll_alreadyallowed@test.org'
cea = CourseEnrollmentAllowed(course_id=self.course_id, email=email_without_user, auto_enroll=False)
cea.save()
unenroll_emails(self.course_id, [email_without_user])
self.assertEqual(User.objects.filter(email=email_without_user).count(), 0)
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user__email=email_without_user).count(), 0)
self.assertEqual(CourseEnrollmentAllowed.objects.filter(course_id=self.course_id, email=email_without_user).count(), 0)
def test_unenroll_alreadyenrolled(self):
user = UserFactory()
ce = CourseEnrollment(course_id=self.course_id, user=user)
ce.save()
unenroll_emails(self.course_id, [user.email])
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user=user).count(), 0)
self.assertEqual(CourseEnrollmentAllowed.objects.filter(course_id=self.course_id, email=user.email).count(), 0)
def test_unenroll_notenrolled(self):
user = UserFactory()
unenroll_emails(self.course_id, [user.email])
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user=user).count(), 0)
self.assertEqual(CourseEnrollmentAllowed.objects.filter(course_id=self.course_id, email=user.email).count(), 0)
def test_unenroll_nosuchuser(self):
email_without_user = 'test_unenroll_nosuchuser@test.org'
unenroll_emails(self.course_id, [email_without_user])
self.assertEqual(User.objects.filter(email=email_without_user).count(), 0)
self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id, user__email=email_without_user).count(), 0)
self.assertEqual(CourseEnrollmentAllowed.objects.filter(course_id=self.course_id, email=email_without_user).count(), 0)
def test_reset_student_attempts(self):
user = UserFactory()
msk = 'robot/module/state/key'
original_state = json.dumps({'attempts': 32, 'otherstuff': 'alsorobots'})
module = StudentModule.objects.create(student=user, course_id=self.course_id, module_state_key=msk, state=original_state)
# lambda to reload the module state from the database
module = lambda: StudentModule.objects.get(student=user, course_id=self.course_id, module_state_key=msk)
self.assertEqual(json.loads(module().state)['attempts'], 32)
reset_student_attempts(self.course_id, user, msk)
self.assertEqual(json.loads(module().state)['attempts'], 0)
def test_delete_student_attempts(self):
user = UserFactory()
msk = 'robot/module/state/key'
original_state = json.dumps({'attempts': 32, 'otherstuff': 'alsorobots'})
StudentModule.objects.create(student=user, course_id=self.course_id, module_state_key=msk, state=original_state)
self.assertEqual(StudentModule.objects.filter(student=user, course_id=self.course_id, module_state_key=msk).count(), 1)
reset_student_attempts(self.course_id, user, msk, delete_module=True)
self.assertEqual(StudentModule.objects.filter(student=user, course_id=self.course_id, module_state_key=msk).count(), 0)
...@@ -172,7 +172,7 @@ def enrolled_students_profiles(request, course_id, csv=False): ...@@ -172,7 +172,7 @@ def enrolled_students_profiles(request, course_id, csv=False):
""" """
course = get_course_with_access(request.user, course_id, 'staff', depth=None) course = get_course_with_access(request.user, course_id, 'staff', depth=None)
available_features = analytics.basic.AVAILABLE_STUDENT_FEATURES + analytics.basic.AVAILABLE_PROFILE_FEATURES available_features = analytics.basic.AVAILABLE_FEATURES
query_features = ['username', 'name', 'email', 'language', 'location', 'year_of_birth', 'gender', query_features = ['username', 'name', 'email', 'language', 'location', 'year_of_birth', 'gender',
'level_of_education', 'mailing_address', 'goals'] 'level_of_education', 'mailing_address', 'goals']
...@@ -226,7 +226,7 @@ def profile_distribution(request, course_id): ...@@ -226,7 +226,7 @@ def profile_distribution(request, course_id):
try: try:
feature_results[feature] = analytics.distributions.profile_distribution(course_id, feature) feature_results[feature] = analytics.distributions.profile_distribution(course_id, feature)
except Exception as e: except Exception as e:
feature_results[feature] = {'error': "can not find distribution for '%s'" % feature} feature_results[feature] = {'error': "Error finding distribution for distribution for '{}'.".format(feature)}
raise e raise e
response_payload = { response_payload = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment