def get_expected_data(self, program_type, request):
        image_field = StdImageSerializerField()
        image_field._context = {'request': request}  # pylint: disable=protected-access

        return {
            'name': program_type.name,
            'logo_image': image_field.to_representation(program_type.logo_image),
        }
 def test_to_representation(self):
     request = make_request()
     program = ProgramFactory(banner_image=make_image_file('test.jpg'))
     field = StdImageSerializerField()
     field._context = {'request': request}  # pylint: disable=protected-access
     expected = {
         size_key: {
             'url': '{}{}'.format('http://testserver', getattr(program.banner_image, size_key).url),
             'width': program.banner_image.field.variations[size_key]['width'],  # pylint: disable=no-member
             'height': program.banner_image.field.variations[size_key]['height']  # pylint: disable=no-member
         } for size_key in program.banner_image.field.variations}  # pylint: disable=no-member
     assert field.to_representation(program.banner_image) == expected
 def test_to_internal_value(self):
     base64_header = "data:image/jpeg;base64,"
     base64_data = (
         "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAKBueIx4ZKCMgoy0qqC+8P//8Nzc8P/////////////////////"
         "/////////////////////////////////////2wBDAaq0tPDS8P///////////////////////////////////////////////////////"
         "///////////////////////wgARCADIAMgDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECA//EABYBAQEBAAAAAAAAAAAAAAA"
         "AAAABAv/aAAwDAQACEAMQAAABwADU6ZMtZFujDQzNwzbowtMNCZ68gAAADTI1kLci3I1INMjTI0yNZAAAAAAAAAAAAAAABrOpQgACStQAl"
         "JrOoCUAACCyazpQgACDUWUSozrNKJQAAILJrOlGTUhAoClIsiCrZZQgACCyazSCgAALZRmiAtyjTJdIKyKiwAAACxQlEsAAAAAAAAAAAAA"
         "AAAAAAAAAAAAAAAAAAAAADcMtDLQy3DLpzDcMunMNwy6cwAABZTpEBQg3lC43g1Lk6cunM65sLz6cwAAAAAAAABQAAAAA/8QAIBAAAwABB"
         "AMBAQAAAAAAAAAAAAERQRAhMUACMEJwIP/aAAgBAQABBQL+ITabIhCEIJEIQhCE9Xz8rnOcZfHiYEZXOPTSl1pSlEylKUpS/gL7L7L7L7L"
         "7L/TYTdjQkNEIRaREJpCEXoXJlnkfL4M5Z4i5zkZz6FplmwuGUyPlC51u/e//xAAUEQEAAAAAAAAAAAAAAAAAAABw/9oACAEDAQE/ASn/x"
         "AAUEQEAAAAAAAAAAAAAAAAAAABw/9oACAECAQE/ASn/xAAUEAEAAAAAAAAAAAAAAAAAAACQ/9oACAEBAAY/Ahx//8QAJBAAAwABBAEEAwE"
         "AAAAAAAAAAAERMRAhMEEgQFFhcWBwwfD/2gAIAQEAAT8h8HKydh2CUwPuYUzlHCyUHA9uRUsmcMomOFaK1wLJ76HoHgjsLH/dn8mQ7D3Q9"
         "zDcSjEYjVC4FszMxkMZBZG66fUUKGVMMED6mYvgZ0yqQ9go+fxBZ4H6BizwP0JZ4H4QfIWeC+L0T4iz4UvGuEs6UvAtHquEs8r1XEvo6X9"
         "bLdkiwCJCEhErIKoYe5I6BqMoQ3sRo02HuSOgefPAdizIvyYeAeI/YzIw0lkPIfX2NGjYPPnkVW0qEZo67GWwdSDSobUFVt0HSQ0FS3p2V"
         "PsaQVW0e79HSopsVFKXSn//2gAMAwEAAgADAAAAEABMIOBEPCPCAAAEIAEIEEAAAAAAAAAAAAAAAAAAPP8A/wDwAIX/AP8A/wDD8/8A/wC"
         "DCpP/AP8A/wAPzQuBBS4D3/8A/DzpBAMAYIX+wyMAAAAYIU8J7f7AAAAQQUgEpAz7AAAAAAAAAAAAAAAEMMMEEMAMAAAAAsQIQo0YY8AAA"
         "AAAAAAA88cAA//EABwRAQADAAIDAAAAAAAAAAAAAAEAESAQMDFQYP/aAAgBAwEBPxD05h88mWGHkywly9GGD0DLly4/H//EABwRAQADAAI"
         "DAAAAAAAAAAAAAAEAESAQMDFQYP/aAAgBAgEBPxD07g5ckcHKZIypWnzgidCXKlSvkP/EACoQAQACAQIEBQUBAQEAAAAAAAEAESExQRAwU"
         "WEgcYGh4ZGxwdHwQGDx/9oACAEBAAE/EPBYOzt8x3PpGgv6S2l1HA3l2i6wFNoXNte3ep9EvT5l4lqmTmqlQRsZQnYdPmUi0vSCUmqmdQ9"
         "e0xiwTMXXpyBYO8RUbJ+4/wB9ZoekdD6R+Wh7B+Z7wj+PWbDGc/Wfg/MODgsxEVGyfuKyDGz/AGYwFXBrElSsZ87gIhTfXvBT2ACJSjt41"
         "Q1dS9msdL+IAsuefxAAV67/ABFQejK0FVF1gC7zOCyDzG1VG6N3eZOLuOgKA9ZfKsdL+JSU2LsghIu5iAHXvLaBUe135mOf+P0JUqVKlSp"
         "XA7R147TeVKlSpUqVKlTbNLkapv4jTk7Jpcg5m8CAVpKQVK05WyaXjWo2ZvM1LgrtxdTk7JpeBBGbXeXweAZmWIhmPB8jZNLg0inxXwEI+"
         "X2TSi0XNYTeb+J/swxMniqZr49kGm5nqRDZ478ThrBEzFtxpHgeC+kvFMtg1FvXlHA1m3A1/wAY8d+O1cp5RpDgsNeIWch5F8SHgC5ePET"
         "eP/HkEd5lq24NltMMOcYhJL14CFsEMBL1LhhN5m1XV/20uDpDSK+soRY3l9hYkSlOkFuy1BCXm/aXOq4KR05Ear1qWzdP1Prh1/8AJo+f7"
         "h9k/HGnDPuYF0DRte7Ajtt6+k1fP9TW8n7k+x+094/ea/40ZczOsQbFH39YEQ6+NAVmFRZC63iqltjLEY0iIsadJSBuWISs3LwNhNkaVGK"
         "Rs+IgtDMCx0ZaaM17RbT1ZXGhTcsRob+c2RpUdqf4wBVe3lOxv0lNz27fuXRceX0mbTpt53KUY26dv3KUeXQ6RGx7Hb+uCBXn+Ihuir7fM"
         "//Z"
     )
     base64_full = base64_header + base64_data
     expected = ContentFile(base64.b64decode(base64_data), name='tmp.jpeg')
     assert list(StdImageSerializerField().to_internal_value(base64_full).chunks()) == list(expected.chunks())
