Files
edx-platform/lms/djangoapps/commerce/api/v1/serializers.py
Kyle McCormick 151bd13666 Use full names for common.djangoapps imports; warn when using old style (#25477)
* Generate common/djangoapps import shims for LMS
* Generate common/djangoapps import shims for Studio
* Stop appending project root to sys.path
* Stop appending common/djangoapps to sys.path
* Import from common.djangoapps.course_action_state instead of course_action_state
* Import from common.djangoapps.course_modes instead of course_modes
* Import from common.djangoapps.database_fixups instead of database_fixups
* Import from common.djangoapps.edxmako instead of edxmako
* Import from common.djangoapps.entitlements instead of entitlements
* Import from common.djangoapps.pipline_mako instead of pipeline_mako
* Import from common.djangoapps.static_replace instead of static_replace
* Import from common.djangoapps.student instead of student
* Import from common.djangoapps.terrain instead of terrain
* Import from common.djangoapps.third_party_auth instead of third_party_auth
* Import from common.djangoapps.track instead of track
* Import from common.djangoapps.util instead of util
* Import from common.djangoapps.xblock_django instead of xblock_django
* Add empty common/djangoapps/__init__.py to fix pytest collection
* Fix pylint formatting violations
* Exclude import_shims/ directory tree from linting
2020-11-10 07:02:01 -05:00

146 lines
4.9 KiB
Python

""" API v1 serializers. """
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 common.djangoapps.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
]