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 IssuerSerializerV1(OriginalJsonSerializerMixin, serializers.Serializer): created_at = DateTimeWithUtcZAtEndField(read_only=True) created_by = BadgeUserIdentifierFieldV1() name = StripTagsCharField(max_length=1024) slug = StripTagsCharField(max_length=255, source='entity_id', read_only=True) image = ValidImageField(required=False) email = serializers.EmailField(max_length=255, required=True) description = StripTagsCharField(max_length=16384, required=False) url = serializers.URLField(max_length=1024, required=True) staff = IssuerStaffSerializerV1(read_only=True, source='cached_issuerstaff', many=True) badgrapp = serializers.CharField(read_only=True, max_length=255, source='cached_badgrapp') class Meta: apispec_definition = ('Issuer', {}) def validate_image(self, image): if image is not None: img_name, img_ext = os.path.splitext(image.name) image.name = 'issuer_logo_' + str(uuid.uuid4()) + img_ext return image def create(self, validated_data, **kwargs): user = validated_data['created_by'] potential_email = validated_data['email'] if not user.is_email_verified(potential_email): raise serializers.ValidationError( "Issuer email must be one of your verified addresses. Add this email to your profile and try again." ) new_issuer = Issuer(**validated_data) # set badgrapp new_issuer.badgrapp = BadgrApp.objects.get_current( self.context.get('request', None)) new_issuer.save() return new_issuer def update(self, instance, validated_data): force_image_resize = False instance.name = validated_data.get('name') if 'image' in validated_data: instance.image = validated_data.get('image') force_image_resize = True instance.email = validated_data.get('email') instance.description = validated_data.get('description') instance.url = validated_data.get('url') # set badgrapp if not instance.badgrapp_id: instance.badgrapp = BadgrApp.objects.get_current( self.context.get('request', None)) instance.save(force_resize=force_image_resize) return instance def to_representation(self, obj): representation = super(IssuerSerializerV1, self).to_representation(obj) representation['json'] = obj.get_json(obi_version='1_1', use_canonical_id=True) if self.context.get('embed_badgeclasses', False): representation['badgeclasses'] = BadgeClassSerializerV1( obj.badgeclasses.all(), many=True, context=self.context).data representation['badgeClassCount'] = len(obj.cached_badgeclasses()) representation['recipientGroupCount'] = len( obj.cached_recipient_groups()) representation['recipientCount'] = sum( g.member_count() for g in obj.cached_recipient_groups()) representation['pathwayCount'] = len(obj.cached_pathways()) return representation
class BadgeClassSerializerV1(OriginalJsonSerializerMixin, serializers.Serializer): created_at = DateTimeWithUtcZAtEndField(read_only=True) created_by = BadgeUserIdentifierFieldV1() id = serializers.IntegerField(required=False, read_only=True) name = StripTagsCharField(max_length=255) image = ValidImageField(required=False) slug = StripTagsCharField(max_length=255, read_only=True, source='entity_id') criteria = MarkdownCharField(allow_blank=True, required=False, write_only=True) criteria_text = MarkdownCharField(required=False, allow_null=True, allow_blank=True) criteria_url = StripTagsCharField(required=False, allow_blank=True, allow_null=True, validators=[URLValidator()]) recipient_count = serializers.IntegerField(required=False, read_only=True) pathway_element_count = serializers.IntegerField(required=False, read_only=True) description = StripTagsCharField(max_length=16384, required=True, convert_null=True) alignment = AlignmentItemSerializerV1(many=True, source='alignment_items', required=False) tags = serializers.ListField(child=StripTagsCharField(max_length=1024), source='tag_items', required=False) expires = BadgeClassExpirationSerializerV1(source='*', required=False, allow_null=True) class Meta: apispec_definition = ('BadgeClass', {}) def to_internal_value(self, data): if 'expires' in data: if not data['expires'] or len(data['expires']) == 0: # if expires was included blank, remove it so to_internal_value() doesnt choke del data['expires'] return super(BadgeClassSerializerV1, self).to_internal_value(data) def to_representation(self, instance): representation = super(BadgeClassSerializerV1, self).to_representation(instance) representation['issuer'] = OriginSetting.HTTP + reverse( 'issuer_json', kwargs={'entity_id': instance.cached_issuer.entity_id}) representation['json'] = instance.get_json(obi_version='1_1', use_canonical_id=True) return representation def validate_image(self, image): if image is not None: img_name, img_ext = os.path.splitext(image.name) image.name = 'issuer_badgeclass_' + str(uuid.uuid4()) + img_ext return image def validate_criteria_text(self, criteria_text): if criteria_text is not None and criteria_text != '': return criteria_text else: return None def validate_criteria_url(self, criteria_url): if criteria_url is not None and criteria_url != '': return criteria_url else: return None def update(self, instance, validated_data): force_image_resize = False new_name = validated_data.get('name') if new_name: new_name = strip_tags(new_name) instance.name = new_name new_description = validated_data.get('description') if new_description: instance.description = strip_tags(new_description) if 'criteria_text' in validated_data: instance.criteria_text = validated_data.get('criteria_text') if 'criteria_url' in validated_data: instance.criteria_url = validated_data.get('criteria_url') if 'image' in validated_data: instance.image = validated_data.get('image') force_image_resize = True instance.alignment_items = validated_data.get('alignment_items') instance.tag_items = validated_data.get('tag_items') instance.expires_amount = validated_data.get('expires_amount', None) instance.expires_duration = validated_data.get('expires_duration', None) instance.save(force_resize=force_image_resize) return instance def validate(self, data): if 'criteria' in data: if 'criteria_url' in data or 'criteria_text' in data: raise serializers.ValidationError( "The criteria field is mutually-exclusive with the criteria_url and criteria_text fields" ) if utils.is_probable_url(data.get('criteria')): data['criteria_url'] = data.pop('criteria') elif not isinstance(data.get('criteria'), str): raise serializers.ValidationError( "Provided criteria text could not be properly processed as URL or plain text." ) else: data['criteria_text'] = data.pop('criteria') else: if data.get('criteria_text', None) is None and data.get( 'criteria_url', None) is None: raise serializers.ValidationError( "One or both of the criteria_text and criteria_url fields must be provided" ) return data def create(self, validated_data, **kwargs): if 'image' not in validated_data: raise serializers.ValidationError( {"image": ["This field is required"]}) if 'issuer' in self.context: validated_data['issuer'] = self.context.get('issuer') new_badgeclass = BadgeClass.objects.create(**validated_data) return new_badgeclass
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 BadgeClassSerializerV1(OriginalJsonSerializerMixin, ExtensionsSaverMixin, serializers.Serializer): created_at = DateTimeWithUtcZAtEndField(read_only=True) created_by = BadgeUserIdentifierFieldV1() id = serializers.IntegerField(required=False, read_only=True) name = StripTagsCharField(max_length=255) image = ValidImageField(required=False) slug = StripTagsCharField(max_length=255, read_only=True, source='entity_id') criteria = MarkdownCharField(allow_blank=True, required=False, write_only=True) criteria_text = MarkdownCharField(required=False, allow_null=True, allow_blank=True) criteria_url = StripTagsCharField(required=False, allow_blank=True, allow_null=True, validators=[URLValidator()]) recipient_count = serializers.IntegerField(required=False, read_only=True, source='v1_api_recipient_count') description = StripTagsCharField(max_length=16384, required=True, convert_null=True) alignment = AlignmentItemSerializerV1(many=True, source='alignment_items', required=False) tags = serializers.ListField(child=StripTagsCharField(max_length=1024), source='tag_items', required=False) extensions = serializers.DictField(source='extension_items', required=False, validators=[BadgeExtensionValidator()]) expires = BadgeClassExpirationSerializerV1(source='*', required=False, allow_null=True) # issuerName = StripTagsCharField(max_length=255) class Meta: apispec_definition = ('BadgeClass', {}) def to_internal_value(self, data): if 'expires' in data: if not data['expires'] or len(data['expires']) == 0: # if expires was included blank, remove it so to_internal_value() doesnt choke del data['expires'] return super(BadgeClassSerializerV1, self).to_internal_value(data) def to_representation(self, instance): representation = super(BadgeClassSerializerV1, self).to_representation(instance) representation['issuerName'] = instance.cached_issuer.name representation['issuer'] = OriginSetting.HTTP + reverse( 'issuer_json', kwargs={'entity_id': instance.cached_issuer.entity_id}) representation['json'] = instance.get_json(obi_version='1_1', use_canonical_id=True) return representation def validate_image(self, image): if image is not None: img_name, img_ext = os.path.splitext(image.name) image.name = 'issuer_badgeclass_' + str(uuid.uuid4()) + img_ext return image def validate_criteria_text(self, criteria_text): if criteria_text is not None and criteria_text != '': return criteria_text else: return None def validate_criteria_url(self, criteria_url): if criteria_url is not None and criteria_url != '': return criteria_url else: return None def validate_extensions(self, extensions): is_formal = False if extensions: for ext_name, ext in extensions.items(): # if "@context" in ext and not ext['@context'].startswith(settings.EXTENSIONS_ROOT_URL): # raise BadgrValidationError( # error_code=999, # error_message=f"extensions @context invalid {ext['@context']}") if ext_name.endswith('ECTSExtension') or ext_name.endswith( 'StudyLoadExtension') or ext_name.endswith( 'CategoryExtension') or ext_name.endswith( 'LevelExtension'): is_formal = True self.formal = is_formal return extensions def add_extensions(self, instance, add_these_extensions, extension_items): for extension_name in add_these_extensions: original_json = extension_items[extension_name] extension = BadgeClassExtension( name=extension_name, original_json=json.dumps(original_json), badgeclass_id=instance.pk) extension.save() def update(self, instance, validated_data): logger.info("UPDATE BADGECLASS") logger.debug(validated_data) force_image_resize = False new_name = validated_data.get('name') if new_name: new_name = strip_tags(new_name) instance.name = new_name new_description = validated_data.get('description') if new_description: instance.description = strip_tags(new_description) # Assure both criteria_url and criteria_text will not be empty if 'criteria_url' in validated_data or 'criteria_text' in validated_data: end_criteria_url = validated_data['criteria_url'] if 'criteria_url' in validated_data \ else instance.criteria_url end_criteria_text = validated_data['criteria_text'] if 'criteria_text' in validated_data \ else instance.criteria_text if ((end_criteria_url is None or not end_criteria_url.strip()) and (end_criteria_text is None or not end_criteria_text.strip())): raise serializers.ValidationError( 'Changes cannot be made that would leave both criteria_url and criteria_text blank.' ) else: instance.criteria_text = end_criteria_text instance.criteria_url = end_criteria_url if 'image' in validated_data: instance.image = validated_data.get('image') force_image_resize = True instance.alignment_items = validated_data.get('alignment_items') instance.tag_items = validated_data.get('tag_items') instance.expires_amount = validated_data.get('expires_amount', None) instance.expires_duration = validated_data.get('expires_duration', None) logger.debug("SAVING EXTENSION") self.save_extensions(validated_data, instance) instance.save(force_resize=force_image_resize) return instance def validate(self, data): if 'criteria' in data: if 'criteria_url' in data or 'criteria_text' in data: raise serializers.ValidationError( "The criteria field is mutually-exclusive with the criteria_url and criteria_text fields" ) if utils.is_probable_url(data.get('criteria')): data['criteria_url'] = data.pop('criteria') elif not isinstance(data.get('criteria'), str): raise serializers.ValidationError( "Provided criteria text could not be properly processed as URL or plain text." ) else: data['criteria_text'] = data.pop('criteria') return data def create(self, validated_data, **kwargs): logger.info("CREATE NEW BADGECLASS") logger.debug(validated_data) if 'image' not in validated_data: raise serializers.ValidationError( {"image": ["This field is required"]}) if 'issuer' in self.context: validated_data['issuer'] = self.context.get('issuer') if validated_data.get('criteria_text', None) is None and validated_data.get( 'criteria_url', None) is None: raise serializers.ValidationError( "One or both of the criteria_text and criteria_url fields must be provided" ) new_badgeclass = BadgeClass.objects.create(**validated_data) return new_badgeclass
class IssuerSerializerV1(OriginalJsonSerializerMixin, ExtensionsSaverMixin, serializers.Serializer): created_at = serializers.DateTimeField(read_only=True) created_by = BadgeUserIdentifierFieldV1() name = StripTagsCharField(max_length=1024) slug = StripTagsCharField(max_length=255, source='entity_id', read_only=True) image = ValidImageField(required=False) email = serializers.EmailField(max_length=255, required=True) description = StripTagsCharField(max_length=16384, required=False) url = serializers.URLField(max_length=1024, required=True) staff = IssuerStaffSerializerV1(read_only=True, source='cached_issuerstaff', many=True) faculty = FacultySerializerV1(required=False, allow_null=True) extensions = serializers.DictField(source='extension_items', required=False, validators=[BadgeExtensionValidator()]) class Meta: apispec_definition = ('Issuer', {}) def validate_image(self, image): if image is not None: img_name, img_ext = os.path.splitext(image.name) image.name = 'issuer_logo_' + str(uuid.uuid4()) + img_ext return image def create(self, validated_data, **kwargs): if self.context['request'].data.get('faculty', None): faculty_id = self.context['request'].data['faculty']['id'] faculty = Faculty.objects.get(pk=faculty_id) validated_data['faculty'] = faculty user = validated_data['created_by'] potential_email = validated_data['email'] # if not user.is_email_verified(potential_email): # raise serializers.ValidationError( # "Issuer email must be one of your verified addresses. Add this email to your profile and try again.") new_issuer = Issuer(**validated_data) # set badgrapp new_issuer.badgrapp = BadgrApp.objects.get_current( self.context.get('request', None)) new_issuer.save() return new_issuer def update(self, instance, validated_data): if self.context['request'].data['faculty']: faculty_id = self.context['request'].data['faculty']['id'] faculty = Faculty.objects.get(pk=faculty_id) validated_data['faculty'] = faculty instance.name = validated_data.get('name') if 'image' in validated_data: instance.image = validated_data.get('image') instance.email = validated_data.get('email') instance.description = validated_data.get('description') instance.url = validated_data.get('url') instance.faculty = validated_data.get('faculty') # set badgrapp if not instance.badgrapp_id: instance.badgrapp = BadgrApp.objects.get_current( self.context.get('request', None)) self.save_extensions(validated_data, instance) instance.save() return instance def to_representation(self, obj): representation = super(IssuerSerializerV1, self).to_representation(obj) representation['json'] = obj.get_json(obi_version='1_1', use_canonical_id=True) if self.context.get('embed_badgeclasses', False): representation['badgeclasses'] = BadgeClassSerializerV1( obj.badgeclasses.all(), many=True, context=self.context).data representation['badgeClassCount'] = len(obj.cached_badgeclasses()) representation['recipientGroupCount'] = len( obj.cached_recipient_groups()) representation['recipientCount'] = sum( g.member_count() for g in obj.cached_recipient_groups()) representation['pathwayCount'] = len(obj.cached_pathways()) return representation def add_extensions(self, instance, add_these_extensions, extension_items): for extension_name in add_these_extensions: original_json = extension_items[extension_name] extension = IssuerExtension( name=extension_name, original_json=json.dumps(original_json), issuer_id=instance.pk) extension.save()
class BadgeClassSerializerV1(OriginalJsonSerializerMixin, ExtensionsSaverMixin, serializers.Serializer): created_at = serializers.DateTimeField(read_only=True) created_by = BadgeUserIdentifierFieldV1() id = serializers.IntegerField(required=False, read_only=True) name = StripTagsCharField(max_length=255) image = ValidImageField(required=False) slug = StripTagsCharField(max_length=255, read_only=True, source='entity_id') criteria = MarkdownCharField(allow_blank=True, required=False, write_only=True) criteria_text = MarkdownCharField(required=False, allow_null=True, allow_blank=True) criteria_url = StripTagsCharField(required=False, allow_blank=True, allow_null=True, validators=[URLValidator()]) recipient_count = serializers.IntegerField(required=False, read_only=True) enrollment_count = serializers.IntegerField(required=False, read_only=True) pathway_element_count = serializers.IntegerField(required=False, read_only=True) description = StripTagsCharField(max_length=16384, required=True, convert_null=True) alignment = AlignmentItemSerializerV1(many=True, source='alignment_items', required=False) tags = serializers.ListField(child=StripTagsCharField(max_length=1024), source='tag_items', required=False) extensions = serializers.DictField(source='extension_items', required=False, validators=[BadgeExtensionValidator()]) class Meta: apispec_definition = ('BadgeClass', {}) def to_representation(self, instance): representation = super(BadgeClassSerializerV1, self).to_representation(instance) representation['issuer'] = OriginSetting.HTTP + reverse( 'issuer_json', kwargs={'entity_id': instance.cached_issuer.entity_id}) representation['json'] = instance.get_json(obi_version='1_1', use_canonical_id=True) return representation def validate_image(self, image): if image is not None: img_name, img_ext = os.path.splitext(image.name) image.name = 'issuer_badgeclass_' + str(uuid.uuid4()) + img_ext return image def validate_criteria_text(self, criteria_text): if criteria_text is not None and criteria_text != '': return criteria_text else: return None def validate_criteria_url(self, criteria_url): if criteria_url is not None and criteria_url != '': return criteria_url else: return None def add_extensions(self, instance, add_these_extensions, extension_items): for extension_name in add_these_extensions: original_json = extension_items[extension_name] extension = BadgeClassExtension( name=extension_name, original_json=json.dumps(original_json), badgeclass_id=instance.pk) extension.save() def update(self, instance, validated_data): new_name = validated_data.get('name') if new_name: new_name = strip_tags(new_name) instance.name = new_name new_description = validated_data.get('description') if new_description: instance.description = strip_tags(new_description) if 'criteria_text' in validated_data: instance.criteria_text = validated_data.get('criteria_text') if 'criteria_url' in validated_data: instance.criteria_url = validated_data.get('criteria_url') if 'image' in validated_data: instance.image = validated_data.get('image') instance.alignment_items = validated_data.get('alignment_items') instance.tag_items = validated_data.get('tag_items') self.save_extensions(validated_data, instance) instance.save() return instance def validate(self, data): if 'criteria' in data: if 'criteria_url' in data or 'criteria_text' in data: raise serializers.ValidationError( "The criteria field is mutually-exclusive with the criteria_url and criteria_text fields" ) if utils.is_probable_url(data.get('criteria')): data['criteria_url'] = data.pop('criteria') elif not isinstance(data.get('criteria'), (str, unicode)): raise serializers.ValidationError( "Provided criteria text could not be properly processed as URL or plain text." ) else: data['criteria_text'] = data.pop('criteria') else: if data.get('criteria_text', None) is None and data.get( 'criteria_url', None) is None: raise serializers.ValidationError( "One or both of the criteria_text and criteria_url fields must be provided" ) return data def filter_extensions(self, validated_data): extension_items = validated_data['extension_items'] for extension_name in extension_items.keys(): extension_item = extension_items[extension_name] if extension_name == 'languageExtension': del extension_item['typedLanguage'] def create(self, validated_data, **kwargs): if 'image' not in validated_data: raise serializers.ValidationError( {"image": ["This field is required"]}) if 'issuer' in self.context: validated_data['issuer'] = self.context.get('issuer') self.filter_extensions(validated_data) new_badgeclass = BadgeClass.objects.create(**validated_data) return new_badgeclass