Example #1
0
class ExternalToolSerializerV2(DetailSerializerV2):
    name = StripTagsCharField(max_length=254)
    clientId = StripTagsCharField(max_length=254, source='client_id')

    class Meta(DetailSerializerV2.Meta):
        model = ExternalTool
        # apispec_definition = ('ExternalTool', {})

    def to_representation(self, instance):
        representation = super(ExternalToolSerializerV2,
                               self).to_representation(instance)
        representation['launchpoints'] = {
            lp.launchpoint: {
                "url":
                "{}{}".format(
                    OriginSetting.HTTP,
                    reverse("v2_api_externaltools_launch",
                            kwargs=dict(
                                launchpoint=lp.launchpoint,
                                entity_id=lp.cached_externaltool.entity_id))),
                "launchUrl":
                lp.launch_url,
                "label":
                lp.label,
                "iconUrl":
                lp.icon_url
            }
            for lp in instance.cached_launchpoints()
        }
        return representation
Example #2
0
class RecipientGroupSerializerV2(DetailSerializerV2):
    createdAt = serializers.DateTimeField(source='created_at', read_only=True)
    createdBy = EntityRelatedFieldV2(source='cached_creator', read_only=True)
    issuer = EntityRelatedFieldV2(source='cached_issuer', read_only=True)

    name = StripTagsCharField(required=False)
    description = StripTagsCharField(required=False)
    active = serializers.BooleanField(source='is_active', default=True)

    members = RecipientGroupMembershipSerializerV2(many=True, source='membership_items', required=False)
    pathways = EntityRelatedFieldV2(many=True, source='pathway_items', required=False, queryset=Pathway.cached)

    class Meta(DetailSerializerV2.Meta):
        model = RecipientGroup

    def update(self, instance, validated_data):
        if 'cached_issuer' in validated_data:
            validated_data.pop('cached_issuer')  # issuer is not updatedable
        super(RecipientGroupSerializerV2, self).update(instance, validated_data)

    def create(self, validated_data):
        if 'cached_issuer' in validated_data:
            # included issuer in request
            validated_data['issuer'] = validated_data.pop('cached_issuer')
        elif 'issuer' in self.context:
            # issuer was passed in context
            validated_data['issuer'] = self.context.get('issuer')
        else:
            # issuer is required on create
            raise serializers.ValidationError({"issuer": "This field is required"})

        return super(RecipientGroupSerializerV2, self).create(validated_data)
Example #3
0
class FacultySerializer(serializers.Serializer):
    id = serializers.ReadOnlyField()
    name = serializers.CharField(max_length=512)
    description_english = StripTagsCharField(max_length=16384, required=False)
    description_dutch = StripTagsCharField(max_length=16384, required=False)
    entity_id = StripTagsCharField(max_length=255, read_only=True)

    class Meta:
        model = Faculty

    def update(self, instance, validated_data):
        instance.name = validated_data.get('name')
        instance.description_english = validated_data.get(
            'description_english')
        instance.description_dutch = validated_data.get('description_dutch')
        instance.save()
        return instance

    def create(self, validated_data, **kwargs):
        user_institution = self.context['request'].user.institution
        user_permissions = user_institution.get_permissions(
            validated_data['created_by'])
        if user_permissions['may_create']:
            validated_data['institution'] = user_institution
            del validated_data['created_by']
            new_faculty = Faculty(**validated_data)
            try:
                new_faculty.save()
            except IntegrityError as e:
                raise serializers.ValidationError(
                    "Faculty name already exists")
            return new_faculty
        else:
            BadgrValidationError("You don't have the necessary permissions",
                                 100)
class BadgeUserProfileSerializer(serializers.Serializer):
    first_name = StripTagsCharField(max_length=30, allow_blank=True)
    last_name = StripTagsCharField(max_length=30, allow_blank=True)
    email = serializers.EmailField(source='primary_email', required=False)
    entity_id = serializers.CharField(read_only=True)
    marketing_opt_in = serializers.BooleanField(required=False)
    institution = InstitutionForProfileSerializer(read_only=True, )
class AlignmentItemSerializerV1(serializers.Serializer):
    target_name = StripTagsCharField()
    target_url = serializers.URLField()
    target_description = StripTagsCharField(required=False, allow_blank=True, allow_null=True)
    target_framework = StripTagsCharField(required=False, allow_blank=True, allow_null=True)
    target_code = StripTagsCharField(required=False, allow_blank=True, allow_null=True)

    class Meta:
        apispec_definition = ('BadgeClassAlignment', {})
class FacultySerializerStatistics(serializers.Serializer):
    name = StripTagsCharField(max_length=255, read_only=True)
    slug = StripTagsCharField(max_length=255, read_only=True, source='entity_id')
    # issuers = IssuerSerializerStatistics(many=True, read_only=True)

    class Meta:
        apispec_definition = ('Faculty', {})

    def to_representation(self, instance):
        representation = super(FacultySerializerStatistics, self).to_representation(instance)
        representation['issuers'] = [IssuerSerializerStatistics().to_representation(issuer) for issuer in instance.issuer_set.all()]
        return representation
Example #7
0
class AlignmentItemSerializerV2(BaseSerializerV2, OriginalJsonSerializerMixin):
    targetName = StripTagsCharField(source='target_name')
    targetUrl = serializers.URLField(source='target_url')
    targetDescription = StripTagsCharField(source='target_description', required=False, allow_null=True, allow_blank=True)
    targetFramework = StripTagsCharField(source='target_framework', required=False, allow_null=True, allow_blank=True)
    targetCode = StripTagsCharField(source='target_code', required=False, allow_null=True, allow_blank=True)

    class Meta:
        apispec_definition = ('BadgeClassAlignment', {
            'properties': {
            }
        })
Example #8
0
class StaffUserProfileSerializerV2(DetailSerializerV2):
    firstName = StripTagsCharField(source='first_name', read_only=True)
    lastName = StripTagsCharField(source='last_name', read_only=True)
    emails = BadgeUserEmailSerializerV2(many=True, source='email_items', read_only=True)
    url = serializers.ListField(child=serializers.URLField(),
                                read_only=True,
                                source='cached_verified_urls',
                                max_length=100)
    telephone = serializers.ListField(child=serializers.CharField(),
                                      read_only=True,
                                      source='cached_verified_phone_numbers',
                                      max_length=100)
    badgrDomain = serializers.CharField(read_only=True, max_length=255, source='badgrapp')
Example #9
0
class RecipientGroupMembershipSerializerV1(LinkedDataEntitySerializer):
    jsonld_type = "RecipientGroupMembership"

    email = serializers.EmailField(write_only=True, source='recipient_identifier')
    name = StripTagsCharField(source='membership_name')
    slug = StripTagsCharField(source='recipient_profile.entity_id', read_only=True)

    def to_representation(self, instance):
        json = super(RecipientGroupMembershipSerializerV1, self).to_representation(instance)
        json.update([('email', instance.recipient_identifier)])

        return json

    def to_internal_value(self, data):
        membership = None

        if not 'recipient_group' in self.context:
            raise ValidationError(
                "RecipientGroup must be present in the context to identify a RecipientProfile."
            )

        try:
            profile = RecipientProfile.objects.get(recipient_identifier=data.get('email'))
            exists = True
        except RecipientProfile.DoesNotExist:
            profile = RecipientProfile(
                recipient_identifier=data.get('email'),
                display_name=data.get('name')
            )
            exists = False

        if exists:
            try:
                membership = RecipientGroupMembership.objects.get(
                    recipient_profile=profile,
                    recipient_group=self.context.get('recipient_group')
                )
                if membership.membership_name != data.get('name'):
                    membership.membership_name = data.get('name')
                    membership.save()
            except RecipientGroupMembership.DoesNotExist:
                pass

        if not membership:
            membership = RecipientGroupMembership(
                recipient_profile=profile,
                recipient_group=self.context.get('recipient_group'),
                membership_name=data.get('name')
            )

        return membership
