Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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"]

                }
            }
        })
Ejemplo n.º 5
0
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",
                }),
            ])
        })
Ejemplo n.º 6
0
class IssuerSerializerV2(DetailSerializerV2, OriginalJsonSerializerMixin):
    openBadgeId = serializers.URLField(source='jsonld_id', read_only=True)
    createdAt = DateTimeWithUtcZAtEndField(source='created_at', read_only=True)
    createdBy = EntityRelatedFieldV2(source='cached_creator',
                                     queryset=BadgeUser.cached,
                                     required=False)
    name = StripTagsCharField(max_length=1024)
    image = ValidImageField(required=False)
    email = serializers.EmailField(max_length=255, required=True)
    description = StripTagsCharField(max_length=16384, required=False)
    url = serializers.URLField(max_length=1024, required=True)
    staff = IssuerStaffSerializerV2(many=True,
                                    source='staff_items',
                                    required=False)
    extensions = serializers.DictField(source='extension_items',
                                       required=False,
                                       validators=[BadgeExtensionValidator()])
    badgrDomain = serializers.SlugRelatedField(required=False,
                                               source='badgrapp',
                                               slug_field='cors',
                                               queryset=BadgrApp.objects)

    class Meta(DetailSerializerV2.Meta):
        model = Issuer
        apispec_definition = ('Issuer', {
            'properties':
            OrderedDict([
                ('entityId', {
                    'type': "string",
                    'format': "string",
                    'description': "Unique identifier for this Issuer",
                    'readOnly': True,
                }),
                ('entityType', {
                    'type': "string",
                    'format': "string",
                    'description': "\"Issuer\"",
                    'readOnly': True,
                }),
                ('openBadgeId', {
                    'type': "string",
                    'format': "url",
                    'description': "URL of the OpenBadge compliant json",
                    'readOnly': True,
                }),
                ('createdAt', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the Issuer was created",
                    'readOnly': True,
                }),
                ('createdBy', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeUser who created this Issuer",
                    'required': False,
                }),
                ('name', {
                    'type': "string",
                    'format': "string",
                    'description': "Name of the Issuer",
                    'required': True,
                }),
                ('image', {
                    'type': "string",
                    'format': "data:image/png;base64",
                    'description':
                    "Base64 encoded string of an image that represents the Issuer",
                    'required': False,
                }),
                ('email', {
                    'type': "string",
                    'format': "email",
                    'description': "Contact email for the Issuer",
                    'required': True,
                }),
                ('url', {
                    'type': "string",
                    'format': "url",
                    'description':
                    "Homepage or website associated with the Issuer",
                    'required': False,
                }),
                ('description', {
                    'type': "string",
                    'format': "text",
                    'description': "Short description of the Issuer",
                    'required': False,
                }),
            ])
        })

    def validate_image(self, image):
        if image is not None:
            img_name, img_ext = os.path.splitext(image.name)
            image.name = 'issuer_logo_' + str(uuid.uuid4()) + img_ext
        return image

    def validate_createdBy(self, val):
        if not request_authenticated_with_server_admin_token(
                self.context.get('request')):
            return None
        return val

    def validate(self, data):
        if data.get('badgrapp'
                    ) and not request_authenticated_with_server_admin_token(
                        self.context.get('request')):
            data.pop('badgrapp')
        return data

    def create(self, validated_data):
        request = self.context.get('request')

        # If a Server Admin declares another user as creator, set it to that other user. Otherwise, use request.user
        user = validated_data.pop('cached_creator', None)
        if user:
            validated_data['created_by'] = user

        potential_email = validated_data['email']
        if validated_data.get('badgrapp') is None:
            validated_data['badgrapp'] = BadgrApp.objects.get_current(request)

        # Server admins are exempt from email verification requirement. They will enforce it themselves.
        if not request_authenticated_with_server_admin_token(
                request) and not validated_data[
                    'created_by'].is_email_verified(potential_email):
            raise serializers.ValidationError(
                "Issuer email must be one of your verified addresses. Add this email to your profile and try again."
            )

        staff = validated_data.pop('staff_items', [])
        new_issuer = super(IssuerSerializerV2, self).create(validated_data)

        # update staff after issuer is created
        new_issuer.staff_items = staff

        return new_issuer

    def update(self, instance, validated_data):
        validated_data.pop('cached_creator', None)

        if 'image' in validated_data:
            self.context['save_kwargs'] = dict(force_resize=True)

        return super(IssuerSerializerV2, self).update(instance, validated_data)