Beispiel #4
0
class CollaboratorFactory(factory.DjangoModelFactory):
    name = factory.Faker('name')
    image = StdImageSerializerField()
    uuid = factory.LazyFunction(uuid4)

    class Meta:
        model = Collaborator
class ProgramTypeSerializer(serializers.ModelSerializer):
    """
    Serializer for the Program Types
    """
    logo_image = StdImageSerializerField()

    class Meta:
        model = ProgramType
        fields = (
            'name', 'logo_image',
        )
    def test_to_representation(self):
        request = make_request()
        # TODO Create test-only model to avoid unnecessary dependency on Program model.
        program = ProgramFactory(banner_image=make_image_file('test.jpg'))
        field = StdImageSerializerField()
        field._context = {'request': request}  # pylint: disable=protected-access

        expected = {
            size_key: {
                'url':
                '{}{}'.format('http://testserver',
                              getattr(program.banner_image, size_key).url),
                'width':
                program.banner_image.field.variations[size_key]['width'],
                'height':
                program.banner_image.field.variations[size_key]['height']
            }
            for size_key in program.banner_image.field.variations
        }

        self.assertDictEqual(field.to_representation(program.banner_image),
                             expected)
    def get_expected_data(self, program, request):
        image_field = StdImageSerializerField()
        image_field._context = {'request': request}  # pylint: disable=protected-access

        return {
            'uuid':
            str(program.uuid),
            'title':
            program.title,
            'subtitle':
            program.subtitle,
            'type':
            program.type.name,
            'status':
            program.status,
            'marketing_slug':
            program.marketing_slug,
            'marketing_url':
            program.marketing_url,
            'banner_image':
            image_field.to_representation(program.banner_image),
            'courses':
            MinimalProgramCourseSerializer(program.courses,
                                           many=True,
                                           context={
                                               'request':
                                               request,
                                               'program':
                                               program,
                                               'course_runs':
                                               list(program.course_runs),
                                           }).data,
            'authoring_organizations':
            MinimalOrganizationSerializer(program.authoring_organizations,
                                          many=True).data,
            'card_image_url':
            program.card_image_url,
        }
    def get_expected_data(self, program, request):
        image_field = StdImageSerializerField()
        image_field._context = {'request': request}  # pylint: disable=protected-access

        return {
            'uuid': str(program.uuid),
            'title': program.title,
            'subtitle': program.subtitle,
            'type': program.type.name,
            'status': program.status,
            'marketing_slug': program.marketing_slug,
            'marketing_url': program.marketing_url,
            'banner_image': image_field.to_representation(program.banner_image),
            'courses': MinimalProgramCourseSerializer(
                program.courses,
                many=True,
                context={
                    'request': request,
                    'program': program,
                    'course_runs': list(program.course_runs),
                }).data,
            'authoring_organizations': MinimalOrganizationSerializer(program.authoring_organizations, many=True).data,
            'card_image_url': program.card_image_url,
        }
 def test_to_internal_value_falsey(self, falsey_value):
     assert StdImageSerializerField().to_internal_value(falsey_value) is None
