Technical Exploration Questions & Answers
This document compiles a series of technical questions and answers that emerged during a research and exploration phase focused on the current user grouping mechanisms in Open edX: Cohorts, Teams, and Enrollment Track groups. The goal of this exploration was to gain an in-depth understanding of the behavior, structure, and limitations of each mechanism to assess the feasibility of unifying them into a more flexible, centralized grouping model. The questions are organized by key topics and serve as a foundation for identifying requirements, risks, and opportunities for the transition to a new model.
1. Current Behavior of Existing Mechanisms
Assignment and Membership
How and under what rules are users assigned to groups in each mechanism (manual assignment, automatic, conditions)?
Cohorts
Automatic (Random):
If automatic cohorts are configured, users are randomly assigned to one of them the first time they access course content.
If there are no automatic cohorts, a default cohort ("Default Cohort") is created, and users are assigned there.
If multiple automatic cohorts exist, the user is randomly assigned to one.
Manual:
Instructors can assign users to specific cohorts from the instructor dashboard in the LMS or by uploading a CSV file.
Manual assignment can be done for any type of cohort (automatic or manual).
It is possible to assign users to a cohort before they enroll in the course, or even before they have a registered account on the platform (using the
UnregisteredLearnerCohortAssignmentmodel).
Teams
Manual:
All teams within a team set are manually managed by instructors or students.
Instructors can assign students to specific teams via a CSV upload.
If the team to which an instructor assigns a student does not exist, the team is created, and the student is assigned.
Types of Team-Sets/Groups/Topics:
Open (open): Students can join, leave, or create teams from the LMS.
Public Managed (public_managed): Only instructors manage teams; teams are visible to all.
Private Managed (private_managed): Only instructors manage teams; team content is private; students can only see their team.
Open Managed (open_managed): Only instructors can create teams; students can join/leave teams freely (requires MFE config enabled).
Enrollment Track
Automatic:
When enrolling, students select a "course mode" (audit, verified, professional, etc.)
Based on their course mode, they are automatically assigned to the corresponding enrollment track group.
Enrollment can be updated during the course (re-enroll or enrollment mode update via support tools).
Can a user belong to multiple groups within the same type?
Group Type | Multiple memberships allowed? |
|---|---|
Cohorts | No (only one cohort per course) If a user is assigned to another, it's automatically removed from the previous one. This is controlled by the |
Teams | No, within the same team set ✅ Yes, across different team sets A user can join only one Team within the same Team-Set, but can belong to several Teams if each one is in a different Team-Set. |
Enrollment Track | No (only one enrollment track per course) It is determined by the user's |
How is membership managed when a user changes groups?
Group Type | Change Process | System Behavior |
|---|---|---|
Cohorts |
|
|
Teams |
|
|
Enrollment Track |
|
|
What restrictions exist when changing groups?
Group Type | Restrictions |
|---|---|
Cohorts | Exclusivity: A user can only belong to one cohort per course.
|
Teams | Uniqueness per Team-Set: A user can only belong to one team per team-set. Depending on the type of team set (e.g.,
Some teams might be full, preventing the change if the new team has already reached its |
Enrollment Track | Not editable through the LMS once assigned.
|
Roles and Permissions
What permissions do different roles (instructors, staff, students) have over groups, and through which interfaces (LMS, Studio)?
Cohorts
Role | Allowed Actions | Available Interfaces |
|---|---|---|
Instructor / Staff |
| LMS (Instructor Dashboard), Studio (Course Outline Restrictions and Discussion Division) |
Student |
| Passive interaction only (LMS) |
Teams
Role | Allowed Actions | Available Interfaces |
|---|---|---|
Instructor / Staff |
|
|
Student | Depending on the type of Team-Set:
| LMS (Team tab, if enabled) |
Enrollment Tracks
Role | Allowed Actions | Available Interfaces |
|---|---|---|
Instructor / Staff |
| Studio (Group Configurations) |
Student |
| Passive interaction only (LMS) |
Technical Structure
Where is the group assignment information stored in the database?
Cohorts
Main group model:
CourseUserGroupUser assignment:
CohortMembershipAdditional group information:
CourseCohortCourse-level configuration:
CourseCohortsSettingsPreliminary assignment (for unregistered users):
UnregisteredLearnerCohortAssignmentRelation to partitions:
CourseUserGroupPartitionGroup
Technical Notes:
All these models are located in:
openedx/core/djangoapps/course_groups/models.pyThe relationship between
CourseUserGroupandCohortMembershipensures uniqueness per course.Partitions allow linking a cohort to specific course content (using Content Groups)
Teams
General configuration and Team-Set:
Not stored in a model, but directly in theCourseBlock(fieldteams_configurationin JSON format).Group model:
CourseTeamUser assignment:
CourseTeamMembership
Technical Notes:
Models are located in:
lms/djangoapps/teams/models.pyEach
CourseTeambelongs to a Team-Set, defined in the course’s advanced settings.CourseTeamMembershipmanages the relationship between users and teams.
Enrollment Track
There is no explicit "group" model.
Group assignment is based on the user's enrollment mode (
CourseEnrollment.mode).
Technical Notes:
Dynamic partitioning uses the
CourseModemodel to generate groups at runtime.EnrollmentTrackUserPartitionandEnrollmentTrackPartitionSchemadefine the logic in the code, but grouping is not explicitly persisted in the database.
What models and fields are used by each mechanism?
Cohorts
Main Models:
CourseUserGroup
id: Group IDname: Group name (unique per course)course_id: The Course the group belongs togroup_type: Currently only "cohort"
CohortMembership
user: Assigned usercourse_user_group: Associated groupcourse_id: Course (may seem redundant, but useful for validation)
CourseCohort
course_user_group: ForeignKey to CourseUserGroupassignment_type: "random" or "manual"
CourseCohortsSettings
course_id: Course ID.is_cohorted: Boolean that enables/disables cohorts in the course
UnregisteredLearnerCohortAssignment
email: Email of unregistered user.cohort: Cohort assigned to the user.
File: openedx/core/djangoapps/course_groups/models.py
Teams
Course Configuration (not a Django model):
teams_configuration (JSON within CourseBlock)
enabled: Whether Teams is enabledmax_team_sizeteam_sets: List of dictionaries with:idnamedescriptionmax_team_sizetype: "open", "public_managed", "private_managed" or "open_managed"user_partition_id
Main Models:
CourseTeam
team_id: Unique IDname,description,country,languagetopic_id: ID of the Team Set it belongs todiscussion_topic_idcourse_idorganization_protected
CourseTeamMembership
user: Assigned userteam: Corresponding teamdate_joined,last_activity_at
File: lms/djangoapps/teams/models.py
Enrollment Track
No dedicated group models. Uses:
CourseEnrollment
usercourse_idmode: "audit", "verified", etc.
Other related models:
CourseMode: Describes available modes for the course
Dynamically generated groups in code using:
EnrollmentTrackUserPartitionEnrollmentTrackPartitionSchema
Files: Logic distributed in openedx/core/lib/partition and openedx/core/djangoapps/course_modes/models.py
APIs for Managing Groups
Cohorts
Legacy API
Base URL: /courses/<course_id>/cohorts/
Available Methods:
Create, list, and update Cohorts
Assign and remove users from Cohorts
Get and update Cohort settings
Key Endpoints:
GET /courses/<course_id>/cohorts/: Lists cohorts in a coursePOST /courses/<course_id>/cohorts/: Creates a new cohortPUT or PATCH /courses/<course_id>/cohorts/<id>/: Updates a cohortPOST /courses/<course_id>/cohorts/<id>/add/: Assigns a user to a cohortDELETE /courses/<course_id>/cohorts/<id>/delete/: Removes a user from a cohortGET /courses/<course_id>/cohorts/settings/: Gets cohort settings for a coursePATCH /courses/<course_id>/cohorts/settings/: Updates cohort settings for a course
Note: There is a GET endpoint to obtain cohort members, but the URL conflicts with /courses/<course_id>/cohorts/<id>
Code Location: openedx/core/djangoapps/course_groups/views.py
DRF API
Base URL: /api/cohorts/v1/
Available Methods:
Create, list, and update Cohorts
Assign, list, and remove users from Cohorts
Get and update Cohort settings
Key Endpoints:
GET /api/cohorts/v1/courses/<course_id>/cohorts/: Lists cohorts in a coursePOST /api/cohorts/v1/courses/<course_id>/cohorts/: Creates a new cohortPATCH /api/courses/<course_id>/cohorts/<id>/: Updates a cohortGET /api/cohorts/v1/courses/<course_id>/cohorts/<id>/: Gets a specific cohortGET /api/cohorts/v1/courses/<course_id>/cohorts/<id>/users/: Lists users in a cohortPOST /api/cohorts/v1/courses/<course_id>/cohorts/<id>/users/<username>/: Assigns a user to a cohortDELETE /api/cohorts/v1/courses/<course_id>/cohorts/<id>/users/<username>/: Removes a user from a cohortPOST /api/cohorts/v1/courses/<course_id>/cohorts/<id>/users/: Assigns multiple users to a cohortPOST /api/cohorts/v1/courses/<course_id>/users/: Assigns users to a cohort using a CSV fileGET /api/cohorts/v1/settings/<course_id>/: Gets cohort settings for a coursePATCH /api/cohorts/v1/settings/<course_id>/: Updates cohort settings for a course
Code Location: openedx/core/djangoapps/course_groups/views.py
API Documentation: /api-docs/#/cohorts
Teams
REST API (v0)
Base URL: /api/team/v0/
Available Methods:
List team sets in a course
List teams in a course or for a user
Create, list, update, or delete teams
Assign, list, or remove team members
Generate or upload CSV files with team memberships
Key Endpoints:
GET /api/team/v0/topics/?course_id=<course_id>: Lists team-sets in a courseGET /api/team/v0/topics/<topic_id>,<course_id>: Gets a specific team-setGET /api/team/v0/teams/?course_id=<course_id>: Lists teams in a coursePOST /api/team/v0/teams/: Creates a teamGET /api/team/v0/teams/<team_id>: Gets a specific teamPATCH /api/team/v0/teams/<team_id>: Updates a teamDELETE /api/team/v0/teams/<team_id>: Deletes a teamGET /api/team/v0/team_membership/?team_id=<team_id>: Lists members of a teamGET /api/team/v0/team_membership/?username=<username>: Lists teams a user belongs to across the platformPOST /api/team/v0/team_membership/: Assigns a user to a team