class BadgeClassSerializerStatistics(serializers.Serializer):
    name = StripTagsCharField(max_length=255, read_only=True)
    slug = StripTagsCharField(max_length=255, read_only=True, source='entity_id')
    description = StripTagsCharField(max_length=16384, convert_null=True, read_only=True)

    class Meta:
        apispec_definition = ('BadgeClass', {})

    def to_representation(self, instance):
        representation = super(BadgeClassSerializerStatistics, self).to_representation(instance)
        representation['assertion_count'] = instance.badgeinstances.all().__len__()
        instance_json = instance.get_json(obi_version='1_1', use_canonical_id=True)
        representation['image'] = instance_json['image']
        return representation
class IssuerSerializerStatistics(serializers.Serializer):
    name = StripTagsCharField(max_length=255, read_only=True)
    slug = StripTagsCharField(max_length=255, read_only=True, source='entity_id')
    description = StripTagsCharField(max_length=16384, convert_null=True, read_only=True)
    badgeclasses = BadgeClassSerializerStatistics(many=True, read_only=True)

    class Meta:
        apispec_definition = ('Issuer', {})

    def to_representation(self, instance):
        representation = super(IssuerSerializerStatistics, self).to_representation(instance)
        instance_json = instance.get_json(obi_version='1_1', use_canonical_id=True)
        image = instance_json.get('image', None)
        representation['image'] = image
        return representation
Example #12
0
class FacultySerializerV1(serializers.Serializer):
    id = serializers.ReadOnlyField()
    name = serializers.CharField(max_length=512)
    slug = StripTagsCharField(max_length=255,
                              read_only=True,
                              source='entity_id')

    class Meta:
        model = Faculty

    def update(self, instance, validated_data):
        instance.name = validated_data.get('name')
        instance.save()
        return instance

    def create(self, validated_data, **kwargs):
        user_institution = self.context['request'].user.institution
        validated_data['institution'] = user_institution
        del validated_data['created_by']
        new_faculty = Faculty(**validated_data)
        try:
            new_faculty.save()
        except IntegrityError as e:
            raise serializers.ValidationError("Faculty name already exists")
        return new_faculty
Example #13
0
class BadgeUserProfileSerializerV1(serializers.Serializer):
    first_name = StripTagsCharField(max_length=30, allow_blank=True)
    last_name = StripTagsCharField(max_length=30, allow_blank=True)
    email = serializers.EmailField(source='primary_email', required=False)
    password = serializers.CharField(style={'input_type': 'password'},
                                     write_only=True,
                                     required=False)
    slug = serializers.CharField(source='entity_id', read_only=True)

    class Meta:
        apispec_definition = ('BadgeUser', {})

    def create(self, validated_data):
        user = BadgeUser.objects.create(
            email=validated_data['primary_email'],
            first_name=validated_data['first_name'],
            last_name=validated_data['last_name'],
            plaintext_password=validated_data['password'],
            request=self.context.get('request', None),
        )
        return user

    def update(self, user, validated_data):
        first_name = validated_data.get('first_name')
        last_name = validated_data.get('last_name')
        password = validated_data.get('password')
        if first_name:
            user.first_name = first_name
        if last_name:
            user.last_name = last_name

        if password:
            user.set_password(password)
            notify_on_password_change(user)

        user.save()
        return user

    def to_representation(self, instance):
        representation = super(BadgeUserProfileSerializerV1,
                               self).to_representation(instance)

        if self.context.get('include_token', False):
            representation['token'] = instance.cached_token()

        return representation
class GroupSerializer(serializers.ModelSerializer):
    name = serializers.CharField(read_only=True, max_length=512)
    slug = StripTagsCharField(max_length=255, source='entity_rank.entity_id')

    class Meta:
        model = Group
        fields = ('name', 'slug')

    def to_representation(self, instance):
        return super(GroupSerializer, self).to_representation(instance)
Example #15
0
class ExternalToolSerializerV1(serializers.Serializer):
    name = StripTagsCharField(max_length=254)
    client_id = StripTagsCharField(max_length=254)
    slug = StripTagsCharField(max_length=255, source='entity_id', read_only=True)

    def to_representation(self, instance):
        representation = super(ExternalToolSerializerV1, self).to_representation(instance)
        representation['launchpoints'] = {
            lp.launchpoint: {
                "url": "{}{}".format(OriginSetting.HTTP, reverse("v1_api_externaltools_launch", kwargs=dict(
                    launchpoint=lp.launchpoint,
                    slug=lp.cached_externaltool.entity_id
                ))),
                "launch_url": lp.launch_url,
                "label": lp.label,
                "icon_url": lp.icon_url
            } for lp in instance.cached_launchpoints()
        }
        return representation
Example #16
0
class InstitutionSerializer(serializers.Serializer):
    description_english = StripTagsCharField(max_length=256, required=True)
    description_dutch = StripTagsCharField(max_length=256, required=True)
    entity_id = StripTagsCharField(max_length=255, read_only=True)
    image = ValidImageField(required=True)
    brin = serializers.CharField(read_only=True)
    name = serializers.CharField(max_length=254, required=True)
    grading_table = serializers.URLField(max_length=254, required=False)

    class Meta:
        model = Institution

    def update(self, instance, validated_data):
        instance.description_english = validated_data.get(
            'description_english')
        instance.description_dutch = validated_data.get('description_dutch')
        if 'image' in validated_data:
            instance.image = validated_data.get('image')
        instance.grading_table = validated_data.get('grading_table')
        instance.name = validated_data.get('name')
        instance.save()
        return instance
Example #17
0
class StudentsEnrolledSerializer(serializers.ModelSerializer):
    """
    Used by LTI
    """
    email = serializers.ReadOnlyField()
    first_name = serializers.ReadOnlyField()
    last_name = serializers.ReadOnlyField()
    edu_id = serializers.ReadOnlyField()
    slug = StripTagsCharField(max_length=255,
                              read_only=True,
                              source='entity_id')

    class Meta:
        model = StudentsEnrolled
        fields = '__all__'
class EvidenceItemSerializer(serializers.Serializer):
    evidence_url = serializers.URLField(max_length=1024,
                                        required=False,
                                        allow_blank=True)
    narrative = MarkdownCharField(required=False, allow_blank=True)
    name = serializers.CharField(max_length=255,
                                 required=False,
                                 allow_null=True,
                                 allow_blank=True)
    description = StripTagsCharField(max_length=16384,
                                     required=False,
                                     allow_null=True,
                                     allow_blank=True)

    def validate(self, attrs):
        if not (attrs.get('evidence_url', None)
                or attrs.get('narrative', None)):
            raise BadgrValidationFieldError(
                'narrative', "Either url or narrative is required", 910)
        return attrs
