1010import logging
1111
1212from django .conf import settings
13- from django .contrib .auth .models import User # lint-amnesty, pylint: disable=imported-auth-user
1413from django .core .exceptions import ObjectDoesNotExist
1514from django .core .serializers .json import DjangoJSONEncoder
1615from django .db .models import Count # lint-amnesty, pylint: disable=unused-import
3837 'level_of_education' , 'mailing_address' , 'goals' , 'meta' ,
3938 'city' , 'country' )
4039PROGRAM_ENROLLMENT_FEATURES = ('external_user_key' , )
40+ ENROLLMENT_FEATURES = ('enrollment_date' , )
4141ORDER_ITEM_FEATURES = ('list_price' , 'unit_cost' , 'status' )
4242ORDER_FEATURES = ('purchase_time' ,)
4343
4949 'bill_to_street2' , 'bill_to_city' , 'bill_to_state' , 'bill_to_postalcode' ,
5050 'bill_to_country' , 'order_type' , 'created' )
5151
52- AVAILABLE_FEATURES = STUDENT_FEATURES + PROFILE_FEATURES + PROGRAM_ENROLLMENT_FEATURES
52+ AVAILABLE_FEATURES = STUDENT_FEATURES + PROFILE_FEATURES + PROGRAM_ENROLLMENT_FEATURES + ENROLLMENT_FEATURES
5353COURSE_REGISTRATION_FEATURES = ('code' , 'course_id' , 'created_by' , 'created_at' , 'is_valid' )
5454COUPON_FEATURES = ('code' , 'course_id' , 'percentage_discount' , 'description' , 'expiration_date' , 'is_active' )
5555CERTIFICATE_FEATURES = ('course_id' , 'mode' , 'status' , 'grade' , 'created_date' , 'is_active' , 'error_reason' )
@@ -84,7 +84,62 @@ def issued_certificates(course_key, features):
8484 return generated_certificates
8585
8686
87- def enrolled_students_features (course_key , features ):
87+ def get_student_features_with_custom (course_key ):
88+ """
89+ Allow site operators to include on the export custom fields if platform has an extending
90+ User model. This can be used if you have an extended model that include for example
91+ an university student number.
92+ Basic example of adding age:
93+ ```python
94+ def get_age(self):
95+ return datetime.datetime.now().year - self.profile.year_of_birth
96+ setattr(User, 'age', property(get_age))
97+ ```
98+ Then you have to add `age` to both site configurations:
99+ - `student_profile_download_fields_custom_student_attributes`
100+ - `student_profile_download_fields` site configurations`
101+ ```json
102+ "student_profile_download_fields_custom_student_attributes": ["age"],
103+ "student_profile_download_fields": [
104+ "id", "username", "name", "email", "language", "location",
105+ "year_of_birth", "gender", "level_of_education", "mailing_address",
106+ "goals", "enrollment_mode", "last_login", "date_joined", "external_user_key",
107+ "enrollment_date", "age"
108+ ]
109+ ```
110+ Example if the platform has a custom user extended model like a One-To-One Link
111+ with the User Model:
112+ ```python
113+ def get_user_extended_model_custom_field(self):
114+ if hasattr(self, "userextendedmodel"):
115+ return self.userextendedmodel.custom_field
116+ return None
117+ setattr(User, 'user_extended_model_custom_field', property(get_user_extended_model_custom_field))
118+ ```
119+ ```json
120+ "student_profile_download_fields_custom_student_attributes": ["user_extended_model_custom_field"],
121+ "student_profile_download_fields": [
122+ "id", "username", "name", "email", "language", "location",
123+ "year_of_birth", "gender", "level_of_education", "mailing_address",
124+ "goals", "enrollment_mode", "last_login", "date_joined", "external_user_key",
125+ "enrollment_date", "user_extended_model_custom_field"
126+ ]
127+ ```
128+ """
129+ return STUDENT_FEATURES + tuple (
130+ configuration_helpers .get_value_for_org (
131+ course_key .org ,
132+ "student_profile_download_fields_custom_student_attributes" ,
133+ getattr (
134+ settings ,
135+ "STUDENT_PROFILE_DOWNLOAD_FIELDS_CUSTOM_STUDENT_ATTRIBUTES" ,
136+ (),
137+ ),
138+ )
139+ )
140+
141+
142+ def enrolled_students_features (course_key , features ): # lint-amnesty, pylint: disable=too-many-statements
88143 """
89144 Return list of student features as dictionaries.
90145
@@ -101,18 +156,25 @@ def enrolled_students_features(course_key, features):
101156 include_enrollment_mode = 'enrollment_mode' in features
102157 include_verification_status = 'verification_status' in features
103158 include_program_enrollments = 'external_user_key' in features
159+ include_enrollment_date = 'enrollment_date' in features
104160 external_user_key_dict = {}
105161
106- students = User .objects .filter (
107- courseenrollment__course_id = course_key ,
108- courseenrollment__is_active = 1 ,
109- ).order_by ('username ' ).select_related ('profile ' )
162+ enrollments = CourseEnrollment .objects .filter (
163+ course_id = course_key ,
164+ is_active = 1 ,
165+ ).select_related ( 'user' ). order_by ('user__username ' ).select_related ('user__profile ' )
110166
111167 if include_cohort_column :
112- students = students .prefetch_related ('course_groups ' )
168+ enrollments = enrollments .prefetch_related ('user__course_groups ' )
113169
114170 if include_team_column :
115- students = students .prefetch_related ('teams' )
171+ enrollments = enrollments .prefetch_related ('user__teams' )
172+
173+
174+ students = [enrollment .user for enrollment in enrollments ]
175+
176+ # student_features = [x for x in get_student_features_with_custom(course_key) if x in features]
177+ # profile_features = [x for x in PROFILE_FEATURES if x in features]
116178
117179 if include_program_enrollments and len (students ) > 0 :
118180 program_enrollments = fetch_program_enrollments_by_students (users = students , realized_only = True )
@@ -128,11 +190,15 @@ def extract_attr(student, feature):
128190 except TypeError :
129191 return str (attr )
130192
131- def extract_student ( student , features ):
193+ def extract_enrollment_student ( enrollment , features ):
132194 """ convert student to dictionary """
195+
133196 student_features = [x for x in STUDENT_FEATURES if x in features ]
134197 profile_features = [x for x in PROFILE_FEATURES if x in features ]
135198
199+ student = enrollment .user
200+
201+
136202 # For data extractions on the 'meta' field
137203 # the feature name should be in the format of 'meta.foo' where
138204 # 'foo' is the keyname in the meta dictionary
@@ -189,9 +255,12 @@ def extract_student(student, features):
189255 # extra external_user_key
190256 student_dict ['external_user_key' ] = external_user_key_dict .get (student .id , '' )
191257
258+ if include_enrollment_date :
259+ student_dict ['enrollment_date' ] = enrollment .created
260+
192261 return student_dict
193262
194- return [extract_student ( student , features ) for student in students ]
263+ return [extract_enrollment_student ( enrollment , features ) for enrollment in enrollments ]
195264
196265
197266def list_may_enroll (course_key , features ):
0 commit comments