This is needed so Masters learners will not be in the same team as verfied and audit learners. If the team is designated as organization protected, it will not be visible or accessible by verified or audit track learners MST-34
221 lines
7.8 KiB
Python
221 lines
7.8 KiB
Python
"""
|
|
Defines serializers used by the Team API.
|
|
"""
|
|
from __future__ import absolute_import, unicode_literals
|
|
|
|
from copy import deepcopy
|
|
|
|
import six
|
|
from django.conf import settings
|
|
from django.contrib.auth.models import User
|
|
from django_countries import countries
|
|
from rest_framework import serializers
|
|
|
|
from lms.djangoapps.teams.api import add_team_count, get_team_count_query_set
|
|
from lms.djangoapps.teams.models import CourseTeam, CourseTeamMembership
|
|
from openedx.core.djangoapps.user_api.accounts.serializers import UserReadOnlySerializer
|
|
from openedx.core.lib.api.fields import ExpandableField
|
|
from openedx.core.lib.api.serializers import CollapsedReferenceSerializer
|
|
|
|
|
|
class CountryField(serializers.Field):
|
|
"""
|
|
Field to serialize a country code.
|
|
"""
|
|
|
|
COUNTRY_CODES = list(dict(countries).keys())
|
|
|
|
def to_representation(self, obj): # pylint: disable=arguments-differ
|
|
"""
|
|
Represent the country as a 2-character unicode identifier.
|
|
"""
|
|
return six.text_type(obj)
|
|
|
|
def to_internal_value(self, data):
|
|
"""
|
|
Check that the code is a valid country code.
|
|
|
|
We leave the data in its original format so that the Django model's
|
|
CountryField can convert it to the internal representation used
|
|
by the django-countries library.
|
|
"""
|
|
if data and data not in self.COUNTRY_CODES:
|
|
raise serializers.ValidationError(
|
|
u"{code} is not a valid country code".format(code=data)
|
|
)
|
|
return data
|
|
|
|
|
|
class UserMembershipSerializer(serializers.ModelSerializer):
|
|
"""Serializes CourseTeamMemberships with only user and date_joined
|
|
|
|
Used for listing team members.
|
|
"""
|
|
profile_configuration = deepcopy(settings.ACCOUNT_VISIBILITY_CONFIGURATION)
|
|
profile_configuration['bulk_shareable_fields'].append('url')
|
|
profile_configuration['public_fields'].append('url')
|
|
|
|
user = ExpandableField(
|
|
collapsed_serializer=CollapsedReferenceSerializer(
|
|
model_class=User,
|
|
id_source='username',
|
|
view_name='accounts_api',
|
|
read_only=True,
|
|
),
|
|
expanded_serializer=UserReadOnlySerializer(configuration=profile_configuration),
|
|
)
|
|
|
|
class Meta(object):
|
|
model = CourseTeamMembership
|
|
fields = ("user", "date_joined", "last_activity_at")
|
|
read_only_fields = ("date_joined", "last_activity_at")
|
|
|
|
|
|
class CourseTeamSerializer(serializers.ModelSerializer):
|
|
"""Serializes a CourseTeam with membership information."""
|
|
id = serializers.CharField(source='team_id', read_only=True) # pylint: disable=invalid-name
|
|
membership = UserMembershipSerializer(many=True, read_only=True)
|
|
country = CountryField()
|
|
|
|
class Meta(object):
|
|
model = CourseTeam
|
|
fields = (
|
|
"id",
|
|
"discussion_topic_id",
|
|
"name",
|
|
"course_id",
|
|
"topic_id",
|
|
"date_created",
|
|
"description",
|
|
"country",
|
|
"language",
|
|
"last_activity_at",
|
|
"membership",
|
|
"organization_protected",
|
|
)
|
|
read_only_fields = ("course_id", "date_created", "discussion_topic_id", "last_activity_at")
|
|
|
|
|
|
class CourseTeamCreationSerializer(serializers.ModelSerializer):
|
|
"""Deserializes a CourseTeam for creation."""
|
|
|
|
country = CountryField(required=False)
|
|
|
|
class Meta(object):
|
|
model = CourseTeam
|
|
fields = (
|
|
"name",
|
|
"course_id",
|
|
"description",
|
|
"topic_id",
|
|
"country",
|
|
"language",
|
|
"organization_protected",
|
|
)
|
|
|
|
def create(self, validated_data):
|
|
team = CourseTeam.create(
|
|
name=validated_data.get("name", ''),
|
|
course_id=validated_data.get("course_id"),
|
|
description=validated_data.get("description", ''),
|
|
topic_id=validated_data.get("topic_id", ''),
|
|
country=validated_data.get("country", ''),
|
|
language=validated_data.get("language", ''),
|
|
organization_protected=validated_data.get("organization_protected", False)
|
|
)
|
|
team.save()
|
|
return team
|
|
|
|
|
|
class CourseTeamSerializerWithoutMembership(CourseTeamSerializer):
|
|
"""The same as the `CourseTeamSerializer`, but elides the membership field.
|
|
|
|
Intended to be used as a sub-serializer for serializing team
|
|
memberships, since the membership field is redundant in that case.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(CourseTeamSerializerWithoutMembership, self).__init__(*args, **kwargs)
|
|
del self.fields['membership']
|
|
|
|
|
|
class MembershipSerializer(serializers.ModelSerializer):
|
|
"""Serializes CourseTeamMemberships with information about both teams and users."""
|
|
profile_configuration = deepcopy(settings.ACCOUNT_VISIBILITY_CONFIGURATION)
|
|
profile_configuration['bulk_shareable_fields'].append('url')
|
|
profile_configuration['public_fields'].append('url')
|
|
|
|
user = ExpandableField(
|
|
collapsed_serializer=CollapsedReferenceSerializer(
|
|
model_class=User,
|
|
id_source='username',
|
|
view_name='accounts_api',
|
|
read_only=True,
|
|
),
|
|
expanded_serializer=UserReadOnlySerializer(configuration=profile_configuration)
|
|
)
|
|
team = ExpandableField(
|
|
collapsed_serializer=CollapsedReferenceSerializer(
|
|
model_class=CourseTeam,
|
|
id_source='team_id',
|
|
view_name='teams_detail',
|
|
read_only=True,
|
|
),
|
|
expanded_serializer=CourseTeamSerializerWithoutMembership(read_only=True),
|
|
)
|
|
|
|
class Meta(object):
|
|
model = CourseTeamMembership
|
|
fields = ("user", "team", "date_joined", "last_activity_at")
|
|
read_only_fields = ("date_joined", "last_activity_at")
|
|
|
|
|
|
class BaseTopicSerializer(serializers.Serializer): # pylint: disable=abstract-method
|
|
"""Serializes a topic without team_count."""
|
|
description = serializers.CharField()
|
|
name = serializers.CharField()
|
|
id = serializers.CharField() # pylint: disable=invalid-name
|
|
|
|
|
|
class TopicSerializer(BaseTopicSerializer): # pylint: disable=abstract-method
|
|
"""
|
|
Adds team_count to the basic topic serializer, checking if team_count
|
|
is already present in the topic data, and if not, querying the CourseTeam
|
|
model to get the count. Requires that `context` is provided with a valid course_id
|
|
in order to filter teams within the course.
|
|
"""
|
|
team_count = serializers.SerializerMethodField()
|
|
|
|
def get_team_count(self, topic):
|
|
"""Get the number of teams associated with this topic"""
|
|
# If team_count is already present (possible if topic data was pre-processed for sorting), return it.
|
|
if 'team_count' in topic:
|
|
return topic['team_count']
|
|
else:
|
|
return get_team_count_query_set(
|
|
[topic['id']],
|
|
self.context['course_id'],
|
|
self.context.get('organization_protection_status')
|
|
).count()
|
|
|
|
|
|
class BulkTeamCountTopicListSerializer(serializers.ListSerializer): # pylint: disable=abstract-method
|
|
"""
|
|
List serializer for efficiently serializing a set of topics.
|
|
"""
|
|
|
|
def to_representation(self, obj): # pylint: disable=arguments-differ
|
|
"""Adds team_count to each topic. """
|
|
data = super(BulkTeamCountTopicListSerializer, self).to_representation(obj)
|
|
add_team_count(data, self.context['course_id'], self.context.get('organization_protection_status'))
|
|
return data
|
|
|
|
|
|
class BulkTeamCountTopicSerializer(BaseTopicSerializer): # pylint: disable=abstract-method
|
|
"""
|
|
Serializes a set of topics, adding the team_count field to each topic as a bulk operation.
|
|
Requires that `context` is provided with a valid course_id in order to filter teams within the course.
|
|
"""
|
|
class Meta(object):
|
|
list_serializer_class = BulkTeamCountTopicListSerializer
|