class IssuerSerializerV2(DetailSerializerV2, OriginalJsonSerializerMixin):
    openBadgeId = serializers.URLField(source='jsonld_id', read_only=True)
    createdAt = DateTimeWithUtcZAtEndField(source='created_at', read_only=True)
    createdBy = EntityRelatedFieldV2(source='cached_creator',
                                     queryset=BadgeUser.cached,
                                     required=False)
    name = StripTagsCharField(max_length=1024)
    image = ValidImageField(required=False)
    email = serializers.EmailField(max_length=255, required=True)
    description = StripTagsCharField(max_length=16384, required=False)
    url = serializers.URLField(max_length=1024, required=True)
    staff = IssuerStaffSerializerV2(many=True,
                                    source='staff_items',
                                    required=False)
    extensions = serializers.DictField(source='extension_items',
                                       required=False,
                                       validators=[BadgeExtensionValidator()])
    badgrDomain = serializers.SlugRelatedField(required=False,
                                               source='badgrapp',
                                               slug_field='cors',
                                               queryset=BadgrApp.objects)

    class Meta(DetailSerializerV2.Meta):
        model = Issuer
        apispec_definition = ('Issuer', {
            'properties':
            OrderedDict([
                ('entityId', {
                    'type': "string",
                    'format': "string",
                    'description': "Unique identifier for this Issuer",
                    'readOnly': True,
                }),
                ('entityType', {
                    'type': "string",
                    'format': "string",
                    'description': "\"Issuer\"",
                    'readOnly': True,
                }),
                ('openBadgeId', {
                    'type': "string",
                    'format': "url",
                    'description': "URL of the OpenBadge compliant json",
                    'readOnly': True,
                }),
                ('createdAt', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the Issuer was created",
                    'readOnly': True,
                }),
                ('createdBy', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeUser who created this Issuer",
                    'required': False,
                }),
                ('name', {
                    'type': "string",
                    'format': "string",
                    'description': "Name of the Issuer",
                    'required': True,
                }),
                ('image', {
                    'type': "string",
                    'format': "data:image/png;base64",
                    'description':
                    "Base64 encoded string of an image that represents the Issuer",
                    'required': False,
                }),
                ('email', {
                    'type': "string",
                    'format': "email",
                    'description': "Contact email for the Issuer",
                    'required': True,
                }),
                ('url', {
                    'type': "string",
                    'format': "url",
                    'description':
                    "Homepage or website associated with the Issuer",
                    'required': False,
                }),
                ('description', {
                    'type': "string",
                    'format': "text",
                    'description': "Short description of the Issuer",
                    'required': False,
                }),
            ])
        })

    def validate_image(self, image):
        if image is not None:
            img_name, img_ext = os.path.splitext(image.name)
            image.name = 'issuer_logo_' + str(uuid.uuid4()) + img_ext
        return image

    def validate_createdBy(self, val):
        if not request_authenticated_with_server_admin_token(
                self.context.get('request')):
            return None
        return val

    def validate(self, data):
        if data.get('badgrapp'
                    ) and not request_authenticated_with_server_admin_token(
                        self.context.get('request')):
            data.pop('badgrapp')
        return data

    def create(self, validated_data):
        request = self.context.get('request')

        # If a Server Admin declares another user as creator, set it to that other user. Otherwise, use request.user
        user = validated_data.pop('cached_creator', None)
        if user:
            validated_data['created_by'] = user

        potential_email = validated_data['email']
        if validated_data.get('badgrapp') is None:
            validated_data['badgrapp'] = BadgrApp.objects.get_current(request)

        # Server admins are exempt from email verification requirement. They will enforce it themselves.
        if not request_authenticated_with_server_admin_token(
                request) and not validated_data[
                    'created_by'].is_email_verified(potential_email):
            raise serializers.ValidationError(
                "Issuer email must be one of your verified addresses. Add this email to your profile and try again."
            )

        staff = validated_data.pop('staff_items', [])
        new_issuer = super(IssuerSerializerV2, self).create(validated_data)

        # update staff after issuer is created
        new_issuer.staff_items = staff

        return new_issuer

    def update(self, instance, validated_data):
        validated_data.pop('cached_creator', None)

        if 'image' in validated_data:
            self.context['save_kwargs'] = dict(force_resize=True)

        return super(IssuerSerializerV2, self).update(instance, validated_data)
Example #20
0
class RecipientGroupMembershipSerializerV2(DetailSerializerV2):
    recipient = BadgeRecipientSerializerV2(source='*')
    name = StripTagsCharField(source='membership_name')
Example #21
0
class RecipientGroupSerializerV1(LinkedDataEntitySerializer):
    jsonld_type = "RecipientGroup"

    name = StripTagsCharField(required=False)
    description = StripTagsCharField(required=False)
    slug = StripTagsCharField(read_only=True, source='entity_id')
    active = serializers.BooleanField(source='is_active', default=True)
    issuer = LinkedDataReferenceField(keys=['slug'], field_names={'slug': 'entity_id'}, model=Issuer)
    member_count = serializers.IntegerField(read_only=True)
    members = RecipientGroupMembershipSerializerV1(
        read_only=False, many=True, required=False, source='cached_members'
    )
    pathways = LinkedDataReferenceList(
        read_only=False, required=False, source='cached_pathways',
        child=LinkedDataReferenceField(read_only=False, keys=['slug'], model=Pathway)
    )

    def to_representation(self, instance):
        if not self.context.get('embedRecipients', False) and 'members' in self.fields:
            self.fields.pop('members')

        return super(RecipientGroupSerializerV1, self).to_representation(instance)

    def create(self, validated_data):
        if 'issuer' not in self.context:
            raise ValidationError("No issuer")
        issuer = self.context.get('issuer')

        name = validated_data.get('name')
        description = validated_data.get('description', '')

        recipient_group = RecipientGroup(
            issuer=issuer,
            name=name,
            is_active=validated_data.get('is_active', True)
        )
        if description:
            recipient_group.description = description
        recipient_group.save()
        return recipient_group

    def update(self, instance, validated_data):
        instance.name = validated_data.get('name', instance.name)
        instance.description = validated_data.get('description', instance.description)
        instance.is_active = validated_data.get('is_active', instance.is_active)

        if 'cached_pathways' in validated_data:
            existing_pathway_ids = set(instance.cached_pathways())
            updated_pathway_ids = set(validated_data.get('cached_pathways'))

            pathways_to_delete = existing_pathway_ids - updated_pathway_ids
            pathways_to_add = updated_pathway_ids - existing_pathway_ids

            for p in pathways_to_delete:
                instance.pathways.remove(p)
                p.publish()

            for p in pathways_to_add:
                instance.pathways.add(p)
                p.publish()

        if 'cached_members' in validated_data:
            existing_members = set(instance.cached_members())
            updated_members = set()

            for m in validated_data.get('cached_members'):
                # Save any newly defined profiles directly to the list,
                # save existing members for comparison
                if m.pk:
                    updated_members.add(m)
                else:
                    if not m.recipient_profile.pk:
                        m.recipient_profile.save()
                        m.recipient_profile_id = m.recipient_profile.pk
                    m.save()

            members_to_delete = existing_members - updated_members

            for m in members_to_delete:
                m.delete()

        instance.save() # update cache
        return instance
