Пример #1
0
class BackpackImportSerializerV2(DetailSerializerV2):
    url = serializers.URLField(required=False)
    image = ValidImageField(required=False)
    assertion = serializers.DictField(required=False)

    def validate(self, attrs):
        # TODO: when test is run, why is assertion field blank???
        if sum(1 if v else 0 for v in attrs.values()) != 1:
            raise serializers.ValidationError(
                "Must provide only one of 'url', 'image' or 'assertion'.")
        return attrs

    def create(self, validated_data):
        try:
            validated_data['imagefile'] = validated_data.pop('image', None)
            instance, created = BadgeCheckHelper.get_or_create_assertion(
                **validated_data)
            if not created:
                if instance.acceptance == BadgeInstance.ACCEPTANCE_ACCEPTED:
                    raise RestframeworkValidationError([{
                        'name':
                        "DUPLICATE_BADGE",
                        'description':
                        "You already have this badge in your backpack"
                    }])
                instance.acceptance = BadgeInstance.ACCEPTANCE_ACCEPTED
                instance.save()
        except DjangoValidationError as e:
            raise RestframeworkValidationError(e.messages)
        return instance
Пример #2
0
class BadgeClassLtiContextStudentSerializer(serializers.ModelSerializer):
    badgeClassEntityId = serializers.CharField(source='badge_class.entity_id')
    contextId = serializers.CharField(source='context_id')
    name = serializers.CharField(source='badge_class.name')
    image = ValidImageField(source='badge_class.image')
    requested = serializers.SerializerMethodField()
    rewarded = serializers.SerializerMethodField()
    denied = serializers.SerializerMethodField()
    revoked = serializers.SerializerMethodField()

    class Meta:
        model = BadgeClassLtiContext
        fields = [
            'badgeClassEntityId', 'contextId', 'name', 'image', 'requested',
            'rewarded', 'denied', 'revoked'
        ]

    def get_requested(self, obj):
        user = self.context['request'].user
        if user.has_edu_id_social_account() and not self.get_revoked(obj):
            if StudentsEnrolled.objects.filter(user=user,
                                               badge_class=obj.badge_class,
                                               date_awarded__isnull=True,
                                               denied=False).exists():
                return True
        return False

    def get_rewarded(self, obj):
        user = self.context['request'].user
        if user.has_edu_id_social_account():
            if StudentsEnrolled.objects.filter(user=user, badge_class=obj.badge_class,
                                               date_awarded__isnull=False, denied=False).exists() \
                    and not self.get_revoked(obj):
                return True
        return False

    def get_denied(self, obj):
        user = self.context['request'].user
        if user.has_edu_id_social_account():
            if StudentsEnrolled.objects.filter(user=user,
                                               badge_class=obj.badge_class,
                                               denied=True).exists():
                return True
        return False

    def get_revoked(self, obj):
        user = self.context['request'].user
        if user.has_edu_id_social_account():

            assertions = StudentsEnrolled.objects.filter(
                user=user, badge_class=obj.badge_class).all()
            if len(assertions) > 0:
                assertion = assertions[0]
            else:
                return False
            if assertion.assertion_is_revoked():
                return True
        return False
Пример #3
0
class BadgeClassLtiContextSerializer(serializers.ModelSerializer):
    badgeClassEntityId = serializers.CharField(source='badge_class.entity_id')
    contextId = serializers.CharField(source='context_id')
    name = serializers.CharField(source='badge_class.name')
    image = ValidImageField(source='badge_class.image')

    class Meta:
        model = BadgeClassLtiContext
        fields = ['badgeClassEntityId', 'contextId', 'name', 'image']
Пример #4
0
class BackpackImportSerializerV2(DetailSerializerV2):
    url = serializers.URLField(required=False)
    image = ValidImageField(required=False)
    assertion = serializers.DictField(required=False)

    def validate(self, attrs):
        if sum(1 if v else 0 for v in attrs.values()) != 1:
            raise serializers.ValidationError(
                "Must provide only one of 'url', 'image' or 'assertion'.")
        return attrs

    def create(self, validated_data):
        try:
            instance, created = BadgeCheckHelper.get_or_create_assertion(
                **validated_data)
        except DjangoValidationError as e:
            raise RestframeworkValidationError(e.messages)
        return instance
