Merge pull request #21339 from edx/mroytman/EDUCATOR-4432-course-level-grade-filter

mroytman/educator 4432 course level grade filter
This commit is contained in:
Michael Roytman
2019-08-13 15:58:50 -04:00
committed by GitHub
2 changed files with 214 additions and 1 deletions

View File

@@ -33,6 +33,7 @@ from lms.djangoapps.grades.grade_utils import are_grades_frozen
from lms.djangoapps.grades.models import (
PersistentSubsectionGrade,
PersistentSubsectionGradeOverride,
PersistentCourseGrade,
)
from lms.djangoapps.grades.rest_api.serializers import (
StudentGradebookEntrySerializer,
@@ -579,6 +580,39 @@ class GradebookView(GradeViewMixin, PaginatedAPIView):
)
)
q_objects.append(Q(selected_assignment_grade_in_range=True))
if request.GET.get('course_grade_min') or request.GET.get('course_grade_max'):
grade_conditions = {}
q_object = Q()
if request.GET.get('course_grade_min'):
course_grade_min = float(request.GET.get('course_grade_min')) / 100
grade_conditions['percent_grade__gte'] = course_grade_min
if course_grade_min == 0:
subquery_grade_absent = ~Exists(
PersistentCourseGrade.objects.filter(
course_id=OuterRef('course'),
user_id=OuterRef('user_id'),
)
)
annotations['course_grade_absent'] = subquery_grade_absent
q_object |= Q(course_grade_absent=True)
if request.GET.get('course_grade_max'):
course_grade_max = float(request.GET.get('course_grade_max')) / 100
grade_conditions['percent_grade__lte'] = course_grade_max
subquery_grade_in_range = Exists(
PersistentCourseGrade.objects.filter(
course_id=OuterRef('course'),
user_id=OuterRef('user_id'),
**grade_conditions
)
)
annotations['course_grade_in_range'] = subquery_grade_in_range
q_object |= Q(course_grade_in_range=True)
q_objects.append(q_object)
entries = []
related_models = ['user']

View File