Ejemplo n.º 7
0
class BadgeInstanceSerializerV2(DetailSerializerV2,
                                OriginalJsonSerializerMixin):
    openBadgeId = serializers.URLField(source='jsonld_id', read_only=True)
    createdAt = DateTimeWithUtcZAtEndField(source='created_at',
                                           read_only=True,
                                           default_timezone=pytz.utc)
    createdBy = EntityRelatedFieldV2(source='cached_creator', read_only=True)
    badgeclass = EntityRelatedFieldV2(source='cached_badgeclass',
                                      required=False,
                                      queryset=BadgeClass.cached)
    badgeclassOpenBadgeId = CachedUrlHyperlinkedRelatedField(
        source='badgeclass_jsonld_id',
        view_name='badgeclass_json',
        lookup_field='entity_id',
        queryset=BadgeClass.cached,
        required=False)
    badgeclassName = serializers.CharField(write_only=True, required=False)

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

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

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

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

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

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

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

    class Meta(DetailSerializerV2.Meta):
        model = BadgeInstance
        apispec_definition = ('Assertion', {
            'properties':
            OrderedDict([
                ('entityId', {
                    'type': "string",
                    'format': "string",
                    'description': "Unique identifier for this Assertion",
                    'readOnly': True,
                }),
                ('entityType', {
                    'type': "string",
                    'format': "string",
                    'description': "\"Assertion\"",
                    'readOnly': True,
                }),
                ('openBadgeId', {
                    'type': "string",
                    'format': "url",
                    'description': "URL of the OpenBadge compliant json",
                    'readOnly': True,
                }),
                ('createdAt', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the Assertion was created",
                    'readOnly': True,
                }),
                ('createdBy', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeUser who created the Assertion",
                    'readOnly': True,
                }),
                ('badgeclass', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeClass that issued this Assertion",
                    'required': False,
                }),
                ('badgeclassOpenBadgeId', {
                    'type': 'string',
                    'format': 'url',
                    'description': "URL of the BadgeClass to award",
                    'required': False,
                }),
                ('badgeclassName', {
                    'type': 'string',
                    'format': 'string',
                    'description':
                    "Name of BadgeClass to create assertion against, case insensitive",
                    'required': False,
                }),
                ('revoked', {
                    'type': 'boolean',
                    'description': "True if this Assertion has been revoked",
                    'readOnly': True,
                }),
                ('revocationReason', {
                    'type': 'string',
                    'format': "string",
                    'description':
                    "Short description of why the Assertion was revoked",
                    'readOnly': True,
                }),
                ('acceptance', {
                    'type': 'string',
                    'description':
                    "Recipient interaction with Assertion. One of: Unaccepted, Accepted, or Rejected",
                    'readOnly': True,
                }),
                ('image', {
                    'type': 'string',
                    'format': 'url',
                    'description': "URL to the baked assertion image",
                    'readOnly': True,
                }),
                ('issuedOn', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the Assertion was issued",
                    'required': False,
                }),
                ('narrative', {
                    'type': 'string',
                    'format': 'markdown',
                    'description': "Markdown narrative of the achievement",
                    'required': False,
                }),
                ('evidence', {
                    'type': 'array',
                    'items': {
                        '$ref': '#/definitions/AssertionEvidence'
                    },
                    'description':
                    "List of evidence associated with the achievement",
                    'required': False,
                }),
                ('recipient', {
                    'type': 'object',
                    '$ref': '#/definitions/BadgeRecipient',
                    'description': "Recipient that was issued the Assertion",
                    'required': False,
                }),
                ('expires', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the Assertion expires",
                    'required': False,
                }),
            ])
        })

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

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

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

        return instance

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

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

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

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

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

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

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

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

        return data
