class BackpackAssertionSerializerV2(DetailSerializerV2, OriginalJsonSerializerMixin): acceptance = serializers.ChoiceField( choices=BadgeInstance.ACCEPTANCE_CHOICES, default=BadgeInstance.ACCEPTANCE_ACCEPTED) # badgeinstance openBadgeId = serializers.URLField(source='jsonld_id', read_only=True) badgeclass = EntityRelatedFieldV2(source='cached_badgeclass', required=False, queryset=BadgeClass.cached) badgeclassOpenBadgeId = serializers.URLField(source='badgeclass_jsonld_id', read_only=True) 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='*') issuedOn = serializers.DateTimeField(source='issued_on', read_only=True) narrative = MarkdownCharField(required=False) evidence = EvidenceItemSerializerV2(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) class Meta(DetailSerializerV2.Meta): model = BadgeInstance
class BackpackAssertionSerializerV2(DetailSerializerV2, OriginalJsonSerializerMixin): acceptance = serializers.ChoiceField(choices=BadgeInstance.ACCEPTANCE_CHOICES, default=BadgeInstance.ACCEPTANCE_ACCEPTED) # badgeinstance openBadgeId = serializers.URLField(source='jsonld_id', read_only=True) badgeclass = EntityRelatedFieldV2(source='cached_badgeclass', required=False, queryset=BadgeClass.cached) badgeclassOpenBadgeId = serializers.URLField(source='badgeclass_jsonld_id', read_only=True) 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='*') issuedOn = serializers.DateTimeField(source='issued_on', read_only=True) narrative = MarkdownCharField(required=False) evidence = EvidenceItemSerializerV2(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) class Meta(DetailSerializerV2.Meta): model = BadgeInstance def to_representation(self, instance): representation = super(BackpackAssertionSerializerV2, self).to_representation(instance) request_kwargs = self.context['kwargs'] expands = request_kwargs['expands'] if 'badgeclass' in expands: representation['badgeclass'] = instance.cached_badgeclass.get_json(include_extra=True, use_canonical_id=True) if 'issuer' in expands: representation['badgeclass']['issuer'] = instance.cached_issuer.get_json(include_extra=True, use_canonical_id=True) return representation
class BadgeConnectAssertionSerializer(BadgeConnectBaseEntitySerializer): id = serializers.URLField(source='jsonld_id', read_only=True) badge = serializers.URLField(source='badgeclass_jsonld_id', read_only=True) image = serializers.FileField(read_only=True) recipient = BadgeRecipientSerializerV2(source='*') issuedOn = serializers.DateTimeField(source='issued_on', read_only=True) narrative = MarkdownCharField(required=False) evidence = EvidenceItemSerializerV2(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) type = serializers.CharField(read_only=True, default='Assertion') class Meta: SCHEMA_TYPE = 'Assertion' model = BadgeInstance apispec_definition = ('BadgeConnectAssertion', { 'properties': OrderedDict([ ('id', { 'type': "string", 'format': "url", 'readOnly': True, 'description': "URL of the BadgeInstance", }), ('badge', { 'type': "string", 'format': "url", 'readOnly': True, 'description': "URL of the BadgeClass", }), ('image', { 'type': "string", 'format': "string", 'readOnly': True, 'description': "Badge Image", }), ('recipient', { 'type': 'object', 'properties': BadgeRecipientSerializerV2.Meta.apispec_definition[1]['properties'], 'readOnly': True, 'description': "Recipient that was issued the Assertion" }), ('issuedOn', { 'type': 'string', 'format': 'ISO8601 timestamp', 'readOnly': True, 'description': "Timestamp when the Assertion was issued", }), ('narrative', { 'type': 'string', 'format': 'markdown', 'description': "Markdown narrative of the achievement", }), ('evidence', { 'type': "string", 'format': "string", 'description': "Unique identifier for this Assertion", }), ('revoked', { 'type': 'boolean', 'readOnly': True, 'description': "True if this Assertion has been revoked", }), ('revocationReason', { 'type': 'string', 'format': "string", 'readOnly': True, 'description': "Short description of why the Assertion was revoked", }), ('expires', { 'type': 'string', 'format': 'ISO8601 timestamp', 'description': "Timestamp when the Assertion expires", }), ('@context', { 'type': 'string', 'format': 'url', 'default': CONTEXT_URI, 'example': CONTEXT_URI, }), ('type', { 'type': 'string', 'default': SCHEMA_TYPE, 'example': SCHEMA_TYPE }) ]) })
class BadgeInstanceSerializerV1(OriginalJsonSerializerMixin, serializers.Serializer): created_at = DateTimeWithUtcZAtEndField(read_only=True, default_timezone=pytz.utc) created_by = BadgeUserIdentifierFieldV1(read_only=True) slug = serializers.CharField(max_length=255, read_only=True, source='entity_id') image = serializers.FileField( read_only=True) # use_url=True, might be necessary email = serializers.EmailField(max_length=1024, required=False, write_only=True) recipient_identifier = serializers.CharField(max_length=1024, required=False) recipient_type = serializers.CharField(default=RECIPIENT_TYPE_EMAIL) allow_uppercase = serializers.BooleanField(default=False, required=False, write_only=True) evidence = serializers.URLField(write_only=True, required=False, allow_blank=True, max_length=1024) narrative = MarkdownCharField(required=False, allow_blank=True, allow_null=True) evidence_items = EvidenceItemSerializer(many=True, required=False) revoked = HumanReadableBooleanField(read_only=True) revocation_reason = serializers.CharField(read_only=True) expires = DateTimeWithUtcZAtEndField(source='expires_at', required=False, allow_null=True, default_timezone=pytz.utc) create_notification = HumanReadableBooleanField(write_only=True, required=False, default=False) allow_duplicate_awards = serializers.BooleanField(write_only=True, required=False, default=True) hashed = serializers.NullBooleanField(default=None, required=False) extensions = serializers.DictField(source='extension_items', required=False, validators=[BadgeExtensionValidator()]) class Meta: apispec_definition = ('Assertion', {}) def validate(self, data): recipient_type = data.get('recipient_type') if data.get('recipient_identifier') and data.get('email') is None: if recipient_type == RECIPIENT_TYPE_EMAIL: recipient_validator = EmailValidator() elif recipient_type in (RECIPIENT_TYPE_URL, RECIPIENT_TYPE_ID): recipient_validator = URLValidator() else: recipient_validator = TelephoneValidator() try: recipient_validator(data['recipient_identifier']) except DjangoValidationError as e: raise serializers.ValidationError(e.message) elif data.get('email') and data.get('recipient_identifier') is None: data['recipient_identifier'] = data.get('email') allow_duplicate_awards = data.pop('allow_duplicate_awards') if allow_duplicate_awards is False and self.context.get( 'badgeclass') is not None: previous_awards = BadgeInstance.objects.filter( recipient_identifier=data['recipient_identifier'], badgeclass=self.context['badgeclass']).filter( Q(expires_at__isnull=True) | Q(expires_at__lt=timezone.now())) if previous_awards.exists(): raise serializers.ValidationError( "A previous award of this badge already exists for this recipient." ) hashed = data.get('hashed', None) if hashed is None: if recipient_type in (RECIPIENT_TYPE_URL, RECIPIENT_TYPE_ID): data['hashed'] = False else: data['hashed'] = True return data def validate_narrative(self, data): if data is None or data == "": return None else: return data def to_representation(self, instance): representation = super(BadgeInstanceSerializerV1, self).to_representation(instance) representation['json'] = instance.get_json(obi_version="1_1", use_canonical_id=True) if self.context.get('include_issuer', False): representation['issuer'] = IssuerSerializerV1( instance.cached_badgeclass.cached_issuer).data else: representation['issuer'] = OriginSetting.HTTP + reverse( 'issuer_json', kwargs={'entity_id': instance.cached_issuer.entity_id}) if self.context.get('include_badge_class', False): representation['badge_class'] = BadgeClassSerializerV1( instance.cached_badgeclass, context=self.context).data else: representation['badge_class'] = OriginSetting.HTTP + reverse( 'badgeclass_json', kwargs={'entity_id': instance.cached_badgeclass.entity_id}) representation['public_url'] = OriginSetting.HTTP + reverse( 'badgeinstance_json', kwargs={'entity_id': instance.entity_id}) return representation def create(self, validated_data): """ Requires self.context to include request (with authenticated request.user) and badgeclass: issuer.models.BadgeClass. """ evidence_items = [] # ob1 evidence url evidence_url = validated_data.get('evidence') if evidence_url: evidence_items.append({'evidence_url': evidence_url}) # ob2 evidence items submitted_items = validated_data.get('evidence_items') if submitted_items: evidence_items.extend(submitted_items) try: return self.context.get('badgeclass').issue( recipient_id=validated_data.get('recipient_identifier'), narrative=validated_data.get('narrative'), evidence=evidence_items, notify=validated_data.get('create_notification'), created_by=self.context.get('request').user, allow_uppercase=validated_data.get('allow_uppercase'), recipient_type=validated_data.get('recipient_type', RECIPIENT_TYPE_EMAIL), badgr_app=BadgrApp.objects.get_current( self.context.get('request')), expires_at=validated_data.get('expires_at', None), extensions=validated_data.get('extension_items', None)) except DjangoValidationError as e: raise serializers.ValidationError(e.message) def update(self, instance, validated_data): updateable_fields = [ 'evidence_items', 'expires_at', 'extension_items', 'hashed', '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=False) instance.save() return instance
class BadgeInstanceSerializerV1(OriginalJsonSerializerMixin, serializers.Serializer): created_at = serializers.DateTimeField(read_only=True) created_by = BadgeUserIdentifierFieldV1(read_only=True) slug = serializers.CharField(max_length=255, read_only=True, source='entity_id') image = serializers.FileField( read_only=True) # use_url=True, might be necessary email = serializers.EmailField(max_length=1024, required=False, write_only=True) recipient_identifier = serializers.CharField(max_length=1024, required=False) recipient_type = serializers.CharField( default=BadgeInstance.RECIPIENT_TYPE_EMAIL) allow_uppercase = serializers.BooleanField(default=False, required=False, write_only=True) evidence = serializers.URLField(write_only=True, required=False, allow_blank=True, max_length=1024) narrative = MarkdownCharField(required=False, allow_blank=True, allow_null=True) evidence_items = EvidenceItemSerializer(many=True, required=False) revoked = HumanReadableBooleanField(read_only=True) revocation_reason = serializers.CharField(read_only=True) expires = serializers.DateTimeField(source='expires_at', required=False, allow_null=True) create_notification = HumanReadableBooleanField(write_only=True, required=False, default=False) hashed = serializers.NullBooleanField(default=None, required=False) extensions = serializers.DictField(source='extension_items', required=False, validators=[BadgeExtensionValidator()]) class Meta: apispec_definition = ('Assertion', {}) def validate(self, data): if data.get('email') and not data.get('recipient_identifier'): data['recipient_identifier'] = data.get('email') hashed = data.get('hashed', None) if hashed is None: recipient_type = data.get('recipient_type') if recipient_type in (BadgeInstance.RECIPIENT_TYPE_URL, BadgeInstance.RECIPIENT_TYPE_ID): data['hashed'] = False else: data['hashed'] = True return data def validate_narrative(self, data): if data is None or data == "": return None else: return data def to_representation(self, instance): # if self.context.get('extended_json'): # self.fields['json'] = V1InstanceSerializer(source='extended_json') representation = super(BadgeInstanceSerializerV1, self).to_representation(instance) representation['json'] = instance.get_json(obi_version="1_1", use_canonical_id=True) if self.context.get('include_issuer', False): representation['issuer'] = IssuerSerializerV1( instance.cached_badgeclass.cached_issuer).data else: representation['issuer'] = OriginSetting.HTTP + reverse( 'issuer_json', kwargs={'entity_id': instance.cached_issuer.entity_id}) if self.context.get('include_badge_class', False): representation['badge_class'] = BadgeClassSerializerV1( instance.cached_badgeclass, context=self.context).data else: representation['badge_class'] = OriginSetting.HTTP + reverse( 'badgeclass_json', kwargs={'entity_id': instance.cached_badgeclass.entity_id}) representation['public_url'] = OriginSetting.HTTP + reverse( 'badgeinstance_json', kwargs={'entity_id': instance.entity_id}) if apps.is_installed('badgebook'): try: from badgebook.models import BadgeObjectiveAward from badgebook.serializers import BadgeObjectiveAwardSerializer try: award = BadgeObjectiveAward.cached.get( badge_instance_id=instance.id) except BadgeObjectiveAward.DoesNotExist: representation['award'] = None else: representation['award'] = BadgeObjectiveAwardSerializer( award).data except ImportError: pass return representation def create(self, validated_data): """ Requires self.context to include request (with authenticated request.user) and badgeclass: issuer.models.BadgeClass. """ evidence_items = [] # ob1 evidence url evidence_url = validated_data.get('evidence') if evidence_url: evidence_items.append({'evidence_url': evidence_url}) # ob2 evidence items submitted_items = validated_data.get('evidence_items') if submitted_items: evidence_items.extend(submitted_items) return self.context.get('badgeclass').issue( recipient_id=validated_data.get('recipient_identifier'), narrative=validated_data.get('narrative'), evidence=evidence_items, notify=validated_data.get('create_notification'), created_by=self.context.get('request').user, allow_uppercase=validated_data.get('allow_uppercase'), recipient_type=validated_data.get( 'recipient_type', BadgeInstance.RECIPIENT_TYPE_EMAIL), badgr_app=BadgrApp.objects.get_current( self.context.get('request')), expires_at=validated_data.get('expires_at', None), extensions=validated_data.get('extension_items', None)) 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() return instance
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
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
class BackpackAssertionSerializerV2(DetailSerializerV2, OriginalJsonSerializerMixin): acceptance = serializers.ChoiceField( choices=BadgeInstance.ACCEPTANCE_CHOICES, default=BadgeInstance.ACCEPTANCE_ACCEPTED) # badgeinstance openBadgeId = serializers.URLField(source='jsonld_id', read_only=True) badgeclass = EntityRelatedFieldV2(source='cached_badgeclass', required=False, queryset=BadgeClass.cached) badgeclassOpenBadgeId = serializers.URLField(source='badgeclass_jsonld_id', read_only=True) 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='*') issuedOn = DateTimeWithUtcZAtEndField(source='issued_on', read_only=True) narrative = MarkdownCharField(required=False) evidence = EvidenceItemSerializerV2(many=True, required=False) revoked = HumanReadableBooleanField(read_only=True) revocationReason = serializers.CharField(source='revocation_reason', read_only=True) expires = DateTimeWithUtcZAtEndField(source='expires_at', required=False) pending = serializers.ReadOnlyField() class Meta(DetailSerializerV2.Meta): model = BadgeInstance apispec_definition = ('BackpackAssertion', { '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, }), ('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", 'readOnly': True, }), ('image', { 'type': 'string', 'format': 'url', 'description': "URL to the baked assertion image", 'readOnly': True, }), ('recipient', { 'type': 'object', 'properties': OrderedDict([ ('identity', { 'type': 'string', 'format': 'string', 'description': 'Either the hash of the identity or the plaintext value', 'required': True, }), ('type', { 'type': 'string', 'enum': [ c[0] for c in BadgeInstance.RECIPIENT_TYPE_CHOICES ], 'description': "Type of identifier used to identify recipient", 'required': False, }), ('hashed', { 'type': 'boolean', 'description': "Whether or not the identity value is hashed.", 'required': False, }), ('plaintextIdentity', { 'type': 'string', 'description': "The plaintext identity", 'required': False, }), ]), 'description': "Recipient that was issued the Assertion", 'required': True, }), ('issuedOn', { 'type': 'string', 'format': 'ISO8601 timestamp', 'description': "Timestamp when the Assertion was issued", 'required': True, }), ('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, }), ('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, }), ('expires', { 'type': 'string', 'format': 'ISO8601 timestamp', 'description': "Timestamp when the Assertion expires", 'required': False, }), ]) }) def to_representation(self, instance): representation = super(BackpackAssertionSerializerV2, self).to_representation(instance) request_kwargs = self.context['kwargs'] expands = request_kwargs.get('expands', []) if self.parent is not None: # we'll have a bare representation instance_data_pointer = representation else: instance_data_pointer = representation['result'][0] if 'badgeclass' in expands: instance_data_pointer[ 'badgeclass'] = instance.cached_badgeclass.get_json( include_extra=True, use_canonical_id=True) if 'issuer' in expands: instance_data_pointer['badgeclass'][ 'issuer'] = instance.cached_issuer.get_json( include_extra=True, use_canonical_id=True) return representation
class BadgeInstanceSerializer(OriginalJsonSerializerMixin, serializers.Serializer): created_at = serializers.DateTimeField(read_only=True) created_by = BadgeUserIdentifierField(read_only=True) entity_id = serializers.CharField(max_length=255, read_only=True) image = serializers.FileField( read_only=True) # use_url=True, might be necessary email = serializers.EmailField(max_length=1024, required=False, write_only=True) recipient_identifier = serializers.CharField(max_length=1024, required=False) recipient_email = serializers.SerializerMethodField() recipient_name = serializers.SerializerMethodField() recipient_type = serializers.CharField( default=BadgeInstance.RECIPIENT_TYPE_EDUID) allow_uppercase = serializers.BooleanField(default=False, required=False, write_only=True) revoked = HumanReadableBooleanField(read_only=True) revocation_reason = serializers.CharField(read_only=True) expires = serializers.DateTimeField(source='expires_at', required=False, allow_null=True) issue_signed = serializers.BooleanField(required=False) signing_password = serializers.CharField(max_length=1024, required=False) enrollment_entity_id = serializers.CharField(max_length=1024, required=False) create_notification = HumanReadableBooleanField(write_only=True, required=False, default=False) hashed = serializers.NullBooleanField(default=None, required=False) extensions = serializers.DictField(source='extension_items', required=False, validators=[BadgeExtensionValidator()]) class Meta: apispec_definition = ('Assertion', {}) def get_recipient_email(self, obj): return obj.get_email_address() def get_recipient_name(self, obj): return obj.get_recipient_name() def validate(self, data): if data.get('email') and not data.get('recipient_identifier'): data['recipient_identifier'] = data.get('email') hashed = data.get('hashed', None) if hashed is None: recipient_type = data.get('recipient_type') if recipient_type in (BadgeInstance.RECIPIENT_TYPE_URL, BadgeInstance.RECIPIENT_TYPE_ID): data['hashed'] = False else: data['hashed'] = True return data def validate_narrative(self, data): if data is None or data == "": return None else: return data def to_representation(self, instance): # if self.context.get('extended_json'): # self.fields['json'] = V1InstanceSerializer(source='extended_json') representation = super(BadgeInstanceSerializer, self).to_representation(instance) representation['json'] = instance.get_json(obi_version="1_1", use_canonical_id=True) if self.context.get('include_issuer', False): representation['issuer'] = IssuerSerializer( instance.cached_badgeclass.cached_issuer).data else: representation['issuer'] = OriginSetting.HTTP + reverse( 'issuer_json', kwargs={'entity_id': instance.cached_issuer.entity_id}) if self.context.get('include_badge_class', False): representation['badge_class'] = BadgeClassSerializer( instance.cached_badgeclass, context=self.context).data else: representation['badge_class'] = OriginSetting.HTTP + reverse( 'badgeclass_json', kwargs={'entity_id': instance.cached_badgeclass.entity_id}) representation['public_url'] = OriginSetting.HTTP + reverse( 'badgeinstance_json', kwargs={'entity_id': instance.entity_id}) if apps.is_installed('badgebook'): try: from badgebook.models import BadgeObjectiveAward from badgebook.serializers import BadgeObjectiveAwardSerializer try: award = BadgeObjectiveAward.cached.get( badge_instance_id=instance.id) except BadgeObjectiveAward.DoesNotExist: representation['award'] = None else: representation['award'] = BadgeObjectiveAwardSerializer( award).data except ImportError: pass return representation def create(self, validated_data): """ Requires self.context to include request (with authenticated request.user) and badgeclass: issuer.models.BadgeClass. """ badgeclass = self.context['request'].data.get('badgeclass') enrollment = StudentsEnrolled.objects.get( entity_id=validated_data.get('enrollment_entity_id')) expires_at = None if badgeclass.expiration_period: expires_at = datetime.datetime.now().replace( microsecond=0, second=0, minute=0, hour=0) + badgeclass.expiration_period if enrollment.badge_instance: raise BadgrValidationError( "Can't award enrollment, it has already been awarded", 213) if self.context['request'].data.get('issue_signed', False): assertion = badgeclass.issue_signed( recipient=enrollment.user, created_by=self.context.get('request').user, allow_uppercase=validated_data.get('allow_uppercase'), recipient_type=validated_data.get( 'recipient_type', BadgeInstance.RECIPIENT_TYPE_EDUID), expires_at=expires_at, extensions=validated_data.get('extension_items', None), identifier=uuid.uuid4().urn, signer=validated_data.get('created_by'), ) else: assertion = badgeclass.issue( recipient=enrollment.user, created_by=self.context.get('request').user, allow_uppercase=validated_data.get('allow_uppercase'), recipient_type=validated_data.get( 'recipient_type', BadgeInstance.RECIPIENT_TYPE_EDUID), expires_at=expires_at, extensions=validated_data.get('extension_items', None)) enrollment.date_awarded = timezone.now() enrollment.badge_instance = assertion enrollment.save() enrollment.user.remove_cached_data(['cached_pending_enrollments']) return assertion def update(self, instance, validated_data): updateable_fields = [ 'evidence_items', 'expires_at', 'extension_items', 'hashed', 'issued_on', '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=False) instance.save() return instance