@@ -29,7 +29,8 @@ from lms.djangoapps.grades.models import (
BlockRecordList,
PersistentSubsectionGrade,
PersistentSubsectionGradeOverride,
PersistentSubsectionGradeOverrideHistory
PersistentSubsectionGradeOverrideHistory,
PersistentCourseGrade,
)
from lms.djangoapps.grades.rest_api.v1.tests.mixins import GradeViewTestMixin
from lms.djangoapps.grades.rest_api.v1.views import CourseEnrollmentPagination
@@ -1031,6 +1032,184 @@ class GradebookViewTest(GradebookViewTestBase):
expected_page_size = user_size
self.assertEqual(len(actual_data['results']), expected_page_size)
@ddt.data(
['login_staff', 4],
['login_course_admin', 5],
['login_course_staff', 5]
)
@ddt.unpack
def test_filter_course_grade_min(self, login_method, num_enrollments):
with patch('lms.djangoapps.grades.course_grade_factory.CourseGradeFactory.read') as mock_grade:
# even though we're creating actual PersistentCourseGrades below, we still need
# mocked subsection grades
mock_grade.side_effect = [
self.mock_course_grade(self.student, passed=True, percent=0.85),
self.mock_course_grade(self.program_student, passed=True, percent=0.75),
]
PersistentCourseGrade(
user_id=self.student.id,
course_id=self.course_key,
percent_grade=0.85
).save()
PersistentCourseGrade(
user_id=self.other_student.id,
course_id=self.course_key,
percent_grade=0.45
).save()
PersistentCourseGrade(
user_id=self.program_student.id,
course_id=self.course_key,
percent_grade=0.75
).save()
with override_waffle_flag(self.waffle_flag, active=True):
getattr(self, login_method)()
resp = self.client.get(
self.get_url(course_key=self.course.id) + '?course_grade_min=50'
)
expected_results = [
OrderedDict([
('user_id', self.student.id),
('username', self.student.username),
('email', ''),
('percent', 0.85),
('section_breakdown', self.expected_subsection_grades()),
]),
OrderedDict([
('user_id', self.program_student.id),
('username', self.program_student.username),
('email', ''),
('external_user_key', 'program_user_key_0'),
('percent', 0.75),
('section_breakdown', self.expected_subsection_grades()),
])
]
self.assertEqual(status.HTTP_200_OK, resp.status_code)
actual_data = dict(resp.data)
self.assertEqual(expected_results, actual_data['results'])
self.assertEqual(actual_data['total_users_count'], num_enrollments)
self.assertEqual(actual_data['filtered_users_count'], 2)
@ddt.data(
['login_staff', 4],
['login_course_admin', 5],
['login_course_staff', 5]
)
@ddt.unpack
def test_filter_course_grade_min_and_max(self, login_method, num_enrollments):
with patch('lms.djangoapps.grades.course_grade_factory.CourseGradeFactory.read') as mock_grade:
# even though we're creating actual PersistentCourseGrades below, we still need
# mocked subsection grades
mock_grade.side_effect = [
self.mock_course_grade(self.program_student, passed=True, percent=0.75),
]
PersistentCourseGrade(
user_id=self.student.id,
course_id=self.course_key,
percent_grade=0.85
).save()
PersistentCourseGrade(
user_id=self.other_student.id,
course_id=self.course_key,
percent_grade=0.45
).save()
PersistentCourseGrade(
user_id=self.program_student.id,
course_id=self.course_key,
percent_grade=0.75
).save()
with override_waffle_flag(self.waffle_flag, active=True):
getattr(self, login_method)()
resp = self.client.get(
self.get_url(course_key=self.course.id) + '?course_grade_min=50&course_grade_max=80'
)
expected_results = [
OrderedDict([
('user_id', self.program_student.id),
('username', self.program_student.username),
('email', ''),
('external_user_key', 'program_user_key_0'),
('percent', 0.75),
('section_breakdown', self.expected_subsection_grades()),
]),
]
self.assertEqual(status.HTTP_200_OK, resp.status_code)
actual_data = dict(resp.data)
self.assertEqual(expected_results, actual_data['results'])
self.assertEqual(actual_data['total_users_count'], num_enrollments)
self.assertEqual(actual_data['filtered_users_count'], 1)
@ddt.data(
['login_staff', 4],
['login_course_admin', 5],
['login_course_staff', 5]
)
@ddt.unpack
def test_filter_course_grade_absent(self, login_method, num_enrollments):
with patch('lms.djangoapps.grades.course_grade_factory.CourseGradeFactory.read') as mock_grade:
# even though we're creating actual PersistentCourseGrades below, we still need
# mocked subsection grades
mock_grade.side_effect = [
self.mock_course_grade(self.student, passed=True, percent=0.0),
self.mock_course_grade(self.other_student, passed=False, percent=0.45),
self.mock_course_grade(self.program_student, passed=True, percent=0.75),
]
PersistentCourseGrade(
user_id=self.other_student.id,
course_id=self.course_key,
percent_grade=0.45
).save()
PersistentCourseGrade(
user_id=self.program_student.id,
course_id=self.course_key,
percent_grade=0.75
).save()
with override_waffle_flag(self.waffle_flag, active=True):
getattr(self, login_method)()
resp = self.client.get(
self.get_url(course_key=self.course.id) + '?course_grade_min=0'
)
expected_results = [
OrderedDict([
('user_id', self.student.id),
('username', self.student.username),
('email', ''),
('percent', 0.0),
('section_breakdown', self.expected_subsection_grades()),
]),
OrderedDict([
('user_id', self.other_student.id),
('username', self.other_student.username),
('email', ''),
('percent', 0.45),
('section_breakdown', self.expected_subsection_grades()),
]),
OrderedDict([
('user_id', self.program_student.id),
('username', self.program_student.username),
('email', ''),
('external_user_key', 'program_user_key_0'),
('percent', 0.75),
('section_breakdown', self.expected_subsection_grades()),
])
]
self.assertEqual(status.HTTP_200_OK, resp.status_code)
actual_data = dict(resp.data)
self.assertEqual(expected_results, actual_data['results'])
self.assertEqual(actual_data['total_users_count'], num_enrollments)
self.assertEqual(actual_data['filtered_users_count'], num_enrollments)
@ddt.ddt
class GradebookBulkUpdateViewTest(GradebookViewTestBase):