-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Include non-obsolete location info in student profile report #11620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| 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.core.urlresolvers import reverse | ||
| from opaque_keys.edx.keys import UsageKey | ||
| import xmodule.graders as xmgraders | ||
|
|
@@ -27,7 +28,8 @@ | |
|
|
||
| STUDENT_FEATURES = ('id', 'username', 'first_name', 'last_name', 'is_staff', 'email') | ||
| PROFILE_FEATURES = ('name', 'language', 'location', 'year_of_birth', 'gender', | ||
| 'level_of_education', 'mailing_address', 'goals', 'meta') | ||
| 'level_of_education', 'mailing_address', 'goals', 'meta', | ||
| 'city', 'country') | ||
| ORDER_ITEM_FEATURES = ('list_price', 'unit_cost', 'status') | ||
| ORDER_FEATURES = ('purchase_time',) | ||
|
|
||
|
|
@@ -222,6 +224,15 @@ def enrolled_students_features(course_key, features): | |
| if include_team_column: | ||
| students = students.prefetch_related('teams') | ||
|
|
||
| def extract_attr(student, feature): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @regisb Do you know why this was not previously needed for "location" and "mailing_address", which aren't actually filled out (why didn't they throw an error also)? In particular, mailing_address is defined in an identical way as city. Looking back at the history of this PR, it appears that country (which is using CountryField) was the only thing having an issue.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is because 'country' is not a string: it's an instance of CountryField. When we try to serialize |
||
| """Evaluate a student attribute that is ready for JSON serialization""" | ||
| attr = getattr(student, feature) | ||
| try: | ||
| DjangoJSONEncoder().default(attr) | ||
| return attr | ||
| except TypeError: | ||
| return unicode(attr) | ||
|
|
||
| def extract_student(student, features): | ||
| """ convert student to dictionary """ | ||
| student_features = [x for x in STUDENT_FEATURES if x in features] | ||
|
|
@@ -236,11 +247,11 @@ def extract_student(student, features): | |
| meta_key = feature.split('.')[1] | ||
| meta_features.append((feature, meta_key)) | ||
|
|
||
| student_dict = dict((feature, getattr(student, feature)) | ||
| student_dict = dict((feature, extract_attr(student, feature)) | ||
| for feature in student_features) | ||
| profile = student.profile | ||
| if profile is not None: | ||
| profile_dict = dict((feature, getattr(profile, feature)) | ||
| profile_dict = dict((feature, extract_attr(profile, feature)) | ||
| for feature in profile_features) | ||
| student_dict.update(profile_dict) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -106,17 +106,35 @@ def test_enrolled_students_features_username(self): | |
| self.assertIn(userreport['username'], [user.username for user in self.users]) | ||
|
|
||
| def test_enrolled_students_features_keys(self): | ||
| query_features = ('username', 'name', 'email') | ||
| query_features = ('username', 'name', 'email', 'city', 'country',) | ||
| for user in self.users: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One more request-- please include a user without city and country so that we can verify the code works when those are not specified (assert the expected values).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done below. |
||
| user.profile.city = "Mos Eisley {}".format(user.id) | ||
| user.profile.country = "Tatooine {}".format(user.id) | ||
| user.profile.save() | ||
| for feature in query_features: | ||
| self.assertIn(feature, AVAILABLE_FEATURES) | ||
| with self.assertNumQueries(1): | ||
| userreports = enrolled_students_features(self.course_key, query_features) | ||
| self.assertEqual(len(userreports), len(self.users)) | ||
| for userreport in userreports: | ||
|
|
||
| userreports = sorted(userreports, key=lambda u: u["username"]) | ||
| users = sorted(self.users, key=lambda u: u.username) | ||
| for userreport, user in zip(userreports, users): | ||
| 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]) | ||
| self.assertEqual(userreport['username'], user.username) | ||
| self.assertEqual(userreport['email'], user.email) | ||
| self.assertEqual(userreport['name'], user.profile.name) | ||
| self.assertEqual(userreport['city'], user.profile.city) | ||
| self.assertEqual(userreport['country'], user.profile.country) | ||
|
|
||
| def test_enrolled_student_with_no_country_city(self): | ||
| userreports = enrolled_students_features(self.course_key, ('username', 'city', 'country',)) | ||
| for userreport in userreports: | ||
| # This behaviour is somewhat inconsistent: None string fields | ||
| # objects are converted to "None", but non-JSON serializable fields | ||
| # are converted to an empty string. | ||
| self.assertEqual(userreport['city'], "None") | ||
| self.assertEqual(userreport['country'], "") | ||
|
|
||
| def test_enrolled_students_meta_features_keys(self): | ||
| """ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bummer, I didn't expand below this to notice that if the course is cohorted, the cohorts column is added to the end (and the same with teams). Therefore, City and Country will not be the last two columns in the spreadsheet if either cohorts or teams are present.
We may need to revert this PR for the release. I will follow up with product.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @cahrens. Please tag me on any changes so I can reflect updates in the doc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the issue exactly?