Example #22
0
class BadgeClassSerializerV1(OriginalJsonSerializerMixin,
                             serializers.Serializer):
    created_at = DateTimeWithUtcZAtEndField(read_only=True)
    created_by = BadgeUserIdentifierFieldV1()
    id = serializers.IntegerField(required=False, read_only=True)
    name = StripTagsCharField(max_length=255)
    image = ValidImageField(required=False)
    slug = StripTagsCharField(max_length=255,
                              read_only=True,
                              source='entity_id')
    criteria = MarkdownCharField(allow_blank=True,
                                 required=False,
                                 write_only=True)
    criteria_text = MarkdownCharField(required=False,
                                      allow_null=True,
                                      allow_blank=True)
    criteria_url = StripTagsCharField(required=False,
                                      allow_blank=True,
                                      allow_null=True,
                                      validators=[URLValidator()])
    recipient_count = serializers.IntegerField(required=False, read_only=True)
    pathway_element_count = serializers.IntegerField(required=False,
                                                     read_only=True)
    description = StripTagsCharField(max_length=16384,
                                     required=True,
                                     convert_null=True)

    alignment = AlignmentItemSerializerV1(many=True,
                                          source='alignment_items',
                                          required=False)
    tags = serializers.ListField(child=StripTagsCharField(max_length=1024),
                                 source='tag_items',
                                 required=False)

    expires = BadgeClassExpirationSerializerV1(source='*',
                                               required=False,
                                               allow_null=True)

    class Meta:
        apispec_definition = ('BadgeClass', {})

    def to_internal_value(self, data):
        if 'expires' in data:
            if not data['expires'] or len(data['expires']) == 0:
                # if expires was included blank, remove it so to_internal_value() doesnt choke
                del data['expires']
        return super(BadgeClassSerializerV1, self).to_internal_value(data)

    def to_representation(self, instance):
        representation = super(BadgeClassSerializerV1,
                               self).to_representation(instance)
        representation['issuer'] = OriginSetting.HTTP + reverse(
            'issuer_json',
            kwargs={'entity_id': instance.cached_issuer.entity_id})
        representation['json'] = instance.get_json(obi_version='1_1',
                                                   use_canonical_id=True)
        return representation

    def validate_image(self, image):
        if image is not None:
            img_name, img_ext = os.path.splitext(image.name)
            image.name = 'issuer_badgeclass_' + str(uuid.uuid4()) + img_ext
        return image

    def validate_criteria_text(self, criteria_text):
        if criteria_text is not None and criteria_text != '':
            return criteria_text
        else:
            return None

    def validate_criteria_url(self, criteria_url):
        if criteria_url is not None and criteria_url != '':
            return criteria_url
        else:
            return None

    def update(self, instance, validated_data):
        force_image_resize = False

        new_name = validated_data.get('name')
        if new_name:
            new_name = strip_tags(new_name)
            instance.name = new_name

        new_description = validated_data.get('description')
        if new_description:
            instance.description = strip_tags(new_description)

        if 'criteria_text' in validated_data:
            instance.criteria_text = validated_data.get('criteria_text')
        if 'criteria_url' in validated_data:
            instance.criteria_url = validated_data.get('criteria_url')

        if 'image' in validated_data:
            instance.image = validated_data.get('image')
            force_image_resize = True

        instance.alignment_items = validated_data.get('alignment_items')
        instance.tag_items = validated_data.get('tag_items')

        instance.expires_amount = validated_data.get('expires_amount', None)
        instance.expires_duration = validated_data.get('expires_duration',
                                                       None)

        instance.save(force_resize=force_image_resize)

        return instance

    def validate(self, data):
        if 'criteria' in data:
            if 'criteria_url' in data or 'criteria_text' in data:
                raise serializers.ValidationError(
                    "The criteria field is mutually-exclusive with the criteria_url and criteria_text fields"
                )

            if utils.is_probable_url(data.get('criteria')):
                data['criteria_url'] = data.pop('criteria')
            elif not isinstance(data.get('criteria'), str):
                raise serializers.ValidationError(
                    "Provided criteria text could not be properly processed as URL or plain text."
                )
            else:
                data['criteria_text'] = data.pop('criteria')

        else:
            if data.get('criteria_text', None) is None and data.get(
                    'criteria_url', None) is None:
                raise serializers.ValidationError(
                    "One or both of the criteria_text and criteria_url fields must be provided"
                )

        return data

    def create(self, validated_data, **kwargs):

        if 'image' not in validated_data:
            raise serializers.ValidationError(
                {"image": ["This field is required"]})

        if 'issuer' in self.context:
            validated_data['issuer'] = self.context.get('issuer')

        new_badgeclass = BadgeClass.objects.create(**validated_data)
        return new_badgeclass
Example #23
0
class IssuerSerializerV1(OriginalJsonSerializerMixin, serializers.Serializer):
    created_at = DateTimeWithUtcZAtEndField(read_only=True)
    created_by = BadgeUserIdentifierFieldV1()
    name = StripTagsCharField(max_length=1024)
    slug = StripTagsCharField(max_length=255,
                              source='entity_id',
                              read_only=True)
    image = ValidImageField(required=False)
    email = serializers.EmailField(max_length=255, required=True)
    description = StripTagsCharField(max_length=16384, required=False)
    url = serializers.URLField(max_length=1024, required=True)
    staff = IssuerStaffSerializerV1(read_only=True,
                                    source='cached_issuerstaff',
                                    many=True)
    badgrapp = serializers.CharField(read_only=True,
                                     max_length=255,
                                     source='cached_badgrapp')

    class Meta:
        apispec_definition = ('Issuer', {})

    def validate_image(self, image):
        if image is not None:
            img_name, img_ext = os.path.splitext(image.name)
            image.name = 'issuer_logo_' + str(uuid.uuid4()) + img_ext
        return image

    def create(self, validated_data, **kwargs):
        user = validated_data['created_by']
        potential_email = validated_data['email']

        if not user.is_email_verified(potential_email):
            raise serializers.ValidationError(
                "Issuer email must be one of your verified addresses. Add this email to your profile and try again."
            )

        new_issuer = Issuer(**validated_data)

        # set badgrapp
        new_issuer.badgrapp = BadgrApp.objects.get_current(
            self.context.get('request', None))

        new_issuer.save()
        return new_issuer

    def update(self, instance, validated_data):
        force_image_resize = False
        instance.name = validated_data.get('name')

        if 'image' in validated_data:
            instance.image = validated_data.get('image')
            force_image_resize = True

        instance.email = validated_data.get('email')
        instance.description = validated_data.get('description')
        instance.url = validated_data.get('url')

        # set badgrapp
        if not instance.badgrapp_id:
            instance.badgrapp = BadgrApp.objects.get_current(
                self.context.get('request', None))

        instance.save(force_resize=force_image_resize)
        return instance

    def to_representation(self, obj):
        representation = super(IssuerSerializerV1, self).to_representation(obj)
        representation['json'] = obj.get_json(obi_version='1_1',
                                              use_canonical_id=True)

        if self.context.get('embed_badgeclasses', False):
            representation['badgeclasses'] = BadgeClassSerializerV1(
                obj.badgeclasses.all(), many=True, context=self.context).data

        representation['badgeClassCount'] = len(obj.cached_badgeclasses())
        representation['recipientGroupCount'] = len(
            obj.cached_recipient_groups())
        representation['recipientCount'] = sum(
            g.member_count() for g in obj.cached_recipient_groups())
        representation['pathwayCount'] = len(obj.cached_pathways())

        return representation