Ejemplo n.º 8
0
class BadgeClassSerializerV2(DetailSerializerV2, OriginalJsonSerializerMixin):
    openBadgeId = serializers.URLField(source='jsonld_id', read_only=True)
    createdAt = DateTimeWithUtcZAtEndField(source='created_at', read_only=True)
    createdBy = EntityRelatedFieldV2(source='cached_creator', read_only=True)
    issuer = EntityRelatedFieldV2(source='cached_issuer',
                                  required=False,
                                  queryset=Issuer.cached)
    issuerOpenBadgeId = serializers.URLField(source='issuer_jsonld_id',
                                             read_only=True)

    name = StripTagsCharField(max_length=1024)
    image = ValidImageField(required=False)
    description = StripTagsCharField(max_length=16384,
                                     required=True,
                                     convert_null=True)

    criteriaUrl = StripTagsCharField(source='criteria_url',
                                     required=False,
                                     allow_null=True,
                                     validators=[URLValidator()])
    criteriaNarrative = MarkdownCharField(source='criteria_text',
                                          required=False,
                                          allow_null=True)

    alignments = AlignmentItemSerializerV2(source='alignment_items',
                                           many=True,
                                           required=False)
    tags = serializers.ListField(child=StripTagsCharField(max_length=1024),
                                 source='tag_items',
                                 required=False)

    expires = BadgeClassExpirationSerializerV2(source='*',
                                               required=False,
                                               allow_null=True)

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

    class Meta(DetailSerializerV2.Meta):
        model = BadgeClass
        apispec_definition = ('BadgeClass', {
            'properties':
            OrderedDict([
                ('entityId', {
                    'type': "string",
                    'format': "string",
                    'description': "Unique identifier for this BadgeClass",
                    'readOnly': True,
                }),
                ('entityType', {
                    'type': "string",
                    'format': "string",
                    'description': "\"BadgeClass\"",
                    'readOnly': True,
                }),
                ('openBadgeId', {
                    'type': "string",
                    'format': "url",
                    'description': "URL of the OpenBadge compliant json",
                    'readOnly': True,
                }),
                ('createdAt', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the BadgeClass was created",
                    'readOnly': True,
                }),
                ('createdBy', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeUser who created this BadgeClass",
                    'readOnly': True,
                }),
                ('issuer', {
                    'type': 'string',
                    'format': 'entityId',
                    'description':
                    "entityId of the Issuer who owns the BadgeClass",
                    'required': False,
                }),
                ('name', {
                    'type': "string",
                    'format': "string",
                    'description': "Name of the BadgeClass",
                    'required': True,
                }),
                ('description', {
                    'type': "string",
                    'format': "string",
                    'description': "Short description of the BadgeClass",
                    'required': True,
                }),
                ('image', {
                    'type': "string",
                    'format': "data:image/png;base64",
                    'description':
                    "Base64 encoded string of an image that represents the BadgeClass.",
                    'required': False,
                }),
                ('criteriaUrl', {
                    'type': "string",
                    'format': "url",
                    'description':
                    "External URL that describes in a human-readable format the criteria for the BadgeClass",
                    'required': False,
                }),
                ('criteriaNarrative', {
                    'type': "string",
                    'format': "markdown",
                    'description':
                    "Markdown formatted description of the criteria",
                    'required': False,
                }),
                ('tags', {
                    'type': "array",
                    'items': {
                        'type': "string",
                        'format': "string"
                    },
                    'description': "List of tags that describe the BadgeClass",
                    'required': False,
                }),
                ('alignments', {
                    'type': "array",
                    'items': {
                        '$ref': '#/definitions/BadgeClassAlignment'
                    },
                    'description':
                    "List of objects describing objectives or educational standards",
                    'required': False,
                }),
                ('expires', {
                    '$ref': "#/definitions/BadgeClassExpiration",
                    'description':
                    "Expiration period for Assertions awarded from this BadgeClass",
                    'required': False,
                }),
            ])
        })

    def to_internal_value(self, data):
        if not isinstance(data, BadgeClass) and 'expires' in data:
            if not data['expires'] or len(data['expires']) == 0:
                # if expires was included blank, remove it so to_internal_value() doesnt choke
                del data['expires']
        return super(BadgeClassSerializerV2, self).to_internal_value(data)

    def update(self, instance, validated_data):
        if 'cached_issuer' in validated_data:
            validated_data.pop('cached_issuer')  # issuer is not updatable

        if 'image' in validated_data:
            self.context['save_kwargs'] = dict(force_resize=True)

        if not IsEditor().has_object_permission(self.context.get('request'),
                                                None, instance.issuer):
            raise serializers.ValidationError({
                "issuer":
                "You do not have permission to edit badges on this issuer."
            })

        return super(BadgeClassSerializerV2,
                     self).update(instance, validated_data)

    def create(self, validated_data):
        if 'image' not in validated_data:
            raise serializers.ValidationError(
                {'image': 'Valid image file or data URI required.'})
        if 'cached_issuer' in validated_data:
            # included issuer in request
            validated_data['issuer'] = validated_data.pop('cached_issuer')
        elif 'issuer' in self.context:
            # issuer was passed in context
            validated_data['issuer'] = self.context.get('issuer')
        else:
            # issuer is required on create
            raise serializers.ValidationError(
                {"issuer": "This field is required"})

        if not IsEditor().has_object_permission(
                self.context.get('request'), None, validated_data['issuer']):
            raise serializers.ValidationError({
                "issuer":
                "You do not have permission to edit badges on this issuer."
            })

        return super(BadgeClassSerializerV2, self).create(validated_data)
