Bien Être au Travail à Grenay
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

480 lines
16 KiB

import datetime
import logging
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.core.exceptions import ValidationError
from django.forms import modelformset_factory
from django.http import Http404
from django.shortcuts import redirect
from django.urls import reverse, reverse_lazy
from django.utils.timezone import now
from django.views.generic import (
CreateView,
DetailView,
FormView,
ListView,
View,
)
from django.views.generic.detail import SingleObjectMixin
from constance import config
from easy_pdf.views import PDFTemplateResponseMixin
from slugify import slugify
from ..main.utils import ActivePageMixin, MsgMixin
from ..organisation.utils import ResponsableRequiredMixin
from .forms import (
EvenementsFiltersForm,
EvtAjoutForm,
EvtImport,
ReservationProcessForm,
ReservationsFiltersForm,
)
from .mail import mail_attestation_presence
from .models import Evenement, Reservation
from .parser import ParseTableur
logger = logging.getLogger(__name__)
# Vues liées aux événements -------------------------------------------------
class EvenementPageMixin(ActivePageMixin):
"""Mixin commun aux pages affichant un ou plusieurs événements."""
active_page = 'evenements'
def get_queryset(self):
"""Joins la réservation de la personne aux événements."""
return Evenement.objects.avec_reservations_pour(
self.request.user
).select_related('lieu')
class EvenementList(LoginRequiredMixin, EvenementPageMixin, ListView):
"""
Liste tous les événements avec la possibilité de les réserver, ou avec
l'état de la réservation si elle existe.
"""
context_object_name = 'evenements'
paginate_by = 8
template_name = 'evenements/evenement_lister.html'
def get(self, request, *args, **kwargs):
self.filters_form = EvenementsFiltersForm(request.GET or None)
self.filters_form.full_clean()
return super().get(request, *args, **kwargs)
def get_queryset(self):
qs = super().get_queryset()
filtres = getattr(self.filters_form, 'cleaned_data', {})
# filtre sur la date de début des événements
if filtres.get('a_partir_du', None):
qs = qs.filter(
actif=True, date_debut__date__gte=filtres['a_partir_du']
)
else:
qs = qs.a_venir()
# filtre sur les evt dont je suis organisateur
if filtres.get('org_evt'):
qs = qs.filter(organisateur=self.request.user)
# trie les événements trouvés
if filtres.get('trier_par', None):
qs = qs.order_by(filtres['trier_par'])
else:
qs = qs.order_by(
self.filters_form.get_initial_for_field(
self.filters_form.fields['trier_par'], 'trier_par'
)
)
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filters_form'] = self.filters_form
return context
class EvenementDetail(LoginRequiredMixin, EvenementPageMixin, DetailView):
"""
Affiche les détails d'un événement et de l'éventuelle réservation pour
la personne connectée.
"""
context_object_name = 'evenement'
template_name = 'evenements/evenement_voir.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['att_presence'] = Reservation.objects.filter(
evenement=self.object, personne=self.request.user, present=True
).exists()
# récupère les réservations à traîter
agents = self.request.user.mes_agents()
if agents:
context['reservations'] = (
Reservation.objects.select_related(
'personne',
)
.a_venir()
.en_attentes()
.filter(
personne__in=agents,
evenement=self.object,
)
.order_by('personne__nom')
)
else:
context['reservations'] = False
return context
class EvenementReserve(MsgMixin, LoginRequiredMixin, View):
"""Réserve un événement donné pour la personne connectée.
en tenant compte des contraintes imposees et affiche les
messages adequat a l'utilisateur
"""
def post(self, request, *args, **kwargs):
pk = kwargs.get('pk', None)
try: # récupère l'événement
evenement = Evenement.objects.get(pk=pk)
except Evenement.DoesNotExist:
raise Http404("L'événement demandé n'existe pas.")
try:
evenement.pose_reservation_personne(self.request.user)
except ValidationError as err:
for msg in err.messages:
self.msg_if('error', msg)
else:
self.msg_if(
'success',
"Une réservation pour « {0} » a été ajoutée.".format(
evenement.nom
),
)
return redirect('evenements:voir', pk=pk)
# Vues liées aux réservations ------------------------------------------------
class ReservationActionMixin(LoginRequiredMixin, View):
"""Mixin pour les actions sur une réservation."""
def dispatch(self, request, pk):
"""Récupère et stocke l'objet `Reservation`."""
try:
self.reservation = Reservation.objects.get(pk=pk)
except Reservation.DoesNotExist:
raise Http404("La réservation demandée n'existe pas.")
return super().dispatch(request, pk)
class MesReservationsList(LoginRequiredMixin, ListView):
"""Liste toutes les réservations de la personne connectée."""
# TODO: Permettre de filtrer sur l'état, la date... et de les trier
context_object_name = 'reservations'
paginate_by = 8
template_name = 'evenements/mes_reservations_lister.html'
def get_queryset(self):
return (
Reservation.objects.a_venir()
.filter(
personne=self.request.user,
)
.order_by('evenement__date_debut')
)
class ReservationList(ResponsableRequiredMixin, ListView):
"""
Liste toutes les réservations des agents de la personne connectée, qui
devrait être un responsable, avec la possibilité de les valider ou non.
"""
context_object_name = 'reservations'
paginate_by = 8
template_name = 'evenements/reservation_lister.html'
def get(self, request, *args, **kwargs):
self.filters_form = ReservationsFiltersForm(request.GET or None)
self.filters_form.full_clean()
return super().get(request, *args, **kwargs)
def get_queryset(self):
qs = Reservation.objects.filter(
personne__in=self.request.user.mes_agents(),
).select_related('evenement', 'personne')
filtres = getattr(self.filters_form, 'cleaned_data', {})
# filtre sur la date de début des événements
if filtres.get('a_partir_du', None):
qs = qs.filter(
evenement__actif=True,
evenement__date_debut__date__gte=filtres['a_partir_du'],
)
else:
qs = qs.a_venir()
# filtre sur le statut de la réservation
if filtres.get('statut', None):
qs = qs.filter(statut=filtres['statut'])
# trie les réservations trouvées
if filtres.get('trier_par', None):
qs = qs.order_by(filtres['trier_par'])
else:
qs = qs.order_by(
self.filters_form.get_initial_for_field(
self.filters_form.fields['trier_par'], 'trier_par'
)
)
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filters_form'] = self.filters_form
return context
class ReservationAnnuler(MsgMixin, LoginRequiredMixin, View):
"""Annule une de ses propres reservations"""
def post(self, request, *args, **kwargs):
pk = self.kwargs.get('pk', None)
try:
# récupère l'événement et son éventuelle réservation
evenement = Evenement.objects.avec_reservations_pour(
self.request.user
).get(pk=pk)
except Evenement.DoesNotExist:
raise Http404("L'événement demandé n'existe pas.")
if not evenement.ma_reservation:
self.msg_if(
'error',
"Vous n'avez pas de réservation pour « {0} » "
"a annuler".format(evenement.nom),
)
if datetime.date.today() > evenement.get_date_auto_val():
self.msg_if('error', "Vous ne pouvez plus annuler cet évenement")
else:
evenement.ma_reservation[0].statut = Reservation.Statut.ANNULE
evenement.ma_reservation[0].save()
# todo: prevenir les resp. par courriel
return redirect('evenements:voir', pk=pk)
class ReservationProcess(ReservationActionMixin, UserPassesTestMixin):
"""Valide ou refuse une réservation donnée."""
def test_func(self):
"""
Vérifie que le demandeur de la réservation est un agent de la
personne connectée.
"""
return (
self.request.user.mes_agents()
.filter(
pk=self.reservation.personne_id,
)
.exists()
)
def post(self, *args, **kwargs):
form = ReservationProcessForm(
self.request.POST,
instance=self.reservation,
)
form.instance.resp = self.request.user
form.instance.date_val = now()
try:
reservation = form.save()
# TODO:
# - avoid a dry except
# - print most comprehensive error message if error
# .save() trigger .errors that trigger form.clean()
# and model.full_clean()
# form.clean() and model.full_clean() can raise
# django.ValidationError
# but when trigger by .save()/.error it raise a ValueError instead
# https://docs.djangoproject.com/en/1.11/topics/forms/modelforms/
# #the-save-method
except:
logger.exception("form.errors = %s", form.errors.as_data())
logger.error(
"user[%s] POST[%s]" % (self.request.user, self.request.POST)
)
messages.error(
self.request,
(
"Impossible de traiter la réservation {0}: "
"{1}".format(
str(self.reservation),
', '.join(form.errors.get('__all__', [])),
)
),
)
else:
msg_act = {
Reservation.Statut.ACCEPTE: 'acceptée',
Reservation.Statut.REFUSE: 'refusée',
}
msg = (
"La réservation {pk} de {pers} a été {act} par {resp}.".format(
pk=reservation.pk,
resp=reservation.resp,
pers=reservation.personne,
act=msg_act.get(reservation.statut, "modifiée"),
)
)
logger.info(msg)
messages.success(self.request, msg)
reservation.mail_reponse_resp()
return redirect('evenements:reservations.lister')
class ImportEvt(LoginRequiredMixin, FormView):
"""
formulaire pour importer un tableur
"""
template_name = "evenements/evenement_import.html"
form_class = EvtImport
success_url = reverse_lazy('evenements:ajouter')
def form_valid(self, form):
f_data = form.cleaned_data.get('fichier')
ptab = ParseTableur(f_data)
self.request.session['evt_init_data'] = ptab.get_data()
return super().form_valid(form)
class AjouterEvt(LoginRequiredMixin, UserPassesTestMixin, CreateView):
"""
formulaire pour valider un import
"""
template_name = "evenements/evenement_ajouter.html"
form_class = EvtAjoutForm
success_url = reverse_lazy('evenements:lister')
def get_initial(self):
"""
si des donnees initiales sont donnees par la session (ImportEvt)
les utiliser et vider la session, sinon, comportement normal
"""
evt_init_data = self.request.session.pop('evt_init_data', None)
if evt_init_data is not None:
return evt_init_data
return super().get_initial()
def test_func(self):
return self.request.user.has_perm('evenements.add_evenement')
def form_valid(self, form):
form.instance.organisateur = self.request.user
return super().form_valid(form)
class PdfPresence(PDFTemplateResponseMixin, LoginRequiredMixin, DetailView):
template_name = 'evenements/pdf/presence.html'
model = Evenement
def get_queryset(self):
qset = super().get_queryset()
if self.request.user.is_admin:
return qset
return qset.filter(organisateur=self.request.user)
def get_pdf_filename(self):
return "presence_evt_{0}_{1}.pdf".format(
self.object.date_debut.strftime('%Y_%m_%d'),
slugify(self.object.nom),
)
def get_context_data(self, **kwargs):
return super().get_context_data(
config=config, title='Feuille de présence', **kwargs
)
class PdfAttestationPresence(
PDFTemplateResponseMixin, LoginRequiredMixin, DetailView
):
template_name = 'evenements/pdf/attestation_presence.html'
model = Evenement
def get_queryset(self):
qset = super().get_queryset()
if self.request.user.is_admin:
return qset
return qset.filter(reservation__personne=self.request.user)
def get_pdf_filename(self):
return "attestation_presence_{0}.pdf".format(
self.object.date_debut.strftime('%Y_%m_%d')
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['config'] = config
try:
context['resa'] = Reservation.objects.get(
evenement=self.object, personne=self.request.user, present=True
)
except Reservation.DoesNotExist:
raise Http404("Vous n'étiez pas présent à cette activité")
return context
class PointagePresence(LoginRequiredMixin, SingleObjectMixin, FormView):
"""
attention dans cette class, 'form' est en fait un
'formset'
"""
template_name = 'evenements/pointage_presence.html'
model = Evenement
def get_queryset(self):
qset = super().get_queryset()
if self.request.user.is_admin:
return qset
return qset.filter(organisateur=self.request.user)
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super().post(request, *args, **kwargs)
def get_form_class(self):
"return a FormSet"
return modelformset_factory(Reservation, fields=('present',), extra=0)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update(
{
'queryset': Reservation.objects.filter(
evenement=self.object
).acceptees()
}
)
return kwargs
def form_valid(self, form):
"""sauve le formset"""
form.save()
mail_attestation_presence(
form.get_queryset(), force_qs_evt=False, resp_rec=True
)
return super().form_valid(form)
def get_success_url(self):
return reverse('evenements:voir', kwargs={'pk': self.object.pk})
Map all the world