from django.db import transaction from django.shortcuts import get_object_or_404 from django.utils import timezone from rest_framework import serializers from rest_framework.exceptions import PermissionDenied, ValidationError from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from .models import Note, Poll, PollVote from .permissions import can_edit_note from .polls import can_vote, is_closed from .serializers import NoteSerializer from .views import visible_notes class PollVoteView(APIView): permission_classes = [IsAuthenticated] @transaction.atomic def put(self, request, pk): note = get_object_or_404(visible_notes(request.user), pk=pk, kind='poll') get_object_or_404(Note.objects.select_for_update(), pk=note.pk) # Relire après le verrou : règles, droits et clôture peuvent avoir changé. note = get_object_or_404(visible_notes(request.user), pk=pk, kind='poll') poll = get_object_or_404(Poll, note=note) if is_closed(poll): raise ValidationError('Ce sondage est clôturé.') if not can_vote(request.user, note): raise PermissionDenied('Ce sondage est réservé aux destinataires indiqués.') choices = serializers.ListField(child=serializers.IntegerField(min_value=1), min_length=1, max_length=30).run_validation(request.data.get('option_ids')) if len(set(choices)) != len(choices) or poll.options.filter(pk__in=choices).count() != len(choices): raise ValidationError('Choisissez des réponses distinctes appartenant à ce sondage.') expected = 1 if poll.vote_mode == 'single' else poll.exact_choices if poll.vote_mode == 'exact' else None if expected is not None and len(choices) != expected: raise ValidationError(f'Sélectionnez exactement {expected} réponse(s).') vote, _ = PollVote.objects.get_or_create(poll=poll, user=request.user) vote.options.set(choices) vote.save() if not poll.locked_at: poll.locked_at = timezone.now() poll.save(update_fields=['locked_at']) fresh = visible_notes(request.user).get(pk=pk) return Response(NoteSerializer(fresh, context={'request': request}).data) class PollCloseView(APIView): permission_classes = [IsAuthenticated] @transaction.atomic def post(self, request, pk): note = get_object_or_404(visible_notes(request.user), pk=pk, kind='poll') get_object_or_404(Note.objects.select_for_update(), pk=note.pk) note = get_object_or_404(visible_notes(request.user), pk=pk, kind='poll') if not can_edit_note(request.user, note): raise PermissionDenied('Seuls l’auteur et les administrateurs autorisés peuvent clôturer ce sondage.') poll = get_object_or_404(Poll, note=note) if not poll.closed_at: poll.closed_at = timezone.now() poll.save(update_fields=['closed_at']) return Response(NoteSerializer(visible_notes(request.user).get(pk=pk), context={'request': request}).data)