OpenSlides/openslides/assignments/serializers.py
Oskar Hahn dd4754d045 Disable the future-lock when updating the restircted data cache
Before this commit, there where two different locks when updating the restricted
data cache. A future lock, what is faster but only works in the same thread. The
other lock is in redis, it is not so fast, but also works in many threads.

The future lock was buggy, because on a second call of update_restricted_data
the same future was reused. So on the second run, the future was already done.

I don't see any way to delete. The last client would have to delete it, but there
is no way to find out which client the last one is.
2019-03-04 21:37:00 +01:00

274 lines
8.3 KiB
Python

from django.db import transaction
from openslides.poll.serializers import default_votes_validator
from openslides.utils.rest_api import (
DecimalField,
DictField,
IntegerField,
ListField,
ListSerializer,
ModelSerializer,
SerializerMethodField,
ValidationError,
)
from .models import (
Assignment,
AssignmentOption,
AssignmentPoll,
AssignmentRelatedUser,
AssignmentVote,
models,
)
from ..utils.validate import validate_html
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 'open_posts' must be greater than 0"}
)
return data
class AssignmentRelatedUserSerializer(ModelSerializer):
"""
Serializer for assignment.models.AssignmentRelatedUser objects.
"""
class Meta:
model = AssignmentRelatedUser
fields = (
"id",
"user",
"elected",
"assignment",
"weight",
) # js-data needs the assignment-id in the nested object to define relations.
class AssignmentVoteSerializer(ModelSerializer):
"""
Serializer for assignment.models.AssignmentVote objects.
"""
class Meta:
model = AssignmentVote
fields = ("weight", "value")
class AssignmentOptionSerializer(ModelSerializer):
"""
Serializer for assignment.models.AssignmentOption objects.
"""
votes = AssignmentVoteSerializer(many=True, read_only=True)
is_elected = SerializerMethodField()
class Meta:
model = AssignmentOption
fields = ("id", "candidate", "is_elected", "votes", "poll", "weight")
def get_is_elected(self, obj):
"""
Returns the election status of the candidate of this option.
"""
return obj.poll.assignment.is_elected(obj.candidate)
class FilterPollListSerializer(ListSerializer):
"""
Customized serializer to filter polls (exclude unpublished).
"""
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]
class AssignmentAllPollSerializer(ModelSerializer):
"""
Serializer for assignment.models.AssignmentPoll objects.
Serializes all polls.
"""
options = AssignmentOptionSerializer(many=True, read_only=True)
votes = ListField(
child=DictField(
child=DecimalField(max_digits=15, decimal_places=6, min_value=-2)
),
write_only=True,
required=False,
)
has_votes = SerializerMethodField()
class Meta:
model = AssignmentPoll
fields = (
"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",)
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 'pollmethod'='yna' poll with two candidates:
"votes": [{"Yes": 10, "No": 4, "Abstain": -2},
{"Yes": -1, "No": 0, "Abstain": -2}]
Example data for a 'pollmethod' ='yn' 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": f"You have to submit data for {len(options)} candidates."
}
)
for index, option in enumerate(options):
if len(votes[index]) != len(instance.get_vote_values()):
raise ValidationError(
{
"detail": f"You have to submit data for {len(instance.get_vote_values())} vote values."
}
)
for vote_value, __ in votes[index].items():
if vote_value not in instance.get_vote_values():
raise ValidationError(
{"detail": f"Vote value {vote_value} is invalid."}
)
instance.set_vote_objects_with_values(
option, votes[index], skip_autoupdate=True
)
# Update remaining writeable fields.
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)
instance.save()
return instance
class AssignmentShortPollSerializer(AssignmentAllPollSerializer):
"""
Serializer for assignment.models.AssignmentPoll objects.
Serializes only short polls (excluded unpublished polls).
"""
class Meta:
list_serializer_class = FilterPollListSerializer
model = AssignmentPoll
fields = (
"id",
"pollmethod",
"description",
"published",
"options",
"votesabstain",
"votesno",
"votesvalid",
"votesinvalid",
"votescast",
"has_votes",
)
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