Commit 1a0e42af by Alex Dusenbery Committed by Alex Dusenbery

EDUCATOR-435 | Include enrollment status in in course and problem grade reports.

parent aac143e6
......@@ -34,6 +34,25 @@ from .utils import upload_csv_to_report_store
TASK_LOG = logging.getLogger('edx.celery.task')
ENROLLED_IN_COURSE = 'enrolled'
NOT_ENROLLED_IN_COURSE = 'unenrolled'
def _user_enrollment_status(user, course_id):
"""
Returns the enrollment activation status in the given course
for the given user.
"""
enrollment_is_active = CourseEnrollment.enrollment_mode_for_user(user, course_id)[1]
if enrollment_is_active:
return ENROLLED_IN_COURSE
return NOT_ENROLLED_IN_COURSE
def _flatten(iterable):
return list(chain.from_iterable(iterable))
class _CourseGradeReportContext(object):
"""
......@@ -209,7 +228,8 @@ class CourseGradeReport(object):
[u'Experiment Group ({})'.format(partition.name) for partition in context.course_experiments] +
(['Team Name'] if context.teams_enabled else []) +
['Enrollment Track', 'Verification Status'] +
['Certificate Eligible', 'Certificate Delivered', 'Certificate Type']
['Certificate Eligible', 'Certificate Delivered', 'Certificate Type'] +
['Enrollment Status']
)
def _error_headers(self):
......@@ -300,7 +320,7 @@ class CourseGradeReport(object):
'percent'
)
grade_results.append([assignment_average])
return [course_grade.percent] + list(chain.from_iterable(grade_results))
return [course_grade.percent] + _flatten(grade_results)
def _user_cohort_group_names(self, user, context):
"""
......@@ -398,7 +418,8 @@ class CourseGradeReport(object):
self._user_experiment_group_names(user, context) +
self._user_team_names(user, bulk_context.teams) +
self._user_verification_mode(user, context, bulk_context.enrollments) +
self._user_certificate_info(user, context, course_grade, bulk_context.certs)
self._user_certificate_info(user, context, course_grade, bulk_context.certs) +
[_user_enrollment_status(user, context.course_id)]
)
return success_rows, error_rows
......@@ -424,10 +445,14 @@ class ProblemGradeReport(object):
graded_scorable_blocks = cls._graded_scorable_blocks_to_header(course_id)
# Just generate the static fields for now.
rows = [list(header_row.values()) + ['Grade'] + list(chain.from_iterable(graded_scorable_blocks.values()))]
rows = [list(header_row.values()) + ['Enrollment Status', 'Grade'] + _flatten(graded_scorable_blocks.values())]
error_rows = [list(header_row.values()) + ['error_msg']]
current_step = {'step': 'Calculating Grades'}
# Bulk fetch and cache enrollment states so we can efficiently determine
# whether each user is currently enrolled in the course.
CourseEnrollment.bulk_fetch_enrollment_states(enrolled_students, course_id)
course = get_course_by_id(course_id)
for student, course_grade, error in CourseGradeFactory().iter(enrolled_students, course):
student_fields = [getattr(student, field_name) for field_name in header_row]
......@@ -442,6 +467,8 @@ class ProblemGradeReport(object):
task_progress.failed += 1
continue
enrollment_status = _user_enrollment_status(student, course_id)
earned_possible_values = []
for block_location in graded_scorable_blocks:
try:
......@@ -454,7 +481,7 @@ class ProblemGradeReport(object):
else:
earned_possible_values.append([u'Not Attempted', problem_score.possible])
rows.append(student_fields + [course_grade.percent] + list(chain.from_iterable(earned_possible_values)))
rows.append(student_fields + [enrollment_status, course_grade.percent] + _flatten(earned_possible_values))
task_progress.succeeded += 1
if task_progress.attempted % status_interval == 0:
......
......@@ -64,6 +64,8 @@ from lms.djangoapps.instructor_task.tasks_helper.enrollments import (
upload_students_csv,
)
from lms.djangoapps.instructor_task.tasks_helper.grades import (
ENROLLED_IN_COURSE,
NOT_ENROLLED_IN_COURSE,
CourseGradeReport,
ProblemGradeReport,
ProblemResponses,
......@@ -393,9 +395,10 @@ class TestInstructorGradeReport(InstructorGradeReportTestCase):
RequestCache.clear_request_cache()
expected_query_count = 41
with patch('lms.djangoapps.instructor_task.tasks_helper.runner._get_current_task'):
with check_mongo_calls(mongo_count):
with self.assertNumQueries(41):
with self.assertNumQueries(expected_query_count):
CourseGradeReport.generate(None, None, course.id, None, 'graded')
def test_inactive_enrollments(self):
......@@ -412,6 +415,9 @@ class TestInstructorGradeReport(InstructorGradeReportTestCase):
mock_current_task.return_value = self.current_task
result = CourseGradeReport.generate(None, None, self.course.id, None, 'graded')
self._verify_cell_data_for_user('active-student', self.course.id, 'Enrollment Status', ENROLLED_IN_COURSE)
self._verify_cell_data_for_user('inactive-student', self.course.id, 'Enrollment Status', NOT_ENROLLED_IN_COURSE)
expected_students = 2
self.assertDictContainsSubset(
{'attempted': expected_students, 'succeeded': expected_students, 'failed': 0}, result
......@@ -696,7 +702,7 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase):
# technically possible in openedx.
self.student_1 = self.create_student(u'üser_1')
self.student_2 = self.create_student(u'üser_2')
self.csv_header_row = [u'Student ID', u'Email', u'Username', u'Grade']
self.csv_header_row = [u'Student ID', u'Email', u'Username', u'Enrollment Status', u'Grade']
@patch('lms.djangoapps.instructor_task.tasks_helper.runner._get_current_task')
def test_no_problems(self, _get_current_task):
......@@ -709,11 +715,11 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase):
self.verify_rows_in_csv([
dict(zip(
self.csv_header_row,
[unicode(self.student_1.id), self.student_1.email, self.student_1.username, '0.0']
[unicode(self.student_1.id), self.student_1.email, self.student_1.username, ENROLLED_IN_COURSE, '0.0']
)),
dict(zip(
self.csv_header_row,
[unicode(self.student_2.id), self.student_2.email, self.student_2.username, '0.0']
[unicode(self.student_2.id), self.student_2.email, self.student_2.username, ENROLLED_IN_COURSE, '0.0']
))
])
......@@ -739,6 +745,7 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase):
unicode(self.student_1.id),
self.student_1.email,
self.student_1.username,
ENROLLED_IN_COURSE,
'0.01', '1.0', '2.0',
]
)),
......@@ -748,6 +755,7 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase):
unicode(self.student_2.id),
self.student_2.email,
self.student_2.username,
ENROLLED_IN_COURSE,
'0.0', u'Not Attempted', '2.0',
]
))
......@@ -805,6 +813,7 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase):
unicode(self.student_1.id),
self.student_1.email,
self.student_1.username,
ENROLLED_IN_COURSE,
'0.01', '1.0', '2.0',
]
)),
......@@ -814,6 +823,7 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase):
unicode(self.student_2.id),
self.student_2.email,
self.student_2.username,
ENROLLED_IN_COURSE,
'0.0', u'Not Attempted', '2.0',
]
)),
......@@ -823,6 +833,7 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase):
unicode(inactive_student.id),
inactive_student.email,
inactive_student.username,
NOT_ENROLLED_IN_COURSE,
'0.0', u'Not Attempted', '2.0',
]
))
......@@ -870,7 +881,7 @@ class TestProblemReportSplitTestContent(TestReportMixin, TestConditionalContent,
)
problem_names = [u'Homework 1: Subsection - problem_a_url', u'Homework 1: Subsection - problem_b_url']
header_row = [u'Student ID', u'Email', u'Username', u'Grade']
header_row = [u'Student ID', u'Email', u'Username', u'Enrollment Status', u'Grade']
for problem in problem_names:
header_row += [problem + ' (Earned)', problem + ' (Possible)']
......@@ -881,6 +892,7 @@ class TestProblemReportSplitTestContent(TestReportMixin, TestConditionalContent,
unicode(self.student_a.id),
self.student_a.email,
self.student_a.username,
ENROLLED_IN_COURSE,
u'1.0', u'2.0', u'2.0', u'Not Available', u'Not Available'
]
)),
......@@ -889,7 +901,9 @@ class TestProblemReportSplitTestContent(TestReportMixin, TestConditionalContent,
[
unicode(self.student_b.id),
self.student_b.email,
self.student_b.username, u'0.5', u'Not Available', u'Not Available', u'1.0', u'2.0'
self.student_b.username,
ENROLLED_IN_COURSE,
u'0.5', u'Not Available', u'Not Available', u'1.0', u'2.0'
]
))
])
......@@ -951,7 +965,7 @@ class TestProblemReportSplitTestContent(TestReportMixin, TestConditionalContent,
title = 'Homework %d 1: Problem section %d - %s' % (i, i, problem_url)
problem_names.append(title)
header_row = [u'Student ID', u'Email', u'Username', u'Grade']
header_row = [u'Student ID', u'Email', u'Username', u'Enrollment Status', u'Grade']
for problem in problem_names:
header_row += [problem + ' (Earned)', problem + ' (Possible)']
......@@ -985,7 +999,7 @@ class TestProblemReportCohortedContent(TestReportMixin, ContentGroupTestCase, In
group_access={self.course.user_partitions[0].id: [self.course.user_partitions[0].groups[1].id]}
)
def _format_user_grade(self, header_row, user, grade):
def _format_user_grade(self, header_row, user, enrollment_status, grade):
"""
Helper method that format the user grade
Args:
......@@ -999,6 +1013,7 @@ class TestProblemReportCohortedContent(TestReportMixin, ContentGroupTestCase, In
unicode(user.id),
user.email,
user.username,
enrollment_status,
] + grade
))
......@@ -1017,25 +1032,29 @@ class TestProblemReportCohortedContent(TestReportMixin, ContentGroupTestCase, In
{'action_name': 'graded', 'attempted': 4, 'succeeded': 4, 'failed': 0}, result
)
problem_names = [u'Homework 1: Subsection - Problem0', u'Homework 1: Subsection - Problem1']
header_row = [u'Student ID', u'Email', u'Username', u'Grade']
header_row = [u'Student ID', u'Email', u'Username', u'Enrollment Status', u'Grade']
for problem in problem_names:
header_row += [problem + ' (Earned)', problem + ' (Possible)']
user_grades = [
{
'user': self.staff_user,
'enrollment_status': ENROLLED_IN_COURSE,
'grade': [u'0.0', u'Not Available', u'Not Available', u'Not Available', u'Not Available'],
},
{
'user': self.alpha_user,
'enrollment_status': ENROLLED_IN_COURSE,
'grade': [u'1.0', u'2.0', u'2.0', u'Not Available', u'Not Available'],
},
{
'user': self.beta_user,
'enrollment_status': ENROLLED_IN_COURSE,
'grade': [u'0.5', u'Not Available', u'Not Available', u'1.0', u'2.0'],
},
{
'user': self.non_cohorted_user,
'enrollment_status': ENROLLED_IN_COURSE,
'grade': [u'0.0', u'Not Available', u'Not Available', u'Not Available', u'Not Available'],
},
]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment