Newer
Older
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.mail import mail_admins, send_mass_mail
from django.template.loader import render_to_string
from django.views.generic import RedirectView, UpdateView, TemplateView, View
from accounts.models import EmailUser
from home.models import ActivityList, InterludesActivity, InterludesParticipant
from home.forms import ActivityForm, BaseActivityFormSet, InscriptionForm
from shared.views import CSVWriteView, SuperuserRequiredMixin
# ==============================
# Site static pages
# ==============================
class HomeView(TemplateView):
"""Vue pour la page d'acceuil"""
template_name = "home.html"
def get_planning_context():
"""Returns the context dict needed to display the planning"""
settings = SiteSettings.load()
context = dict()
context['planning'] = InterludesActivity.objects.filter(on_planning=True).order_by("title")
if settings.date_start is not None:
context['friday'] = settings.date_start.day
context['saturday'] = (settings.date_start + timedelta(days=1)).day
context['sunday'] = (settings.date_start + timedelta(days=2)).day
else:
context['friday'] = 1
context['saturday'] = 2
context['sunday'] = 3
return context
class ActivityView(TemplateView):
"""Vue pour la liste des activités"""
template_name = "activites.html"
def get_context_data(self, **kwargs):
"""ajoute la liste des activités au contexte"""
context = super(ActivityView, self).get_context_data(**kwargs)
context['activities'] = InterludesActivity.objects.filter(display=True).order_by("title")
context.update(get_planning_context())
return context
class FAQView(TemplateView):
"""Vue pour la FAQ"""
# ==============================
# Registration
# ==============================
class RegisterClosed(TemplateView):
"""Vue pour quand les inscriptions ne sont pas ouvertes"""
template_name = "inscription/closed.html"
class RegisterSignIn(TemplateView):
"""Vue affichée quand les inscriptions sont ouverte mais
l'utilisateur n'est pas connecté"""
template_name = "inscription/signin.html"
class RegisterUpdateView(LoginRequiredMixin, TemplateView):
"""Vue pour s'inscrire et modifier son inscription"""
template_name = "inscription/form.html"
form_class = InscriptionForm
formset_class = formset_factory(form=ActivityForm, extra=3, formset=BaseActivityFormSet)
@staticmethod
def get_activities(participant):
activities = ActivityList.objects.filter(participant=participant).order_by("priority")
return [{"activity": act.activity} for act in activities]
@staticmethod
def set_activities(participant, formset):
# delete old activites
ActivityList.objects.filter(participant=participant).delete()
priority = 0
for form in formset:
data = form.cleaned_data
if data:
activity = data["activity"]
ActivityList(priority=priority, participant=participant, activity=activity).save()
priority += 1
def get(self, request, *args, **kwargs):
participant = request.user.profile
activities = self.get_activities(participant)
form = self.form_class(instance=participant)
formset = self.formset_class(initial=activities)
context = {"form": form, "formset": formset}
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, instance=request.user.profile)
formset = self.formset_class(request.POST)
if not (form.is_valid() and formset.is_valid()):
context = {"form": form, "formset": formset}
return render(request, self.template_name, context)
form.save()
self.set_activities(request.user.profile, formset)
messages.success(request, "Votre inscription a bien été enregistrée")
return redirect("accounts:profile", permanent=False)
class RegisterView(View):
"""Vue pour l'inscription
repartie sur les vue RegisterClosed, RegisterSignIn et RegisterUpdateView"""
def dispatch(self, request, *args, **kwargs):
settings = SiteSettings.load()
if not settings.inscriptions_open:
return RegisterClosed.as_view()(request)
if not request.user.is_authenticated:
return RegisterSignIn.as_view()(request)
return RegisterUpdateView.as_view()(request)
class UnregisterView(LoginRequiredMixin, RedirectView):
pattern_name = "accounts:profile"
def get_redirect_url(self, *args, **kwargs):
participant = self.request.user.profile
participant.is_registered = False
participant.save()
messages.success(self.request, "Vous avez été désinscrit")
return reverse(self.pattern_name)
# ==============================
# Admin views
# ==============================
class AdminView(SuperuserRequiredMixin, TemplateView):
template_name = "admin.html"
def get_metrics(self):
registered = InterludesParticipant.objects.filter(is_registered = True)
acts = InterludesActivity.objects.all()
wishes = ActivityList.objects.filter(participant__is_registered=True)
class metrics:
participants = registered.count()
ulm = registered.filter(school="U").count()
lyon = registered.filter(school="L").count()
rennes = registered.filter(school="R").count()
saclay = registered.filter(school="P").count()
non_registered = EmailUser.objects.filter(is_active=True).count() - participants
# mugs = registered.filter(mug=True).count()
sleeps = registered.filter(sleeps=True).count()
meal1 = registered.filter(meal_friday_evening=True).count()
meal2 = registered.filter(meal_saturday_morning=True).count()
meal3 = registered.filter(meal_saturday_midday=True).count()
meal4 = registered.filter(meal_saturday_evening=True).count()
meal5 = registered.filter(meal_sunday_morning=True).count()
meal6 = registered.filter(meal_sunday_midday=True).count()
meals = meal1 + meal2 + meal3 + meal4 + meal5 + meal6
displayed = acts.filter(display=True).count()
planning = acts.filter(on_planning=True).count()
act_ins = acts.filter(display=True, must_subscribe=True).count()
st_present = acts.filter(display=True, status=InterludesActivity.Status.PRESENT).count()
st_distant = acts.filter(display=True, status=InterludesActivity.Status.DISTANT).count()
st_both = acts.filter(display=True, status=InterludesActivity.Status.BOTH).count()
true_ins = acts.filter(subscribing_open=True).count()
wish = wishes.count()
granted = wishes.filter(accepted=True).count()
malformed = ActivityList.objects.filter(activity__subscribing_open=False).count()
# validation de la repartition des activités
accepted = wishes.filter(accepted=True)
# order_by is useless but required
counts = accepted.values("activity").annotate(total=Count("id")).order_by("activity")
def validate_activity_participant_nb(self):
""" Vérifie que le nombre de participant inscrit
à chaque activité est compris entre le min et le max"""
activities = InterludesActivity.objects.filter(subscribing_open=True)
min_fails = ""
max_fails = ""
for act in activities:
total = ActivityList.objects.filter(
activity=act, accepted=True, participant__is_registered=True
).aggregate(total=Count("id"))["total"]
max = act.max_participants
min = act.min_participants
if max != 0 and max < total:
max_fails += "<br> • {}: {} inscrits (maximum {})".format(
act.title, total, max
)
if min > total:
min_fails += "<br> • {}: {} inscrits (minimum {})".format(
)
message = ""
if min_fails:
message += '<li class="error">Activités en sous-effectif : {}</li>'.format(min_fails)
else:
message += '<li class="success">Aucune activité en sous-effectif</li>'
if max_fails:
message += '<li class="error">Activités en sur-effectif : {}</li>'.format(max_fails)
else:
message += '<li class="success">Aucune activité en sur-effectif</li>'
return message
def validate_activity_conflicts(self):
"""Vérifie que personne n'est inscrit à des activités simultanées"""
activities = InterludesActivity.objects.filter(subscribing_open=True)
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
conflicts = []
for i, act1 in enumerate(activities):
for act2 in activities[i+1:]:
if act1.conflicts(act2):
conflicts.append((act1, act2))
base_qs = ActivityList.objects.filter(accepted=True, participant__is_registered=True)
errors = ""
for act1, act2 in conflicts:
participants1 = base_qs.filter(activity=act1).values("participant")
participants2 = base_qs.filter(activity=act1).values("participant")
intersection = participants1 & participants2
if intersection:
print(intersection)
errors += '<br> •  {} participe(nt) à la fois à "{}" et à "{}"'.format(
",".join(str(InterludesParticipant.objects.get(id=x["participant"])) for x in intersection),
act1.title, act2.title
)
if errors:
return '<li class="error">Des participants ont plusieurs activités au même moment :{}</li>'.format(
errors
)
return '<li class="success">Aucun inscrit à plusieurs activités simultanées</li>'
def validate_activity_allocation(self):
settings = SiteSettings.load()
validations = '<ul class="messagelist">'
# validate global settings
if not settings.inscriptions_open:
validations += '<li class="success">Les inscriptions sont fermées</li>'
else:
validations += '<li class="error">Les inscriptions sont encores ouvertes</li>'
if settings.activities_allocated:
validations += '<li class="success">La répartition est marquée comme effectuée</li>'
else:
validations += '<li class="error">La répartition n\'est pas marquée comme effectuée</li>'
# longer validations
validations += self.validate_activity_participant_nb()
validations += self.validate_activity_conflicts()
validations += '</ul>'
user_email_nb = InterludesParticipant.objects.filter(is_registered=True).count()
orga_email_nb = InterludesActivity.objects.filter(
subscribing_open=True, communicate_participants=True
).count()
return {
"validations": validations,
"user_email_nb": user_email_nb,
"orga_email_nb": orga_email_nb,
"validation_errors": '<li class="error">' in validations
}
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context["metrics"] = self.get_metrics()
context.update(get_planning_context())
context.update(self.validate_activity_allocation())
class ExportActivities(SuperuserRequiredMixin, CSVWriteView):
filename = "activites_interludes"
model = InterludesActivity
class ExportParticipants(SuperuserRequiredMixin, CSVWriteView):
filename = "participants_interludes"
headers = [
"id", "mail", "prénom", "nom", "ENS", "Dors sur place", #"Tasse",
"Repas vendredi", "Repas S matin", "Repas S midi", "Repas S soir",
"Repas D matin", "Repas D soir"
]
def get_rows(self):
profiles = InterludesParticipant.objects.filter(is_registered=True).all()
rows = []
for profile in profiles:
rows.append([
profile.user.id,
profile.user.email,
profile.user.first_name,
profile.user.last_name,
profile.school,
profile.sleeps,
profile.meal_friday_evening,
profile.meal_saturday_morning,
profile.meal_saturday_midday,
profile.meal_saturday_evening,
profile.meal_sunday_morning,
profile.meal_sunday_midday,
])
return rows
class ExportActivityChoices(SuperuserRequiredMixin, CSVWriteView):
filename = "choix_activite_interludes"
model = ActivityList
headers = ["id_participant", "nom_participant", "priorité", "obtenu", "nom_activité", "id_activité"]
def get_rows(self):
activities = ActivityList.objects.all()
rows = []
for act in activities:
if act.participant.is_registered:
rows.append([
act.participant.id, str(act.participant), act.priority,
act.accepted, str(act.activity), act.activity.id
class SendEmailBase(SuperuserRequiredMixin, RedirectView):
"""Classe abstraite pour l'envoie d'un groupe d'emails"""
pattern_name = "site_admin"
from_address = None
def send_emails(self):
raise NotImplementedError("{}.send_emails isn't implemented".format(self.__class__.__name__))
def get_redirect_url(self, *args, **kwargs):
settings = SiteSettings.load()
if settings.allow_mass_mail:
self.send_emails()
else:
messages.error(self.request, "L'envoi de mail de masse est désactivé dans les réglages")
return reverse(self.pattern_name)
class SendUserEmail(SendEmailBase):
"""Envoie aux utilisateurs leur repartition d'activité"""
def get_emails(self):
"""genere les mails a envoyer"""
participants = InterludesParticipant.objects.filter(is_registered=True)
settings = SiteSettings.load()
for participant in participants:
my_activities = ActivityList.objects.filter(participant=participant)
message = render_to_string("email/user.html", {
"user": participant.user,
"settings": settings,
"requested_activities_nb": my_activities.count(),
"activities": my_activities.filter(accepted=True),
})
emails.append((
"Vos activités interludes", # subject
message,
self.from_address, # From:
))
return emails
def send_emails(self):
settings = SiteSettings.load()
if settings.user_notified:
messages.error(self.request, "Les participants ont déjà reçu un mail annonçant la répartition. Modifiez les réglages pour en envoyer un autre")
return
settings.user_notified = True
settings.save()
emails = self.get_emails()
nb_sent = send_mass_mail(emails, fail_silently=False)
mail_admins(
"Emails de répartition envoyés aux participants",
"Les participants ont reçu un mail leur communiquant la répartition des activités\n"
"Nombre total de mail envoyés: {}\n\n"
"-- Site Interludes (mail généré automatiquement".format(nb_sent)
)
messages.success(self.request, "{} mails envoyés aux utilisateurs".format(nb_sent))
class SendOrgaEmail(SendEmailBase):
"""
Envoie aux organisateur leur communiquant les nom/mail des inscrits
à leurs activités
"""
def get_emails(self):
"""genere les mails a envoyer"""
activities = InterludesActivity.objects.filter(
subscribing_open=True, communicate_participants=True
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
)
emails = []
settings = SiteSettings.load()
for act in activities:
participants = ActivityList.objects.filter(activity=act, accepted=True)
message = render_to_string("email/orga.html", {
"activity": act,
"settings": settings,
"participants": participants,
})
emails.append((
"Vos activités interludes", # subject
message,
self.from_address, # From:
[act.host_email] # To:
))
return emails
def send_emails(self):
settings = SiteSettings.load()
if settings.orga_notified:
messages.error(self.request, "Les orgas ont déjà reçu un mail avec leur listes d'inscrits. Modifiez les réglages pour en envoyer un autre")
return
settings.orga_notified = True
settings.save()
emails = self.get_emails()
nb_sent = send_mass_mail(emails, fail_silently=False)
mail_admins(
"Listes d'inscrits envoyés aux orgas",
"Les mails communiquant aux organisateurs leur listes d'inscrit ont été envoyés\n"
"Nombre total de mail envoyés: {}\n\n"
"-- Site Interludes (mail généré automatiquement".format(nb_sent)
)
messages.success(self.request, "{} mails envoyés aux orgas".format(nb_sent))
# ==============================
# Sitemap
# ==============================
return ["home", "inscription", "activites", "FAQ"]
def location(self, item):
# Priorize home page over the rest in search results
if obj == "home" or obj == "":
return 0.8
else: