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 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 RecipientGroupSerializerV2(DetailSerializerV2): createdAt = serializers.DateTimeField(source='created_at', read_only=True) createdBy = EntityRelatedFieldV2(source='cached_creator', read_only=True) issuer = EntityRelatedFieldV2(source='cached_issuer', read_only=True) name = StripTagsCharField(required=False) description = StripTagsCharField(required=False) active = serializers.BooleanField(source='is_active', default=True) members = RecipientGroupMembershipSerializerV2(many=True, source='membership_items', required=False) pathways = EntityRelatedFieldV2(many=True, source='pathway_items', required=False, queryset=Pathway.cached) class Meta(DetailSerializerV2.Meta): model = RecipientGroup def update(self, instance, validated_data): if 'cached_issuer' in validated_data: validated_data.pop('cached_issuer') # issuer is not updatedable super(RecipientGroupSerializerV2, self).update(instance, validated_data) def create(self, validated_data): if 'cached_issuer' in validated_data: # included issuer in request validated_data['issuer'] = validated_data.pop('cached_issuer') elif 'issuer' in self.context: # issuer was passed in context validated_data['issuer'] = self.context.get('issuer') else: # issuer is required on create raise serializers.ValidationError({"issuer": "This field is required"}) return super(RecipientGroupSerializerV2, self).create(validated_data)
class IssuerStaffSerializerV2(DetailSerializerV2): user = EntityRelatedFieldV2(source='cached_user', queryset=BadgeUser.cached) role = serializers.CharField(validators=[ChoicesValidator(dict(IssuerStaff.ROLE_CHOICES).keys())]) class Meta(DetailSerializerV2.Meta): apispec_definition = ('IssuerStaff', { 'properties': { 'role': { 'type': "string", 'enum': ["staff", "editor", "owner"] } } })
class BackpackCollectionSerializerV2(DetailSerializerV2): name = serializers.CharField() description = MarkdownCharField(required=False) owner = EntityRelatedFieldV2(read_only=True, source='created_by') share_url = serializers.URLField(read_only=True) shareHash = serializers.CharField(read_only=True, source='share_hash') published = serializers.BooleanField(required=False) assertions = EntityRelatedFieldV2(many=True, source='badge_items', required=False, queryset=BadgeInstance.cached) class Meta(DetailSerializerV2.Meta): model = BackpackCollection apispec_definition = ('Collection', { 'properties': OrderedDict([ ('entityId', { 'type': "string", 'format': "string", 'description': "Unique identifier for this Collection", }), ('entityType', { 'type': "string", 'format': "string", 'description': "\"Collection\"", }), ('createdAt', { 'type': 'string', 'format': 'ISO8601 timestamp', 'description': "Timestamp when the Collection was created", }), ('createdBy', { 'type': 'string', 'format': 'entityId', 'description': "BadgeUser who created this Collection", }), ('name', { 'type': "string", 'format': "string", 'description': "Name of the Collection", }), ('description', { 'type': "string", 'format': "text", 'description': "Short description of the Collection", }), ('share_url', { 'type': "string", 'format': "url", 'description': "A public URL for sharing the Collection. Read only.", }), ('shareHash', { 'type': "string", 'format': "url", 'description': "The share hash that allows construction of a public sharing URL. Read only.", }), ('published', { 'type': "boolean", 'description': "True if the Collection has a public share URL", }), ('assertions', { 'type': "array", 'items': { '$ref': '#/definitions/Assertion' }, 'description': "List of Assertions in the collection", }), ]) })
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)
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 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)
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
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 BadgeClassSerializerV2(DetailSerializerV2, OriginalJsonSerializerMixin): openBadgeId = serializers.URLField(source='jsonld_id', read_only=True) createdAt = serializers.DateTimeField(source='created_at', read_only=True) createdBy = EntityRelatedFieldV2(source='cached_creator', read_only=True) issuer = EntityRelatedFieldV2(source='cached_issuer', required=False, queryset=Issuer.cached) issuerOpenBadgeId = serializers.URLField(source='issuer_jsonld_id', read_only=True) name = StripTagsCharField(max_length=1024) image = ValidImageField(required=False) description = StripTagsCharField(max_length=16384, required=True, convert_null=True) criteriaUrl = StripTagsCharField(source='criteria_url', required=False, allow_null=True, validators=[URLValidator()]) criteriaNarrative = MarkdownCharField(source='criteria_text', required=False, allow_null=True) alignments = AlignmentItemSerializerV2(source='alignment_items', many=True, required=False) tags = serializers.ListField(child=StripTagsCharField(max_length=1024), source='tag_items', required=False) extensions = serializers.DictField(source='extension_items', required=False, validators=[BadgeExtensionValidator()]) class Meta(DetailSerializerV2.Meta): model = BadgeClass apispec_definition = ('BadgeClass', { 'properties': OrderedDict([ ('entityId', { 'type': "string", 'format': "string", 'description': "Unique identifier for this BadgeClass", }), ('entityType', { 'type': "string", 'format': "string", 'description': "\"BadgeClass\"", }), ('openBadgeId', { 'type': "string", 'format': "url", 'description': "URL of the OpenBadge compliant json", }), ('createdAt', { 'type': 'string', 'format': 'ISO8601 timestamp', 'description': "Timestamp when the BadgeClass was created", }), ('createdBy', { 'type': 'string', 'format': 'entityId', 'description': "BadgeUser who created this BadgeClass", }), ('issuer', { 'type': 'string', 'format': 'entityId', 'description': "entityId of the Issuer who owns the BadgeClass", }), ('name', { 'type': "string", 'format': "string", 'description': "Name of the BadgeClass", }), ('description', { 'type': "string", 'format': "string", 'description': "Short description of the BadgeClass", }), ('image', { 'type': "string", 'format': "data:image/png;base64", 'description': "Base64 encoded string of an image that represents the BadgeClass.", }), ('criteriaUrl', { 'type': "string", 'format': "url", 'description': "External URL that describes in a human-readable format the criteria for the BadgeClass" }), ('criteriaNarrative', { 'type': "string", 'format': "markdown", 'description': "Markdown formatted description of the criteria" }), ('tags', { 'type': "array", 'items': { 'type': "string", 'format': "string" }, 'description': "List of tags that describe the BadgeClass" }), ('alignments', { 'type': "array", 'items': { '$ref': '#/definitions/BadgeClassAlignment' }, 'description': "List of objects describing objectives or educational standards" }), ]) }) def update(self, instance, validated_data): if 'cached_issuer' in validated_data: validated_data.pop('cached_issuer') # issuer is not updatable return super(BadgeClassSerializerV2, self).update(instance, validated_data) def create(self, validated_data): if 'cached_issuer' in validated_data: # included issuer in request validated_data['issuer'] = validated_data.pop('cached_issuer') elif 'issuer' in self.context: # issuer was passed in context validated_data['issuer'] = self.context.get('issuer') else: # issuer is required on create raise serializers.ValidationError( {"issuer": "This field is required"}) return super(BadgeClassSerializerV2, self).create(validated_data)
class 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