class MinimalProgramSerializer(serializers.ModelSerializer):
    authoring_organizations = MinimalOrganizationSerializer(many=True)
    banner_image = StdImageSerializerField()
    courses = serializers.SerializerMethodField()
    type = serializers.SlugRelatedField(slug_field='name', queryset=ProgramType.objects.all())

    @classmethod
    def prefetch_queryset(cls):
        return Program.objects.all().select_related('type', 'partner').prefetch_related(
            'excluded_course_runs',
            # `type` is serialized by a third-party serializer. Providing this field name allows us to
            # prefetch `applicable_seat_types`, a m2m on `ProgramType`, through `type`, a foreign key to
            # `ProgramType` on `Program`.
            'type__applicable_seat_types',
            'authoring_organizations',
            Prefetch('courses', queryset=MinimalProgramCourseSerializer.prefetch_queryset()),
        )

    class Meta:
        model = Program
        fields = (
            'uuid', 'title', 'subtitle', 'type', 'status', 'marketing_slug', 'marketing_url', 'banner_image',
            'courses', 'authoring_organizations', 'card_image_url',
        )
        read_only_fields = ('uuid', 'marketing_url', 'banner_image')

    def get_courses(self, program):
        if program.order_courses_by_start_date:
            courses, course_runs = self.sort_courses(program)
        else:
            courses, course_runs = program.courses.all(), list(program.course_runs)

        course_serializer = MinimalProgramCourseSerializer(
            courses,
            many=True,
            context={
                'request': self.context.get('request'),
                'published_course_runs_only': self.context.get('published_course_runs_only'),
                'exclude_utm': self.context.get('exclude_utm'),
                'program': program,
                'course_runs': course_runs,
                'use_full_course_serializer': self.context.get('use_full_course_serializer', False),
            }
        )

        return course_serializer.data

    def sort_courses(self, program):
        """
        Sorting by enrollment start then by course start yields a list ordered by course start, with
        ties broken by enrollment start. This works because Python sorting is stable: two objects with
        equal keys appear in the same order in sorted output as they appear in the input.

        Courses are only created if there's at least one course run belonging to that course, so
        course_runs should never be empty. If it is, key functions in this method attempting to find the
        min of an empty sequence will raise a ValueError.
        """
        course_runs = list(program.course_runs)

        def min_run_enrollment_start(course):
            # Enrollment starts may be empty. When this is the case, we make the same assumption as
            # the LMS: no enrollment_start is equivalent to (offset-aware) datetime.datetime.min.
            min_datetime = datetime.datetime.min.replace(tzinfo=pytz.UTC)

            # Course runs excluded from the program are excluded here, too.
            #
            # If this becomes a candidate for optimization in the future, be careful sorting null values
            # in the database. PostgreSQL and MySQL sort null values as if they are higher than non-null
            # values, while SQLite does the opposite.
            #
            # For more, refer to https://docs.djangoproject.com/en/1.10/ref/models/querysets/#latest.
            _course_runs = [course_run for course_run in course_runs if course_run.course == course]

            # Return early if we have no course runs since min() will fail.
            if not _course_runs:
                return min_datetime

            run = min(_course_runs, key=lambda run: run.enrollment_start or min_datetime)

            return run.enrollment_start or min_datetime

        def min_run_start(course):
            # Course starts may be empty. Since this means the course can't be started, missing course
            # start date is equivalent to (offset-aware) datetime.datetime.max.
            max_datetime = datetime.datetime.max.replace(tzinfo=pytz.UTC)

            _course_runs = [course_run for course_run in course_runs if course_run.course == course]

            # Return early if we have no course runs since min() will fail.
            if not _course_runs:
                return max_datetime

            run = min(_course_runs, key=lambda run: run.start or max_datetime)

            return run.start or max_datetime

        courses = list(program.courses.all())
        courses.sort(key=min_run_enrollment_start)
        courses.sort(key=min_run_start)

        return courses, course_runs