Example #24
0
class PathwaySerializer(serializers.Serializer):
    slug = StripTagsCharField(read_only=True)
    name = StripTagsCharField(max_length=254, required=False)
    description = StripTagsCharField(required=False)
    alignmentUrl = StripTagsCharField(required=False, allow_null=True)
    issuer = LinkedDataReferenceField(keys=['slug'],
                                      model=Issuer,
                                      many=False,
                                      read_only=True,
                                      field_names={'slug': 'entity_id'})
    groups = LinkedDataReferenceList(required=False,
                                     read_only=False,
                                     child=LinkedDataReferenceField(
                                         keys=['slug'],
                                         model=RecipientGroup,
                                         read_only=False,
                                         field_names={'slug': 'entity_id'}))
    completionBadge = LinkedDataReferenceField(
        ['slug'],
        BadgeClass,
        read_only=True,
        required=False,
        allow_null=True,
        source='completion_badge',
        field_names={'slug': 'entity_id'})
    rootChildCount = serializers.IntegerField(
        read_only=True, source='cached_root_element.cached_children.count')
    elementCount = serializers.IntegerField(read_only=True,
                                            source='cached_elements.count')

    def to_representation(self, instance):
        issuer_slug = instance.cached_issuer.entity_id

        representation = super(PathwaySerializer,
                               self).to_representation(instance)

        representation['@id'] = instance.jsonld_id
        if self.context.get('include_context', False):
            representation.update([
                ('@context', "https://badgr.io/public/contexts/pathways"),
                ("@type", "Pathway")
            ])

        if self.context.get('include_structure', False):
            self.context.update({
                'pathway_slug': instance.slug,
            })
            element_serializer = PathwayElementSerializer(
                instance.cached_elements(), many=True, context=self.context)
            representation.update([
                ('rootElement', OriginSetting.HTTP +
                 reverse('pathway_element_detail',
                         kwargs={
                             'issuer_slug': issuer_slug,
                             'pathway_slug': instance.slug,
                             'element_slug': instance.cached_root_element.slug
                         })), ('elements', element_serializer.data)
            ])
        return representation

    def create(self, validated_data, **kwargs):
        # TODO: Replace with validate_name and validate_description methods that check for self.instance
        if not validated_data.get('name') or not validated_data.get(
                'description'):
            raise ValidationError(
                "Values for name and description are required to create a Pathway."
            )

        issuer_slug = self.context.get('issuer_slug', None)
        if not issuer_slug:
            raise ValidationError("Could not determine issuer")
        try:
            issuer = Issuer.cached.get_by_slug_or_id(issuer_slug)
        except Issuer.DoesNotExist:
            raise ValidationError("Could not determine issuer")

        name = validated_data.get('name')

        pathway = Pathway(issuer=issuer)
        pathway.save(name_hint=name)
        root_element = PathwayElement(
            pathway=pathway,
            parent_element=None,
            name=name,
            description=validated_data.get('description'),
            alignment_url=validated_data.get('alignmentUrl'))
        root_element.save()
        pathway.root_element = root_element
        pathway.save()

        if 'groups' in validated_data and len(validated_data.get('groups')):
            pathway.recipient_groups.add(set(validated_data.get('groups')))
            pathway.save()  # update cache

        return pathway

    def update(self, instance, validated_data):
        if 'groups' in validated_data:
            existing_groups = set(instance.recipient_groups.all())
            updated_groups = set(validated_data.get('groups'))

            groups_to_delete = existing_groups - updated_groups
            groups_to_add = updated_groups - existing_groups

            for g in groups_to_delete:
                instance.recipient_groups.remove(g)
                g.publish()

            for g in groups_to_add:
                instance.recipient_groups.add(g)
                g.publish()

        instance.save()  # update caches, sloppily
        return instance
Example #25
0
class BadgeClassSerializerV1(OriginalJsonSerializerMixin, ExtensionsSaverMixin,
                             serializers.Serializer):
    created_at = DateTimeWithUtcZAtEndField(read_only=True)
    created_by = BadgeUserIdentifierFieldV1()
    id = serializers.IntegerField(required=False, read_only=True)
    name = StripTagsCharField(max_length=255)
    image = ValidImageField(required=False)
    slug = StripTagsCharField(max_length=255,
                              read_only=True,
                              source='entity_id')
    criteria = MarkdownCharField(allow_blank=True,
                                 required=False,
                                 write_only=True)
    criteria_text = MarkdownCharField(required=False,
                                      allow_null=True,
                                      allow_blank=True)
    criteria_url = StripTagsCharField(required=False,
                                      allow_blank=True,
                                      allow_null=True,
                                      validators=[URLValidator()])
    recipient_count = serializers.IntegerField(required=False,
                                               read_only=True,
                                               source='v1_api_recipient_count')
    description = StripTagsCharField(max_length=16384,
                                     required=True,
                                     convert_null=True)

    alignment = AlignmentItemSerializerV1(many=True,
                                          source='alignment_items',
                                          required=False)
    tags = serializers.ListField(child=StripTagsCharField(max_length=1024),
                                 source='tag_items',
                                 required=False)

    extensions = serializers.DictField(source='extension_items',
                                       required=False,
                                       validators=[BadgeExtensionValidator()])

    expires = BadgeClassExpirationSerializerV1(source='*',
                                               required=False,
                                               allow_null=True)

    # issuerName = StripTagsCharField(max_length=255)

    class Meta:
        apispec_definition = ('BadgeClass', {})

    def to_internal_value(self, data):
        if 'expires' in data:
            if not data['expires'] or len(data['expires']) == 0:
                # if expires was included blank, remove it so to_internal_value() doesnt choke
                del data['expires']
        return super(BadgeClassSerializerV1, self).to_internal_value(data)

    def to_representation(self, instance):
        representation = super(BadgeClassSerializerV1,
                               self).to_representation(instance)
        representation['issuerName'] = instance.cached_issuer.name
        representation['issuer'] = OriginSetting.HTTP + reverse(
            'issuer_json',
            kwargs={'entity_id': instance.cached_issuer.entity_id})
        representation['json'] = instance.get_json(obi_version='1_1',
                                                   use_canonical_id=True)
        return representation

    def validate_image(self, image):
        if image is not None:
            img_name, img_ext = os.path.splitext(image.name)
            image.name = 'issuer_badgeclass_' + str(uuid.uuid4()) + img_ext
        return image

    def validate_criteria_text(self, criteria_text):
        if criteria_text is not None and criteria_text != '':
            return criteria_text
        else:
            return None

    def validate_criteria_url(self, criteria_url):
        if criteria_url is not None and criteria_url != '':
            return criteria_url
        else:
            return None

    def validate_extensions(self, extensions):
        is_formal = False
        if extensions:
            for ext_name, ext in extensions.items():
                # if "@context" in ext and not ext['@context'].startswith(settings.EXTENSIONS_ROOT_URL):
                #     raise BadgrValidationError(
                #         error_code=999,
                #         error_message=f"extensions @context invalid {ext['@context']}")
                if ext_name.endswith('ECTSExtension') or ext_name.endswith(
                        'StudyLoadExtension') or ext_name.endswith(
                            'CategoryExtension') or ext_name.endswith(
                                'LevelExtension'):
                    is_formal = True
        self.formal = is_formal
        return extensions

    def add_extensions(self, instance, add_these_extensions, extension_items):
        for extension_name in add_these_extensions:
            original_json = extension_items[extension_name]
            extension = BadgeClassExtension(
                name=extension_name,
                original_json=json.dumps(original_json),
                badgeclass_id=instance.pk)
            extension.save()

    def update(self, instance, validated_data):
        logger.info("UPDATE BADGECLASS")
        logger.debug(validated_data)

        force_image_resize = False

        new_name = validated_data.get('name')
        if new_name:
            new_name = strip_tags(new_name)
            instance.name = new_name

        new_description = validated_data.get('description')
        if new_description:
            instance.description = strip_tags(new_description)

        # Assure both criteria_url and criteria_text will not be empty
        if 'criteria_url' in validated_data or 'criteria_text' in validated_data:
            end_criteria_url = validated_data['criteria_url'] if  'criteria_url' in validated_data \
                else instance.criteria_url
            end_criteria_text = validated_data['criteria_text'] if 'criteria_text' in validated_data \
                else instance.criteria_text

            if ((end_criteria_url is None or not end_criteria_url.strip()) and
                (end_criteria_text is None or not end_criteria_text.strip())):
                raise serializers.ValidationError(
                    'Changes cannot be made that would leave both criteria_url and criteria_text blank.'
                )
            else:
                instance.criteria_text = end_criteria_text
                instance.criteria_url = end_criteria_url

        if 'image' in validated_data:
            instance.image = validated_data.get('image')
            force_image_resize = True

        instance.alignment_items = validated_data.get('alignment_items')
        instance.tag_items = validated_data.get('tag_items')

        instance.expires_amount = validated_data.get('expires_amount', None)
        instance.expires_duration = validated_data.get('expires_duration',
                                                       None)

        logger.debug("SAVING EXTENSION")
        self.save_extensions(validated_data, instance)

        instance.save(force_resize=force_image_resize)

        return instance

    def validate(self, data):
        if 'criteria' in data:
            if 'criteria_url' in data or 'criteria_text' in data:
                raise serializers.ValidationError(
                    "The criteria field is mutually-exclusive with the criteria_url and criteria_text fields"
                )

            if utils.is_probable_url(data.get('criteria')):
                data['criteria_url'] = data.pop('criteria')
            elif not isinstance(data.get('criteria'), str):
                raise serializers.ValidationError(
                    "Provided criteria text could not be properly processed as URL or plain text."
                )
            else:
                data['criteria_text'] = data.pop('criteria')
        return data

    def create(self, validated_data, **kwargs):

        logger.info("CREATE NEW BADGECLASS")
        logger.debug(validated_data)

        if 'image' not in validated_data:
            raise serializers.ValidationError(
                {"image": ["This field is required"]})

        if 'issuer' in self.context:
            validated_data['issuer'] = self.context.get('issuer')

        if validated_data.get('criteria_text',
                              None) is None and validated_data.get(
                                  'criteria_url', None) is None:
            raise serializers.ValidationError(
                "One or both of the criteria_text and criteria_url fields must be provided"
            )

        new_badgeclass = BadgeClass.objects.create(**validated_data)
        return new_badgeclass