Ejemplo n.º 9
0
class IssuerSerializerV2(DetailSerializerV2, OriginalJsonSerializerMixin):
    openBadgeId = serializers.URLField(source='jsonld_id', read_only=True)
    createdAt = serializers.DateTimeField(source='created_at', read_only=True)
    createdBy = EntityRelatedFieldV2(source='cached_creator', read_only=True)
    name = StripTagsCharField(max_length=1024)
    image = ValidImageField(required=False)
    email = serializers.EmailField(max_length=255, required=True)
    description = StripTagsCharField(max_length=16384, required=False)
    url = serializers.URLField(max_length=1024, required=True)
    staff = IssuerStaffSerializerV2(many=True,
                                    source='staff_items',
                                    required=False)
    extensions = serializers.DictField(source='extension_items',
                                       required=False,
                                       validators=[BadgeExtensionValidator()])

    class Meta(DetailSerializerV2.Meta):
        model = Issuer
        apispec_definition = ('Issuer', {
            'properties':
            OrderedDict([
                ('entityId', {
                    'type': "string",
                    'format': "string",
                    'description': "Unique identifier for this Issuer",
                }),
                ('entityType', {
                    'type': "string",
                    'format': "string",
                    'description': "\"Issuer\"",
                }),
                ('openBadgeId', {
                    'type': "string",
                    'format': "url",
                    'description': "URL of the OpenBadge compliant json",
                }),
                ('createdAt', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the Issuer was created",
                }),
                ('createdBy', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeUser who created this Issuer",
                }),
                ('name', {
                    'type': "string",
                    'format': "string",
                    'description': "Name of the Issuer",
                }),
                ('image', {
                    'type':
                    "string",
                    'format':
                    "data:image/png;base64",
                    'description':
                    "Base64 encoded string of an image that represents the Issuer",
                }),
                ('email', {
                    'type': "string",
                    'format': "email",
                    'description': "Contact email for the Issuer",
                }),
                ('url', {
                    'type':
                    "string",
                    'format':
                    "url",
                    'description':
                    "Homepage or website associated with the Issuer",
                }),
                ('description', {
                    'type': "string",
                    'format': "text",
                    'description': "Short description of the Issuer",
                }),
            ])
        })

    def validate_image(self, image):
        if image is not None:
            img_name, img_ext = os.path.splitext(image.name)
            image.name = 'issuer_logo_' + str(uuid.uuid4()) + img_ext
        return image

    def create(self, validated_data):
        staff = validated_data.pop('staff_items', [])
        new_issuer = super(IssuerSerializerV2, self).create(validated_data)

        # update staff after issuer is created
        new_issuer.staff_items = staff

        # set badgrapp
        new_issuer.badgrapp = BadgrApp.objects.get_current(
            self.context.get('request', None))

        return new_issuer
Ejemplo n.º 10
0
class BadgeInstanceSerializerV2(DetailSerializerV2,
                                OriginalJsonSerializerMixin):
    openBadgeId = serializers.URLField(source='jsonld_id', read_only=True)
    createdAt = serializers.DateTimeField(source='created_at', read_only=True)
    createdBy = EntityRelatedFieldV2(source='cached_creator', read_only=True)
    badgeclass = EntityRelatedFieldV2(source='cached_badgeclass',
                                      required=False,
                                      queryset=BadgeClass.cached)
    badgeclassOpenBadgeId = CachedUrlHyperlinkedRelatedField(
        source='badgeclass_jsonld_id',
        view_name='badgeclass_json',
        lookup_field='entity_id',
        queryset=BadgeClass.cached,
        required=False)

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

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

    issuedOn = serializers.DateTimeField(source='issued_on', required=False)
    narrative = MarkdownCharField(required=False, allow_null=True)
    evidence = EvidenceItemSerializerV2(source='evidence_items',
                                        many=True,
                                        required=False)

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

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

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

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

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

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

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

        return instance

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

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

            if 'cached_badgeclass' in data:
                # included badgeclass in request
                data['badgeclass'] = data.pop('cached_badgeclass')
            elif 'badgeclass' in self.context:
                # badgeclass was passed in context
                data['badgeclass'] = self.context.get('badgeclass')
            elif 'badgeclass_jsonld_id' in data:
                data['badgeclass'] = data.pop('badgeclass_jsonld_id')
            else:
                raise serializers.ValidationError(
                    {"badgeclass": ["This field is required"]})

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

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

        return data
