325 lines
7.0 KiB
Python
325 lines
7.0 KiB
Python
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',
|
||
)
|
||
)
|