Example #26
0
class PathwayElementSerializer(LinkedDataEntitySerializer):
    name = StripTagsCharField()
    slug = StripTagsCharField(required=False)
    description = StripTagsCharField()
    parent = serializers.CharField(required=False)
    alignmentUrl = StripTagsCharField(required=False,
                                      allow_null=True,
                                      allow_blank=True)
    ordering = serializers.IntegerField(required=False, default=99)
    completionBadge = LinkedDataReferenceField(
        keys=['slug'],
        model=BadgeClass,
        read_only=False,
        required=False,
        allow_null=True,
        source='completion_badgeclass',
        field_names={'slug': 'entity_id'})
    requirements = JSONDictField(required=False, allow_null=True)
    children = serializers.ListField(required=False,
                                     allow_null=True,
                                     child=serializers.CharField(
                                         required=False, allow_null=False))

    def to_representation(self, instance):
        include_requirements = self.context.get('include_requirements', True)
        representation = super(PathwayElementSerializer,
                               self).to_representation(instance)
        representation['alignmentUrl'] = instance.get_alignment_url()

        representation['children'] = [
            child.jsonld_id for child in instance.cached_children()
        ]

        if include_requirements and instance.completion_requirements:
            completion_serializer = PathwayElementCompletionSpecSerializer(
                instance.completion_requirements, context=self.context)
            representation['requirements'] = completion_serializer.data

        return representation

    def create(self, validated_data):
        pathway_slug = self.context.get('pathway_slug', None)
        if not pathway_slug:
            raise ValidationError("Could not determine pathway")
        try:
            pathway = Pathway.cached.get_by_slug_or_id(pathway_slug)
        except Pathway.DoesNotExist:
            raise ValidationError("Could not determine pathway")

        parent_slug = validated_data.get('parent')
        try:
            parent_element = PathwayElement.cached.get_by_slug_or_id(
                parent_slug)
        except PathwayElement.DoesNotExist:
            raise ValidationError("Invalid parent")
        else:
            if parent_element.pathway != pathway:
                raise ValidationError("Invalid parent")

        try:
            ordering = int(validated_data.get('ordering', 99))
        except ValueError:
            ordering = 99

        completion_requirements = None
        requirement_string = validated_data.get('requirements', None)
        if requirement_string:
            try:
                completion_requirements = CompletionRequirementSpecFactory.parse(
                    requirement_string).serialize()
            except ValueError as e:
                raise ValidationError("Invalid completion spec: {}".format(
                    str(e)))

        element = PathwayElement(
            pathway=pathway,
            parent_element=parent_element,
            ordering=ordering,
            name=validated_data.get('name'),
            description=validated_data.get('description', None),
            alignment_url=validated_data.get('alignmentUrl', None),
            completion_badgeclass=validated_data.get('completion_badgeclass'),
            completion_requirements=completion_requirements)
        element.save()
        return element

    def update(self, element, validated_data):
        parent_element = None
        parent_slug = validated_data.get('parent')
        if parent_slug:
            try:
                parent_element = PathwayElement.cached.get_by_slug_or_id(
                    parent_slug)
            except PathwayElement.DoesNotExist:
                raise ValidationError("Invalid parent")

        completion_requirements = None
        requirements = validated_data.get('requirements')
        if requirements:
            try:
                completion_requirements = CompletionRequirementSpecFactory.parse(
                    requirements).serialize()
            except ValueError as e:
                raise ValidationError("Invalid requirements: {}".format(e))

        child_ids = validated_data.get('children')
        order = 1
        if child_ids:
            for element_id in child_ids:
                try:
                    r = resolve(element_id.replace(OriginSetting.HTTP, ''))
                except Resolver404:
                    raise ValidationError(
                        "Invalid child id: {}".format(element_id))

                element_slug = r.kwargs.get('element_slug')

                try:
                    child = PathwayElement.cached.get(slug=element_slug)
                except PathwayElement.DoesNotExist:
                    raise ValidationError(
                        "Invalid child id: {}".format(element_id))
                else:
                    old_child_parent = child.parent_element
                    child.parent_element = element
                    child.ordering = order
                    order += 1
                    child.save()
                    old_child_parent.publish()

        old_parent = None
        if parent_element:
            old_parent = element.parent_element
            element.parent_element = parent_element

        element.completion_badgeclass = validated_data.get(
            'completion_badgeclass')
        element.name = validated_data.get('name')
        element.description = validated_data.get('description')
        element.alignment_url = validated_data.get(
            'alignmentUrl') if validated_data.get('alignmentUrl') else None
        element.ordering = validated_data.get('ordering', 99)
        element.completion_requirements = completion_requirements
        element.save()

        if old_parent:
            old_parent.publish()

        return element