Пример #5
0
class BadgeClassLtiContextSerializer(BadgrBaseModelSerializer):
    badgeClassEntityId = serializers.CharField(source='badge_class.entity_id')
    contextId = serializers.CharField(source='context_id')
    name = serializers.CharField(source='badge_class.name')
    image = ValidImageField(source='badge_class.image')
    issuer_slug = serializers.CharField(source='badge_class.issuer.entity_id')
    can_award = serializers.SerializerMethodField()

    class Meta:
        model = BadgeClassLtiContext
        fields = ['badgeClassEntityId','contextId','name','image','issuer_slug','can_award']

    def get_can_award(self,obj):
        user = self.context['request'].user
        if user in obj.badge_class.issuer.staff.all():
            if IssuerStaff.objects.filter(user=user,issuer=obj.badge_class.issuer).all()[0].role in \
                    [IssuerStaff.ROLE_OWNER ,IssuerStaff.ROLE_EDITOR,IssuerStaff.ROLE_STAFF]:
                return True
        return False
Пример #6
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
Пример #7
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
Пример #8
0
class BadgeInstanceSerializerV2(DetailSerializerV2, OriginalJsonSerializerMixin):
    openBadgeId = serializers.URLField(source='jsonld_id', read_only=True)
    createdAt = DateTimeWithUtcZAtEndField(source='created_at', read_only=True, default_timezone=pytz.utc)
    createdBy = EntityRelatedFieldV2(source='cached_creator', read_only=True)
    badgeclass = EntityRelatedFieldV2(source='cached_badgeclass', required=False, queryset=BadgeClass.cached)
    badgeclassOpenBadgeId = CachedUrlHyperlinkedRelatedField(
        source='badgeclass_jsonld_id', view_name='badgeclass_json', lookup_field='entity_id',
        queryset=BadgeClass.cached, required=False)
    badgeclassName = serializers.CharField(write_only=True, required=False)

    issuer = EntityRelatedFieldV2(source='cached_issuer', required=False, queryset=Issuer.cached)
    issuerOpenBadgeId = serializers.URLField(source='issuer_jsonld_id', read_only=True)

    image = ValidImageField(read_only=True, use_public=True, source='*')
    recipient = BadgeRecipientSerializerV2(source='*', required=False)

    issuedOn = DateTimeWithUtcZAtEndField(source='issued_on', required=False, default_timezone=pytz.utc)
    narrative = MarkdownCharField(required=False, allow_null=True)
    evidence = EvidenceItemSerializerV2(source='evidence_items', many=True, required=False)

    revoked = HumanReadableBooleanField(read_only=True)
    revocationReason = serializers.CharField(source='revocation_reason', read_only=True)
    acceptance = serializers.CharField(read_only=True)

    expires = DateTimeWithUtcZAtEndField(source='expires_at', required=False, allow_null=True, default_timezone=pytz.utc)

    notify = HumanReadableBooleanField(write_only=True, required=False, default=False)
    allowDuplicateAwards = serializers.BooleanField(write_only=True, required=False, default=True)

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

    class Meta(DetailSerializerV2.Meta):
        model = BadgeInstance
        apispec_definition = ('Assertion', {
            'properties': OrderedDict([
                ('entityId', {
                    'type': "string",
                    'format': "string",
                    'description': "Unique identifier for this Assertion",
                    'readOnly': True,
                }),
                ('entityType', {
                    'type': "string",
                    'format': "string",
                    'description': "\"Assertion\"",
                    '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 Assertion was created",
                    'readOnly': True,
                }),
                ('createdBy', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeUser who created the Assertion",
                    'readOnly': True,
                }),

                ('badgeclass', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeClass that issued this Assertion",
                    'required': False,
                }),
                ('badgeclassOpenBadgeId', {
                    'type': 'string',
                    'format': 'url',
                    'description': "URL of the BadgeClass to award",
                    'required': False,
                }),
                ('badgeclassName', {
                    'type': 'string',
                    'format': 'string',
                    'description': "Name of BadgeClass to create assertion against, case insensitive",
                    'required': False,
                }),
                ('revoked', {
                    'type': 'boolean',
                    'description': "True if this Assertion has been revoked",
                    'readOnly': True,
                }),
                ('revocationReason', {
                    'type': 'string',
                    'format': "string",
                    'description': "Short description of why the Assertion was revoked",
                    'readOnly': True,
                }),
                ('acceptance', {
                    'type': 'string',
                    'description': "Recipient interaction with Assertion. One of: Unaccepted, Accepted, or Rejected",
                    'readOnly': True,
                }),
                ('image', {
                    'type': 'string',
                    'format': 'url',
                    'description': "URL to the baked assertion image",
                    'readOnly': True,
                }),
                ('issuedOn', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the Assertion was issued",
                    'required': False,
                }),
                ('narrative', {
                    'type': 'string',
                    'format': 'markdown',
                    'description': "Markdown narrative of the achievement",
                    'required': False,
                }),
                ('evidence', {
                    'type': 'array',
                    'items': {
                        '$ref': '#/definitions/AssertionEvidence'
                    },
                    'description': "List of evidence associated with the achievement",
                    'required': False,
                }),
                ('recipient', {
                    'type': 'object',
                    '$ref': '#/definitions/BadgeRecipient',
                    'description': "Recipient that was issued the Assertion",
                    'required': False,
                }),
                ('expires', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the Assertion expires",
                    'required': False,
                }),
            ])
        })

    def validate_issuedOn(self, value):
        if value > timezone.now():
            raise serializers.ValidationError("Only issuedOn dates in the past are acceptable.")
        if value.year < 1583:
            raise serializers.ValidationError("Only issuedOn dates after the introduction of the Gregorian calendar are allowed.")
        return value

    def update(self, instance, validated_data):
        updateable_fields = [
            'evidence_items',
            'expires_at',
            'extension_items',
            'hashed',
            'issued_on',
            'narrative',
            'recipient_identifier',
            'recipient_type'
        ]

        for field_name in updateable_fields:
            if field_name in validated_data:
                setattr(instance, field_name, validated_data.get(field_name))
        instance.rebake(save=True)

        return instance

    def create(self, validated_data):
        if 'cached_issuer' in validated_data:
            # ignore issuer in request
            validated_data.pop('cached_issuer')
        return super().create(validated_data)

    def validate(self, data):
        request = self.context.get('request', None)
        expected_issuer = self.context.get('kwargs', {}).get('issuer')
        badgeclass_identifiers = ['badgeclass_jsonld_id', 'badgeclassName', 'cached_badgeclass', 'badgeclass']
        badge_instance_properties = list(data.keys())

        if 'badgeclass' in self.context:
            badge_instance_properties.append('badgeclass')

        if sum([el in badgeclass_identifiers for el in badge_instance_properties]) > 1:
            raise serializers.ValidationError('Multiple badge class identifiers. Exactly one of the following badge class identifiers are allowed: badgeclass, badgeclassName, or badgeclassOpenBadgeId')

        if request and request.method != 'PUT':
            # recipient and badgeclass are only required on create, ignored on update
            if 'recipient_identifier' not in data:
                raise serializers.ValidationError({'recipient': ["This field is required"]})

            if 'cached_badgeclass' in data:
                # included badgeclass in request
                data['badgeclass'] = data.pop('cached_badgeclass')
            elif 'badgeclass' in self.context:
                # badgeclass was passed in context
                data['badgeclass'] = self.context.get('badgeclass')
            elif 'badgeclass_jsonld_id' in data:
                data['badgeclass'] = data.pop('badgeclass_jsonld_id')
            elif 'badgeclassName' in data:
                name = data.pop('badgeclassName')
                matches = BadgeClass.objects.filter(name=name, issuer=expected_issuer)
                len_matches = len(matches)
                if len_matches == 1:
                    data['badgeclass'] = matches.first()
                elif len_matches == 0:
                    raise serializers.ValidationError("No matching BadgeClass found with name {}".format(name))
                else:
                    raise serializers.ValidationError("Could not award; {} BadgeClasses with name {}".format(len_matches, name))
            else:
                raise serializers.ValidationError({"badgeclass": ["This field is required"]})

            allow_duplicate_awards = data.pop('allowDuplicateAwards')
            if allow_duplicate_awards is False:
                previous_awards = BadgeInstance.objects.filter(
                    recipient_identifier=data['recipient_identifier'], badgeclass=data['badgeclass']
                ).filter(
                    revoked=False
                ).filter(
                    Q(expires_at__isnull=True) | Q(expires_at__gt=timezone.now())
                )
                if previous_awards.exists():
                    raise serializers.ValidationError(
                        "A previous award of this badge already exists for this recipient.")

        if expected_issuer and data['badgeclass'].issuer_id != expected_issuer.id:
            raise serializers.ValidationError({"badgeclass": ["Could not find matching badgeclass for this issuer."]})

        if 'badgeclass' in data:
            data['issuer'] = data['badgeclass'].issuer

        return data
