Skip to content

Commit

Permalink
feat: configurable roll-forward of flex grading (#32584)
Browse files Browse the repository at this point in the history
Add ability to roll-forward ORA flex peer grading feature. Where enabled
for an Organization or course, flex peer grading will be turned on at the
course level for new course runs and course reruns. Where disabled,
a new course or course rerun will preserve existing / default setting
value.
  • Loading branch information
nsprenkle authored Jul 7, 2023
1 parent 87c2e09 commit 452433a
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 1 deletion.
34 changes: 34 additions & 0 deletions cms/djangoapps/contentstore/tests/test_contentstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,36 @@ def test_create_course(self):
"""Test new course creation - happy path"""
self.assert_created_course()

@ddt.data(True, False)
@mock.patch(
'cms.djangoapps.contentstore.views.course.default_enable_flexible_peer_openassessments'
)
def test_create_course__default_enable_flexible_peer_openassessments(
self,
mock_toggle_state,
mock_default_enable_flexible_peer_openassessments
):
"""
Test that flex peer grading is forced on, when enabled
"""
# Given a new course run
test_course_data = {}
test_course_data.update(self.course_data)
course_key = _get_course_id(self.store, test_course_data)

# ... with org configured to / not to enable flex grading
mock_default_enable_flexible_peer_openassessments.return_value = mock_toggle_state

# When I create a new course
new_course_data = _create_course(self, course_key, test_course_data)

# Then the process completes successfully
new_course_key = CourseKey.from_string(new_course_data['course_key'])
new_course = self.store.get_course(new_course_key)

# ... and our setting got toggled appropriately on the course
self.assertEqual(new_course.force_on_flexible_peer_openassessments, mock_toggle_state)

@override_settings(DEFAULT_COURSE_LANGUAGE='hr')
def test_create_course_default_language(self):
"""Test new course creation and verify default language"""
Expand Down Expand Up @@ -2104,6 +2134,8 @@ def test_accessibility(self):
def _create_course(test, course_key, course_data):
"""
Creates a course via an AJAX request and verifies the URL returned in the response.
Returns the data of the POST response
"""
course_url = get_url('course_handler', course_key, 'course_key_string')
response = test.client.ajax_post(course_url, course_data)
Expand All @@ -2112,6 +2144,8 @@ def _create_course(test, course_key, course_data):
test.assertNotIn('ErrMsg', data)
test.assertEqual(data['url'], course_url)

return data


def _get_course_id(store, course_data):
"""Returns the course ID."""
Expand Down
52 changes: 52 additions & 0 deletions cms/djangoapps/contentstore/tests/test_course_create_rerun.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@


import datetime
from itertools import product
from unittest import mock

import ddt
Expand Down Expand Up @@ -317,3 +318,54 @@ def test_course_creation_without_permission_for_specific_organization(self):
'run': '2021_T1'
})
self.assertEqual(response.status_code, 403)

@ddt.data(*product([True, False], [True, False]))
@ddt.unpack
@mock.patch(
'cms.djangoapps.contentstore.views.course.default_enable_flexible_peer_openassessments'
)
def test_default_enable_flexible_peer_openassessments_on_rerun(
self,
mock_toggle_state,
mock_original_course_setting,
mock_default_enable_flexible_peer_openassessments
):
"""
Test that flex peer grading is forced on, when enabled
"""
# Given a valid course to rerun
add_organization({
'name': 'Test Flex Grading',
'short_name': self.source_course_key.org,
'description': 'Test roll-forward of flex grading setting',
})
source_course = self.store.get_course(self.source_course_key)
source_course.force_on_flexible_peer_openassessments = mock_original_course_setting
self.store.update_item(source_course, self.user.id)
mock_default_enable_flexible_peer_openassessments.return_value = mock_toggle_state

# When I create a new course
response = self.client.ajax_post(self.course_create_rerun_url, {
'source_course_key': str(self.source_course_key),
'org': self.source_course_key.org,
'course': self.source_course_key.course,
'run': 'copy',
'display_name': 'New, exciting course!',
})

# Then the process completes successfully
self.assertEqual(response.status_code, 200)

data = parse_json(response)
dest_course_key = CourseKey.from_string(data['destination_course_key'])
dest_course = self.store.get_course(dest_course_key)

# ... and our setting got enabled appropriately on our new course
if mock_toggle_state:
self.assertTrue(dest_course.force_on_flexible_peer_openassessments)
# ... or preserved if the default enable setting is not on
else:
self.assertEqual(
source_course.force_on_flexible_peer_openassessments,
dest_course.force_on_flexible_peer_openassessments
)
23 changes: 23 additions & 0 deletions cms/djangoapps/contentstore/toggles.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,3 +476,26 @@ def use_new_course_team_page(course_key):
Returns a boolean if new studio course team mfe is enabled
"""
return ENABLE_NEW_STUDIO_COURSE_TEAM_PAGE.is_enabled(course_key)


# .. toggle_name: contentstore.default_enable_flexible_peer_openassessments
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag turns on the force_on_flexible_peer_openassessments
# setting for course reruns or new courses, where enabled.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-06-27
# .. toggle_target_removal_date: 2024-01-27
# .. toggle_tickets: AU-1289
# .. toggle_warning:
DEFAULT_ENABLE_FLEXIBLE_PEER_OPENASSESSMENTS = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.default_enable_flexible_peer_openassessments', __name__)


def default_enable_flexible_peer_openassessments(course_key):
"""
Returns a boolean if ORA flexible peer grading should be toggled on for a
course rerun or new course. We expect this to be set at the organization
level to opt in/out of rolling forward this feature.
"""
return DEFAULT_ENABLE_FLEXIBLE_PEER_OPENASSESSMENTS.is_enabled(course_key)
13 changes: 12 additions & 1 deletion cms/djangoapps/contentstore/views/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,14 @@
from ..courseware_index import CoursewareSearchIndexer, SearchIndexingError
from ..tasks import rerun_course as rerun_course_task
from ..toggles import (
default_enable_flexible_peer_openassessments,
split_library_view_on_dashboard,
use_new_course_outline_page,
use_new_home_page,
use_new_updates_page,
use_new_advanced_settings_page,
use_new_grading_page,
use_new_schedule_details_page,
use_new_schedule_details_page
)
from ..utils import (
add_instructor,
Expand Down Expand Up @@ -993,6 +994,12 @@ def create_new_course(user, org, number, run, fields):
new_course = create_new_course_in_store(store_for_new_course, user, org, number, run, fields)
add_organization_course(org_data, new_course.id)
update_course_discussions_settings(new_course.id)

# Enable certain fields rolling forward, where configured
if default_enable_flexible_peer_openassessments(new_course.id):
new_course.force_on_flexible_peer_openassessments = True
modulestore().update_item(new_course, new_course.published_by)

return new_course


Expand Down Expand Up @@ -1056,6 +1063,10 @@ def rerun_course(user, source_course_key, org, number, run, fields, background=T
fields['enrollment_end'] = None
fields['video_upload_pipeline'] = {}

# Enable certain fields rolling forward, where configured
if default_enable_flexible_peer_openassessments(source_course_key):
fields['force_on_flexible_peer_openassessments'] = True

json_fields = json.dumps(fields, cls=EdxJSONEncoder)
args = [str(source_course_key), str(destination_course_key), user.id, json_fields]

Expand Down

0 comments on commit 452433a

Please sign in to comment.