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):
...
@@ -929,12 +929,18 @@ class CourseEnrollmentManager(models.Manager):
return
is_course_full
return
is_course_full
def
users_enrolled_in
(
self
,
course_id
):
def
users_enrolled_in
(
self
,
course_id
,
include_inactive
=
False
):
"""Return a queryset of User for every user enrolled in the course."""
"""
return
User
.
objects
.
filter
(
Return a queryset of User for every user enrolled in the course. If
courseenrollment__course_id
=
course_id
,
`include_inactive` is True, returns both active and inactive enrollees
courseenrollment__is_active
=
True
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
):
def
enrollment_counts
(
self
,
course_id
):
"""
"""
...
...
common/djangoapps/student/tests/test_models.py
View file @
b33a9436
...
@@ -21,6 +21,7 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase):
...
@@ -21,6 +21,7 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
CourseEnrollmentTests
,
self
)
.
setUp
()
super
(
CourseEnrollmentTests
,
self
)
.
setUp
()
self
.
user
=
UserFactory
.
create
()
self
.
user
=
UserFactory
.
create
()
self
.
user_2
=
UserFactory
.
create
()
def
test_enrollment_status_hash_cache_key
(
self
):
def
test_enrollment_status_hash_cache_key
(
self
):
username
=
'test-user'
username
=
'test-user'
...
@@ -82,3 +83,23 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase):
...
@@ -82,3 +83,23 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase):
# Modifying enrollments should delete the cached value.
# Modifying enrollments should delete the cached value.
CourseEnrollmentFactory
.
create
(
user
=
self
.
user
)
CourseEnrollmentFactory
.
create
(
user
=
self
.
user
)
self
.
assertIsNone
(
cache
.
get
(
CourseEnrollment
.
enrollment_status_hash_cache_key
(
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):
...
@@ -272,7 +272,8 @@ class CourseGradeReport(object):
def
grouper
(
iterable
,
chunk_size
=
self
.
USER_BATCH_SIZE
,
fillvalue
=
None
):
def
grouper
(
iterable
,
chunk_size
=
self
.
USER_BATCH_SIZE
,
fillvalue
=
None
):
args
=
[
iter
(
iterable
)]
*
chunk_size
args
=
[
iter
(
iterable
)]
*
chunk_size
return
izip_longest
(
*
args
,
fillvalue
=
fillvalue
)
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'
)
users
=
users
.
select_related
(
'profile__allow_certificate'
)
return
grouper
(
users
)
return
grouper
(
users
)
...
@@ -412,7 +413,7 @@ class ProblemGradeReport(object):
...
@@ -412,7 +413,7 @@ class ProblemGradeReport(object):
start_time
=
time
()
start_time
=
time
()
start_date
=
datetime
.
now
(
UTC
)
start_date
=
datetime
.
now
(
UTC
)
status_interval
=
100
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
)
task_progress
=
TaskProgress
(
action_name
,
enrolled_students
.
count
(),
start_time
)
# This struct encapsulates both the display names of each static item in the
# 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)
...
@@ -162,21 +162,21 @@ class InstructorTaskCourseTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase)
self
.
login
(
user_email
,
"test"
)
self
.
login
(
user_email
,
"test"
)
self
.
current_user
=
username
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."""
"""Creates a user and enrolls them in the test course."""
if
email
is
None
:
if
email
is
None
:
email
=
InstructorTaskCourseTestCase
.
get_user_email
(
username
)
email
=
InstructorTaskCourseTestCase
.
get_user_email
(
username
)
thisuser
=
UserFactory
.
create
(
username
=
username
,
email
=
email
,
is_staff
=
is_staff
)
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
return
thisuser
def
create_instructor
(
self
,
username
,
email
=
None
):
def
create_instructor
(
self
,
username
,
email
=
None
):
"""Creates an instructor for the test course."""
"""Creates an instructor for the test course."""
return
self
.
_create_user
(
username
,
email
,
is_staff
=
True
)
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."""
"""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
@staticmethod
def
get_task_status
(
task_id
):
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):
...
@@ -398,6 +398,25 @@ class TestInstructorGradeReport(InstructorGradeReportTestCase):
with
self
.
assertNumQueries
(
41
):
with
self
.
assertNumQueries
(
41
):
CourseGradeReport
.
generate
(
None
,
None
,
course
.
id
,
None
,
'graded'
)
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
):
class
TestTeamGradeReport
(
InstructorGradeReportTestCase
):
""" Test that teams appear correctly in the grade report when it is enabled for the course. """
""" Test that teams appear correctly in the grade report when it is enabled for the course. """
...
@@ -760,6 +779,55 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase):
...
@@ -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
)
@attr
(
shard
=
3
)
class
TestProblemReportSplitTestContent
(
TestReportMixin
,
TestConditionalContent
,
InstructorTaskModuleTestCase
):
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