Example #27
0
class BadgeClassSerializerV2(DetailSerializerV2, OriginalJsonSerializerMixin):
    openBadgeId = serializers.URLField(source='jsonld_id', read_only=True)
    createdAt = serializers.DateTimeField(source='created_at', read_only=True)
    createdBy = EntityRelatedFieldV2(source='cached_creator', read_only=True)
    issuer = EntityRelatedFieldV2(source='cached_issuer',
                                  required=False,
                                  queryset=Issuer.cached)
    issuerOpenBadgeId = serializers.URLField(source='issuer_jsonld_id',
                                             read_only=True)

    name = StripTagsCharField(max_length=1024)
    image = ValidImageField(required=False)
    description = StripTagsCharField(max_length=16384,
                                     required=True,
                                     convert_null=True)

    criteriaUrl = StripTagsCharField(source='criteria_url',
                                     required=False,
                                     allow_null=True,
                                     validators=[URLValidator()])
    criteriaNarrative = MarkdownCharField(source='criteria_text',
                                          required=False,
                                          allow_null=True)

    alignments = AlignmentItemSerializerV2(source='alignment_items',
                                           many=True,
                                           required=False)
    tags = serializers.ListField(child=StripTagsCharField(max_length=1024),
                                 source='tag_items',
                                 required=False)

    extensions = serializers.DictField(source='extension_items',
                                       required=False,
                                       validators=[BadgeExtensionValidator()])

    class Meta(DetailSerializerV2.Meta):
        model = BadgeClass
        apispec_definition = ('BadgeClass', {
            'properties':
            OrderedDict([
                ('entityId', {
                    'type': "string",
                    'format': "string",
                    'description': "Unique identifier for this BadgeClass",
                }),
                ('entityType', {
                    'type': "string",
                    'format': "string",
                    'description': "\"BadgeClass\"",
                }),
                ('openBadgeId', {
                    'type': "string",
                    'format': "url",
                    'description': "URL of the OpenBadge compliant json",
                }),
                ('createdAt', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the BadgeClass was created",
                }),
                ('createdBy', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeUser who created this BadgeClass",
                }),
                ('issuer', {
                    'type':
                    'string',
                    'format':
                    'entityId',
                    'description':
                    "entityId of the Issuer who owns the BadgeClass",
                }),
                ('name', {
                    'type': "string",
                    'format': "string",
                    'description': "Name of the BadgeClass",
                }),
                ('description', {
                    'type': "string",
                    'format': "string",
                    'description': "Short description of the BadgeClass",
                }),
                ('image', {
                    'type':
                    "string",
                    'format':
                    "data:image/png;base64",
                    'description':
                    "Base64 encoded string of an image that represents the BadgeClass.",
                }),
                ('criteriaUrl', {
                    'type':
                    "string",
                    'format':
                    "url",
                    'description':
                    "External URL that describes in a human-readable format the criteria for the BadgeClass"
                }),
                ('criteriaNarrative', {
                    'type':
                    "string",
                    'format':
                    "markdown",
                    'description':
                    "Markdown formatted description of the criteria"
                }),
                ('tags', {
                    'type': "array",
                    'items': {
                        'type': "string",
                        'format': "string"
                    },
                    'description': "List of tags that describe the BadgeClass"
                }),
                ('alignments', {
                    'type':
                    "array",
                    'items': {
                        '$ref': '#/definitions/BadgeClassAlignment'
                    },
                    'description':
                    "List of objects describing objectives or educational standards"
                }),
            ])
        })

    def update(self, instance, validated_data):
        if 'cached_issuer' in validated_data:
            validated_data.pop('cached_issuer')  # issuer is not updatable
        return super(BadgeClassSerializerV2,
                     self).update(instance, validated_data)

    def create(self, validated_data):
        if 'cached_issuer' in validated_data:
            # included issuer in request
            validated_data['issuer'] = validated_data.pop('cached_issuer')
        elif 'issuer' in self.context:
            # issuer was passed in context
            validated_data['issuer'] = self.context.get('issuer')
        else:
            # issuer is required on create
            raise serializers.ValidationError(
                {"issuer": "This field is required"})

        return super(BadgeClassSerializerV2, self).create(validated_data)
class BadgeClassSerializerV2(DetailSerializerV2, OriginalJsonSerializerMixin):
    openBadgeId = serializers.URLField(source='jsonld_id', read_only=True)
    createdAt = DateTimeWithUtcZAtEndField(source='created_at', read_only=True)
    createdBy = EntityRelatedFieldV2(source='cached_creator', read_only=True)
    issuer = EntityRelatedFieldV2(source='cached_issuer',
                                  required=False,
                                  queryset=Issuer.cached)
    issuerOpenBadgeId = serializers.URLField(source='issuer_jsonld_id',
                                             read_only=True)

    name = StripTagsCharField(max_length=1024)
    image = ValidImageField(required=False)
    description = StripTagsCharField(max_length=16384,
                                     required=True,
                                     convert_null=True)

    criteriaUrl = StripTagsCharField(source='criteria_url',
                                     required=False,
                                     allow_null=True,
                                     validators=[URLValidator()])
    criteriaNarrative = MarkdownCharField(source='criteria_text',
                                          required=False,
                                          allow_null=True)

    alignments = AlignmentItemSerializerV2(source='alignment_items',
                                           many=True,
                                           required=False)
    tags = serializers.ListField(child=StripTagsCharField(max_length=1024),
                                 source='tag_items',
                                 required=False)

    expires = BadgeClassExpirationSerializerV2(source='*',
                                               required=False,
                                               allow_null=True)

    extensions = serializers.DictField(source='extension_items',
                                       required=False,
                                       validators=[BadgeExtensionValidator()])

    class Meta(DetailSerializerV2.Meta):
        model = BadgeClass
        apispec_definition = ('BadgeClass', {
            'properties':
            OrderedDict([
                ('entityId', {
                    'type': "string",
                    'format': "string",
                    'description': "Unique identifier for this BadgeClass",
                    'readOnly': True,
                }),
                ('entityType', {
                    'type': "string",
                    'format': "string",
                    'description': "\"BadgeClass\"",
                    'readOnly': True,
                }),
                ('openBadgeId', {
                    'type': "string",
                    'format': "url",
                    'description': "URL of the OpenBadge compliant json",
                    'readOnly': True,
                }),
                ('createdAt', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the BadgeClass was created",
                    'readOnly': True,
                }),
                ('createdBy', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeUser who created this BadgeClass",
                    'readOnly': True,
                }),
                ('issuer', {
                    'type': 'string',
                    'format': 'entityId',
                    'description':
                    "entityId of the Issuer who owns the BadgeClass",
                    'required': False,
                }),
                ('name', {
                    'type': "string",
                    'format': "string",
                    'description': "Name of the BadgeClass",
                    'required': True,
                }),
                ('description', {
                    'type': "string",
                    'format': "string",
                    'description': "Short description of the BadgeClass",
                    'required': True,
                }),
                ('image', {
                    'type': "string",
                    'format': "data:image/png;base64",
                    'description':
                    "Base64 encoded string of an image that represents the BadgeClass.",
                    'required': False,
                }),
                ('criteriaUrl', {
                    'type': "string",
                    'format': "url",
                    'description':
                    "External URL that describes in a human-readable format the criteria for the BadgeClass",
                    'required': False,
                }),
                ('criteriaNarrative', {
                    'type': "string",
                    'format': "markdown",
                    'description':
                    "Markdown formatted description of the criteria",
                    'required': False,
                }),
                ('tags', {
                    'type': "array",
                    'items': {
                        'type': "string",
                        'format': "string"
                    },
                    'description': "List of tags that describe the BadgeClass",
                    'required': False,
                }),
                ('alignments', {
                    'type': "array",
                    'items': {
                        '$ref': '#/definitions/BadgeClassAlignment'
                    },
                    'description':
                    "List of objects describing objectives or educational standards",
                    'required': False,
                }),
                ('expires', {
                    '$ref': "#/definitions/BadgeClassExpiration",
                    'description':
                    "Expiration period for Assertions awarded from this BadgeClass",
                    'required': False,
                }),
            ])
        })

    def to_internal_value(self, data):
        if not isinstance(data, BadgeClass) and 'expires' in data:
            if not data['expires'] or len(data['expires']) == 0:
                # if expires was included blank, remove it so to_internal_value() doesnt choke
                del data['expires']
        return super(BadgeClassSerializerV2, self).to_internal_value(data)

    def update(self, instance, validated_data):
        if 'cached_issuer' in validated_data:
            validated_data.pop('cached_issuer')  # issuer is not updatable

        if 'image' in validated_data:
            self.context['save_kwargs'] = dict(force_resize=True)

        if not IsEditor().has_object_permission(self.context.get('request'),
                                                None, instance.issuer):
            raise serializers.ValidationError({
                "issuer":
                "You do not have permission to edit badges on this issuer."
            })

        return super(BadgeClassSerializerV2,
                     self).update(instance, validated_data)

    def create(self, validated_data):
        if 'image' not in validated_data:
            raise serializers.ValidationError(
                {'image': 'Valid image file or data URI required.'})
        if 'cached_issuer' in validated_data:
            # included issuer in request
            validated_data['issuer'] = validated_data.pop('cached_issuer')
        elif 'issuer' in self.context:
            # issuer was passed in context
            validated_data['issuer'] = self.context.get('issuer')
        else:
            # issuer is required on create
            raise serializers.ValidationError(
                {"issuer": "This field is required"})

        if not IsEditor().has_object_permission(
                self.context.get('request'), None, validated_data['issuer']):
            raise serializers.ValidationError({
                "issuer":
                "You do not have permission to edit badges on this issuer."
            })

        return super(BadgeClassSerializerV2, self).create(validated_data)
