Skip to content

Commit 6f391d9

Browse files
authored
feat: enrollment_date added to csv report and add custom fields method (#37264)
* chore: enrollment_date added to csv report and add custom fields method managing * test: tests added * fix: pylint fix * fix: new line at test_basic.py added * feat: new function added to handle available features with custom fields * chore: replace include_ parameters with direct feature checks * feat: type validation for custom attributes added * chore: site config name and variable updated, attribute fixing erased * test: tests updated
1 parent 1fed4be commit 6f391d9

File tree

3 files changed

+475
-102
lines changed

3 files changed

+475
-102
lines changed

lms/djangoapps/instructor/views/api.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,7 +1477,7 @@ def post(self, request, course_id, csv=False): # pylint: disable=redefined-oute
14771477
course_key = CourseKey.from_string(course_id)
14781478
course = get_course_by_id(course_key)
14791479
report_type = _('enrolled learner profile')
1480-
available_features = instructor_analytics_basic.AVAILABLE_FEATURES
1480+
available_features = instructor_analytics_basic.get_available_features(course_key)
14811481

14821482
# Allow for sites to be able to define additional columns.
14831483
# Note that adding additional columns has the potential to break
@@ -1493,9 +1493,40 @@ def post(self, request, course_id, csv=False): # pylint: disable=redefined-oute
14931493
query_features = [
14941494
'id', 'username', 'name', 'email', 'language', 'location',
14951495
'year_of_birth', 'gender', 'level_of_education', 'mailing_address',
1496-
'goals', 'enrollment_mode', 'last_login', 'date_joined', 'external_user_key'
1496+
'goals', 'enrollment_mode', 'last_login', 'date_joined', 'external_user_key',
1497+
'enrollment_date',
14971498
]
14981499

1500+
additional_attributes = configuration_helpers.get_value_for_org(
1501+
course_key.org,
1502+
"additional_student_profile_attributes"
1503+
)
1504+
if additional_attributes:
1505+
# Fail fast: must be list/tuple of strings.
1506+
if not isinstance(additional_attributes, (list, tuple)):
1507+
return JsonResponseBadRequest(
1508+
_('Invalid additional student attribute configuration: expected list of strings, got {type}.')
1509+
.format(type=type(additional_attributes).__name__)
1510+
)
1511+
if not all(isinstance(v, str) for v in additional_attributes):
1512+
return JsonResponseBadRequest(
1513+
_('Invalid additional student attribute configuration: all entries must be strings.')
1514+
)
1515+
# Reject empty string entries explicitly.
1516+
if any(v == '' for v in additional_attributes):
1517+
return JsonResponseBadRequest(
1518+
_('Invalid additional student attribute configuration: empty attribute names are not allowed.')
1519+
)
1520+
# Validate each attribute is in available_features; allow duplicates as provided.
1521+
invalid = [v for v in additional_attributes if v not in available_features]
1522+
if invalid:
1523+
return JsonResponseBadRequest(
1524+
_('Invalid additional student attributes: {attrs}').format(
1525+
attrs=', '.join(invalid)
1526+
)
1527+
)
1528+
query_features.extend(additional_attributes)
1529+
14991530
# Provide human-friendly and translatable names for these features. These names
15001531
# will be displayed in the table generated in data_download.js. It is not (yet)
15011532
# used as the header row in the CSV, but could be in the future.
@@ -1515,8 +1546,16 @@ def post(self, request, course_id, csv=False): # pylint: disable=redefined-oute
15151546
'last_login': _('Last Login'),
15161547
'date_joined': _('Date Joined'),
15171548
'external_user_key': _('External User Key'),
1549+
'enrollment_date': _('Enrollment Date'),
15181550
}
15191551

1552+
if additional_attributes:
1553+
for attr in additional_attributes:
1554+
if attr not in query_features_names:
1555+
formatted_name = attr.replace('_', ' ').title()
1556+
# pylint: disable-next=translation-of-non-string
1557+
query_features_names[attr] = _(formatted_name)
1558+
15201559
for field in settings.PROFILE_INFORMATION_REPORT_PRIVATE_FIELDS:
15211560
keep_field_private(query_features, field)
15221561
query_features_names.pop(field, None)

0 commit comments

Comments
 (0)