コード例 #1
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 = serializers.FileField(read_only=True)
    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 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
コード例 #2
0
class BadgeInstanceSerializerV2(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)
    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)

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

    image = serializers.FileField(read_only=True)
    recipient = BadgeRecipientSerializerV2(source='*', required=False)

    issuedOn = serializers.DateTimeField(source='issued_on', required=False)
    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)

    expires = serializers.DateTimeField(source='expires_at',
                                        required=False,
                                        allow_null=True)

    notify = HumanReadableBooleanField(write_only=True,
                                       required=False,
                                       default=False)

    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",
                }),
                ('entityType', {
                    'type': "string",
                    'format': "string",
                    'description': "\"Assertion\"",
                }),
                ('openBadgeId', {
                    'type': "string",
                    'format': "url",
                    'description': "URL of the OpenBadge compliant json",
                }),
                ('createdAt', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the Assertion was created",
                }),
                ('createdBy', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeUser who created the Assertion",
                }),
                ('badgeclass', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeClass that issued this Assertion",
                }),
                ('badgeclassOpenBadgeId', {
                    'type': 'string',
                    'format': 'url',
                    'description': "URL of the BadgeClass to award",
                }),
                ('revoked', {
                    'type': 'boolean',
                    'description': "True if this Assertion has been revoked",
                }),
                ('revocationReason', {
                    'type':
                    'string',
                    'format':
                    "string",
                    'description':
                    "Short description of why the Assertion was revoked",
                }),
                ('image', {
                    'type': 'string',
                    'format': 'url',
                    'description': "URL to the baked assertion image",
                }),
                ('issuedOn', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the Assertion was issued",
                }),
                ('narrative', {
                    'type': 'string',
                    'format': 'markdown',
                    'description': "Markdown narrative of the achievement",
                }),
                ('evidence', {
                    'type':
                    'array',
                    'items': {
                        '$ref': '#/definitions/AssertionEvidence'
                    },
                    'description':
                    "List of evidence associated with the achievement"
                }),
                ('recipient', {
                    'type':
                    'object',
                    'properties':
                    OrderedDict([
                        ('identity', {
                            'type':
                            'string',
                            'format':
                            'string',
                            'description':
                            'Either the hash of the identity or the plaintext value'
                        }),
                        ('type', {
                            'type':
                            'string',
                            'enum': [
                                c[0]
                                for c in BadgeInstance.RECIPIENT_TYPE_CHOICES
                            ],
                            'description':
                            "Type of identifier used to identify recipient"
                        }),
                        ('hashed', {
                            'type':
                            'boolean',
                            'description':
                            "Whether or not the identity value is hashed."
                        }),
                        ('plaintextIdentity', {
                            'type': 'string',
                            'description': "The plaintext identity"
                        }),
                    ]),
                    'description':
                    "Recipient that was issued the Assertion"
                }),
                ('expires', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the Assertion expires",
                }),
            ])
        })

    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.save()
        instance.rebake()

        return instance

    def validate(self, data):
        request = self.context.get('request', None)

        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_identifier': ["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')
            else:
                raise serializers.ValidationError(
                    {"badgeclass": ["This field is required"]})

        expected_issuer = self.context.get('kwargs', {}).get('issuer')
        if expected_issuer and data['badgeclass'].issuer != expected_issuer:
            raise serializers.ValidationError({
                "badgeclass":
                ["Could not find matching badgeclass for this issuer."]
            })

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

        return data