Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
1a0e42af
Commit
1a0e42af
authored
May 26, 2017
by
Alex Dusenbery
Committed by
Alex Dusenbery
May 31, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
EDUCATOR-435 | Include enrollment status in in course and problem grade reports.
parent
aac143e6
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
60 additions
and
14 deletions
+60
-14
lms/djangoapps/instructor_task/tasks_helper/grades.py
+32
-5
lms/djangoapps/instructor_task/tests/test_tasks_helper.py
+28
-9
No files found.
lms/djangoapps/instructor_task/tasks_helper/grades.py
View file @
1a0e42af
...
...
@@ -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
:
...
...
lms/djangoapps/instructor_task/tests/test_tasks_helper.py
View file @
1a0e42af
...
...
@@ -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'
],
},
]
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment