Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions cms/djangoapps/contentstore/course_group_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,21 @@
from cms.djangoapps.contentstore.utils import reverse_usage_url
from common.djangoapps.util.db import MYSQL_MAX_INT, generate_int_id
from lms.lib.utils import get_parent_unit
# Re-exported for backward compatibility - other modules import these from here
from openedx.core.djangoapps.course_groups.constants import ( # pylint: disable=unused-import
COHORT_SCHEME,
CONTENT_GROUP_CONFIGURATION_DESCRIPTION,
CONTENT_GROUP_CONFIGURATION_NAME,
ENROLLMENT_SCHEME,
RANDOM_SCHEME,
)
from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_user_partition
from xmodule.partitions.partitions import MINIMUM_UNUSED_PARTITION_ID, ReadOnlyUserPartitionError, UserPartition # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.partitions.partitions_service import get_all_partitions_for_course # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.split_test_block import get_split_user_partitions # lint-amnesty, pylint: disable=wrong-import-order

MINIMUM_GROUP_ID = MINIMUM_UNUSED_PARTITION_ID

RANDOM_SCHEME = "random"
COHORT_SCHEME = "cohort"
ENROLLMENT_SCHEME = "enrollment_track"

CONTENT_GROUP_CONFIGURATION_DESCRIPTION = _(
'The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.'
)

CONTENT_GROUP_CONFIGURATION_NAME = _('Content Groups')

log = logging.getLogger(__name__)


Expand Down
11 changes: 0 additions & 11 deletions cms/djangoapps/contentstore/views/preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,17 +200,6 @@ def _prepare_runtime_for_preview(request, block):
# See the docstring of `DjangoXBlockUserService`.
deprecated_anonymous_user_id = anonymous_id_for_user(request.user, None)

# NOTE: As of Ulmo, these services only apply to the preview views. If you want a service to be present in all
# Studio ModuleStoreRuntimes, then add it to load_services_for_studio.
# HISTORICAL CONTEXT: Until Ulmo, the `block.runtime._services.update(service)` call below would
# actually update the services dictionary for all runtimes, as `_services` was aliased between them.
# This caused a grading bug, under certain conditions, so it was fixed
# in https://github.com/openedx/openedx-platform/pull/37825; now, every runtime gets a fresh,
# independent copy of `_services`. That's good, except that some Studio code had become dependent
# on the bugged behavior and thus expected the "preview" services below to be present in all Studio runtimes.
# We fixed the known instance of that bugged assumption here:
# https://github.com/openedx/openedx-platform/pull/37900.
# This comment is left here as a note for future devs investigating similar bugs.
services = {
"studio_user_permissions": StudioPermissionsService(request.user),
"i18n": XBlockI18nService,
Expand Down
8 changes: 8 additions & 0 deletions cms/envs/help_tokens.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,27 @@
[pages]
default = course_author:quickstarts/build_a_course.html
home = course_author:concepts/open_edx_platform/what_is_studio.html
develop_course = course_author:how-tos/set_up_course/create_new_course.html
outline = course_author:concepts/open_edx_platform/about_course_outline.html
unit = course_author:concepts/open_edx_platform/about_course_units.html
visibility = course_author:references/controlling_content_visibility.html
updates = course_author:concepts/communication/about_course_updates_handouts.html
pages = course_author:how-tos/course_development/manage_custom_page.html
files = course_author:references/course_development/files_page.html
textbooks = course_author:how-tos/course_development/manage_textbooks.html
schedule = course_author:how-tos/set_up_course/set_course_schedule.html
grading = course_author:how-tos/grading/set_grade_range.html
team_course = course_author:references/course_development/course_team_roles.html
team_library = course_author:how-tos/course_development/add_users_to_libraries.html
advanced = course_author:quickstarts/build_a_course.html
checklist = course_author:concepts/releasing-course/course_launch_checklist.html
import_library = course_author:how-tos/course_development/export_import_library.html#import-a-legacy-library
import_course = course_author:how-tos/releasing-course/import_course.html
export_library = course_author:how-tos/course_development/export_import_library.html#export-a-legacy-library
export_course = course_author:how-tos/releasing-course/export_course.html
welcome = course_author:index.html
login = course_author:index.html
register = course_author:index.html
content_libraries = course_author:concepts/instructional_design/libraries.html
content_groups = course_author:concepts/advanced_features/about_content_groups.html
enrollment_tracks = course_author:how-tos/student_management/manage_course_enrollments.html
Expand All @@ -26,6 +33,7 @@ container = course_author:references/course_development/parent_child_components.
video = course_author:references/course_development/guide_to_video.html
certificates = course_author:concepts/open_edx_platform/about_certificates.html
content_highlights = course_author:how-tos/course_development/manage_course_highlight_emails.html
image_accessibility = course_author:references/accessibility/accessibility_best_practices_checklist.html#use-best-practices-for-describing-images
social_sharing = course_author:how-tos/course_development/social_sharing.html

# below are the language directory names for the different locales
Expand Down
74 changes: 74 additions & 0 deletions lms/djangoapps/instructor/views/serializers_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from lms.djangoapps.discussion.django_comment_client.utils import has_forum_access
from lms.djangoapps.instructor import permissions
from lms.djangoapps.instructor.views.instructor_dashboard import get_analytics_dashboard_message
from openedx.core.djangoapps.course_groups.rest_api.serializers import GroupSerializer
from openedx.core.djangoapps.django_comment_common.models import FORUM_ROLE_ADMINISTRATOR
from xmodule.modulestore.django import modulestore

Expand Down Expand Up @@ -475,3 +476,76 @@ class ORASummarySerializer(serializers.Serializer):
waiting = serializers.IntegerField()
staff = serializers.IntegerField()
final_grade_received = serializers.IntegerField()


class ContentGroupConfigurationSerializer(serializers.Serializer):
"""
Serializer for a content group configuration (UserPartition with scheme='cohort').

Content groups enable course creators to assign different course content
to different learner cohorts.
"""

id = serializers.IntegerField(
help_text="Unique identifier for this content group configuration"
)
name = serializers.CharField(
max_length=255,
help_text="Human-readable name of the configuration"
)
scheme = serializers.CharField(
help_text="Partition scheme (always 'cohort' for content groups)"
)
description = serializers.CharField(
allow_blank=True,
help_text="Detailed description of how this group is used"
)
parameters = serializers.DictField(
help_text="Additional partition parameters (usually empty for cohort scheme)"
)
groups = GroupSerializer(
many=True,
help_text="List of groups (cohorts) in this configuration"
)
active = serializers.BooleanField(
help_text="Whether this configuration is active"
)
version = serializers.IntegerField(
help_text="Configuration version number (always 3 for current UserPartition format)"
)
is_read_only = serializers.BooleanField(
required=False,
default=False,
help_text="Whether this configuration is read-only (system-managed)"
)


class ContentGroupsListResponseSerializer(serializers.Serializer):
"""
Response serializer for listing all content groups.

Returns content group configurations along with context about whether
to show enrollment tracks and experiment groups.
"""

all_group_configurations = ContentGroupConfigurationSerializer(
many=True,
help_text="List of content group configurations (only scheme='cohort' partitions)"
)
should_show_enrollment_track = serializers.BooleanField(
help_text="Whether enrollment track groups should be displayed"
)
should_show_experiment_groups = serializers.BooleanField(
help_text="Whether experiment groups should be displayed"
)
context_course = serializers.JSONField(
required=False,
allow_null=True,
help_text="Course context object (null in API responses)"
)
group_configuration_url = serializers.CharField(
help_text="Base URL for accessing individual group configurations"
)
course_outline_url = serializers.CharField(
help_text="URL to the course outline page"
)
9 changes: 9 additions & 0 deletions lms/envs/help_tokens.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@
default = learner:index.html
instructor = course_author:index.html
course = learner:index.html
profile = learner:concepts/open_edx_platform/what_is_profile_page.html
dashboard = learner:concepts/open_edx_platform/what_is_course_dashboard.html
courseinfo = learner:SFD_start_course.html
progress = learner:SFD_check_progress.html
learneraccountsettings = learner:how-tos/update_course_specific_settings.html
learnerdashboard = learner:concepts/open_edx_platform/what_is_course_dashboard.html
programs = learner:OpenSFD_enrolling.html
bookmarks = learner:SFD_bookmarks.html
notes = learner:SFD_notes.html
wiki = learner:SFD_wiki.html
discussions = learner:sfd_discussions/index.html

cohortmanual = course_author:references/advanced_features/managing_cohort_assignment.html#implementing-a-manual-assignment-strategy
cohortautomatic = course_author:references/advanced_features/managing_cohort_assignment.html#implementing-an-automated-assignment-strategy

# below are the language directory names for the different locales
[locales]
default = en
Expand Down
13 changes: 13 additions & 0 deletions openedx/core/djangoapps/course_groups/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""
Constants for course groups.
"""
from django.utils.translation import gettext_lazy as _

COHORT_SCHEME = 'cohort'
RANDOM_SCHEME = 'random'
ENROLLMENT_SCHEME = 'enrollment_track'

CONTENT_GROUP_CONFIGURATION_NAME = _('Content Groups')
CONTENT_GROUP_CONFIGURATION_DESCRIPTION = _(
'Use this group configuration to control access to content.'
)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
swagger: '2.0'
info:
title: Content Groups API v2
version: 2.0.0
description: |
REST API for managing content group configurations.

Content groups allow course authors to restrict access to specific
course content based on cohort membership.

host: courses.example.com
basePath: /
schemes:
- https

securityDefinitions:
JWTAuth:
type: apiKey
in: header
name: Authorization
description: JWT token authentication.

security:
- JWTAuth: []

tags:
- name: Content Groups
description: Content group configuration management

parameters:
CourseId:
name: course_id
in: path
required: true
type: string
description: The course key (e.g., course-v1:org+course+run)
ConfigurationId:
name: configuration_id
in: path
required: true
type: integer
description: The ID of the content group configuration

paths:
/api/cohorts/v2/courses/{course_id}/group_configurations:
get:
tags:
- Content Groups
summary: List content group configurations
description: |
Returns all content group configurations (scheme='cohort') for a course.
If no content group exists, an empty one is automatically created.
operationId: listGroupConfigurations
produces:
- application/json
parameters:
- $ref: '#/parameters/CourseId'
responses:
200:
description: Content groups retrieved successfully
schema:
$ref: '#/definitions/ContentGroupsListResponse'
400:
description: Invalid course key
401:
description: Authentication required
403:
description: User lacks instructor permission
404:
description: Course not found

/api/cohorts/v2/courses/{course_id}/group_configurations/{configuration_id}:
get:
tags:
- Content Groups
summary: Get content group configuration details
description: |
Retrieve a specific content group configuration by ID.
Only returns configurations with scheme='cohort'.
operationId: getGroupConfiguration
produces:
- application/json
parameters:
- $ref: '#/parameters/CourseId'
- $ref: '#/parameters/ConfigurationId'
responses:
200:
description: Configuration retrieved successfully
schema:
$ref: '#/definitions/ContentGroupConfiguration'
400:
description: Invalid course key
401:
description: Authentication required
403:
description: User lacks instructor permission
404:
description: Configuration not found

definitions:
Group:
type: object
properties:
id:
type: integer
description: Unique identifier for the group
name:
type: string
description: Display name of the group
version:
type: integer
description: Version number of the group
usage:
type: array
items:
type: object
description: List of content blocks using this group

ContentGroupConfiguration:
type: object
properties:
id:
type: integer
description: Unique identifier for the configuration
name:
type: string
description: Display name (typically "Content Groups")
scheme:
type: string
enum: [cohort]
description: Partition scheme type
description:
type: string
description: Human-readable description
parameters:
type: object
description: Additional configuration parameters
groups:
type: array
items:
$ref: '#/definitions/Group'
description: List of groups in this configuration
active:
type: boolean
description: Whether this configuration is active
version:
type: integer
description: Version number of the configuration
read_only:
type: boolean
description: Whether this configuration is system-managed

ContentGroupsListResponse:
type: object
properties:
all_group_configurations:
type: array
items:
$ref: '#/definitions/ContentGroupConfiguration'
description: List of content group configurations
should_show_enrollment_track:
type: boolean
description: Whether enrollment track groups should be displayed
should_show_experiment_groups:
type: boolean
description: Whether experiment groups should be displayed
group_configuration_url:
type: string
description: Base URL for accessing individual configurations
course_outline_url:
type: string
description: URL to the course outline
Loading
Loading