Example #29
0
class CollectionSerializerV1(serializers.Serializer):
    name = StripTagsCharField(required=True, max_length=128)
    slug = StripTagsCharField(required=False, max_length=128, source='entity_id')
    description = StripTagsCharField(required=False, allow_blank=True, allow_null=True, max_length=255)
    share_hash = serializers.CharField(read_only=True)
    share_url = serializers.CharField(read_only=True, max_length=1024)
    badges = CollectionBadgeSerializerV1(
        read_only=False, many=True, required=False, source='cached_collects'
    )
    published = serializers.BooleanField(required=False)

    class Meta:
        apispec_definition = ('Collection', {})

    def to_representation(self, instance):
        representation = super(CollectionSerializerV1, self).to_representation(instance)
        if representation.get('share_url', None) is None:
            #  V1 api expects share_url to be an empty string and not null if unpublished
            representation['share_url'] = ""
        return representation

    def create(self, validated_data):
        owner = validated_data.get('created_by', self.context.get('user', None))
        new_collection = BackpackCollection.objects.create(
            name=validated_data.get('name'),
            description=validated_data.get('description', ''),
            created_by=owner
        )
        published = validated_data.get('published', False)
        if published:
            new_collection.published = published
            new_collection.save()

        for collect in validated_data.get('cached_collects', []):
            collect.collection = new_collection
            collect.badgeuser = new_collection.created_by
            collect.save()

        return new_collection

    def update(self, instance, validated_data):
        instance.name = validated_data.get('name', instance.name)
        instance.description = validated_data.get('description', instance.description)

        if instance.published == False and validated_data['published'] == True:
            for badge in instance.badge_items:
                badge.public = True
                badge.save()
        instance.published = validated_data.get('published', instance.published)

        if 'cached_collects' in validated_data\
                and validated_data['cached_collects'] is not None:

            existing_entries = list(instance.cached_collects())
            updated_ids = set()

            for entry in validated_data['cached_collects']:
                if not entry.pk:
                    entry.save()
                updated_ids.add(entry.pk)

            for old_entry in existing_entries:
                if old_entry.pk not in updated_ids:
                    old_entry.delete()

        instance.save()
        return instance
Example #30
0
class IssuerSerializerV2(DetailSerializerV2, OriginalJsonSerializerMixin):
    openBadgeId = serializers.URLField(source='jsonld_id', read_only=True)
    createdAt = serializers.DateTimeField(source='created_at', read_only=True)
    createdBy = EntityRelatedFieldV2(source='cached_creator', read_only=True)
    name = StripTagsCharField(max_length=1024)
    image = ValidImageField(required=False)
    email = serializers.EmailField(max_length=255, required=True)
    description = StripTagsCharField(max_length=16384, required=False)
    url = serializers.URLField(max_length=1024, required=True)
    staff = IssuerStaffSerializerV2(many=True,
                                    source='staff_items',
                                    required=False)
    extensions = serializers.DictField(source='extension_items',
                                       required=False,
                                       validators=[BadgeExtensionValidator()])

    class Meta(DetailSerializerV2.Meta):
        model = Issuer
        apispec_definition = ('Issuer', {
            'properties':
            OrderedDict([
                ('entityId', {
                    'type': "string",
                    'format': "string",
                    'description': "Unique identifier for this Issuer",
                }),
                ('entityType', {
                    'type': "string",
                    'format': "string",
                    'description': "\"Issuer\"",
                }),
                ('openBadgeId', {
                    'type': "string",
                    'format': "url",
                    'description': "URL of the OpenBadge compliant json",
                }),
                ('createdAt', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the Issuer was created",
                }),
                ('createdBy', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeUser who created this Issuer",
                }),
                ('name', {
                    'type': "string",
                    'format': "string",
                    'description': "Name of the Issuer",
                }),
                ('image', {
                    'type':
                    "string",
                    'format':
                    "data:image/png;base64",
                    'description':
                    "Base64 encoded string of an image that represents the Issuer",
                }),
                ('email', {
                    'type': "string",
                    'format': "email",
                    'description': "Contact email for the Issuer",
                }),
                ('url', {
                    'type':
                    "string",
                    'format':
                    "url",
                    'description':
                    "Homepage or website associated with the Issuer",
                }),
                ('description', {
                    'type': "string",
                    'format': "text",
                    'description': "Short description of the Issuer",
                }),
            ])
        })

    def validate_image(self, image):
        if image is not None:
            img_name, img_ext = os.path.splitext(image.name)
            image.name = 'issuer_logo_' + str(uuid.uuid4()) + img_ext
        return image

    def create(self, validated_data):
        staff = validated_data.pop('staff_items', [])
        new_issuer = super(IssuerSerializerV2, self).create(validated_data)

        # update staff after issuer is created
        new_issuer.staff_items = staff

        # set badgrapp
        new_issuer.badgrapp = BadgrApp.objects.get_current(
            self.context.get('request', None))

        return new_issuer