refactor: new logic for when to enable Gradebook bulk management (#28080)

Formerly, bulk management was enabled by sending feature feature
toggle to Gradebook which also checked for the presence of a 
master's track to enable the feature. This default enables for all
courses with a master's track and also allows selective enabling 
for courses with the feature flag.
This commit is contained in:
Nathan Sprenkle
2021-08-02 11:06:08 -04:00
committed by GitHub
parent 5fa4fcea5e
commit 121afd0c5f
4 changed files with 54 additions and 9 deletions

View File

@@ -16,7 +16,7 @@ from common.djangoapps.track.event_transaction_utils import create_new_event_tra
from lms.djangoapps.grades import constants, context, course_data, events from lms.djangoapps.grades import constants, context, course_data, events
# Grades APIs that should NOT belong within the Grades subsystem # Grades APIs that should NOT belong within the Grades subsystem
# TODO move Gradebook to be an external feature outside of core Grades # TODO move Gradebook to be an external feature outside of core Grades
from lms.djangoapps.grades.config.waffle import gradebook_can_see_bulk_management, is_writable_gradebook_enabled from lms.djangoapps.grades.config.waffle import gradebook_bulk_management_enabled, is_writable_gradebook_enabled
# Public Grades Factories # Public Grades Factories
from lms.djangoapps.grades.course_grade_factory import CourseGradeFactory from lms.djangoapps.grades.course_grade_factory import CourseGradeFactory
from lms.djangoapps.grades.models_api import * from lms.djangoapps.grades.models_api import *

View File

@@ -150,10 +150,8 @@ def is_writable_gradebook_enabled(course_key):
return waffle_flags()[WRITABLE_GRADEBOOK].is_enabled(course_key) return waffle_flags()[WRITABLE_GRADEBOOK].is_enabled(course_key)
def gradebook_can_see_bulk_management(course_key): def gradebook_bulk_management_enabled(course_key):
""" """
Returns whether bulk management features should be visible for the given course. Returns whether bulk management features should be specially enabled for a given course.
(provided that course contains a masters track, as of this writing)
""" """
return waffle_flags()[BULK_MANAGEMENT].is_enabled(course_key) return waffle_flags()[BULK_MANAGEMENT].is_enabled(course_key)

View File

@@ -21,7 +21,7 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from common.djangoapps.student.auth import has_course_author_access from common.djangoapps.student.auth import has_course_author_access
from common.djangoapps.student.models import CourseEnrollment, CourseAccessRole from common.djangoapps.student.models import CourseAccessRole, CourseEnrollment, CourseMode
from common.djangoapps.student.roles import BulkRoleCache from common.djangoapps.student.roles import BulkRoleCache
from common.djangoapps.track.event_transaction_utils import ( from common.djangoapps.track.event_transaction_utils import (
create_new_event_transaction_id, create_new_event_transaction_id,
@@ -35,7 +35,7 @@ from lms.djangoapps.grades.api import CourseGradeFactory, clear_prefetched_cours
from lms.djangoapps.grades.api import constants as grades_constants from lms.djangoapps.grades.api import constants as grades_constants
from lms.djangoapps.grades.api import context as grades_context from lms.djangoapps.grades.api import context as grades_context
from lms.djangoapps.grades.api import events as grades_events from lms.djangoapps.grades.api import events as grades_events
from lms.djangoapps.grades.api import gradebook_can_see_bulk_management as can_see_bulk_management from lms.djangoapps.grades.api import gradebook_bulk_management_enabled
from lms.djangoapps.grades.api import is_writable_gradebook_enabled, prefetch_course_and_subsection_grades from lms.djangoapps.grades.api import is_writable_gradebook_enabled, prefetch_course_and_subsection_grades
from lms.djangoapps.grades.course_data import CourseData from lms.djangoapps.grades.course_data import CourseData
from lms.djangoapps.grades.grade_utils import are_grades_frozen from lms.djangoapps.grades.grade_utils import are_grades_frozen
@@ -56,6 +56,7 @@ from lms.djangoapps.grades.subsection_grade_factory import SubsectionGradeFactor
from lms.djangoapps.grades.tasks import recalculate_subsection_grade_v3 from lms.djangoapps.grades.tasks import recalculate_subsection_grade_v3
from lms.djangoapps.program_enrollments.api import get_external_key_by_user_and_course from lms.djangoapps.program_enrollments.api import get_external_key_by_user_and_course
from openedx.core.djangoapps.course_groups import cohorts from openedx.core.djangoapps.course_groups import cohorts
from openedx.core.djangoapps.enrollments.api import get_course_enrollment_details
from openedx.core.djangoapps.util.forms import to_bool from openedx.core.djangoapps.util.forms import to_bool
from openedx.core.lib.api.view_utils import ( from openedx.core.lib.api.view_utils import (
DeveloperErrorViewMixin, DeveloperErrorViewMixin,
@@ -281,10 +282,19 @@ class CourseGradingView(BaseCourseView):
'assignment_types': self._get_assignment_types(course), 'assignment_types': self._get_assignment_types(course),
'subsections': self._get_subsections(course, graded_only), 'subsections': self._get_subsections(course, graded_only),
'grades_frozen': are_grades_frozen(course_key), 'grades_frozen': are_grades_frozen(course_key),
'can_see_bulk_management': can_see_bulk_management(course_key), 'can_see_bulk_management': self.can_see_bulk_management(course_key),
} }
return Response(results) return Response(results)
def can_see_bulk_management(self, course_key):
"""
Whether or not to show bulk management for this course. Currently, if a course has a
master's track or is enabled with the grades.bulk_management course waffle flag.
"""
course_modes = get_course_enrollment_details(str(course_key), include_expired=True).get('course_modes', [])
course_has_masters_track = any((course_mode['slug'] == CourseMode.MASTERS for course_mode in course_modes))
return course_has_masters_track or gradebook_bulk_management_enabled(course_key)
def _get_assignment_types(self, course): def _get_assignment_types(self, course):
""" """
Helper function that returns a serialized dict of assignment types Helper function that returns a serialized dict of assignment types

View File

@@ -32,7 +32,7 @@ from common.djangoapps.student.tests.factories import InstructorFactory
from common.djangoapps.student.tests.factories import StaffFactory from common.djangoapps.student.tests.factories import StaffFactory
from lms.djangoapps.certificates.data import CertificateStatuses from lms.djangoapps.certificates.data import CertificateStatuses
from lms.djangoapps.certificates.models import GeneratedCertificate from lms.djangoapps.certificates.models import GeneratedCertificate
from lms.djangoapps.grades.config.waffle import WRITABLE_GRADEBOOK, waffle_flags from lms.djangoapps.grades.config.waffle import BULK_MANAGEMENT, WRITABLE_GRADEBOOK, waffle_flags
from lms.djangoapps.grades.constants import GradeOverrideFeatureEnum from lms.djangoapps.grades.constants import GradeOverrideFeatureEnum
from lms.djangoapps.grades.course_data import CourseData from lms.djangoapps.grades.course_data import CourseData
from lms.djangoapps.grades.course_grade import CourseGrade from lms.djangoapps.grades.course_grade import CourseGrade
@@ -245,6 +245,43 @@ class CourseGradingViewTest(SharedModuleStoreTestCase, APITestCase):
expected_data['grades_frozen'] = True expected_data['grades_frozen'] = True
assert expected_data == resp.data assert expected_data == resp.data
@patch('lms.djangoapps.grades.rest_api.v1.gradebook_views.get_course_enrollment_details')
def test_can_see_bulk_management_non_masters(self, mock_course_enrollment_details):
# Given a course without a master's track
mock_course_enrollment_details.return_value = {'course_modes': [{'slug': 'not-masters'}]}
# When getting course grading view
self.client.login(username=self.staff.username, password=self.password)
resp = self.client.get(self.get_url(self.course_key))
# Course staff should not be shown bulk management controls
assert resp.status_code == status.HTTP_200_OK
assert resp.data['can_see_bulk_management'] is False
@patch('lms.djangoapps.grades.rest_api.v1.gradebook_views.get_course_enrollment_details')
def test_can_see_bulk_management_masters(self, mock_course_enrollment_details):
# Given a course with a master's track
mock_course_enrollment_details.return_value = {'course_modes': [{'slug': 'not-masters'}, {'slug': 'masters'}]}
# When getting course grading view
self.client.login(username=self.staff.username, password=self.password)
resp = self.client.get(self.get_url(self.course_key))
# Course staff should be shown bulk management controls (default on for master's track courses)
assert resp.status_code == status.HTTP_200_OK
assert resp.data['can_see_bulk_management'] is True
@override_waffle_flag(waffle_flags()[BULK_MANAGEMENT], active=True)
def test_can_see_bulk_management_force_enabled(self):
# Given a course without (or with) a master's track where bulk management is enabled with the config flag
# When getting course grading view
self.client.login(username=self.staff.username, password=self.password)
resp = self.client.get(self.get_url(self.course_key))
# # Course staff should be able to see bulk management
assert resp.status_code == status.HTTP_200_OK
assert resp.data['can_see_bulk_management'] is True
class GradebookViewTestBase(GradeViewTestMixin, APITestCase): class GradebookViewTestBase(GradeViewTestMixin, APITestCase):
""" """