Select Git revision
serviceWorker.js
Forked from
Thomas Sainrat / formation-git
Source project has a limited visibility.
admin.py 9.33 KiB
"""Visits admin panel configuration."""
from django import forms
from django.contrib import admin, messages
from django.template.defaultfilters import pluralize
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.http import HttpResponse
import csv
from .models import Participation, Place, Visit
from profiles.models import Student
from users.models import User
import codecs
# Register your models here.
class SchoolFilter(admin.SimpleListFilter):
title = 'établissement'
parameter_name = 'profiles__school'
def lookups(self, request, model_admin):
list_of_school = []
query = Student.objects.values_list(
"school", flat=True).distinct()
for school in query:
list_of_school.append((school, school))
return list_of_school
def queryset(self, request, queryset):
if self.value():
emails = Student.objects.filter(
school=self.value()).values_list("user__email", flat=True)
return queryset.filter(user__email__in=emails)
class RegistrationsOpenFilter(admin.SimpleListFilter):
"""Custom filter to filter visits by their registration openness.
In Django docs:
https://docs.djangoproject.com/fr/2.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter
"""
title = 'état des inscriptions'
parameter_name = 'registrations_open'
def lookups(self, request, model_admin):
yield (False, 'Fermées')
yield (True, 'Ouvertes')
def queryset(self, request, queryset):
registrations_open = self.value()
if registrations_open is None:
return queryset
return queryset.registrations_open(state=registrations_open)
def value(self):
"""Convert the querystring value to a nullable boolean."""
value = super().value()
return {None: None, 'True': True, 'False': False}[value]
class VisitForm(forms.ModelForm):
"""Custom admin form for Visit."""
class Meta: # noqa
model = Visit
fields = '__all__'
def clean(self):
"""Validate dates and time.
- Deadline must be before the date
- End time must be after start time
Keep in mind that values may be `None` if not provided in the form.
"""
cleaned_data = super().clean()
date = cleaned_data.get('date')
deadline = cleaned_data.get('deadline')
start_time = cleaned_data.get('start_time')
end_time = cleaned_data.get('end_time')
if deadline is not None:
if deadline.date() >= date:
error = forms.ValidationError(
"La date limite d'inscription doit être avant la "
"date de la sortie."
)
self.add_error('deadline', error)
if end_time is not None and start_time is not None:
if end_time <= start_time:
error = forms.ValidationError(
"L'heure de fin doit être après l'heure de début.")
self.add_error('start_time', error)
self.add_error('end_time', error)
class ParticipationInline(admin.TabularInline):
"""Inline for Participation."""
# template = "visits/visit_tabular.md"
actions = ["export_as_csv"]
model = Visit.participants.through
extra = 0
fields = ('name', 'school', 'user', 'submitted', 'present')
readonly_fields = ('name', 'school', 'user', 'submitted')
def school(self, participation: Participation):
"""Return a link to the participation's user's school."""
school = Student.objects.get(user=participation.user).school
return school
school.short_description = "Établissement"
def name(self, participation: Participation):
"""Returns the participation's user's name"""
return participation.user.first_name + " " + participation.user.last_name
name.short_description = "Nom"
class Media:
css = {"all": ("css/hide_admin_original.css",)}
def accept_selected_participations(modeladmin, request, queryset):
"""Accept selected participations in list view."""
for obj in queryset:
obj.accepted = 1
obj.save()
count = queryset.count()
s = pluralize(count)
messages.add_message(request, messages.SUCCESS,
f'{count} participation{s} acceptée{s} avec succès.')
accept_selected_participations.short_description = (
'Accepter les participations sélectionnées')
def reject_selected_participations(modeladmin, request, queryset):
"""Reject selected participations in list view."""
for obj in queryset:
obj.accepted = 0
obj.save()
count = queryset.count()
s = pluralize(count)
messages.add_message(request, messages.SUCCESS,
f'{count} participation{s} rejetée{s} avec succès.') # rejeté place accepté
reject_selected_participations.short_description = (
'Rejeter les participations sélectionnées')
def wait_selected_participations(modeladmin, request, queryset):
"""Reject selected participations in list view."""
for obj in queryset:
obj.accepted = 2 # in wait
obj.save()
count = queryset.count()
s = pluralize(count)
messages.add_message(request, messages.SUCCESS,
f'{count} participation{s} en attente{s} avec succès.')
wait_selected_participations.short_description = (
'Mettre en attentes les participations sélectionnées')
@admin.register(Participation)
class ParticipationAdmin(admin.ModelAdmin):
"""Admin panel for visit participations."""
list_display = ('submitted', 'visit', 'user_link', 'accepted', 'present')
list_filter = (SchoolFilter, 'submitted', 'accepted', 'present')
actions = [accept_selected_participations, reject_selected_participations]
def user_link(self, participation: Participation):
"""Return a link to the participation's user."""
url = reverse("admin:users_user_change", args=[participation.user.id])
link = f'<a href="{url}">{participation.user}</a>'
return mark_safe(link)
user_link.short_description = 'Utilisateur'
actions = ["export_as_csv"]
def school(self, participation: Participation):
"""Return a link to the participation's user's school."""
school = Student.objects.get(user=participation.user).school
return school
school.short_description = "Établissement"
def export_as_csv(self, request, queryset):
meta = self.model._meta
field_names = [field.name for field in meta.fields]
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(
meta)
response.write(codecs.BOM_UTF8) # force response to be UTF-8
writer = csv.writer(response, delimiter=';')
writer.writerow(['first_name', 'last_name', 'school', 'grade',
'phone_number', 'scholarship'] + field_names)
list_email = queryset.values_list("user__email", flat=True)
nb_user = 0
for obj in queryset:
name = User.objects.filter(
email=str(list_email[nb_user])).values('first_name', 'last_name', 'phone_number')
school = Student.objects.filter(
user__email=str(list_email[nb_user])).values('school', 'grade', 'scholarship')
row = writer.writerow([name[0]['first_name'], name[0]['last_name'], school[0]['school'], school[0]['grade'], name[0]['phone_number'], school[0]['scholarship']] + [getattr(obj, field)
for field in field_names])
nb_user += 1
return response
export_as_csv.short_description = "Exporter sélection (en .csv)"
@ admin.register(Visit.organizers.through)
class VisitOrganizersAdmin(admin.ModelAdmin):
"""Admin panel for visit organizers."""
list_display = ('visit', 'tutor',)
class OrganizersInline(admin.TabularInline):
"""Inline for visit organizers."""
model = Visit.organizers.through
extra = 0
@ admin.register(Visit)
class VisitAdmin(admin.ModelAdmin):
"""Admin panel for visits."""
# IDEA create a dashboard using:
# https://medium.com/@hakibenita/how-to-turn-django-admin-into-a-lightweight-dashboard-a0e0bbf609ad
form = VisitForm
inlines = (OrganizersInline, ParticipationInline,)
list_display = ('__str__', 'place', 'date', 'start_time', 'deadline',
'_registrations_open', 'num_participants')
list_filter = ('date', RegistrationsOpenFilter)
search_fields = ('title', 'place',)
exclude = ('participants', 'organizers',)
def num_participants(self, obj):
return obj.participants.count()
num_participants.short_description = 'Participants'
@ admin.register(Place)
class PlaceAdmin(admin.ModelAdmin):
"""Admin panel for places."""
list_display = ('name', 'address', 'num_visits', 'last_visit')
list_display_links = ('name', 'last_visit')
def num_visits(self, obj):
return obj.visit_set.count()
num_visits.short_description = 'Nombre de sorties'
def last_visit(self, obj):
return obj.visit_set.passed().order_by('date').first()
last_visit.short_description = 'Dernière sortie'