2015-06-14 23:26:06 +02:00
|
|
|
from django.db import transaction
|
|
|
|
|
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 (
|
2019-07-10 08:31:58 +02:00
|
|
|
BooleanField,
|
2018-08-22 17:34:16 +02:00
|
|
|
DecimalField,
|
2015-06-14 23:26:06 +02:00
|
|
|
DictField,
|
|
|
|
IntegerField,
|
|
|
|
ListField,
|
|
|
|
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
|
|
|
|
2019-07-10 08:31:58 +02:00
|
|
|
from ..utils.auth import has_perm
|
2019-03-04 18:28:21 +01:00
|
|
|
from ..utils.autoupdate import inform_changed_data
|
2019-03-06 14:53:24 +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,
|
|
|
|
)
|
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.
|
|
|
|
"""
|
2019-01-06 16:22:33 +01:00
|
|
|
if data["open_posts"] and data["open_posts"] is not None and data["open_posts"] < 1:
|
|
|
|
raise ValidationError(
|
2019-01-12 23:01:42 +01:00
|
|
|
{"detail": "Value for 'open_posts' must be greater than 0"}
|
2019-01-06 16:22:33 +01:00
|
|
|
)
|
2017-01-14 12:07:14 +01:00
|
|
|
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
|
|
|
"""
|
2019-01-06 16:22:33 +01:00
|
|
|
|
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 = (
|
2019-01-06 16:22:33 +01:00
|
|
|
"id",
|
|
|
|
"user",
|
|
|
|
"elected",
|
|
|
|
"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.
|
|
|
|
"""
|
2019-01-06 16:22:33 +01:00
|
|
|
|
2015-01-17 14:25:05 +01:00
|
|
|
class Meta:
|
|
|
|
model = AssignmentVote
|
2019-01-06 16:22:33 +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.
|
|
|
|
"""
|
2019-01-06 16:22:33 +01:00
|
|
|
|
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
|
2019-01-06 16:22:33 +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.
|
2019-07-17 09:50:24 +02:00
|
|
|
If the candidate is None (e.g. deleted) the result is False.
|
2016-01-09 19:27:02 +01:00
|
|
|
"""
|
2019-07-17 09:50:24 +02:00
|
|
|
if not obj.candidate:
|
|
|
|
return False
|
2016-01-09 19:27:02 +01:00
|
|
|
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 AssignmentAllPollSerializer(ModelSerializer):
|
2015-01-17 14:25:05 +01:00
|
|
|
"""
|
|
|
|
Serializer for assignment.models.AssignmentPoll objects.
|
|
|
|
|
|
|
|
Serializes all polls.
|
|
|
|
"""
|
2019-01-06 16:22:33 +01:00
|
|
|
|
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(
|
2019-01-06 16:22:33 +01:00
|
|
|
child=DecimalField(max_digits=15, decimal_places=6, min_value=-2)
|
|
|
|
),
|
2015-12-18 20:18:03 +01:00
|
|
|
write_only=True,
|
2019-01-06 16:22:33 +01:00
|
|
|
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 = (
|
2019-01-06 16:22:33 +01:00
|
|
|
"id",
|
|
|
|
"pollmethod",
|
|
|
|
"description",
|
|
|
|
"published",
|
|
|
|
"options",
|
|
|
|
"votesabstain",
|
|
|
|
"votesno",
|
|
|
|
"votesvalid",
|
|
|
|
"votesinvalid",
|
|
|
|
"votescast",
|
|
|
|
"votes",
|
|
|
|
"has_votes",
|
|
|
|
"assignment",
|
|
|
|
) # js-data needs the assignment-id in the nested object to define relations.
|
|
|
|
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.
|
2019-01-06 16:22:33 +01:00
|
|
|
votes = validated_data.get("votes")
|
2015-06-14 23:26:06 +02:00
|
|
|
if votes:
|
|
|
|
options = list(instance.get_options())
|
|
|
|
if len(votes) != len(options):
|
2019-01-06 16:22:33 +01:00
|
|
|
raise ValidationError(
|
|
|
|
{
|
2019-01-12 23:01:42 +01:00
|
|
|
"detail": f"You have to submit data for {len(options)} candidates."
|
2019-01-06 16:22:33 +01:00
|
|
|
}
|
|
|
|
)
|
2015-06-14 23:26:06 +02:00
|
|
|
for index, option in enumerate(options):
|
|
|
|
if len(votes[index]) != len(instance.get_vote_values()):
|
2019-01-06 16:22:33 +01:00
|
|
|
raise ValidationError(
|
|
|
|
{
|
2019-01-12 23:01:42 +01:00
|
|
|
"detail": f"You have to submit data for {len(instance.get_vote_values())} vote values."
|
2019-01-06 16:22:33 +01:00
|
|
|
}
|
|
|
|
)
|
2019-01-12 23:01:42 +01:00
|
|
|
for vote_value, __ in votes[index].items():
|
2015-06-14 23:26:06 +02:00
|
|
|
if vote_value not in instance.get_vote_values():
|
2019-01-06 16:22:33 +01:00
|
|
|
raise ValidationError(
|
2019-01-12 23:01:42 +01:00
|
|
|
{"detail": f"Vote value {vote_value} is invalid."}
|
2019-01-06 16:22:33 +01: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.
|
2019-01-06 16:22:33 +01:00
|
|
|
instance.description = validated_data.get("description", instance.description)
|
|
|
|
instance.published = validated_data.get("published", instance.published)
|
|
|
|
instance.votesabstain = validated_data.get(
|
|
|
|
"votesabstain", instance.votesabstain
|
|
|
|
)
|
|
|
|
instance.votesno = validated_data.get("votesno", instance.votesno)
|
|
|
|
instance.votesvalid = validated_data.get("votesvalid", instance.votesvalid)
|
|
|
|
instance.votesinvalid = validated_data.get(
|
|
|
|
"votesinvalid", instance.votesinvalid
|
|
|
|
)
|
|
|
|
instance.votescast = validated_data.get("votescast", instance.votescast)
|
2015-06-14 23:26:06 +02:00
|
|
|
instance.save()
|
|
|
|
return instance
|
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.
|
|
|
|
"""
|
2019-01-06 16:22:33 +01: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)
|
2019-07-10 08:31:58 +02:00
|
|
|
agenda_create = BooleanField(write_only=True, required=False, allow_null=True)
|
2019-01-06 16:22:33 +01:00
|
|
|
agenda_type = IntegerField(
|
2019-07-10 08:31:58 +02:00
|
|
|
write_only=True, required=False, min_value=1, max_value=3, allow_null=True
|
2019-01-06 16:22:33 +01:00
|
|
|
)
|
2018-01-20 09:54:46 +01:00
|
|
|
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 = (
|
2019-01-06 16:22:33 +01:00
|
|
|
"id",
|
|
|
|
"title",
|
|
|
|
"description",
|
|
|
|
"open_posts",
|
|
|
|
"phase",
|
|
|
|
"assignment_related_users",
|
|
|
|
"poll_description_default",
|
|
|
|
"polls",
|
|
|
|
"agenda_item_id",
|
2019-04-23 16:57:35 +02:00
|
|
|
"list_of_speakers_id",
|
2019-07-10 08:31:58 +02:00
|
|
|
"agenda_create",
|
2019-01-06 16:22:33 +01:00
|
|
|
"agenda_type",
|
|
|
|
"agenda_parent_id",
|
|
|
|
"tags",
|
2019-04-26 13:25:45 +02:00
|
|
|
"attachments",
|
2019-01-06 16:22:33 +01:00
|
|
|
)
|
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):
|
2019-01-06 16:22:33 +01:00
|
|
|
if "description" in data:
|
|
|
|
data["description"] = validate_html(data["description"])
|
2017-11-20 16:40:39 +01:00
|
|
|
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.
|
|
|
|
"""
|
2019-03-04 18:28:21 +01:00
|
|
|
tags = validated_data.pop("tags", [])
|
2019-04-26 13:25:45 +02:00
|
|
|
attachments = validated_data.pop("attachments", [])
|
2019-07-10 08:31:58 +02:00
|
|
|
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)
|
|
|
|
|
2018-01-20 09:54:46 +01:00
|
|
|
assignment = Assignment(**validated_data)
|
2019-07-10 08:31:58 +02:00
|
|
|
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
|
|
|
|
|
2018-01-20 09:54:46 +01:00
|
|
|
assignment.save()
|
2019-03-04 18:28:21 +01:00
|
|
|
assignment.tags.add(*tags)
|
2019-04-26 13:25:45 +02:00
|
|
|
assignment.attachments.add(*attachments)
|
2019-03-04 18:28:21 +01:00
|
|
|
inform_changed_data(assignment)
|
2018-01-20 09:54:46 +01:00
|
|
|
return assignment
|