class PathwayElementCompletionSerializer(serializers.Serializer): pathway = LinkedDataReferenceField(keys=['slug'], model=Pathway, read_only=False) recipients = LinkedDataReferenceField(keys=['slug'], model=RecipientProfile, many=True, read_only=False, field_names={'slug': 'entity_id'}) recipientGroups = LinkedDataReferenceField( keys=['slug'], model=RecipientGroup, many=True, read_only=False, field_names={'slug': 'entity_id'}) recipientCompletions = RecipientCompletionSerializer(many=True) def to_representation(self, data): base_representation = super(PathwayElementCompletionSerializer, self).to_representation(data) ret = OrderedDict() ret["@context"] = "https://badgr.io/public/contexts/pathways" ret["@type"] = "PathwayElementsCompletionReport" ret.update(base_representation) return ret
class RecipientCompletionSerializer(serializers.Serializer): recipient = LinkedDataReferenceField(keys=['slug'], model=RecipientProfile, field_names={'slug': 'entity_id'}) # completions = serializers.ListField(child=serializers.DictField()) def to_representation(self, instance): represenation = super(RecipientCompletionSerializer, self).to_representation(instance) # walk completions and scrub PathwayElements into jsonld references def _walk(node): if 'element' in node and isinstance(node['element'], PathwayElement): node['element'] = { '@id': node['element'].jsonld_id, 'slug': node['element'].slug } if 'children' in node: for child in list(node['children'].values()): _walk(child) for completion in instance['completions']: _walk(completion) represenation['completions'] = instance['completions'] return represenation
class RecipientGroupSerializerV1(LinkedDataEntitySerializer): jsonld_type = "RecipientGroup" name = StripTagsCharField(required=False) description = StripTagsCharField(required=False) slug = StripTagsCharField(read_only=True, source='entity_id') active = serializers.BooleanField(source='is_active', default=True) issuer = LinkedDataReferenceField(keys=['slug'], field_names={'slug': 'entity_id'}, model=Issuer) member_count = serializers.IntegerField(read_only=True) members = RecipientGroupMembershipSerializerV1( read_only=False, many=True, required=False, source='cached_members' ) pathways = LinkedDataReferenceList( read_only=False, required=False, source='cached_pathways', child=LinkedDataReferenceField(read_only=False, keys=['slug'], model=Pathway) ) def to_representation(self, instance): if not self.context.get('embedRecipients', False) and 'members' in self.fields: self.fields.pop('members') return super(RecipientGroupSerializerV1, self).to_representation(instance) def create(self, validated_data): if 'issuer' not in self.context: raise ValidationError("No issuer") issuer = self.context.get('issuer') name = validated_data.get('name') description = validated_data.get('description', '') recipient_group = RecipientGroup( issuer=issuer, name=name, is_active=validated_data.get('is_active', True) ) if description: recipient_group.description = description recipient_group.save() return recipient_group def update(self, instance, validated_data): instance.name = validated_data.get('name', instance.name) instance.description = validated_data.get('description', instance.description) instance.is_active = validated_data.get('is_active', instance.is_active) if 'cached_pathways' in validated_data: existing_pathway_ids = set(instance.cached_pathways()) updated_pathway_ids = set(validated_data.get('cached_pathways')) pathways_to_delete = existing_pathway_ids - updated_pathway_ids pathways_to_add = updated_pathway_ids - existing_pathway_ids for p in pathways_to_delete: instance.pathways.remove(p) p.publish() for p in pathways_to_add: instance.pathways.add(p) p.publish() if 'cached_members' in validated_data: existing_members = set(instance.cached_members()) updated_members = set() for m in validated_data.get('cached_members'): # Save any newly defined profiles directly to the list, # save existing members for comparison if m.pk: updated_members.add(m) else: if not m.recipient_profile.pk: m.recipient_profile.save() m.recipient_profile_id = m.recipient_profile.pk m.save() members_to_delete = existing_members - updated_members for m in members_to_delete: m.delete() instance.save() # update cache return instance
class PathwaySerializer(serializers.Serializer): slug = StripTagsCharField(read_only=True) name = StripTagsCharField(max_length=254, required=False) description = StripTagsCharField(required=False) alignmentUrl = StripTagsCharField(required=False, allow_null=True) issuer = LinkedDataReferenceField(keys=['slug'], model=Issuer, many=False, read_only=True, field_names={'slug': 'entity_id'}) groups = LinkedDataReferenceList(required=False, read_only=False, child=LinkedDataReferenceField( keys=['slug'], model=RecipientGroup, read_only=False, field_names={'slug': 'entity_id'})) completionBadge = LinkedDataReferenceField( ['slug'], BadgeClass, read_only=True, required=False, allow_null=True, source='completion_badge', field_names={'slug': 'entity_id'}) rootChildCount = serializers.IntegerField( read_only=True, source='cached_root_element.cached_children.count') elementCount = serializers.IntegerField(read_only=True, source='cached_elements.count') def to_representation(self, instance): issuer_slug = instance.cached_issuer.entity_id representation = super(PathwaySerializer, self).to_representation(instance) representation['@id'] = instance.jsonld_id if self.context.get('include_context', False): representation.update([ ('@context', "https://badgr.io/public/contexts/pathways"), ("@type", "Pathway") ]) if self.context.get('include_structure', False): self.context.update({ 'pathway_slug': instance.slug, }) element_serializer = PathwayElementSerializer( instance.cached_elements(), many=True, context=self.context) representation.update([ ('rootElement', OriginSetting.HTTP + reverse('pathway_element_detail', kwargs={ 'issuer_slug': issuer_slug, 'pathway_slug': instance.slug, 'element_slug': instance.cached_root_element.slug })), ('elements', element_serializer.data) ]) return representation def create(self, validated_data, **kwargs): # TODO: Replace with validate_name and validate_description methods that check for self.instance if not validated_data.get('name') or not validated_data.get( 'description'): raise ValidationError( "Values for name and description are required to create a Pathway." ) issuer_slug = self.context.get('issuer_slug', None) if not issuer_slug: raise ValidationError("Could not determine issuer") try: issuer = Issuer.cached.get_by_slug_or_id(issuer_slug) except Issuer.DoesNotExist: raise ValidationError("Could not determine issuer") name = validated_data.get('name') pathway = Pathway(issuer=issuer) pathway.save(name_hint=name) root_element = PathwayElement( pathway=pathway, parent_element=None, name=name, description=validated_data.get('description'), alignment_url=validated_data.get('alignmentUrl')) root_element.save() pathway.root_element = root_element pathway.save() if 'groups' in validated_data and len(validated_data.get('groups')): pathway.recipient_groups.add(set(validated_data.get('groups'))) pathway.save() # update cache return pathway def update(self, instance, validated_data): if 'groups' in validated_data: existing_groups = set(instance.recipient_groups.all()) updated_groups = set(validated_data.get('groups')) groups_to_delete = existing_groups - updated_groups groups_to_add = updated_groups - existing_groups for g in groups_to_delete: instance.recipient_groups.remove(g) g.publish() for g in groups_to_add: instance.recipient_groups.add(g) g.publish() instance.save() # update caches, sloppily return instance
class PathwayElementSerializer(LinkedDataEntitySerializer): name = StripTagsCharField() slug = StripTagsCharField(required=False) description = StripTagsCharField() parent = serializers.CharField(required=False) alignmentUrl = StripTagsCharField(required=False, allow_null=True, allow_blank=True) ordering = serializers.IntegerField(required=False, default=99) completionBadge = LinkedDataReferenceField( keys=['slug'], model=BadgeClass, read_only=False, required=False, allow_null=True, source='completion_badgeclass', field_names={'slug': 'entity_id'}) requirements = JSONDictField(required=False, allow_null=True) children = serializers.ListField(required=False, allow_null=True, child=serializers.CharField( required=False, allow_null=False)) def to_representation(self, instance): include_requirements = self.context.get('include_requirements', True) representation = super(PathwayElementSerializer, self).to_representation(instance) representation['alignmentUrl'] = instance.get_alignment_url() representation['children'] = [ child.jsonld_id for child in instance.cached_children() ] if include_requirements and instance.completion_requirements: completion_serializer = PathwayElementCompletionSpecSerializer( instance.completion_requirements, context=self.context) representation['requirements'] = completion_serializer.data return representation def create(self, validated_data): pathway_slug = self.context.get('pathway_slug', None) if not pathway_slug: raise ValidationError("Could not determine pathway") try: pathway = Pathway.cached.get_by_slug_or_id(pathway_slug) except Pathway.DoesNotExist: raise ValidationError("Could not determine pathway") parent_slug = validated_data.get('parent') try: parent_element = PathwayElement.cached.get_by_slug_or_id( parent_slug) except PathwayElement.DoesNotExist: raise ValidationError("Invalid parent") else: if parent_element.pathway != pathway: raise ValidationError("Invalid parent") try: ordering = int(validated_data.get('ordering', 99)) except ValueError: ordering = 99 completion_requirements = None requirement_string = validated_data.get('requirements', None) if requirement_string: try: completion_requirements = CompletionRequirementSpecFactory.parse( requirement_string).serialize() except ValueError as e: raise ValidationError("Invalid completion spec: {}".format( str(e))) element = PathwayElement( pathway=pathway, parent_element=parent_element, ordering=ordering, name=validated_data.get('name'), description=validated_data.get('description', None), alignment_url=validated_data.get('alignmentUrl', None), completion_badgeclass=validated_data.get('completion_badgeclass'), completion_requirements=completion_requirements) element.save() return element def update(self, element, validated_data): parent_element = None parent_slug = validated_data.get('parent') if parent_slug: try: parent_element = PathwayElement.cached.get_by_slug_or_id( parent_slug) except PathwayElement.DoesNotExist: raise ValidationError("Invalid parent") completion_requirements = None requirements = validated_data.get('requirements') if requirements: try: completion_requirements = CompletionRequirementSpecFactory.parse( requirements).serialize() except ValueError as e: raise ValidationError("Invalid requirements: {}".format(e)) child_ids = validated_data.get('children') order = 1 if child_ids: for element_id in child_ids: try: r = resolve(element_id.replace(OriginSetting.HTTP, '')) except Resolver404: raise ValidationError( "Invalid child id: {}".format(element_id)) element_slug = r.kwargs.get('element_slug') try: child = PathwayElement.cached.get(slug=element_slug) except PathwayElement.DoesNotExist: raise ValidationError( "Invalid child id: {}".format(element_id)) else: old_child_parent = child.parent_element child.parent_element = element child.ordering = order order += 1 child.save() old_child_parent.publish() old_parent = None if parent_element: old_parent = element.parent_element element.parent_element = parent_element element.completion_badgeclass = validated_data.get( 'completion_badgeclass') element.name = validated_data.get('name') element.description = validated_data.get('description') element.alignment_url = validated_data.get( 'alignmentUrl') if validated_data.get('alignmentUrl') else None element.ordering = validated_data.get('ordering', 99) element.completion_requirements = completion_requirements element.save() if old_parent: old_parent.publish() return element