Files
edx-platform/lms/djangoapps/commerce/api/v1/serializers.py
Ned Batchelder b3a731550c Fix problems that drf-yasg uncovered.
These were originally fixed individually, but had to be reverted, and
are now combined in one commit.  The originals were:

7b9040f6b0 This enum was backwards
8774ff1f9b Use ref_name to disambiguate serializers that drf-yasg would otherwise assume are the same.
8a44397139 Is this field missing because it is None?
4a1154a7ca Give a safer buffer for clearing the rate limiting
64c47856dd DRF 3.7.4 changed how you delegate to another view, so don't
7359ca4fb2 Is this right? It fixes two tests
fdd66e5390 Adjust the expected error message for DRF 3.7.7
9257f68fd8 The default TIME_ZONE should be UTC
2019-06-21 18:04:15 -04:00

146 lines
4.9 KiB
Python

""" API v1 serializers. """
from __future__ import absolute_import
from datetime import datetime
import pytz
import six
from django.utils.translation import ugettext as _
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from rest_framework import serializers
from course_modes.models import CourseMode
from xmodule.modulestore.django import modulestore
from .models import UNDEFINED, Course
class CourseModeSerializer(serializers.ModelSerializer):
""" CourseMode serializer. """
name = serializers.CharField(source='mode_slug')
price = serializers.IntegerField(source='min_price')
expires = serializers.DateTimeField(
source='expiration_datetime',
required=False,
allow_null=True,
format=None
)
def get_identity(self, data):
try:
return data.get('name', None)
except AttributeError:
return None
class Meta(object):
model = CourseMode
fields = ('name', 'currency', 'price', 'sku', 'bulk_sku', 'expires')
# For disambiguating within the drf-yasg swagger schema
ref_name = 'commerce.CourseMode'
def validate_course_id(course_id):
"""
Check that course id is valid and exists in modulestore.
"""
try:
course_key = CourseKey.from_string(six.text_type(course_id))
except InvalidKeyError:
raise serializers.ValidationError(
_(u"{course_id} is not a valid course key.").format(
course_id=course_id
)
)
if not modulestore().has_course(course_key):
raise serializers.ValidationError(
_(u'Course {course_id} does not exist.').format(
course_id=course_id
)
)
class PossiblyUndefinedDateTimeField(serializers.DateTimeField):
"""
We need a DateTime serializer that can deal with the non-JSON-serializable
UNDEFINED object.
"""
def to_representation(self, value):
if value is UNDEFINED:
return None
return super(PossiblyUndefinedDateTimeField, self).to_representation(value)
class CourseSerializer(serializers.Serializer):
""" Course serializer. """
id = serializers.CharField(validators=[validate_course_id]) # pylint: disable=invalid-name
name = serializers.CharField(read_only=True)
verification_deadline = PossiblyUndefinedDateTimeField(format=None, allow_null=True, required=False)
modes = CourseModeSerializer(many=True)
class Meta(object):
# For disambiguating within the drf-yasg swagger schema
ref_name = 'commerce.Course'
def validate(self, attrs):
""" Ensure the verification deadline occurs AFTER the course mode enrollment deadlines. """
verification_deadline = attrs.get('verification_deadline', None)
if verification_deadline:
upgrade_deadline = None
# Find the earliest upgrade deadline
for mode in attrs['modes']:
expires = mode.get("expiration_datetime")
if expires:
# If we don't already have an upgrade_deadline value, use datetime.max so that we can actually
# complete the comparison.
upgrade_deadline = min(expires, upgrade_deadline or datetime.max.replace(tzinfo=pytz.utc))
# In cases where upgrade_deadline is None (e.g. the verified professional mode), allow a verification
# deadline to be set anyway.
if upgrade_deadline is not None and verification_deadline < upgrade_deadline:
raise serializers.ValidationError(
'Verification deadline must be after the course mode upgrade deadlines.')
return attrs
def create(self, validated_data):
"""
Create course modes for a course.
arguments:
validated_data: The result of self.validate() - a dictionary containing 'id', 'modes', and optionally
a 'verification_deadline` key.
returns:
A ``commerce.api.v1.models.Course`` object.
"""
kwargs = {}
if 'verification_deadline' in validated_data:
kwargs['verification_deadline'] = validated_data['verification_deadline']
course = Course(
validated_data["id"],
self._new_course_mode_models(validated_data["modes"]),
**kwargs
)
course.save()
return course
def update(self, instance, validated_data):
"""Update course modes for an existing course. """
validated_data["modes"] = self._new_course_mode_models(validated_data["modes"])
instance.update(validated_data)
instance.save()
return instance
@staticmethod
def _new_course_mode_models(modes_data):
"""Convert validated course mode data to CourseMode objects. """
return [
CourseMode(**modes_dict)
for modes_dict in modes_data
]