2015-06-14 23:26:06 +02:00
|
|
|
from django.db import transaction
|
|
|
|
from django.utils.translation import ugettext as _
|
|
|
|
|
2016-02-09 21:04:29 +01:00
|
|
|
from openslides.poll.serializers import default_votes_validator
|
2015-06-14 23:26:06 +02:00
|
|
|
from openslides.utils.rest_api import (
|
|
|
|
DictField,
|
|
|
|
IntegerField,
|
|
|
|
ListField,
|
|
|
|
ListSerializer,
|
|
|
|
ModelSerializer,
|
2016-01-09 19:27:02 +01:00
|
|
|
SerializerMethodField,
|
2015-06-16 10:37:23 +02:00
|
|
|
ValidationError,
|
|
|
|
)
|
2015-01-17 14:25:05 +01:00
|
|
|
|
2017-11-20 16:40:39 +01:00
|
|
|
from ..utils.validate import validate_html
|
2015-01-17 14:25:05 +01:00
|
|
|
from .models import (
|
|
|
|
Assignment,
|
|
|
|
AssignmentOption,
|
|
|
|
AssignmentPoll,
|
2015-06-16 10:37:23 +02:00
|
|
|
AssignmentRelatedUser,
|
|
|
|
AssignmentVote,
|
|
|
|
models,
|
|
|
|
)
|
2015-01-17 14:25:05 +01:00
|
|
|
|
|
|
|
|
2017-01-14 12:07:14 +01:00
|
|
|
def posts_validator(data):
|
|
|
|
"""
|
|
|
|
Validator for open posts. It checks that the values for the open posts are greater than 0.
|
|
|
|
"""
|
|
|
|
if (data['open_posts'] and data['open_posts'] is not None and data['open_posts'] < 1):
|
|
|
|
raise ValidationError({'detail': _('Value for {} must be greater than 0').format('open_posts')})
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
2015-02-12 18:48:14 +01:00
|
|
|
class AssignmentRelatedUserSerializer(ModelSerializer):
|
2015-01-17 14:25:05 +01:00
|
|
|
"""
|
2015-01-25 15:10:34 +01:00
|
|
|
Serializer for assignment.models.AssignmentRelatedUser objects.
|
2015-01-17 14:25:05 +01:00
|
|
|
"""
|
|
|
|
class Meta:
|
2015-01-25 15:10:34 +01:00
|
|
|
model = AssignmentRelatedUser
|
2015-01-17 14:25:05 +01:00
|
|
|
fields = (
|
|
|
|
'id',
|
2015-01-25 15:10:34 +01:00
|
|
|
'user',
|
2016-01-09 16:26:00 +01:00
|
|
|
'elected',
|
2016-12-06 12:21:29 +01:00
|
|
|
'assignment',
|
|
|
|
'weight') # js-data needs the assignment-id in the nested object to define relations.
|
2015-01-17 14:25:05 +01:00
|
|
|
|
|
|
|
|
2015-02-12 18:48:14 +01:00
|
|
|
class AssignmentVoteSerializer(ModelSerializer):
|
2015-01-17 14:25:05 +01:00
|
|
|
"""
|
|
|
|
Serializer for assignment.models.AssignmentVote objects.
|
|
|
|
"""
|
|
|
|
class Meta:
|
|
|
|
model = AssignmentVote
|
2015-02-04 00:08:38 +01:00
|
|
|
fields = ('weight', 'value',)
|
2015-01-17 14:25:05 +01:00
|
|
|
|
|
|
|
|
2015-02-12 18:48:14 +01:00
|
|
|
class AssignmentOptionSerializer(ModelSerializer):
|
2015-01-17 14:25:05 +01:00
|
|
|
"""
|
|
|
|
Serializer for assignment.models.AssignmentOption objects.
|
|
|
|
"""
|
2015-11-25 21:31:08 +01:00
|
|
|
votes = AssignmentVoteSerializer(many=True, read_only=True)
|
2016-01-09 19:27:02 +01:00
|
|
|
is_elected = SerializerMethodField()
|
2015-01-17 14:25:05 +01:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = AssignmentOption
|
2016-12-06 12:21:29 +01:00
|
|
|
fields = ('id', 'candidate', 'is_elected', 'votes', 'poll', 'weight')
|
2016-01-09 19:27:02 +01:00
|
|
|
|
|
|
|
def get_is_elected(self, obj):
|
|
|
|
"""
|
|
|
|
Returns the election status of the candidate of this option.
|
|
|
|
"""
|
|
|
|
return obj.poll.assignment.is_elected(obj.candidate)
|
2015-01-17 14:25:05 +01:00
|
|
|
|
|
|
|
|
2015-02-12 18:48:14 +01:00
|
|
|
class FilterPollListSerializer(ListSerializer):
|
2015-01-17 14:25:05 +01:00
|
|
|
"""
|
2015-01-24 16:35:50 +01:00
|
|
|
Customized serializer to filter polls (exclude unpublished).
|
2015-01-17 14:25:05 +01:00
|
|
|
"""
|
|
|
|
def to_representation(self, data):
|
|
|
|
"""
|
|
|
|
List of object instances -> List of dicts of primitive datatypes.
|
|
|
|
|
|
|
|
This method is adapted to filter the data and exclude unpublished polls.
|
|
|
|
"""
|
|
|
|
# Dealing with nested relationships, data can be a Manager,
|
|
|
|
# so, first get a queryset from the Manager if needed
|
|
|
|
iterable = data.filter(published=True) if isinstance(data, models.Manager) else data
|
|
|
|
return [self.child.to_representation(item) for item in iterable]
|
|
|
|
|
|
|
|
|
2015-02-12 18:48:14 +01:00
|
|
|
class AssignmentAllPollSerializer(ModelSerializer):
|
2015-01-17 14:25:05 +01:00
|
|
|
"""
|
|
|
|
Serializer for assignment.models.AssignmentPoll objects.
|
|
|
|
|
|
|
|
Serializes all polls.
|
|
|
|
"""
|
2015-11-25 21:31:08 +01:00
|
|
|
options = AssignmentOptionSerializer(many=True, read_only=True)
|
2015-06-14 23:26:06 +02:00
|
|
|
votes = ListField(
|
|
|
|
child=DictField(
|
|
|
|
child=IntegerField(min_value=-2)),
|
2015-12-18 20:18:03 +01:00
|
|
|
write_only=True,
|
|
|
|
required=False)
|
2016-01-09 19:27:02 +01:00
|
|
|
has_votes = SerializerMethodField()
|
2015-01-17 14:25:05 +01:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = AssignmentPoll
|
|
|
|
fields = (
|
|
|
|
'id',
|
2016-08-26 13:46:57 +02:00
|
|
|
'pollmethod',
|
2015-01-17 14:25:05 +01:00
|
|
|
'description',
|
|
|
|
'published',
|
2015-11-25 21:31:08 +01:00
|
|
|
'options',
|
2018-07-03 15:47:31 +02:00
|
|
|
'votesabstain',
|
|
|
|
'votesno',
|
2015-01-17 14:25:05 +01:00
|
|
|
'votesvalid',
|
|
|
|
'votesinvalid',
|
2015-06-14 23:26:06 +02:00
|
|
|
'votescast',
|
2015-11-25 19:27:34 +01:00
|
|
|
'votes',
|
2016-01-09 19:27:02 +01:00
|
|
|
'has_votes',
|
2015-11-25 19:27:34 +01:00
|
|
|
'assignment') # js-data needs the assignment-id in the nested object to define relations.
|
2016-08-26 13:46:57 +02:00
|
|
|
read_only_fields = ('pollmethod',)
|
2016-02-09 21:04:29 +01:00
|
|
|
validators = (default_votes_validator,)
|
2015-06-14 23:26:06 +02:00
|
|
|
|
2016-01-09 19:27:02 +01:00
|
|
|
def get_has_votes(self, obj):
|
|
|
|
"""
|
|
|
|
Returns True if this poll has some votes.
|
|
|
|
"""
|
|
|
|
return obj.has_votes()
|
|
|
|
|
2015-06-14 23:26:06 +02:00
|
|
|
@transaction.atomic
|
|
|
|
def update(self, instance, validated_data):
|
|
|
|
"""
|
|
|
|
Customized update method for polls. To update votes use the write
|
|
|
|
only field 'votes'.
|
|
|
|
|
2016-08-26 13:46:57 +02:00
|
|
|
Example data for a 'pollmethod'='yna' poll with two candidates:
|
2015-06-14 23:26:06 +02:00
|
|
|
|
|
|
|
"votes": [{"Yes": 10, "No": 4, "Abstain": -2},
|
|
|
|
{"Yes": -1, "No": 0, "Abstain": -2}]
|
2015-11-27 23:09:38 +01:00
|
|
|
|
2016-08-26 13:46:57 +02:00
|
|
|
Example data for a 'pollmethod' ='yn' poll with two candidates:
|
2015-11-27 23:09:38 +01:00
|
|
|
"votes": [{"Votes": 10}, {"Votes": 0}]
|
2015-06-14 23:26:06 +02:00
|
|
|
"""
|
|
|
|
# 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})
|
2016-10-12 11:23:53 +02:00
|
|
|
instance.set_vote_objects_with_values(option, votes[index], skip_autoupdate=True)
|
2015-06-14 23:26:06 +02:00
|
|
|
|
|
|
|
# Update remaining writeable fields.
|
|
|
|
instance.description = validated_data.get('description', instance.description)
|
|
|
|
instance.published = validated_data.get('published', instance.published)
|
2018-07-03 15:47:31 +02:00
|
|
|
instance.votesabstain = validated_data.get('votesabstain', instance.votesabstain)
|
|
|
|
instance.votesno = validated_data.get('votesno', instance.votesno)
|
2015-06-14 23:26:06 +02:00
|
|
|
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
|
2015-01-17 14:25:05 +01:00
|
|
|
|
|
|
|
|
|
|
|
class AssignmentShortPollSerializer(AssignmentAllPollSerializer):
|
|
|
|
"""
|
|
|
|
Serializer for assignment.models.AssignmentPoll objects.
|
|
|
|
|
2016-01-09 19:27:02 +01:00
|
|
|
Serializes only short polls (excluded unpublished polls).
|
2015-01-17 14:25:05 +01:00
|
|
|
"""
|
|
|
|
class Meta:
|
|
|
|
list_serializer_class = FilterPollListSerializer
|
|
|
|
model = AssignmentPoll
|
|
|
|
fields = (
|
|
|
|
'id',
|
2016-08-26 13:46:57 +02:00
|
|
|
'pollmethod',
|
2015-01-17 14:25:05 +01:00
|
|
|
'description',
|
|
|
|
'published',
|
2015-11-25 21:31:08 +01:00
|
|
|
'options',
|
2018-07-03 15:47:31 +02:00
|
|
|
'votesabstain',
|
|
|
|
'votesno',
|
2015-01-17 14:25:05 +01:00
|
|
|
'votesvalid',
|
|
|
|
'votesinvalid',
|
2016-01-13 15:28:30 +01:00
|
|
|
'votescast',
|
|
|
|
'has_votes',)
|
2015-01-17 14:25:05 +01:00
|
|
|
|
|
|
|
|
2015-02-12 18:48:14 +01:00
|
|
|
class AssignmentFullSerializer(ModelSerializer):
|
2015-01-17 14:25:05 +01:00
|
|
|
"""
|
|
|
|
Serializer for assignment.models.Assignment objects. With all polls.
|
|
|
|
"""
|
2015-03-29 15:49:37 +02:00
|
|
|
assignment_related_users = AssignmentRelatedUserSerializer(many=True, read_only=True)
|
2015-01-25 15:10:34 +01:00
|
|
|
polls = AssignmentAllPollSerializer(many=True, read_only=True)
|
2018-01-20 09:54:46 +01:00
|
|
|
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)
|
2015-01-17 14:25:05 +01:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = Assignment
|
|
|
|
fields = (
|
2015-02-04 00:08:38 +01:00
|
|
|
'id',
|
2015-03-01 08:42:47 +01:00
|
|
|
'title',
|
2015-01-17 14:25:05 +01:00
|
|
|
'description',
|
2015-03-01 08:42:47 +01:00
|
|
|
'open_posts',
|
|
|
|
'phase',
|
2015-03-29 15:49:37 +02:00
|
|
|
'assignment_related_users',
|
2015-01-24 16:35:50 +01:00
|
|
|
'poll_description_default',
|
2015-01-25 15:10:34 +01:00
|
|
|
'polls',
|
2015-10-24 19:02:43 +02:00
|
|
|
'agenda_item_id',
|
2018-01-20 09:54:46 +01:00
|
|
|
'agenda_type',
|
|
|
|
'agenda_parent_id',
|
2015-06-22 14:23:42 +02:00
|
|
|
'tags',)
|
2017-01-14 12:07:14 +01:00
|
|
|
validators = (posts_validator,)
|
2015-01-17 14:25:05 +01:00
|
|
|
|
2017-11-20 16:40:39 +01:00
|
|
|
def validate(self, data):
|
|
|
|
if 'description' in data:
|
|
|
|
data['description'] = validate_html(data['description'])
|
|
|
|
return data
|
|
|
|
|
2018-01-20 09:54:46 +01:00
|
|
|
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
|
|
|
|
|
2015-01-17 14:25:05 +01:00
|
|
|
|
|
|
|
class AssignmentShortSerializer(AssignmentFullSerializer):
|
|
|
|
"""
|
|
|
|
Serializer for assignment.models.Assignment objects. Without unpublished poll.
|
|
|
|
"""
|
2015-06-22 14:23:42 +02:00
|
|
|
assignment_related_users = AssignmentRelatedUserSerializer(many=True, read_only=True)
|
2015-01-25 15:10:34 +01:00
|
|
|
polls = AssignmentShortPollSerializer(many=True, read_only=True)
|
2015-01-17 14:25:05 +01:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = Assignment
|
|
|
|
fields = (
|
2015-02-04 00:08:38 +01:00
|
|
|
'id',
|
2015-03-01 08:42:47 +01:00
|
|
|
'title',
|
2015-01-17 14:25:05 +01:00
|
|
|
'description',
|
2015-03-01 08:42:47 +01:00
|
|
|
'open_posts',
|
|
|
|
'phase',
|
2015-03-29 15:49:37 +02:00
|
|
|
'assignment_related_users',
|
2015-01-24 16:35:50 +01:00
|
|
|
'poll_description_default',
|
2015-01-25 15:10:34 +01:00
|
|
|
'polls',
|
2015-10-24 19:02:43 +02:00
|
|
|
'agenda_item_id',
|
2015-06-22 14:23:42 +02:00
|
|
|
'tags',)
|
2017-01-14 12:07:14 +01:00
|
|
|
validators = (posts_validator,)
|
2017-11-20 16:40:39 +01:00
|
|
|
|
|
|
|
def validate(self, data):
|
|
|
|
if 'description' in data:
|
|
|
|
data['description'] = validate_html(data['description'])
|
|
|
|
return data
|