Пример #9
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
Пример #10
0
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)
Пример #11
0
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)
Пример #12
0
class IssuerSerializer(OriginalJsonSerializerMixin, ExtensionsSaverMixin,
                       InternalValueErrorOverrideMixin,
                       serializers.Serializer):
    created_at = serializers.DateTimeField(read_only=True)
    created_by = BadgeUserIdentifierField()
    name = StripTagsCharField(max_length=1024)
    entity_id = StripTagsCharField(max_length=255, read_only=True)
    image = ValidImageField(required=False)
    email = serializers.EmailField(max_length=255, required=True)
    description_english = StripTagsCharField(max_length=16384, required=False)
    description_dutch = StripTagsCharField(max_length=16384, required=False)
    url = serializers.URLField(max_length=1024, required=True)
    faculty = FacultySlugRelatedField(slug_field='entity_id', required=True)
    extensions = serializers.DictField(source='extension_items',
                                       required=False,
                                       validators=[BadgeExtensionValidator()])

    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_permissions = validated_data['faculty'].get_permissions(
            validated_data['created_by'])
        if user_permissions['may_create']:
            new_issuer = Issuer(**validated_data)
            # set badgrapp
            new_issuer.badgrapp = BadgrApp.objects.get_current(
                self.context.get('request', None))
            try:
                new_issuer.save()
            except IntegrityError:
                raise BadgrValidationFieldError(
                    'name',
                    "There is already an Issuer with this name inside this Issuer group",
                    908)
            return new_issuer
        else:
            raise BadgrValidationError(
                "You don't have the necessary permissions", 100)

    def update(self, instance, validated_data):
        if instance.assertions and instance.name != validated_data["name"]:
            raise BadgrValidationError(
                "Cannot change the name, assertions have already been issued within this entity",
                214)
        [
            setattr(instance, attr, validated_data.get(attr))
            for attr in validated_data
        ]
        self.save_extensions(validated_data, instance)
        if not instance.badgrapp_id:
            instance.badgrapp = BadgrApp.objects.get_current(
                self.context.get('request', None))
        try:
            instance.save()
        except IntegrityError:
            raise BadgrValidationFieldError(
                'name',
                "There is already an Issuer with this name inside this Issuer group",
                908)
        return instance

    def to_representation(self, obj):
        representation = super(IssuerSerializer, 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'] = BadgeClassSerializer(
                obj.badgeclasses.all(), many=True, context=self.context).data
        if not representation['image']:
            representation['image'] = obj.institution.image_url()
        return representation

    def to_internal_value_error_override(self, data):
        """Function used in combination with the InternalValueErrorOverrideMixin to override serializer excpetions when
        data is internalised (i.e. the to_internal_value() method is called)"""
        errors = OrderedDict()
        if data.get('email', False):
            try:
                EmailValidator().__call__(data.get('email'))
            except ValidationError:
                e = OrderedDict([
                    ('email',
                     [ErrorDetail('Enter a valid email address.', code=509)])
                ])
                errors = OrderedDict(chain(errors.items(), e.items()))
        return errors

    def add_extensions(self, instance, add_these_extensions, extension_items):
        for extension_name in add_these_extensions:
            original_json = extension_items[extension_name]
            extension = IssuerExtension(
                name=extension_name,
                original_json=json.dumps(original_json),
                issuer_id=instance.pk)
            extension.save()
