123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- from rest_framework import serializers
- from django.urls import reverse
- from django.utils.crypto import get_random_string
- from django.utils.translation import ugettext as _
- from django.utils.translation import ungettext
- from misago.threads.models import Poll
- __all__ = [
- 'PollSerializer',
- 'EditPollSerializer',
- 'NewPollSerializer',
- 'PollChoiceSerializer',
- ]
- MAX_POLL_OPTIONS = 16
- class PollSerializer(serializers.ModelSerializer):
- acl = serializers.SerializerMethodField()
- choices = serializers.SerializerMethodField()
- api = serializers.SerializerMethodField()
- url = serializers.SerializerMethodField()
- class Meta:
- model = Poll
- fields = [
- 'id',
- 'poster_name',
- 'posted_on',
- 'length',
- 'question',
- 'allowed_choices',
- 'allow_revotes',
- 'votes',
- 'is_public',
- 'acl',
- 'choices',
- 'api',
- 'url',
- ]
- def get_api(self, obj):
- return {
- 'index': obj.get_api_url(),
- 'votes': obj.get_votes_api_url(),
- }
- def get_url(self, obj):
- return {
- 'poster': self.get_poster_url(obj),
- }
- def get_poster_url(self, obj):
- if obj.poster_id:
- return reverse(
- 'misago:user', kwargs={
- 'slug': obj.poster_slug,
- 'pk': obj.poster_id,
- }
- )
- else:
- return None
- def get_acl(self, obj):
- try:
- return obj.acl
- except AttributeError:
- return None
- def get_choices(self, obj):
- return obj.choices
- class EditPollSerializer(serializers.ModelSerializer):
- length = serializers.IntegerField(required=True, min_value=0, max_value=180)
- question = serializers.CharField(required=True, max_length=255)
- allowed_choices = serializers.IntegerField(required=True, min_value=1)
- choices = serializers.ListField(
- allow_empty=False,
- child=serializers.DictField(),
- )
- class Meta:
- model = Poll
- fields = [
- 'length',
- 'question',
- 'allowed_choices',
- 'allow_revotes',
- 'choices',
- ]
- def validate_choices(self, choices):
- clean_choices = list(map(self.clean_choice, choices))
- # generate hashes for added choices
- choices_map = {}
- for choice in self.instance.choices:
- choices_map[choice['hash']] = choice
- final_choices = []
- for choice in clean_choices:
- if choice['hash'] in choices_map:
- choices_map[choice['hash']].update({'label': choice['label']})
- final_choices.append(choices_map[choice['hash']])
- else:
- choice.update({
- 'hash': get_random_string(12),
- 'votes': 0,
- })
- final_choices.append(choice)
- self.validate_choices_num(final_choices)
- return final_choices
- def clean_choice(self, choice):
- clean_choice = {
- 'hash': choice.get('hash', get_random_string(12)),
- 'label': choice.get('label', ''),
- }
- serializer = PollChoiceSerializer(data=clean_choice)
- if not serializer.is_valid():
- raise serializers.ValidationError(_("One or more poll choices are invalid."))
- return serializer.data
- def validate_choices_num(self, choices):
- total_choices = len(choices)
- if total_choices < 2:
- raise serializers.ValidationError(_("You need to add at least two choices to a poll."))
- if total_choices > MAX_POLL_OPTIONS:
- message = ungettext(
- "You can't add more than %(limit_value)s option to a single poll (added %(show_value)s).",
- "You can't add more than %(limit_value)s options to a single poll (added %(show_value)s).",
- MAX_POLL_OPTIONS,
- )
- raise serializers.ValidationError(
- message % {
- 'limit_value': MAX_POLL_OPTIONS,
- 'show_value': total_choices,
- }
- )
- def validate(self, data):
- if data['allowed_choices'] > len(data['choices']):
- raise serializers.ValidationError(
- _("Number of allowed choices can't be greater than number of all choices.")
- )
- return data
- def update(self, instance, validated_data):
- if instance.choices:
- self.update_choices(instance, validated_data['choices'])
- return super().update(instance, validated_data)
- def update_choices(self, instance, cleaned_choices):
- removed_hashes = []
- final_hashes = [c['hash'] for c in cleaned_choices]
- for choice in instance.choices:
- if choice['hash'] not in final_hashes:
- instance.votes -= choice['votes']
- removed_hashes.append(choice['hash'])
- if removed_hashes:
- instance.pollvote_set.filter(choice_hash__in=removed_hashes).delete()
- class NewPollSerializer(EditPollSerializer):
- class Meta:
- model = Poll
- fields = [
- 'length',
- 'question',
- 'allowed_choices',
- 'allow_revotes',
- 'is_public',
- 'choices',
- ]
- def validate_choices(self, choices):
- clean_choices = list(map(self.clean_choice, choices))
- self.validate_choices_num(clean_choices)
- for choice in clean_choices:
- choice.update({
- 'hash': get_random_string(12),
- 'votes': 0,
- })
- return clean_choices
- class PollChoiceSerializer(serializers.Serializer):
- hash = serializers.CharField(required=True, min_length=12, max_length=12)
- label = serializers.CharField(required=True, max_length=255)
|