committed by
Michael Youngstrom
parent
0bb7652a75
commit
3e92168e80
@@ -3,21 +3,24 @@ Student and course analytics.
|
||||
|
||||
Serve miscellaneous course and student data
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import datetime
|
||||
import json
|
||||
|
||||
import six
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.urls import reverse
|
||||
from django.db.models import Count, Q
|
||||
from django.urls import reverse
|
||||
from edx_proctoring.api import get_exam_violation_report
|
||||
from opaque_keys.edx.keys import UsageKey
|
||||
from six import text_type
|
||||
|
||||
from courseware.models import StudentModule
|
||||
import xmodule.graders as xmgraders
|
||||
from courseware.models import StudentModule
|
||||
from lms.djangoapps.certificates.models import CertificateStatuses, GeneratedCertificate
|
||||
from lms.djangoapps.grades.api import context as grades_context
|
||||
from lms.djangoapps.verify_student.services import IDVerificationService
|
||||
@@ -114,7 +117,7 @@ def sale_order_record_features(course_id, features):
|
||||
coupon_codes = [redemption.coupon.code for redemption in coupon_redemption]
|
||||
order_item_dict.update({'coupon_code': ", ".join(coupon_codes)})
|
||||
|
||||
sale_order_dict.update(dict(order_item_dict.items()))
|
||||
sale_order_dict.update(dict(list(order_item_dict.items())))
|
||||
|
||||
return sale_order_dict
|
||||
|
||||
@@ -172,7 +175,7 @@ def sale_record_features(course_id, features):
|
||||
|
||||
course_reg_dict['course_id'] = text_type(course_id)
|
||||
course_reg_dict.update({'codes': ", ".join(codes)})
|
||||
sale_dict.update(dict(course_reg_dict.items()))
|
||||
sale_dict.update(dict(list(course_reg_dict.items())))
|
||||
|
||||
return sale_dict
|
||||
|
||||
@@ -240,7 +243,7 @@ def enrolled_students_features(course_key, features):
|
||||
DjangoJSONEncoder().default(attr)
|
||||
return attr
|
||||
except TypeError:
|
||||
return unicode(attr)
|
||||
return six.text_type(attr)
|
||||
|
||||
def extract_student(student, features):
|
||||
""" convert student to dictionary """
|
||||
@@ -528,7 +531,7 @@ def dump_grading_context(course):
|
||||
gcontext = grades_context.grading_context_for_course(course)
|
||||
msg += "graded sections:\n"
|
||||
|
||||
msg += '%s\n' % gcontext['all_graded_subsections_by_type'].keys()
|
||||
msg += '%s\n' % list(gcontext['all_graded_subsections_by_type'].keys())
|
||||
for (gsomething, gsvals) in gcontext['all_graded_subsections_by_type'].items():
|
||||
msg += u"--> Section %s:\n" % (gsomething)
|
||||
for sec in gsvals:
|
||||
|
||||
@@ -4,8 +4,12 @@ Student and course analytics.
|
||||
Format and create csv responses
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import csv
|
||||
|
||||
import six
|
||||
from six.moves import map
|
||||
from django.http import HttpResponse
|
||||
|
||||
|
||||
@@ -28,11 +32,11 @@ def create_csv_response(filename, header, datarows):
|
||||
quotechar='"',
|
||||
quoting=csv.QUOTE_ALL)
|
||||
|
||||
encoded_header = [unicode(s).encode('utf-8') for s in header]
|
||||
encoded_header = [six.text_type(s).encode('utf-8') for s in header]
|
||||
csvwriter.writerow(encoded_header)
|
||||
|
||||
for datarow in datarows:
|
||||
encoded_row = [unicode(s).encode('utf-8') for s in datarow]
|
||||
encoded_row = [six.text_type(s).encode('utf-8') for s in datarow]
|
||||
csvwriter.writerow(encoded_row)
|
||||
|
||||
return response
|
||||
@@ -79,7 +83,7 @@ def format_dictlist(dictlist, features):
|
||||
return vals
|
||||
|
||||
header = features
|
||||
datarows = map(dict_to_entry, dictlist)
|
||||
datarows = list(map(dict_to_entry, dictlist))
|
||||
|
||||
return header, datarows
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ The distribution in a course for gender might look like:
|
||||
}
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.db.models import Count
|
||||
|
||||
from student.models import CourseEnrollment, UserProfile
|
||||
|
||||
@@ -2,17 +2,20 @@
|
||||
Tests for instructor.basic
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import datetime
|
||||
import json
|
||||
|
||||
import pytz
|
||||
from django.urls import reverse
|
||||
from django.db.models import Q
|
||||
from django.urls import reverse
|
||||
from edx_proctoring.api import create_exam
|
||||
from edx_proctoring.models import ProctoredExamStudentAttempt
|
||||
from mock import MagicMock, Mock, patch
|
||||
from opaque_keys.edx.locator import UsageKey
|
||||
from six import text_type
|
||||
from six.moves import range, zip
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from course_modes.tests.factories import CourseModeFactory
|
||||
@@ -55,7 +58,7 @@ class TestAnalyticsBasic(ModuleStoreTestCase):
|
||||
def setUp(self):
|
||||
super(TestAnalyticsBasic, self).setUp()
|
||||
self.course_key = self.store.make_course_key('robot', 'course', 'id')
|
||||
self.users = tuple(UserFactory() for _ in xrange(30))
|
||||
self.users = tuple(UserFactory() for _ in range(30))
|
||||
self.ces = tuple(CourseEnrollment.enroll(user, self.course_key)
|
||||
for user in self.users)
|
||||
self.instructor = InstructorFactory(course_key=self.course_key)
|
||||
@@ -118,7 +121,7 @@ class TestAnalyticsBasic(ModuleStoreTestCase):
|
||||
userreports = enrolled_students_features(self.course_key, ['username'])
|
||||
self.assertEqual(len(userreports), len(self.users))
|
||||
for userreport in userreports:
|
||||
self.assertEqual(userreport.keys(), ['username'])
|
||||
self.assertEqual(list(userreport.keys()), ['username'])
|
||||
self.assertIn(userreport['username'], [user.username for user in self.users])
|
||||
|
||||
def test_enrolled_students_features_keys(self):
|
||||
@@ -197,7 +200,7 @@ class TestAnalyticsBasic(ModuleStoreTestCase):
|
||||
course = CourseFactory.create(org="test", course="course1", display_name="run1")
|
||||
course.cohort_config = {'cohorted': True, 'auto_cohort': True, 'auto_cohort_groups': ['cohort']}
|
||||
self.store.update_item(course, self.instructor.id)
|
||||
cohorted_students = [UserFactory.create() for _ in xrange(10)]
|
||||
cohorted_students = [UserFactory.create() for _ in range(10)]
|
||||
cohort = CohortFactory.create(name='cohort', course_id=course.id, users=cohorted_students)
|
||||
cohorted_usernames = [student.username for student in cohorted_students]
|
||||
non_cohorted_student = UserFactory.create()
|
||||
@@ -233,7 +236,7 @@ class TestAnalyticsBasic(ModuleStoreTestCase):
|
||||
self.assertEqual(len(may_enroll), len(self.students_who_may_enroll) - len(self.users))
|
||||
email_adresses = [student.email for student in self.students_who_may_enroll]
|
||||
for student in may_enroll:
|
||||
self.assertEqual(student.keys(), ['email'])
|
||||
self.assertEqual(list(student.keys()), ['email'])
|
||||
self.assertIn(student['email'], email_adresses)
|
||||
|
||||
def test_get_student_exam_attempt_features(self):
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
""" Tests for analytics.csvs """
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
from six.moves import range
|
||||
|
||||
from instructor_analytics.csvs import create_csv_response, format_dictlist, format_instances
|
||||
|
||||
@@ -98,7 +101,7 @@ class TestAnalyticsFormatInstances(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAnalyticsFormatInstances, self).setUp()
|
||||
self.instances = [self.TestDataClass() for _ in xrange(5)]
|
||||
self.instances = [self.TestDataClass() for _ in range(5)]
|
||||
|
||||
def test_format_instances_response(self):
|
||||
features = ['a_var', 'c_var', 'd_var']
|
||||
@@ -108,7 +111,7 @@ class TestAnalyticsFormatInstances(TestCase):
|
||||
'aval',
|
||||
'cval',
|
||||
'dval',
|
||||
] for _ in xrange(len(self.instances))])
|
||||
] for _ in range(len(self.instances))])
|
||||
|
||||
def test_format_instances_response_noinstances(self):
|
||||
features = ['a_var']
|
||||
@@ -119,7 +122,7 @@ class TestAnalyticsFormatInstances(TestCase):
|
||||
def test_format_instances_response_nofeatures(self):
|
||||
header, datarows = format_instances(self.instances, [])
|
||||
self.assertEqual(header, [])
|
||||
self.assertEqual(datarows, [[] for _ in xrange(len(self.instances))])
|
||||
self.assertEqual(datarows, [[] for _ in range(len(self.instances))])
|
||||
|
||||
def test_format_instances_response_nonexistantfeature(self):
|
||||
with pytest.raises(AttributeError):
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
""" Tests for analytics.distributions """
|
||||
from __future__ import print_function
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
from six.moves import range
|
||||
|
||||
from instructor_analytics.distributions import AVAILABLE_PROFILE_FEATURES, profile_distribution
|
||||
from student.models import CourseEnrollment
|
||||
@@ -21,7 +22,7 @@ class TestAnalyticsDistributions(TestCase):
|
||||
profile__gender=['m', 'f', 'o'][i % 3],
|
||||
profile__level_of_education=['a', 'hs', 'el'][i % 3],
|
||||
profile__year_of_birth=i + 1930
|
||||
) for i in xrange(30)]
|
||||
) for i in range(30)]
|
||||
|
||||
self.ces = [CourseEnrollment.enroll(user, self.course_id)
|
||||
for user in self.users]
|
||||
@@ -82,12 +83,12 @@ class TestAnalyticsDistributionsNoData(TestCase):
|
||||
|
||||
self.users = [UserFactory(
|
||||
profile__year_of_birth=i + 1930,
|
||||
) for i in xrange(5)]
|
||||
) for i in range(5)]
|
||||
|
||||
self.nodata_users = [UserFactory(
|
||||
profile__year_of_birth=None,
|
||||
profile__gender=[None, ''][i % 2]
|
||||
) for i in xrange(4)]
|
||||
) for i in range(4)]
|
||||
|
||||
self.users += self.nodata_users
|
||||
|
||||
|
||||
Reference in New Issue
Block a user