Пример #13
0
class InstitutionSerializer(InternalValueErrorOverrideMixin,
                            serializers.Serializer):
    description_english = StripTagsCharField(max_length=256,
                                             required=False,
                                             allow_null=True,
                                             allow_blank=True)
    description_dutch = StripTagsCharField(max_length=256,
                                           required=False,
                                           allow_null=True,
                                           allow_blank=True)
    entity_id = StripTagsCharField(max_length=255, read_only=True)
    image_english = ValidImageField(required=False, allow_null=True)
    image_dutch = ValidImageField(required=False, allow_null=True)
    brin = serializers.CharField(read_only=True)
    name_english = serializers.CharField(max_length=254,
                                         required=False,
                                         allow_null=True,
                                         allow_blank=True)
    name_dutch = serializers.CharField(max_length=254,
                                       required=False,
                                       allow_null=True,
                                       allow_blank=True)
    grading_table = serializers.URLField(max_length=254,
                                         required=False,
                                         allow_null=True,
                                         allow_blank=True)
    award_allowed_institutions = PrimaryKeyRelatedField(
        many=True, queryset=Institution.objects.all(), 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_english' in validated_data:
            instance.image_english = validated_data.get('image_english')
        if 'image_dutch' in validated_data:
            instance.image_dutch = validated_data.get('image_dutch')
        instance.grading_table = validated_data.get('grading_table')
        instance.name_english = validated_data.get('name_english')
        instance.name_dutch = validated_data.get('name_dutch')
        instance.award_allowed_institutions.set(
            validated_data.get('award_allowed_institutions', []))
        instance.save()
        return instance

    def to_internal_value_error_override(self, data):
        """Function used in combination with the InternalValueErrorOverrideMixin to override serializer exceptions when
        data is internalised (i.e. the to_internal_value() method is called)"""
        errors = OrderedDict()
        institution = self.context['request'].user.institution
        if institution.default_language == institution.DEFAULT_LANGUAGE_ENGLISH:
            if not data.get('name_english', False):
                e = OrderedDict([
                    ('name_english',
                     [ErrorDetail('English name is required', code=924)])
                ])
                errors = OrderedDict(chain(errors.items(), e.items()))
            if not data.get('image_english', False):
                e = OrderedDict([
                    ('image_english',
                     [ErrorDetail('English image is required', code=926)])
                ])
                errors = OrderedDict(chain(errors.items(), e.items()))
            if not data.get('description_english', False):
                e = OrderedDict([('description_english', [
                    ErrorDetail('English description is required', code=925)
                ])])
                errors = OrderedDict(chain(errors.items(), e.items()))
        if institution.default_language == institution.DEFAULT_LANGUAGE_DUTCH:
            if not data.get('name_dutch', False):
                e = OrderedDict([
                    ('name_dutch',
                     [ErrorDetail('Dutch name is required', code=912)])
                ])
                errors = OrderedDict(chain(errors.items(), e.items()))
            if not data.get('image_dutch', False):
                e = OrderedDict([
                    ('image_dutch',
                     [ErrorDetail('Dutch image is required', code=918)])
                ])
                errors = OrderedDict(chain(errors.items(), e.items()))
            if not data.get('description_dutch', False):
                e = OrderedDict([
                    ('description_dutch',
                     [ErrorDetail('Dutch description is required', code=913)])
                ])
                errors = OrderedDict(chain(errors.items(), e.items()))
        if institution.institution_type != 'MBO' and not data.get(
                'grading_table', False):
            e = OrderedDict([
                ('grading_table',
                 [ErrorDetail('Grading Table is required', code=903)])
            ])
            errors = OrderedDict(chain(errors.items(), e.items()))
        return errors
Пример #14
0
class IssuerSerializer(OriginalJsonSerializerMixin, ExtensionsSaverMixin,
                       InternalValueErrorOverrideMixin,
                       serializers.Serializer):
    created_at = serializers.DateTimeField(read_only=True)
    created_by = BadgeUserIdentifierField()
    name_english = StripTagsCharField(max_length=1024,
                                      required=False,
                                      allow_null=True,
                                      allow_blank=True)
    name_dutch = StripTagsCharField(max_length=1024,
                                    required=False,
                                    allow_null=True,
                                    allow_blank=True)
    entity_id = StripTagsCharField(max_length=255, read_only=True)
    image_english = ValidImageField(required=False)
    image_dutch = ValidImageField(required=False)
    email = serializers.EmailField(max_length=255, required=True)
    description_english = StripTagsCharField(max_length=16384,
                                             required=False,
                                             allow_null=True,
                                             allow_blank=True)
    description_dutch = StripTagsCharField(max_length=16384,
                                           required=False,
                                           allow_null=True,
                                           allow_blank=True)
    url_english = serializers.URLField(max_length=1024,
                                       required=False,
                                       allow_null=True,
                                       allow_blank=True)
    url_dutch = serializers.URLField(max_length=1024,
                                     required=False,
                                     allow_null=True,
                                     allow_blank=True)
    faculty = FacultySlugRelatedField(slug_field='entity_id', required=True)
    extensions = serializers.DictField(source='extension_items',
                                       required=False,
                                       validators=[BadgeExtensionValidator()])

    def _validate_image(self, image):
        img_name, img_ext = os.path.splitext(image.name)
        image.name = 'issuer_logo_' + str(uuid.uuid4()) + img_ext
        image = resize_image(image)
        app = BadgrApp.objects.get_current(self.context.get('request', None))
        if app.is_demo_environment:
            image = add_watermark(image)
        if verify_svg(image):
            image = scrub_svg_image(image)
        return image

    def validate_image_english(self, image_english):
        if image_english is not None:
            image_english = self._validate_image(image_english)
        return image_english

    def validate_image_dutch(self, image_dutch):
        if image_dutch is not None:
            image_dutch = self._validate_image(image_dutch)
        return image_dutch

    def create(self, validated_data, **kwargs):
        user_permissions = validated_data['faculty'].get_permissions(
            validated_data['created_by'])
        if user_permissions['may_create']:
            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
        else:
            raise BadgrValidationError(
                "You don't have the necessary permissions", 100)

    def update(self, instance, validated_data):
        if instance.assertions and instance.name_english and instance.name_english != validated_data[
                "name_english"]:
            raise BadgrValidationError(
                "Cannot change the name, assertions have already been issued within this entity",
                214)
        if instance.assertions and instance.name_dutch and instance.name_dutch != validated_data[
                "name_dutch"]:
            raise BadgrValidationError(
                "Cannot change the name, assertions have already been issued within this entity",
                214)
        [
            setattr(instance, attr, validated_data.get(attr))
            for attr in validated_data
        ]
        self.save_extensions(validated_data, instance)
        if not instance.badgrapp_id:
            instance.badgrapp = BadgrApp.objects.get_current(
                self.context.get('request', None))
        instance.save()
        return instance

    def to_representation(self, obj):
        representation = super(IssuerSerializer, 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'] = BadgeClassSerializer(
                obj.badgeclasses.all(), many=True, context=self.context).data
        if not representation['image_english']:
            representation['image_english'] = obj.institution.image_url()
        if not representation['image_dutch']:
            representation['image_dutch'] = obj.institution.image_url()
        return representation

    def to_internal_value_error_override(self, data):
        """Function used in combination with the InternalValueErrorOverrideMixin to override serializer exceptions
        before the instance is saved (i.e. the save() method is called)"""
        errors = OrderedDict()
        institution = self.context['request'].user.institution
        if institution.default_language == institution.DEFAULT_LANGUAGE_DUTCH:
            if not data.get('name_dutch', False):
                e = OrderedDict([
                    ('name_dutch',
                     [ErrorDetail('Dutch name is required', code=912)])
                ])
                errors = OrderedDict(chain(errors.items(), e.items()))
            if not data.get('description_dutch', False):
                e = OrderedDict([
                    ('description_dutch',
                     [ErrorDetail('Dutch description is required', code=913)])
                ])
                errors = OrderedDict(chain(errors.items(), e.items()))
            if not data.get('url_dutch', False):
                e = OrderedDict([
                    ('url_dutch',
                     [ErrorDetail('Dutch url is required', code=915)])
                ])
                errors = OrderedDict(chain(errors.items(), e.items()))
        if institution.default_language == institution.DEFAULT_LANGUAGE_ENGLISH:
            if not data.get('name_english', False):
                e = OrderedDict([
                    ('name_english',
                     [ErrorDetail('English name is required', code=924)])
                ])
                errors = OrderedDict(chain(errors.items(), e.items()))
            if not data.get('description_english', False):
                e = OrderedDict([('description_english', [
                    ErrorDetail('English description is required', code=925)
                ])])
                errors = OrderedDict(chain(errors.items(), e.items()))
            if not data.get('url_english', False):
                e = OrderedDict([
                    ('url_english',
                     [ErrorDetail('English url is required', code=923)])
                ])
                errors = OrderedDict(chain(errors.items(), e.items()))
        return errors

    def add_extensions(self, instance, add_these_extensions, extension_items):
        for extension_name in add_these_extensions:
            original_json = extension_items[extension_name]
            extension = IssuerExtension(
                name=extension_name,
                original_json=json.dumps(original_json),
                issuer_id=instance.pk)
            extension.save()
Пример #15
0
class BadgeClassSerializer(OriginalJsonSerializerMixin, ExtensionsSaverMixin,
                           InternalValueErrorOverrideMixin,
                           serializers.Serializer):
    created_at = serializers.DateTimeField(read_only=True)
    created_by = BadgeUserIdentifierField()
    name = StripTagsCharField(max_length=255)
    image = ValidImageField(required=True)
    formal = serializers.BooleanField(required=True)
    is_private = serializers.BooleanField(required=False, default=False)
    narrative_required = serializers.BooleanField(required=False,
                                                  default=False)
    evidence_required = serializers.BooleanField(required=False, default=False)
    entity_id = StripTagsCharField(max_length=255, read_only=True)
    issuer = IssuerSlugRelatedField(slug_field='entity_id', required=True)
    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()])
    description = StripTagsCharField(max_length=16384,
                                     required=True,
                                     convert_null=True)
    alignments = AlignmentItemSerializer(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()])
    expiration_period = PeriodField(required=False)
    award_allowed_institutions = PrimaryKeyRelatedField(
        many=True, queryset=Institution.objects.all(), required=False)

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

    def get_expiration_period(self, instance):
        if instance.expiration_period:
            return instance.expiration_period.days

    def to_representation(self, instance):
        representation = super(BadgeClassSerializer,
                               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
            image = resize_image(image)
            app = BadgrApp.objects.get_current(
                self.context.get('request', None))
            if app.is_demo_environment:
                image = add_watermark(image)
            if verify_svg(image):
                image = scrub_svg_image(image)
        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_name(self, name):
        return strip_tags(name)

    def validate_description(self, description):
        return strip_tags(description)

    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'):
                    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):
        if instance.assertions and len(
            [ass for ass in instance.assertions if not ass.revoked]) > 0:
            raise BadgrValidationError(
                error_code=999,
                error_message=
                "Cannot change any value, assertions have already been issued")
        self.save_extensions(validated_data, instance)
        for key, value in validated_data.items():
            if key is not 'award_allowed_institutions':
                setattr(instance, key, value)
        instance.award_allowed_institutions.set(
            validated_data.get('award_allowed_institutions', []))
        try:
            instance.save()
        except IntegrityError:
            raise BadgrValidationFieldError(
                'name',
                "There is already a Badgeclass with this name inside this Issuer",
                911)
        return instance

    def validate_alignments(self, alignments):
        alignment_max = 8
        if alignments.__len__() >= alignment_max:
            raise BadgrValidationFieldError(
                'alignments',
                "There are too many Related educational framework objects, the maximum is {}."
                .format(str(alignment_max), 922))
        return alignments

    def to_internal_value_error_override(self, data):
        """Function used in combination with the InternalValueErrorOverrideMixin to override serializer excpetions when
        data is internalised (i.e. the to_internal_value() method is called)"""
        errors = OrderedDict()
        if not data.get('criteria_text', False) and not data.get(
                'criteria_url', False):
            e = OrderedDict([
                ('criteria_text', [
                    ErrorDetail(
                        'Either criteria_url or criteria_text is required',
                        code=905)
                ]),
                ('criteria_url', [
                    ErrorDetail(
                        'Either criteria_url or criteria_text is required',
                        code=905)
                ])
            ])
            errors = OrderedDict(chain(errors.items(), e.items()))
        if data.get('criteria_url', False):
            if not utils.is_probable_url(data.get('criteria_url')):
                e = OrderedDict([
                    ('criteria_url',
                     [ErrorDetail('Must be a proper url.', code=902)])
                ])
                errors = OrderedDict(chain(errors.items(), e.items()))
        if data.get('name') == settings.EDUID_BADGE_CLASS_NAME:
            e = OrderedDict([('name', [
                ErrorDetail(
                    f"{settings.EDUID_BADGE_CLASS_NAME} is a reserved name for badgeclasses",
                    code=907)
            ])])
            errors = OrderedDict(chain(errors.items(), e.items()))
        return errors

    def create(self, validated_data, **kwargs):
        user_permissions = validated_data['issuer'].get_permissions(
            validated_data['created_by'])
        if user_permissions['may_create']:
            if validated_data['formal'] and not validated_data[
                    'issuer'].faculty.institution.grondslag_formeel:
                raise BadgrValidationError(
                    "Cannot create a formal badgeclass for an institution without the judicial basis for formal badges",
                    215)
            if not validated_data['formal'] and not validated_data[
                    'issuer'].faculty.institution.grondslag_informeel:
                raise BadgrValidationError(
                    "Cannot create an informal badgeclass for an institution without the judicial basis for informal badges",
                    216)
            try:
                new_badgeclass = BadgeClass.objects.create(**validated_data)
            except IntegrityError:
                raise BadgrValidationFieldError(
                    'name',
                    "There is already a Badgeclass with this name inside this Issuer",
                    911)
            return new_badgeclass
        else:
            raise BadgrValidationError(
                "You don't have the necessary permissions", 100)