Ejemplo n.º 11
0
class BadgeClassSerializerV2(DetailSerializerV2, OriginalJsonSerializerMixin):
    openBadgeId = serializers.URLField(source='jsonld_id', read_only=True)
    createdAt = serializers.DateTimeField(source='created_at', read_only=True)
    createdBy = EntityRelatedFieldV2(source='cached_creator', read_only=True)
    issuer = EntityRelatedFieldV2(source='cached_issuer',
                                  required=False,
                                  queryset=Issuer.cached)
    issuerOpenBadgeId = serializers.URLField(source='issuer_jsonld_id',
                                             read_only=True)

    name = StripTagsCharField(max_length=1024)
    image = ValidImageField(required=False)
    description = StripTagsCharField(max_length=16384,
                                     required=True,
                                     convert_null=True)

    criteriaUrl = StripTagsCharField(source='criteria_url',
                                     required=False,
                                     allow_null=True,
                                     validators=[URLValidator()])
    criteriaNarrative = MarkdownCharField(source='criteria_text',
                                          required=False,
                                          allow_null=True)

    alignments = AlignmentItemSerializerV2(source='alignment_items',
                                           many=True,
                                           required=False)
    tags = serializers.ListField(child=StripTagsCharField(max_length=1024),
                                 source='tag_items',
                                 required=False)

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

    class Meta(DetailSerializerV2.Meta):
        model = BadgeClass
        apispec_definition = ('BadgeClass', {
            'properties':
            OrderedDict([
                ('entityId', {
                    'type': "string",
                    'format': "string",
                    'description': "Unique identifier for this BadgeClass",
                }),
                ('entityType', {
                    'type': "string",
                    'format': "string",
                    'description': "\"BadgeClass\"",
                }),
                ('openBadgeId', {
                    'type': "string",
                    'format': "url",
                    'description': "URL of the OpenBadge compliant json",
                }),
                ('createdAt', {
                    'type': 'string',
                    'format': 'ISO8601 timestamp',
                    'description': "Timestamp when the BadgeClass was created",
                }),
                ('createdBy', {
                    'type': 'string',
                    'format': 'entityId',
                    'description': "BadgeUser who created this BadgeClass",
                }),
                ('issuer', {
                    'type':
                    'string',
                    'format':
                    'entityId',
                    'description':
                    "entityId of the Issuer who owns the BadgeClass",
                }),
                ('name', {
                    'type': "string",
                    'format': "string",
                    'description': "Name of the BadgeClass",
                }),
                ('description', {
                    'type': "string",
                    'format': "string",
                    'description': "Short description of the BadgeClass",
                }),
                ('image', {
                    'type':
                    "string",
                    'format':
                    "data:image/png;base64",
                    'description':
                    "Base64 encoded string of an image that represents the BadgeClass.",
                }),
                ('criteriaUrl', {
                    'type':
                    "string",
                    'format':
                    "url",
                    'description':
                    "External URL that describes in a human-readable format the criteria for the BadgeClass"
                }),
                ('criteriaNarrative', {
                    'type':
                    "string",
                    'format':
                    "markdown",
                    'description':
                    "Markdown formatted description of the criteria"
                }),
                ('tags', {
                    'type': "array",
                    'items': {
                        'type': "string",
                        'format': "string"
                    },
                    'description': "List of tags that describe the BadgeClass"
                }),
                ('alignments', {
                    'type':
                    "array",
                    'items': {
                        '$ref': '#/definitions/BadgeClassAlignment'
                    },
                    'description':
                    "List of objects describing objectives or educational standards"
                }),
            ])
        })

    def update(self, instance, validated_data):
        if 'cached_issuer' in validated_data:
            validated_data.pop('cached_issuer')  # issuer is not updatable
        return super(BadgeClassSerializerV2,
                     self).update(instance, validated_data)

    def create(self, validated_data):
        if 'cached_issuer' in validated_data:
            # included issuer in request
            validated_data['issuer'] = validated_data.pop('cached_issuer')
        elif 'issuer' in self.context:
            # issuer was passed in context
            validated_data['issuer'] = self.context.get('issuer')
        else:
            # issuer is required on create
            raise serializers.ValidationError(
                {"issuer": "This field is required"})

        return super(BadgeClassSerializerV2, self).create(validated_data)
Ejemplo n.º 12
0
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