Implements the connection from the teams feature to the content groups feature. This implementation uses the dynamic partition generator extension point to associate content groups with the users that belong to a Team. This implementation was heavily inspired by the enrollment tracks dynamic partitions.
184 lines
6.7 KiB
Python
184 lines
6.7 KiB
Python
"""
|
|
This is a service-like API that assigns tracks which groups users are in for various
|
|
user partitions. It uses the user_service key/value store provided by the LMS runtime to
|
|
persist the assignments.
|
|
"""
|
|
import logging
|
|
from typing import Dict
|
|
|
|
from django.conf import settings
|
|
from django.contrib.auth import get_user_model
|
|
from opaque_keys.edx.keys import CourseKey
|
|
from openedx.core.lib.cache_utils import request_cached
|
|
from openedx.core.lib.dynamic_partitions_generators import DynamicPartitionGeneratorsPluginManager
|
|
|
|
from xmodule.modulestore.django import modulestore
|
|
from xmodule.partitions.partitions import get_partition_from_id
|
|
from .partitions import Group
|
|
|
|
User = get_user_model()
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
FEATURES = getattr(settings, 'FEATURES', {})
|
|
|
|
|
|
@request_cached()
|
|
def get_all_partitions_for_course(course, active_only=False):
|
|
"""
|
|
A method that returns all `UserPartitions` associated with a course, as a List.
|
|
This will include the ones defined in course.user_partitions, but it may also
|
|
include dynamically included partitions (such as the `EnrollmentTrackUserPartition`).
|
|
|
|
Args:
|
|
course: the course for which user partitions should be returned.
|
|
active_only: if `True`, only partitions with `active` set to True will be returned.
|
|
|
|
Returns:
|
|
A List of UserPartitions associated with the course.
|
|
"""
|
|
all_partitions = course.user_partitions + _get_dynamic_partitions(course)
|
|
if active_only:
|
|
all_partitions = [partition for partition in all_partitions if partition.active]
|
|
return all_partitions
|
|
|
|
|
|
def get_user_partition_groups(course_key: CourseKey, user_partitions: list, user: User,
|
|
partition_dict_key: str = 'name') -> Dict[str, Group]:
|
|
"""
|
|
Collect group ID for each partition in this course for this user.
|
|
Arguments:
|
|
course_key (CourseKey)
|
|
user_partitions (list[UserPartition])
|
|
user (User)
|
|
partition_dict_key - i.e. 'id', 'name' depending on which partition attribute you want as a key.
|
|
Returns:
|
|
dict[partition_dict_key: Group]: Mapping from user partitions to the group to
|
|
which the user belongs in each partition. If the user isn't
|
|
in a group for a particular partition, then that partition's
|
|
ID will not be in the dict.
|
|
"""
|
|
|
|
partition_groups = {}
|
|
for partition in user_partitions:
|
|
group = partition.scheme.get_group_for_user(
|
|
course_key,
|
|
user,
|
|
partition,
|
|
)
|
|
|
|
if group is not None:
|
|
partition_groups[getattr(partition, partition_dict_key)] = group
|
|
return partition_groups
|
|
|
|
|
|
def _get_dynamic_partitions(course):
|
|
"""
|
|
Return the dynamic user partitions for this course.
|
|
If none exists, returns an empty array.
|
|
"""
|
|
dynamic_partition_generators = DynamicPartitionGeneratorsPluginManager.get_available_plugins().values()
|
|
generated_partitions = []
|
|
for generator in dynamic_partition_generators:
|
|
generated_partition = generator(course)
|
|
if generated_partition:
|
|
# If the generator returns a list of partitions, add them all to the list.
|
|
# Otherwise, just add the single partition. This is needed for cases where
|
|
# a single generator can return multiple partitions, such as the TeamUserPartition.
|
|
if isinstance(generated_partition, list):
|
|
generated_partitions.extend(generated_partition)
|
|
else:
|
|
generated_partitions.append(generated_partition)
|
|
|
|
return generated_partitions
|
|
|
|
|
|
class PartitionService:
|
|
"""
|
|
This is an XBlock service that returns information about the user partitions associated
|
|
with a given course.
|
|
"""
|
|
|
|
def __init__(self, course_id, cache=None, course=None):
|
|
self._course_id = course_id
|
|
self._cache = cache
|
|
self.course = course
|
|
|
|
def get_course(self):
|
|
"""
|
|
Return the course instance associated with this PartitionService.
|
|
This default implementation looks up the course from the modulestore.
|
|
"""
|
|
return self.course or modulestore().get_course(self._course_id)
|
|
|
|
@property
|
|
def course_partitions(self):
|
|
"""
|
|
Return the set of partitions assigned to self._course_id (both those set directly on the course
|
|
through course.user_partitions, and any dynamic partitions that exist). Note: this returns
|
|
both active and inactive partitions.
|
|
"""
|
|
if course := self.get_course():
|
|
return get_all_partitions_for_course(course)
|
|
return []
|
|
|
|
def get_user_group_id_for_partition(self, user, user_partition_id):
|
|
"""
|
|
If the user is already assigned to a group in user_partition_id, return the
|
|
group_id.
|
|
|
|
If not, assign them to one of the groups, persist that decision, and
|
|
return the group_id.
|
|
|
|
Args:
|
|
user_partition_id -- an id of a partition that's hopefully in the
|
|
runtime.user_partitions list.
|
|
|
|
Returns:
|
|
The id of one of the groups in the specified user_partition_id (as a string).
|
|
|
|
Raises:
|
|
ValueError if the user_partition_id isn't found.
|
|
"""
|
|
cache_key = "PartitionService.ugidfp.{}.{}.{}".format(
|
|
user.id, self._course_id, user_partition_id
|
|
)
|
|
|
|
if self._cache and (cache_key in self._cache):
|
|
return self._cache[cache_key]
|
|
|
|
user_partition = self.get_user_partition(user_partition_id)
|
|
if user_partition is None:
|
|
raise ValueError(
|
|
"Configuration problem! No user_partition with id {} "
|
|
"in course {}".format(user_partition_id, self._course_id)
|
|
)
|
|
|
|
group = self.get_group(user, user_partition)
|
|
group_id = group.id if group else None
|
|
|
|
if self._cache is not None:
|
|
self._cache[cache_key] = group_id
|
|
|
|
return group_id
|
|
|
|
def get_user_partition(self, user_partition_id):
|
|
"""
|
|
Look for a user partition with a matching id in the course's partitions.
|
|
Note that this method can return an inactive user partition.
|
|
|
|
Returns:
|
|
A UserPartition, or None if not found.
|
|
"""
|
|
return get_partition_from_id(self.course_partitions, user_partition_id)
|
|
|
|
def get_group(self, user, user_partition, assign=True):
|
|
"""
|
|
Returns the group from the specified user partition to which the user is assigned.
|
|
If the user has not yet been assigned, a group will be chosen for them based upon
|
|
the partition's scheme.
|
|
"""
|
|
return user_partition.scheme.get_group_for_user(
|
|
self._course_id, user, user_partition, assign=assign,
|
|
)
|