This PR changes the default behaviour of the discussions experience by making the previous "unit-level visibility" the default mechanism for configuring discussions. Prior to this PR, under the new discussions configuration experience, all units would automatically get assigned a discussion topic and have discussions enabled for them (other than units in graded or exam subsections). However, if authors wanted they could enabled a custom visibility mode which would allow toggling discussions on or off on a per-unit level. This PR makes this custom visibility mode the standard behaviour (and eventually, only behaviour) and enables discussion for all units by default. This replicates the behaviour that already existed, however, now gives authors control over disabling discussions for individual units by default. It also removes the ability to disable discussions for all units (while still keeping course-wide discussions) enabled.
112 lines
4.7 KiB
Python
112 lines
4.7 KiB
Python
"""
|
|
Tasks for discussions
|
|
"""
|
|
import logging
|
|
|
|
from celery import shared_task
|
|
from edx_django_utils.monitoring import set_code_owner_attribute
|
|
from opaque_keys.edx.keys import CourseKey
|
|
from openedx_events.learning.data import CourseDiscussionConfigurationData, DiscussionTopicContext
|
|
from openedx_events.learning.signals import COURSE_DISCUSSIONS_CHANGED
|
|
from xmodule.modulestore import ModuleStoreEnum
|
|
from xmodule.modulestore.django import modulestore
|
|
|
|
from .models import DiscussionsConfiguration
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
@shared_task
|
|
@set_code_owner_attribute
|
|
def update_discussions_settings_from_course_task(course_key_str: str):
|
|
"""
|
|
Celery task that creates or updates discussions settings for a course.
|
|
|
|
Args:
|
|
course_key_str (str): course key string
|
|
"""
|
|
course_key = CourseKey.from_string(course_key_str)
|
|
config_data = update_discussions_settings_from_course(course_key)
|
|
COURSE_DISCUSSIONS_CHANGED.send_event(configuration=config_data)
|
|
|
|
|
|
def update_discussions_settings_from_course(course_key: CourseKey) -> CourseDiscussionConfigurationData:
|
|
"""
|
|
When there are changes to a course, construct a new data structure containing all the context needed to update the
|
|
course's discussion settings in the database.
|
|
|
|
Args:
|
|
course_key (CourseKey): The course that was recently updated.
|
|
|
|
Returns:
|
|
(CourseDiscussionConfigurationData): structured discusion configuration data.
|
|
"""
|
|
log.info(f"Updating discussion settings for course: {course_key}")
|
|
store = modulestore()
|
|
|
|
discussions_config = DiscussionsConfiguration.get(course_key)
|
|
supports_in_context = discussions_config.supports_in_context_discussions()
|
|
provider_type = discussions_config.provider_type
|
|
|
|
def iter_discussable_units():
|
|
sections = store.get_items(course_key, qualifiers={'category': 'sequential'})
|
|
# Start at 99 so that the initial increment starts it at 100.
|
|
# This leaves the first 100 slots for the course wide topics, which is only a concern if there are more
|
|
# than that many.
|
|
idx = 99
|
|
for section in sections:
|
|
for unit in section.get_children():
|
|
# Increment index even for skipped units so that the index is more stable and won't change
|
|
# if settings change, only if a unit is added or removed.
|
|
idx += 1
|
|
# If unit-level visibility is enabled and the unit doesn't have discussion enabled, skip it.
|
|
if unit_level_visibility and not getattr(unit, 'discussion_enabled', False):
|
|
continue
|
|
# If the unit is in a graded section and graded sections aren't enabled skip it.
|
|
if section.graded and not enable_graded_units:
|
|
continue
|
|
# If the unit is an exam, skip it.
|
|
if section.is_practice_exam or section.is_proctored_enabled or section.is_time_limited:
|
|
continue
|
|
yield DiscussionTopicContext(
|
|
usage_key=unit.location,
|
|
title=unit.display_name,
|
|
group_id=None,
|
|
ordering=idx,
|
|
)
|
|
|
|
with store.branch_setting(ModuleStoreEnum.Branch.published_only, course_key):
|
|
course = store.get_course(course_key)
|
|
provider = course.discussions_settings.get('provider', provider_type)
|
|
enable_in_context = course.discussions_settings.get('enable_in_context', True)
|
|
provider_config = course.discussions_settings.get(provider, {})
|
|
unit_level_visibility = course.discussions_settings.get('unit_level_visibility', True)
|
|
enable_graded_units = course.discussions_settings.get('enable_graded_units', False)
|
|
contexts = []
|
|
if supports_in_context:
|
|
sorted_topics = sorted(
|
|
course.discussion_topics.items(),
|
|
key=lambda item: item[1].get("sort_key", item[0])
|
|
)
|
|
contexts = [
|
|
DiscussionTopicContext(
|
|
title=topic_name,
|
|
external_id=topic_config.get('id', None),
|
|
ordering=idx,
|
|
)
|
|
for idx, (topic_name, topic_config) in enumerate(sorted_topics)
|
|
if topic_config.get('id', None)
|
|
]
|
|
if enable_in_context:
|
|
contexts.extend(list(iter_discussable_units()))
|
|
config_data = CourseDiscussionConfigurationData(
|
|
course_key=course_key,
|
|
enable_in_context=enable_in_context,
|
|
enable_graded_units=enable_graded_units,
|
|
unit_level_visibility=unit_level_visibility,
|
|
provider_type=provider,
|
|
plugin_configuration=provider_config,
|
|
contexts=contexts,
|
|
)
|
|
return config_data
|