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
b33a9436
Commit
b33a9436
authored
May 02, 2017
by
Alex Dusenbery
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
TNL-6832 | Include inactive enrollees in course and problem grade reports.
parent
fd5eef38
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
108 additions
and
12 deletions
+108
-12
common/djangoapps/student/models.py
+12
-6
common/djangoapps/student/tests/test_models.py
+21
-0
lms/djangoapps/instructor_task/tasks_helper/grades.py
+3
-2
lms/djangoapps/instructor_task/tests/test_base.py
+4
-4
lms/djangoapps/instructor_task/tests/test_tasks_helper.py
+68
-0
No files found.
common/djangoapps/student/models.py
View file @
b33a9436
...
...
@@ -929,12 +929,18 @@ class CourseEnrollmentManager(models.Manager):
return
is_course_full
def
users_enrolled_in
(
self
,
course_id
):
"""Return a queryset of User for every user enrolled in the course."""
return
User
.
objects
.
filter
(
courseenrollment__course_id
=
course_id
,
courseenrollment__is_active
=
True
)
def
users_enrolled_in
(
self
,
course_id
,
include_inactive
=
False
):
"""
Return a queryset of User for every user enrolled in the course. If
`include_inactive` is True, returns both active and inactive enrollees
for the course. Otherwise returns actively enrolled users only.
"""
filter_kwargs
=
{
'courseenrollment__course_id'
:
course_id
,
}
if
not
include_inactive
:
filter_kwargs
[
'courseenrollment__is_active'
]
=
True
return
User
.
objects
.
filter
(
**
filter_kwargs
)
def
enrollment_counts
(
self
,
course_id
):
"""
...
...
common/djangoapps/student/tests/test_models.py
View file @
b33a9436
...
...
@@ -21,6 +21,7 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase):
def
setUp
(
self
):
super
(
CourseEnrollmentTests
,
self
)
.
setUp
()
self
.
user
=
UserFactory
.
create
()
self
.
user_2
=
UserFactory
.
create
()
def
test_enrollment_status_hash_cache_key
(
self
):
username
=
'test-user'
...
...
@@ -82,3 +83,23 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase):
# Modifying enrollments should delete the cached value.
CourseEnrollmentFactory
.
create
(
user
=
self
.
user
)
self
.
assertIsNone
(
cache
.
get
(
CourseEnrollment
.
enrollment_status_hash_cache_key
(
self
.
user
)))
def
test_users_enrolled_in_active_only
(
self
):
"""CourseEnrollment.users_enrolled_in should return only Users with active enrollments when
`include_inactive` has its default value (False)."""
CourseEnrollmentFactory
.
create
(
user
=
self
.
user
,
course_id
=
self
.
course
.
id
,
is_active
=
True
)
CourseEnrollmentFactory
.
create
(
user
=
self
.
user_2
,
course_id
=
self
.
course
.
id
,
is_active
=
False
)
active_enrolled_users
=
list
(
CourseEnrollment
.
objects
.
users_enrolled_in
(
self
.
course
.
id
))
self
.
assertEqual
([
self
.
user
],
active_enrolled_users
)
def
test_users_enrolled_in_all
(
self
):
"""CourseEnrollment.users_enrolled_in should return active and inactive users when
`include_inactive` is True."""
CourseEnrollmentFactory
.
create
(
user
=
self
.
user
,
course_id
=
self
.
course
.
id
,
is_active
=
True
)
CourseEnrollmentFactory
.
create
(
user
=
self
.
user_2
,
course_id
=
self
.
course
.
id
,
is_active
=
False
)
all_enrolled_users
=
list
(
CourseEnrollment
.
objects
.
users_enrolled_in
(
self
.
course
.
id
,
include_inactive
=
True
)
)
self
.
assertListEqual
([
self
.
user
,
self
.
user_2
],
all_enrolled_users
)
lms/djangoapps/instructor_task/tasks_helper/grades.py
View file @
b33a9436
...
...
@@ -272,7 +272,8 @@ class CourseGradeReport(object):
def
grouper
(
iterable
,
chunk_size
=
self
.
USER_BATCH_SIZE
,
fillvalue
=
None
):
args
=
[
iter
(
iterable
)]
*
chunk_size
return
izip_longest
(
*
args
,
fillvalue
=
fillvalue
)
users
=
CourseEnrollment
.
objects
.
users_enrolled_in
(
context
.
course_id
)
users
=
CourseEnrollment
.
objects
.
users_enrolled_in
(
context
.
course_id
,
include_inactive
=
True
)
users
=
users
.
select_related
(
'profile__allow_certificate'
)
return
grouper
(
users
)
...
...
@@ -412,7 +413,7 @@ class ProblemGradeReport(object):
start_time
=
time
()
start_date
=
datetime
.
now
(
UTC
)
status_interval
=
100
enrolled_students
=
CourseEnrollment
.
objects
.
users_enrolled_in
(
course_id
)
enrolled_students
=
CourseEnrollment
.
objects
.
users_enrolled_in
(
course_id
,
include_inactive
=
True
)
task_progress
=
TaskProgress
(
action_name
,
enrolled_students
.
count
(),
start_time
)
# This struct encapsulates both the display names of each static item in the
...
...
lms/djangoapps/instructor_task/tests/test_base.py
View file @
b33a9436
...
...
@@ -162,21 +162,21 @@ class InstructorTaskCourseTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase)
self
.
login
(
user_email
,
"test"
)
self
.
current_user
=
username
def
_create_user
(
self
,
username
,
email
=
None
,
is_staff
=
False
,
mode
=
'honor'
):
def
_create_user
(
self
,
username
,
email
=
None
,
is_staff
=
False
,
mode
=
'honor'
,
enrollment_active
=
True
):
"""Creates a user and enrolls them in the test course."""
if
email
is
None
:
email
=
InstructorTaskCourseTestCase
.
get_user_email
(
username
)
thisuser
=
UserFactory
.
create
(
username
=
username
,
email
=
email
,
is_staff
=
is_staff
)
CourseEnrollmentFactory
.
create
(
user
=
thisuser
,
course_id
=
self
.
course
.
id
,
mode
=
mode
)
CourseEnrollmentFactory
.
create
(
user
=
thisuser
,
course_id
=
self
.
course
.
id
,
mode
=
mode
,
is_active
=
enrollment_active
)
return
thisuser
def
create_instructor
(
self
,
username
,
email
=
None
):
"""Creates an instructor for the test course."""
return
self
.
_create_user
(
username
,
email
,
is_staff
=
True
)
def
create_student
(
self
,
username
,
email
=
None
,
mode
=
'honor'
):
def
create_student
(
self
,
username
,
email
=
None
,
mode
=
'honor'
,
enrollment_active
=
True
):
"""Creates a student for the test course."""
return
self
.
_create_user
(
username
,
email
,
is_staff
=
False
,
mode
=
mode
)
return
self
.
_create_user
(
username
,
email
,
is_staff
=
False
,
mode
=
mode
,
enrollment_active
=
enrollment_active
)
@staticmethod
def
get_task_status
(
task_id
):
...
...
lms/djangoapps/instructor_task/tests/test_tasks_helper.py
View file @
b33a9436
...
...
@@ -398,6 +398,25 @@ class TestInstructorGradeReport(InstructorGradeReportTestCase):
with
self
.
assertNumQueries
(
41
):
CourseGradeReport
.
generate
(
None
,
None
,
course
.
id
,
None
,
'graded'
)
def
test_inactive_enrollments
(
self
):
"""
Test that students with inactive enrollments are included in report.
"""
self
.
create_student
(
'active-student'
,
'active@example.com'
)
self
.
create_student
(
'inactive-student'
,
'inactive@example.com'
,
enrollment_active
=
False
)
self
.
current_task
=
Mock
()
self
.
current_task
.
update_state
=
Mock
()
with
patch
(
'lms.djangoapps.instructor_task.tasks_helper.runner._get_current_task'
)
as
mock_current_task
:
mock_current_task
.
return_value
=
self
.
current_task
result
=
CourseGradeReport
.
generate
(
None
,
None
,
self
.
course
.
id
,
None
,
'graded'
)
expected_students
=
2
self
.
assertDictContainsSubset
(
{
'attempted'
:
expected_students
,
'succeeded'
:
expected_students
,
'failed'
:
0
},
result
)
class
TestTeamGradeReport
(
InstructorGradeReportTestCase
):
""" Test that teams appear correctly in the grade report when it is enabled for the course. """
...
...
@@ -760,6 +779,55 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase):
}
])
@patch
(
'lms.djangoapps.instructor_task.tasks_helper.runner._get_current_task'
)
def
test_inactive_enrollment_included
(
self
,
_get_current_task
):
"""
Students with inactive enrollments in a course should be included in Problem Grade Report.
"""
inactive_student
=
self
.
create_student
(
'inactive-student'
,
'inactive@example.com'
,
enrollment_active
=
False
)
vertical
=
ItemFactory
.
create
(
parent_location
=
self
.
problem_section
.
location
,
category
=
'vertical'
,
metadata
=
{
'graded'
:
True
},
display_name
=
'Problem Vertical'
)
self
.
define_option_problem
(
u'Problem1'
,
parent
=
vertical
)
self
.
submit_student_answer
(
self
.
student_1
.
username
,
u'Problem1'
,
[
'Option 1'
])
result
=
ProblemGradeReport
.
generate
(
None
,
None
,
self
.
course
.
id
,
None
,
'graded'
)
self
.
assertDictContainsSubset
({
'action_name'
:
'graded'
,
'attempted'
:
3
,
'succeeded'
:
3
,
'failed'
:
0
},
result
)
problem_name
=
u'Homework 1: Subsection - Problem1'
header_row
=
self
.
csv_header_row
+
[
problem_name
+
' (Earned)'
,
problem_name
+
' (Possible)'
]
self
.
verify_rows_in_csv
([
dict
(
zip
(
header_row
,
[
unicode
(
self
.
student_1
.
id
),
self
.
student_1
.
email
,
self
.
student_1
.
username
,
'0.01'
,
'1.0'
,
'2.0'
,
]
)),
dict
(
zip
(
header_row
,
[
unicode
(
self
.
student_2
.
id
),
self
.
student_2
.
email
,
self
.
student_2
.
username
,
'0.0'
,
u'Not Attempted'
,
'2.0'
,
]
)),
dict
(
zip
(
header_row
,
[
unicode
(
inactive_student
.
id
),
inactive_student
.
email
,
inactive_student
.
username
,
'0.0'
,
u'Not Attempted'
,
'2.0'
,
]
))
])
@attr
(
shard
=
3
)
class
TestProblemReportSplitTestContent
(
TestReportMixin
,
TestConditionalContent
,
InstructorTaskModuleTestCase
):
...
...
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