feat: Gestion du planning [3]

This commit is contained in:
Luc SORIGNET
2025-05-03 15:12:17 +02:00
parent cb4fe74a9e
commit 58144ba0d0
39 changed files with 939 additions and 1864 deletions

View File

@ -2,6 +2,7 @@ from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.conf import settings
from School.models import SchoolClass
from Establishment.models import Establishment
@ -14,25 +15,33 @@ class RecursionType(models.IntegerChoices):
class Planning(models.Model):
establishment = models.ForeignKey(Establishment, on_delete=models.PROTECT)
school_class = models.ForeignKey(
SchoolClass,
on_delete=models.CASCADE,
related_name="planning",
null=True, # Permet des valeurs nulles
blank=True # Rend le champ facultatif dans les formulaires
)
name = models.CharField(max_length=255)
description = models.TextField(default="", blank=True, null=True)
color= models.CharField(max_length=255, default="#000000")
def __str__(self):
return f'Planning for {self.user.username}'
return f'Planning {self.name}'
class Events(models.Model):
planning = models.ForeignKey(Planning, on_delete=models.PROTECT)
planning = models.ForeignKey(Planning, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
description = models.TextField()
description = models.TextField(default="", blank=True, null=True)
start = models.DateTimeField()
end = models.DateTimeField()
recursionType = models.IntegerField(choices=RecursionType, default=0)
recursionEnd = models.DateTimeField(default=None, blank=True, null=True)
color= models.CharField(max_length=255)
location = models.CharField(max_length=255, default="", blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'Event for {self.user.username}'
return f'Event {self.title}'

View File

@ -7,5 +7,5 @@ urlpatterns = [
re_path(r'^plannings/(?P<id>[0-9]+)$', PlanningWithIdView.as_view(), name="planning"),
re_path(r'^events$', EventsView.as_view(), name="events"),
re_path(r'^events/(?P<id>[0-9]+)$', EventsWithIdView.as_view(), name="events"),
re_path(r'^events/upcoming', UpcomingEventsView.as_view(), name="events"),
re_path(r'^events/upcoming', UpcomingEventsView.as_view(), name="events"),
]

View File

@ -1,18 +1,32 @@
from django.http.response import JsonResponse
from rest_framework.views import APIView
from django.utils import timezone
from dateutil.relativedelta import relativedelta
from .models import Planning, Events
from .models import Planning, Events, RecursionType
from .serializers import PlanningSerializer, EventsSerializer
from N3wtSchool import bdd
class PlanningView(APIView):
def get(self, request):
plannings=bdd.getAllObjects(Planning)
planning_serializer=PlanningSerializer(plannings, many=True)
establishment_id = request.GET.get('establishment_id', None)
planning_mode = request.GET.get('planning_mode', None)
plannings = bdd.getAllObjects(Planning)
if establishment_id is not None:
plannings = plannings.filter(establishment=establishment_id)
# Filtrer en fonction du planning_mode
if planning_mode == "classSchedule":
plannings = plannings.filter(school_class__isnull=False)
elif planning_mode == "planning":
plannings = plannings.filter(school_class__isnull=True)
planning_serializer = PlanningSerializer(plannings.distinct(), many=True)
return JsonResponse(planning_serializer.data, safe=False)
def post(self, request):
@ -56,17 +70,63 @@ class PlanningWithIdView(APIView):
class EventsView(APIView):
def get(self, request):
events = bdd.getAllObjects(Events)
establishment_id = request.GET.get('establishment_id', None)
planning_mode = request.GET.get('planning_mode', None)
filterParams = {}
plannings=[]
events = Events.objects.all()
if establishment_id is not None :
filterParams['establishment'] = establishment_id
if planning_mode is not None:
filterParams['school_class__isnull'] = (planning_mode!="classSchedule")
if filterParams:
plannings = Planning.objects.filter(**filterParams)
events = Events.objects.filter(planning__in=plannings)
events_serializer = EventsSerializer(events, many=True)
return JsonResponse(events_serializer.data, safe=False)
def post(self, request):
events_serializer = EventsSerializer(data=request.data)
if events_serializer.is_valid():
events_serializer.save()
event = events_serializer.save()
# Gérer les événements récurrents
if event.recursionType != RecursionType.RECURSION_NONE:
self.create_recurring_events(event)
return JsonResponse(events_serializer.data, status=201)
return JsonResponse(events_serializer.errors, status=400)
def create_recurring_events(self, event):
current_start = event.start
current_end = event.end
while current_start < event.recursionEnd:
if event.recursionType == RecursionType.RECURSION_DAILY:
current_start += relativedelta(days=1)
current_end += relativedelta(days=1)
elif event.recursionType == RecursionType.RECURSION_WEEKLY:
current_start += relativedelta(weeks=1)
current_end += relativedelta(weeks=1)
elif event.recursionType == RecursionType.RECURSION_MONTHLY:
current_start += relativedelta(months=1)
current_end += relativedelta(months=1)
else:
break # Pour d'autres types de récurrence non gérés
# Créer une nouvelle occurrence
Events.objects.create(
planning=event.planning,
title=event.title,
description=event.description,
start=current_start,
end=current_end,
recursionEnd=event.recursionEnd,
recursionType=event.recursionType, # Les occurrences ne sont pas récurrentes
color=event.color,
location=event.location,
)
class EventsWithIdView(APIView):
def put(self, request, id):
try:
@ -92,6 +152,18 @@ class EventsWithIdView(APIView):
class UpcomingEventsView(APIView):
def get(self, request):
current_date = timezone.now()
upcoming_events = Events.objects.filter(start__gte=current_date)
establishment_id = request.GET.get('establishment_id', None)
if establishment_id is not None:
# Filtrer les plannings par establishment_id et sans school_class
plannings = Planning.objects.filter(establishment=establishment_id, school_class__isnull=True)
# Filtrer les événements associés à ces plannings et qui sont à venir
upcoming_events = Events.objects.filter(planning__in=plannings, start__gte=current_date)
else:
# Récupérer tous les événements à venir si aucun establishment_id n'est fourni
# et les plannings ne doivent pas être rattachés à une school_class
plannings = Planning.objects.filter(school_class__isnull=True)
upcoming_events = Events.objects.filter(planning__in=plannings, start__gte=current_date)
events_serializer = EventsSerializer(upcoming_events, many=True)
return JsonResponse(events_serializer.data, safe=False)