class TopicSerializer(ModelSerializer): """ Serializer for core.models.Topic objects. """ agenda_type = IntegerField(write_only=True, required=False, min_value=1, max_value=2) agenda_parent_id = IntegerField(write_only=True, required=False, min_value=1) agenda_comment = CharField(write_only=True, required=False, allow_blank=True) agenda_duration = IntegerField(write_only=True, required=False, min_value=1) agenda_weight = IntegerField(write_only=True, required=False, min_value=1) class Meta: model = Topic fields = ( 'id', 'title', 'text', 'attachments', 'agenda_item_id', 'agenda_type', 'agenda_parent_id', 'agenda_comment', 'agenda_duration', 'agenda_weight', ) def validate(self, data): if 'text' in data: data['text'] = validate_html(data['text']) return data def create(self, validated_data): """ Customized create method. Set information about related agenda item into agenda_item_update_information container. """ agenda_type = validated_data.pop('agenda_type', None) agenda_parent_id = validated_data.pop('agenda_parent_id', None) agenda_comment = validated_data.pop('agenda_comment', None) agenda_duration = validated_data.pop('agenda_duration', None) agenda_weight = validated_data.pop('agenda_weight', None) attachments = validated_data.pop('attachments', []) topic = Topic(**validated_data) topic.agenda_item_update_information['type'] = agenda_type topic.agenda_item_update_information['parent_id'] = agenda_parent_id topic.agenda_item_update_information['comment'] = agenda_comment topic.agenda_item_update_information['duration'] = agenda_duration topic.agenda_item_update_information['weight'] = agenda_weight topic.save() topic.attachments.add(*attachments) return topic
class TopicSerializer(ModelSerializer): """ Serializer for core.models.Topic objects. """ agenda_type = IntegerField( write_only=True, required=False, min_value=1, max_value=3, allow_null=True ) agenda_parent_id = IntegerField(write_only=True, required=False, min_value=1) agenda_comment = CharField(write_only=True, required=False, allow_blank=True) agenda_duration = IntegerField(write_only=True, required=False, min_value=1) agenda_weight = IntegerField(write_only=True, required=False, min_value=1) class Meta: model = Topic fields = ( "id", "title", "text", "attachments", "agenda_item_id", "agenda_type", "agenda_parent_id", "agenda_comment", "agenda_duration", "agenda_weight", ) def validate(self, data): if "text" in data: data["text"] = validate_html(data["text"]) return data def create(self, validated_data): """ Customized create method. Set information about related agenda item into agenda_item_update_information container. """ agenda_type = validated_data.pop("agenda_type", None) agenda_parent_id = validated_data.pop("agenda_parent_id", None) agenda_comment = validated_data.pop("agenda_comment", None) agenda_duration = validated_data.pop("agenda_duration", None) agenda_weight = validated_data.pop("agenda_weight", None) attachments = validated_data.pop("attachments", []) topic = Topic(**validated_data) topic.agenda_item_update_information["type"] = agenda_type topic.agenda_item_update_information["parent_id"] = agenda_parent_id topic.agenda_item_update_information["comment"] = agenda_comment topic.agenda_item_update_information["duration"] = agenda_duration topic.agenda_item_update_information["weight"] = agenda_weight topic.save(skip_autoupdate=True) topic.attachments.add(*attachments) inform_changed_data(topic) return topic
class AssignmentFullSerializer(ModelSerializer): """ Serializer for assignment.models.Assignment objects. With all polls. """ assignment_related_users = AssignmentRelatedUserSerializer(many=True, read_only=True) polls = AssignmentAllPollSerializer(many=True, read_only=True) agenda_type = IntegerField(write_only=True, required=False, min_value=1, max_value=3) agenda_parent_id = IntegerField(write_only=True, required=False, min_value=1) class Meta: model = Assignment fields = ( 'id', 'title', 'description', 'open_posts', 'phase', 'assignment_related_users', 'poll_description_default', 'polls', 'agenda_item_id', 'agenda_type', 'agenda_parent_id', 'tags', ) validators = (posts_validator, ) def validate(self, data): if 'description' in data: data['description'] = validate_html(data['description']) return data def create(self, validated_data): """ Customized create method. Set information about related agenda item into agenda_item_update_information container. """ agenda_type = validated_data.pop('agenda_type', None) agenda_parent_id = validated_data.pop('agenda_parent_id', None) assignment = Assignment(**validated_data) assignment.agenda_item_update_information['type'] = agenda_type assignment.agenda_item_update_information[ 'parent_id'] = agenda_parent_id assignment.save() return assignment
class AssignmentAllPollSerializer(ModelSerializer): """ Serializer for assignment.models.AssignmentPoll objects. Serializes all polls. """ options = AssignmentOptionSerializer(many=True, read_only=True) votes = ListField( child=DictField( child=IntegerField(min_value=-2)), write_only=True, required=False) has_votes = SerializerMethodField() class Meta: model = AssignmentPoll fields = ( 'id', 'yesnoabstain', 'description', 'published', 'options', 'votesvalid', 'votesinvalid', 'votescast', 'votes', 'has_votes', 'assignment') # js-data needs the assignment-id in the nested object to define relations. read_only_fields = ('yesnoabstain',) validators = (default_votes_validator,) def get_has_votes(self, obj): """ Returns True if this poll has some votes. """ return obj.has_votes() @transaction.atomic def update(self, instance, validated_data): """ Customized update method for polls. To update votes use the write only field 'votes'. Example data for a 'yesnoabstain'=true poll with two candidates: "votes": [{"Yes": 10, "No": 4, "Abstain": -2}, {"Yes": -1, "No": 0, "Abstain": -2}] Example data for a 'yesnoabstain'=false poll with two candidates: "votes": [{"Votes": 10}, {"Votes": 0}] """ # Update votes. votes = validated_data.get('votes') if votes: options = list(instance.get_options()) if len(votes) != len(options): raise ValidationError({ 'detail': _('You have to submit data for %d candidates.') % len(options)}) for index, option in enumerate(options): if len(votes[index]) != len(instance.get_vote_values()): raise ValidationError({ 'detail': _('You have to submit data for %d vote values.') % len(instance.get_vote_values())}) for vote_value, vote_weight in votes[index].items(): if vote_value not in instance.get_vote_values(): raise ValidationError({ 'detail': _('Vote value %s is invalid.') % vote_value}) instance.set_vote_objects_with_values(option, votes[index]) # Update remaining writeable fields. instance.description = validated_data.get('description', instance.description) instance.published = validated_data.get('published', instance.published) instance.votesvalid = validated_data.get('votesvalid', instance.votesvalid) instance.votesinvalid = validated_data.get('votesinvalid', instance.votesinvalid) instance.votescast = validated_data.get('votescast', instance.votescast) instance.save() return instance
class MotionSerializer(ModelSerializer): """ Serializer for motion.models.Motion objects. """ active_version = PrimaryKeyRelatedField(read_only=True) log_messages = MotionLogSerializer(many=True, read_only=True) polls = MotionPollSerializer(many=True, read_only=True) reason = CharField(allow_blank=True, required=False, write_only=True) text = CharField(write_only=True) title = CharField(max_length=255, write_only=True) versions = MotionVersionSerializer(many=True, read_only=True) workflow_id = IntegerField(min_value=1, required=False, validators=[validate_workflow_field], write_only=True) class Meta: model = Motion fields = ( 'id', 'identifier', 'title', 'text', 'reason', 'versions', 'active_version', 'parent', 'category', 'submitters', 'supporters', 'state', 'workflow_id', 'tags', 'attachments', 'polls', 'agenda_item_id', 'log_messages', ) read_only_fields = ( 'parent', 'state' ) # Some other fields are also read_only. See definitions above. @transaction.atomic def create(self, validated_data): """ Customized method to create a new motion from some data. """ motion = Motion() motion.title = validated_data['title'] motion.text = validated_data['text'] motion.reason = validated_data.get('reason', '') motion.identifier = validated_data.get('identifier') motion.category = validated_data.get('category') motion.reset_state(validated_data.get('workflow_id')) motion.save() if validated_data.get('submitters'): motion.submitters.add(*validated_data['submitters']) else: motion.submitters.add(validated_data['request_user']) motion.supporters.add(*validated_data.get('supporters', [])) motion.attachments.add(*validated_data.get('attachments', [])) motion.tags.add(*validated_data.get('tags', [])) return motion @transaction.atomic def update(self, motion, validated_data): """ Customized method to update a motion. """ # Identifier and category. for key in ('identifier', 'category'): if key in validated_data.keys(): setattr(motion, key, validated_data[key]) # Workflow. workflow_id = validated_data.get('workflow_id') if workflow_id is not None and workflow_id != motion.workflow: motion.reset_state(workflow_id) # Decide if a new version is saved to the database. if (motion.state.versioning and not validated_data.get('disable_versioning', False)): # TODO version = motion.get_new_version() else: version = motion.get_last_version() # Title, text, reason. for key in ('title', 'text', 'reason'): if key in validated_data.keys(): setattr(version, key, validated_data[key]) motion.save(use_version=version) # Submitters, supporters, attachments and tags for key in ('submitters', 'supporters', 'attachments', 'tags'): if key in validated_data.keys(): attr = getattr(motion, key) attr.clear() attr.add(*validated_data[key]) return motion
class MotionPollSerializer(ModelSerializer): """ Serializer for motion.models.MotionPoll objects. """ yes = SerializerMethodField() no = SerializerMethodField() abstain = SerializerMethodField() votes = DictField(child=IntegerField(min_value=-2, allow_null=True), write_only=True) has_votes = SerializerMethodField() class Meta: model = MotionPoll fields = ('id', 'motion', 'yes', 'no', 'abstain', 'votesvalid', 'votesinvalid', 'votescast', 'votes', 'has_votes') validators = (default_votes_validator, ) def __init__(self, *args, **kwargs): # The following dictionary is just a cache for several votes. self._votes_dicts = {} return super().__init__(*args, **kwargs) def get_yes(self, obj): try: result = self.get_votes_dict(obj)['Yes'] except KeyError: result = None return result def get_no(self, obj): try: result = self.get_votes_dict(obj)['No'] except KeyError: result = None return result def get_abstain(self, obj): try: result = self.get_votes_dict(obj)['Abstain'] except KeyError: result = None return result def get_votes_dict(self, obj): try: votes_dict = self._votes_dicts[obj.pk] except KeyError: votes_dict = self._votes_dicts[obj.pk] = {} for vote in obj.get_votes(): votes_dict[vote.value] = vote.weight return votes_dict def get_has_votes(self, obj): """ Returns True if this poll has some votes. """ return obj.has_votes() @transaction.atomic def update(self, instance, validated_data): """ Customized update method for polls. To update votes use the write only field 'votes'. Example data: "votes": {"Yes": 10, "No": 4, "Abstain": -2} """ # Update votes. votes = validated_data.get('votes') if votes: if len(votes) != len(instance.get_vote_values()): raise ValidationError({ 'detail': _('You have to submit data for %d vote values.') % len(instance.get_vote_values()) }) for vote_value, vote_weight in votes.items(): if vote_value not in instance.get_vote_values(): raise ValidationError({ 'detail': _('Vote value %s is invalid.') % vote_value }) instance.set_vote_objects_with_values(instance.get_options().get(), votes) # Update remaining writeable fields. instance.votesvalid = validated_data.get('votesvalid', instance.votesvalid) instance.votesinvalid = validated_data.get('votesinvalid', instance.votesinvalid) instance.votescast = validated_data.get('votescast', instance.votescast) instance.save() return instance
class MotionSerializer(ModelSerializer): """ Serializer for motion.models.Motion objects. """ active_version = PrimaryKeyRelatedField(read_only=True) comments = MotionCommentsJSONSerializerField(required=False) log_messages = MotionLogSerializer(many=True, read_only=True) polls = MotionPollSerializer(many=True, read_only=True) reason = CharField(allow_blank=True, required=False, write_only=True) state_required_permission_to_see = SerializerMethodField() text = CharField(write_only=True) title = CharField(max_length=255, write_only=True) versions = MotionVersionSerializer(many=True, read_only=True) workflow_id = IntegerField(min_value=1, required=False, validators=[validate_workflow_field], write_only=True) class Meta: model = Motion fields = ( 'id', 'identifier', 'title', 'text', 'reason', 'versions', 'active_version', 'parent', 'category', 'motion_block', 'origin', 'submitters', 'supporters', 'comments', 'state', 'state_required_permission_to_see', 'workflow_id', 'recommendation', 'tags', 'attachments', 'polls', 'agenda_item_id', 'log_messages', ) read_only_fields = ( 'state', 'recommendation', ) # Some other fields are also read_only. See definitions above. def validate(self, data): if 'text' in data: data['text'] = validate_html(data['text']) if 'reason' in data: data['reason'] = validate_html(data['reason']) validated_comments = [] for comment in data.get('comments', []): validated_comments.append(validate_html(comment)) data['comments'] = validated_comments return data @transaction.atomic def create(self, validated_data): """ Customized method to create a new motion from some data. """ motion = Motion() motion.title = validated_data['title'] motion.text = validated_data['text'] motion.reason = validated_data.get('reason', '') motion.identifier = validated_data.get('identifier') motion.category = validated_data.get('category') motion.motion_block = validated_data.get('motion_block') motion.origin = validated_data.get('origin', '') motion.comments = validated_data.get('comments') motion.parent = validated_data.get('parent') motion.reset_state(validated_data.get('workflow_id')) motion.save() if validated_data.get('submitters'): motion.submitters.add(*validated_data['submitters']) elif validated_data['request_user'].is_authenticated(): motion.submitters.add(validated_data['request_user']) motion.supporters.add(*validated_data.get('supporters', [])) motion.attachments.add(*validated_data.get('attachments', [])) motion.tags.add(*validated_data.get('tags', [])) return motion @transaction.atomic def update(self, motion, validated_data): """ Customized method to update a motion. """ # Identifier, category, motion_block, origin and comments. for key in ('identifier', 'category', 'motion_block', 'origin', 'comments'): if key in validated_data.keys(): setattr(motion, key, validated_data[key]) # Workflow. workflow_id = validated_data.get('workflow_id') if workflow_id is not None and workflow_id != motion.workflow: motion.reset_state(workflow_id) # Decide if a new version is saved to the database. if (motion.state.versioning and not validated_data.get('disable_versioning', False)): # TODO version = motion.get_new_version() else: version = motion.get_last_version() # Title, text, reason. for key in ('title', 'text', 'reason'): if key in validated_data.keys(): setattr(version, key, validated_data[key]) motion.save(use_version=version) # Submitters, supporters, attachments and tags for key in ('submitters', 'supporters', 'attachments', 'tags'): if key in validated_data.keys(): attr = getattr(motion, key) attr.clear() attr.add(*validated_data[key]) return motion def get_state_required_permission_to_see(self, motion): """ Returns the permission (as string) that is required for non managers that are not submitters to see this motion in this state. Hint: Most states have and empty string here so this restriction is disabled. """ return motion.state.required_permission_to_see
class AssignmentSerializer(ModelSerializer): """ Serializer for assignment.models.Assignment objects. With all polls. """ assignment_related_users = AssignmentRelatedUserSerializer(many=True, read_only=True) agenda_create = BooleanField(write_only=True, required=False, allow_null=True) agenda_type = IntegerField(write_only=True, required=False, min_value=1, max_value=3, allow_null=True) agenda_parent_id = IntegerField(write_only=True, required=False, min_value=1) polls = IdPrimaryKeyRelatedField(many=True, read_only=True) class Meta: model = Assignment fields = ( "id", "title", "description", "open_posts", "phase", "assignment_related_users", "default_poll_description", "agenda_item_id", "list_of_speakers_id", "agenda_create", "agenda_type", "agenda_parent_id", "tags", "attachments", "number_poll_candidates", "polls", ) validators = (posts_validator, ) def validate(self, data): if "description" in data: data["description"] = validate_html_strict(data["description"]) return data def create(self, validated_data): """ Customized create method. Set information about related agenda item into agenda_item_update_information container. """ tags = validated_data.pop("tags", []) attachments = validated_data.pop("attachments", []) request_user = validated_data.pop( "request_user") # this should always be there agenda_create = validated_data.pop("agenda_create", None) agenda_type = validated_data.pop("agenda_type", None) agenda_parent_id = validated_data.pop("agenda_parent_id", None) assignment = Assignment(**validated_data) if has_perm(request_user, "agenda.can_manage"): assignment.agenda_item_update_information["create"] = agenda_create assignment.agenda_item_update_information["type"] = agenda_type assignment.agenda_item_update_information[ "parent_id"] = agenda_parent_id assignment.save() assignment.tags.add(*tags) assignment.attachments.add(*attachments) inform_changed_data(assignment) return assignment