From c007499a54b941f6c8f69c26e747919d6dbd593a Mon Sep 17 00:00:00 2001 From: atesker Date: Fri, 25 Oct 2019 10:55:22 -0400 Subject: [PATCH] PROD-826 - Add CourseWaffleFlag that restricts the set of ProblemGradeReport enrollees to only verified learners. cr comments added unit test removed debug style --- common/djangoapps/student/models.py | 6 +++- .../instructor_task/config/waffle.py | 35 +++++++++++++++++++ .../instructor_task/tasks_helper/grades.py | 8 ++++- .../tests/test_tasks_helper.py | 23 ++++++++++++ 4 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 lms/djangoapps/instructor_task/config/waffle.py diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 01ad3b3c87..a3ad3b184a 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -1021,17 +1021,21 @@ class CourseEnrollmentManager(models.Manager): return is_course_full - def users_enrolled_in(self, course_id, include_inactive=False): + def users_enrolled_in(self, course_id, include_inactive=False, verified_only=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. + If 'verified_only' is True, returns report only for verified enrollees. """ + # enrolled filter_kwargs = { 'courseenrollment__course_id': course_id, } if not include_inactive: filter_kwargs['courseenrollment__is_active'] = True + if verified_only: + filter_kwargs['courseenrollment__mode'] = CourseMode.VERIFIED return User.objects.filter(**filter_kwargs) def enrollment_counts(self, course_id): diff --git a/lms/djangoapps/instructor_task/config/waffle.py b/lms/djangoapps/instructor_task/config/waffle.py new file mode 100644 index 0000000000..7eebe17d0f --- /dev/null +++ b/lms/djangoapps/instructor_task/config/waffle.py @@ -0,0 +1,35 @@ +""" +This module contains various configuration settings via +waffle switches for the instructor_task app. +""" +from __future__ import absolute_import + +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag, WaffleFlagNamespace + + +INSTRUCTOR_TASK_WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(name=u'instructor_task') + +# Course-specific flags +PROBLEM_GRADE_REPORT_VERIFIED_ONLY = u'problem_grade_report_verified_only' + + +def waffle_flags(): + """ + Returns the namespaced, cached, audited Waffle flags dictionary for Grades. + """ + return { + PROBLEM_GRADE_REPORT_VERIFIED_ONLY: CourseWaffleFlag( + waffle_namespace=INSTRUCTOR_TASK_WAFFLE_FLAG_NAMESPACE, + flag_name=PROBLEM_GRADE_REPORT_VERIFIED_ONLY, + flag_undefined_default=False, + ), + } + + +def problem_grade_report_verified_only(course_id): + """ + Returns True if problem grade reports should only + return rows for verified students in the given course, + False otherwise. + """ + return waffle_flags()[PROBLEM_GRADE_REPORT_VERIFIED_ONLY].is_enabled(course_id) diff --git a/lms/djangoapps/instructor_task/tasks_helper/grades.py b/lms/djangoapps/instructor_task/tasks_helper/grades.py index 64de7dd286..2f358e3218 100644 --- a/lms/djangoapps/instructor_task/tasks_helper/grades.py +++ b/lms/djangoapps/instructor_task/tasks_helper/grades.py @@ -24,6 +24,7 @@ from lms.djangoapps.courseware.courses import get_course_by_id from lms.djangoapps.courseware.user_state_client import DjangoXBlockUserStateClient from lms.djangoapps.instructor_analytics.basic import list_problem_responses from lms.djangoapps.instructor_analytics.csvs import format_dictlist +from lms.djangoapps.instructor_task.config.waffle import problem_grade_report_verified_only from lms.djangoapps.certificates.models import CertificateWhitelist, GeneratedCertificate, certificate_info_for_user from lms.djangoapps.grades.api import CourseGradeFactory from lms.djangoapps.grades.api import context as grades_context @@ -525,7 +526,12 @@ class ProblemGradeReport(object): start_date = datetime.now(UTC) status_interval = 100 task_id = _xmodule_instance_args.get('task_id') if _xmodule_instance_args is not None else None - enrolled_students = CourseEnrollment.objects.users_enrolled_in(course_id, include_inactive=True) + + enrolled_students = CourseEnrollment.objects.users_enrolled_in( + course_id, + include_inactive=True, + verified_only=problem_grade_report_verified_only(course_id), + ) task_progress = TaskProgress(action_name, enrolled_students.count(), start_time) # This struct encapsulates both the display names of each static item in the diff --git a/lms/djangoapps/instructor_task/tests/test_tasks_helper.py b/lms/djangoapps/instructor_task/tests/test_tasks_helper.py index 3c05f59766..a5b2106dc1 100644 --- a/lms/djangoapps/instructor_task/tests/test_tasks_helper.py +++ b/lms/djangoapps/instructor_task/tests/test_tasks_helper.py @@ -44,6 +44,7 @@ from lms.djangoapps.grades.subsection_grade import CreateSubsectionGrade from lms.djangoapps.grades.transformer import GradesTransformer from lms.djangoapps.instructor_analytics.basic import UNAVAILABLE, list_problem_responses from lms.djangoapps.instructor_task.tasks_helper.certs import generate_students_certificates +# from lms.djangoapps.instructor_task.config.waffle import problem_grade_report_verified_only from lms.djangoapps.instructor_task.tasks_helper.enrollments import ( upload_enrollment_report, upload_exec_summary_report, @@ -940,6 +941,28 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase): ))) ]) + @patch('lms.djangoapps.instructor_task.tasks_helper.runner._get_current_task') + def test_single_problem_verified_student_only(self, _get_current_task): + with patch( + 'lms.djangoapps.instructor_task.tasks_helper.grades.problem_grade_report_verified_only', + return_value=True, + ): + student_verified = self.create_student(u'user_verified', mode='verified') + 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']) + self.submit_student_answer(student_verified.username, u'Problem1', ['Option 1']) + result = ProblemGradeReport.generate(None, None, self.course.id, None, 'graded') + self.assertDictContainsSubset( + {'action_name': 'graded', 'attempted': 1, 'succeeded': 1, 'failed': 0}, result + ) + @patch('lms.djangoapps.instructor_task.tasks_helper.runner._get_current_task') @patch('lms.djangoapps.grades.course_grade_factory.CourseGradeFactory.iter') @ddt.data(u'Cannot grade student', '')