from django.contrib.auth import get_user_model from django.db import transaction from django.db.models import Q from django.shortcuts import get_object_or_404 from django.utils import timezone from rest_framework import generics from rest_framework.exceptions import ( PermissionDenied, ValidationError, ) from rest_framework.pagination import PageNumberPagination from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from .models import Note from users.permissions import require_permission from .permissions import ( can_complete_note, can_edit_note, ) from .serializers import ( NoteCompletionSerializer, NoteSerializer, NoteUserSerializer, ) def visible_notes(user): """ Retourne uniquement les publications que l'utilisateur a le droit de voir. Publique : visible par toute l'équipe. Privée : visible par l'auteur et les destinataires. """ return ( Note.objects .select_related('author', 'poll') .prefetch_related('recipients', 'poll__options', 'poll__votes__options', 'poll__votes__user') .filter( Q(is_public=True) | Q(author=user) | Q(recipients=user) ) .distinct() ) class NotePagination(PageNumberPagination): page_size = 24 class NoteListView( generics.ListCreateAPIView, ): permission_classes = [ IsAuthenticated, ] serializer_class = NoteSerializer pagination_class = NotePagination def get_queryset(self): notes = visible_notes( self.request.user, ) params = self.request.query_params kind = params.get( 'kind', '', ) scope = params.get( 'scope', 'all', ) state = params.get( 'state', 'all', ) # ----------------------------- # Type # ----------------------------- if kind: if kind not in Note.Kind.values: raise ValidationError({ 'kind': 'Type de publication inconnu.', }) notes = notes.filter( kind=kind, ) # ----------------------------- # Portée # ----------------------------- if scope == 'mine': notes = notes.filter( author=self.request.user, ) elif scope == 'to_me': notes = notes.filter( Q(is_public=True, recipients__isnull=True) | Q(recipients=self.request.user), ) elif scope != 'all': raise ValidationError({ 'scope': 'Filtre inconnu.', }) # ----------------------------- # État des tâches # ----------------------------- if state in ( 'pending', 'done', ): notes = notes.filter( kind=Note.Kind.TASK, completed_at__isnull=( state == 'pending' ), ) elif state != 'all': raise ValidationError({ 'state': 'État inconnu.', }) # ----------------------------- # Recherche # ----------------------------- search = params.get( 'search', '', ).strip() if search: notes = notes.filter( Q( title__icontains=search, ) | Q( content__icontains=search, ) ) return notes.distinct() def perform_create( self, serializer, ): require_permission(self.request.user, 'create_publication') serializer.save( author=self.request.user, ) class NoteDetailView( generics.RetrieveUpdateDestroyAPIView, ): permission_classes = [ IsAuthenticated, ] serializer_class = NoteSerializer def get_queryset(self): return visible_notes( self.request.user, ) def get_object(self): note = super().get_object() if ( self.request.method not in ( 'GET', 'HEAD', 'OPTIONS', ) and not can_edit_note( self.request.user, note, ) ): raise PermissionDenied( 'Seuls l’auteur et les administrateurs ' 'peuvent modifier ou supprimer ' 'cette publication.' ) return note @transaction.atomic def update( self, request, *args, **kwargs, ): Note.objects.select_for_update().get(pk=self.get_object().pk) return super().update( request, *args, **kwargs, ) class NoteCompletionView(APIView): permission_classes = [ IsAuthenticated, ] @transaction.atomic def patch( self, request, pk, ): data = NoteCompletionSerializer( data=request.data, ) data.is_valid( raise_exception=True, ) note = get_object_or_404( visible_notes(request.user) .select_for_update(), pk=pk, ) if note.kind != Note.Kind.TASK: raise ValidationError({ 'kind': 'Seules les tâches peuvent être terminées.', }) if not can_complete_note( request.user, note, ): raise PermissionDenied( 'Seuls l’auteur, les destinataires ' 'et les administrateurs peuvent ' 'terminer cette tâche.' ) is_done = ( data.validated_data[ 'is_done' ] ) if note.is_done != is_done: note.completed_at = ( timezone.now() if is_done else None ) note.save( update_fields=[ 'completed_at', 'updated_at', ], ) return Response( NoteSerializer( note, context={ 'request': request, }, ).data ) class NoteRecipientListView( generics.ListAPIView, ): permission_classes = [ IsAuthenticated, ] serializer_class = ( NoteUserSerializer ) queryset = ( get_user_model() .objects .filter( is_active=True, ) .order_by( 'first_name', 'last_name', 'username', ) )