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:
@@ -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 *
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user