class PersonSerializer(serializers.ModelSerializer):
    """Serializer for the ``Person`` model."""
    position = PositionSerializer(required=False)
    profile_image = StdImageSerializerField(required=False)
    works = serializers.SlugRelatedField(many=True, read_only=True, slug_field='value', source='person_works')
    urls = serializers.SerializerMethodField()

    @classmethod
    def prefetch_queryset(cls):
        return Person.objects.all().select_related(
            'position__organization'
        ).prefetch_related('person_works', 'person_networks')

    class Meta(object):
        model = Person
        fields = (
            'uuid', 'given_name', 'family_name', 'bio', 'profile_image_url', 'slug', 'position', 'profile_image',
            'partner', 'works', 'urls'
        )
        extra_kwargs = {
            'partner': {'write_only': True}
        }

    def validate(self, data):
        validated_data = super(PersonSerializer, self).validate(data)
        validated_data['works'] = self.initial_data.get('works', [])
        validated_data['urls'] = self.initial_data.get('urls')
        return validated_data

    def create(self, validated_data):
        position_data = validated_data.pop('position')
        works_data = validated_data.pop('works', [])
        urls_data = validated_data.pop('urls', {})

        person = Person.objects.create(**validated_data)
        Position.objects.create(person=person, **position_data)

        person_social_networks = []
        for url_type in [PersonSocialNetwork.FACEBOOK, PersonSocialNetwork.TWITTER, PersonSocialNetwork.BLOG]:
            value = urls_data.get(url_type)
            if value:
                person_social_networks.append(PersonSocialNetwork(person=person, type=url_type, value=value))
        PersonSocialNetwork.objects.bulk_create(person_social_networks)

        person_works = [PersonWork(person=person, value=work_data) for work_data in works_data]
        PersonWork.objects.bulk_create(person_works)

        return person

    def get_social_network_url(self, url_type, obj):
        social_network = obj.person_networks.filter(type=url_type).first()

        if social_network:
            return social_network.value

    def get_urls(self, obj):
        return {
            PersonSocialNetwork.FACEBOOK: self.get_social_network_url(PersonSocialNetwork.FACEBOOK, obj),
            PersonSocialNetwork.TWITTER: self.get_social_network_url(PersonSocialNetwork.TWITTER, obj),
            PersonSocialNetwork.BLOG: self.get_social_network_url(PersonSocialNetwork.BLOG, obj),
        }