diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index fe3484cf9c..be8e68eeaa 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -1213,7 +1213,7 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red query_features = [ 'id', 'username', 'name', 'email', 'language', 'location', 'year_of_birth', 'gender', 'level_of_education', 'mailing_address', - 'goals', + 'goals', 'enrollment_mode', 'verification_status', ] # Provide human-friendly and translatable names for these features. These names @@ -1231,6 +1231,8 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red 'level_of_education': _('Level of Education'), 'mailing_address': _('Mailing Address'), 'goals': _('Goals'), + 'enrollment_mode': _('Enrollment Mode'), + 'verification_status': _('Verification Status'), } if is_course_cohorted(course.id): diff --git a/lms/djangoapps/instructor_analytics/basic.py b/lms/djangoapps/instructor_analytics/basic.py index 91068aef91..f3625e1de2 100644 --- a/lms/djangoapps/instructor_analytics/basic.py +++ b/lms/djangoapps/instructor_analytics/basic.py @@ -17,13 +17,14 @@ from django.core.serializers.json import DjangoJSONEncoder from django.core.urlresolvers import reverse from opaque_keys.edx.keys import UsageKey import xmodule.graders as xmgraders -from student.models import CourseEnrollmentAllowed +from student.models import CourseEnrollmentAllowed, CourseEnrollment from edx_proctoring.api import get_all_exam_attempts from courseware.models import StudentModule from certificates.models import GeneratedCertificate from django.db.models import Count from certificates.models import CertificateStatuses from lms.djangoapps.grades.context import grading_context_for_course +from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers @@ -213,6 +214,8 @@ def enrolled_students_features(course_key, features): """ include_cohort_column = 'cohort' in features include_team_column = 'team' in features + include_enrollment_mode = 'enrollment_mode' in features + include_verification_status = 'verification_status' in features students = User.objects.filter( courseenrollment__course_id=course_key, @@ -256,7 +259,7 @@ def enrolled_students_features(course_key, features): for feature in profile_features) student_dict.update(profile_dict) - # now featch the requested meta fields + # now fetch the requested meta fields meta_dict = json.loads(profile.meta) if profile.meta else {} for meta_feature, meta_key in meta_features: student_dict[meta_feature] = meta_dict.get(meta_key) @@ -275,6 +278,18 @@ def enrolled_students_features(course_key, features): (team.name for team in student.teams.all() if team.course_id == course_key), UNAVAILABLE ) + + if include_enrollment_mode or include_verification_status: + enrollment_mode = CourseEnrollment.enrollment_mode_for_user(student, course_key)[0] + if include_verification_status: + student_dict['verification_status'] = SoftwareSecurePhotoVerification.verification_status_for_user( + student, + course_key, + enrollment_mode + ) + if include_enrollment_mode: + student_dict['enrollment_mode'] = enrollment_mode + return student_dict return [extract_student(student, features) for student in students] diff --git a/lms/djangoapps/instructor_analytics/tests/test_basic.py b/lms/djangoapps/instructor_analytics/tests/test_basic.py index 6b66bdcda7..8ae477f506 100644 --- a/lms/djangoapps/instructor_analytics/tests/test_basic.py +++ b/lms/djangoapps/instructor_analytics/tests/test_basic.py @@ -149,6 +149,34 @@ class TestAnalyticsBasic(ModuleStoreTestCase): self.assertIn(userreport['meta.position'], ["edX expert {}".format(user.id) for user in self.users]) self.assertIn(userreport['meta.company'], ["Open edX Inc {}".format(user.id) for user in self.users]) + def test_enrolled_students_enrollment_verification(self): + """ + Assert that we can get enrollment mode and verification status + """ + query_features = ('enrollment_mode', 'verification_status') + userreports = enrolled_students_features(self.course_key, query_features) + self.assertEqual(len(userreports), len(self.users)) + # by default all users should have "audit" as their enrollment mode + # and "N/A" as their verification status + for userreport in userreports: + self.assertEqual(set(userreport.keys()), set(query_features)) + self.assertIn(userreport['enrollment_mode'], ["audit"]) + self.assertIn(userreport['verification_status'], ["N/A"]) + # make sure that the user report respects whatever value + # is returned by verification and enrollment code + with patch("student.models.CourseEnrollment.enrollment_mode_for_user") as enrollment_patch: + with patch( + "lms.djangoapps.verify_student.models.SoftwareSecurePhotoVerification.verification_status_for_user" + ) as verify_patch: + enrollment_patch.return_value = ["verified"] + verify_patch.return_value = "dummy verification status" + userreports = enrolled_students_features(self.course_key, query_features) + self.assertEqual(len(userreports), len(self.users)) + for userreport in userreports: + self.assertEqual(set(userreport.keys()), set(query_features)) + self.assertIn(userreport['enrollment_mode'], ["verified"]) + self.assertIn(userreport['verification_status'], ["dummy verification status"]) + def test_enrolled_students_features_keys_cohorted(self): course = CourseFactory.create(org="test", course="course1", display_name="run1") course.cohort_config = {'cohorted': True, 'auto_cohort': True, 'auto_cohort_groups': ['cohort']}