"src/BackendBundle/Repository/TeamRepository.php" did not exist on "0adcbd7d04ee1563d4c848d973ac0cb81a2c49e6"
Select Git revision
DeviceRepository.php
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'