class IssuerSerializerV1(OriginalJsonSerializerMixin, ExtensionsSaverMixin,
                         serializers.Serializer):
    created_at = serializers.DateTimeField(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)
    faculty = FacultySerializerV1(required=False, allow_null=True)
    extensions = serializers.DictField(source='extension_items',
                                       required=False,
                                       validators=[BadgeExtensionValidator()])

    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):
        if self.context['request'].data.get('faculty', None):
            faculty_id = self.context['request'].data['faculty']['id']
            faculty = Faculty.objects.get(pk=faculty_id)
            validated_data['faculty'] = faculty
        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):
        if self.context['request'].data['faculty']:
            faculty_id = self.context['request'].data['faculty']['id']
            faculty = Faculty.objects.get(pk=faculty_id)
            validated_data['faculty'] = faculty

        instance.name = validated_data.get('name')

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

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

        # set badgrapp
        if not instance.badgrapp_id:
            instance.badgrapp = BadgrApp.objects.get_current(
                self.context.get('request', None))
        self.save_extensions(validated_data, instance)
        instance.save()
        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

    def add_extensions(self, instance, add_these_extensions, extension_items):
        for extension_name in add_these_extensions:
            original_json = extension_items[extension_name]
            extension = IssuerExtension(
                name=extension_name,
                original_json=json.dumps(original_json),
                issuer_id=instance.pk)
            extension.save()
class BadgeClassSerializerV1(OriginalJsonSerializerMixin, ExtensionsSaverMixin,
                             serializers.Serializer):
    created_at = serializers.DateTimeField(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)
    enrollment_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)
    extensions = serializers.DictField(source='extension_items',
                                       required=False,
                                       validators=[BadgeExtensionValidator()])

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

    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 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):

        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')

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

        self.save_extensions(validated_data, instance)

        instance.save()

        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, unicode)):
                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 filter_extensions(self, validated_data):
        extension_items = validated_data['extension_items']
        for extension_name in extension_items.keys():
            extension_item = extension_items[extension_name]
            if extension_name == 'languageExtension':
                del extension_item['typedLanguage']

    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')

        self.filter_extensions(validated_data)
        new_badgeclass = BadgeClass.objects.create(**validated_data)
        return new_badgeclass
Пример #18
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
Пример #19
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)
Пример #20
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