Browse Source

Nettoyage + commentaires

master
scayac 2 months ago
parent
commit
8a1679c568
  1. 253
      main/views.py

253
main/views.py

@ -1,146 +1,179 @@
from django.http import JsonResponse # Standard library
from django.views.decorators.http import require_GET
import csv import csv
import io import io
from datetime import timedelta from datetime import timedelta
from django.http import HttpResponse # Django
from django.db import models
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.utils import timezone from django.utils import timezone
from django.views.decorators.http import require_GET
# Third party
from reportlab.pdfgen import canvas from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4 from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm from reportlab.lib.units import mm
import qrcode
from PIL import Image
from channels.layers import get_channel_layer from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync from asgiref.sync import async_to_sync
# Local
from .models import Course, Arrivee, Coureur from .models import Course, Arrivee, Coureur
from django.db import models
from .forms import CourseForm, ScanForm, DossardForm from .forms import CourseForm, ScanForm, DossardForm
import qrcode # =====================================
from PIL import Image # Fonctions utilitaires
# =====================================
def seconds_to_hms(delta: timedelta) -> str: def seconds_to_hms(delta: timedelta) -> str:
if delta is None: """Convertit une durée timedelta en chaîne formatée HhMMmSSs"""
return '' if delta is None:
total = int(delta.total_seconds()) return ''
hours = total // 3600 total = int(delta.total_seconds())
minutes = (total % 3600) // 60 hours = total // 3600
seconds = total % 60 minutes = (total % 3600) // 60
return f"{hours}h{minutes:02d}m{seconds:02d}s" seconds = total % 60
return f"{hours}h{minutes:02d}m{seconds:02d}s"
@login_required @login_required
@require_GET @require_GET
def coureur_autocomplete(request): def coureur_autocomplete(request):
q = request.GET.get('q', '').strip() """Endpoint AJAX pour l'autocomplétion des coureurs.
results = [] Recherche sur nom, prénom, classe et dossard.
if len(q) >= 2: Retourne les 10 premiers résultats au format {id, label}."""
# Recherche sur nom, prénom, classe, dossard (id) q = request.GET.get('q', '').strip()
qs = Coureur.objects.filter( results = []
models.Q(nom__icontains=q) | if len(q) >= 2:
models.Q(prenom__icontains=q) | qs = Coureur.objects.filter(
models.Q(classe__icontains=q) | models.Q(nom__icontains=q) |
models.Q(id__icontains=q) models.Q(prenom__icontains=q) |
).order_by('nom', 'prenom')[:10] models.Q(classe__icontains=q) |
for c in qs: models.Q(id__icontains=q)
label = f"{c.nom} {c.prenom} ({c.classe}) [dossard: {c.id}]" ).order_by('nom', 'prenom')[:10]
results.append({'id': c.id, 'label': label}) for c in qs:
return JsonResponse(results, safe=False) label = f"{c.nom} {c.prenom} ({c.classe}) [dossard: {c.id}]"
results.append({'id': c.id, 'label': label})
return JsonResponse(results, safe=False)
# =====================================
# Vues principales
# =====================================
@login_required @login_required
def main_view(request): def main_view(request):
courses = Course.objects.filter(owner=request.user) """Page d'accueil listant les courses de l'utilisateur.
if request.method == 'POST' and request.headers.get('x-requested-with') == 'XMLHttpRequest': Permet aussi la création AJAX de nouvelles courses."""
from django.http import JsonResponse courses = Course.objects.filter(owner=request.user)
nom = request.POST.get('nom')
date = timezone.localdate() if request.method == 'POST' and request.headers.get('x-requested-with') == 'XMLHttpRequest':
if not nom: nom = request.POST.get('nom')
return JsonResponse({'success': False, 'error': "Le nom de la course est requis."}) date = timezone.localdate()
if Course.objects.filter(nom=nom, date=date).exists(): if not nom:
return JsonResponse({'success': False, 'error': "Une course avec ce nom existe d\u00e9j\u00e0 aujourd'hui."}) return JsonResponse({'success': False, 'error': "Le nom de la course est requis."})
course = Course.objects.create(nom=nom, date=date, owner=request.user) if Course.objects.filter(nom=nom, date=date).exists():
return JsonResponse({'success': True, 'course_id': course.id}) return JsonResponse({'success': False, 'error': "Une course avec ce nom existe déjà aujourd'hui."})
form = CourseForm() course = Course.objects.create(nom=nom, date=date, owner=request.user)
return render(request, 'main.html', { return JsonResponse({'success': True, 'course_id': course.id})
'title': 'Accueil',
'courses': courses, form = CourseForm()
'form': form, return render(request, 'main.html', {
'now': timezone.localdate() 'title': 'Accueil',
}) 'courses': courses,
'form': form,
'now': timezone.localdate()
})
# =====================================
# Vues d'export
# =====================================
@login_required @login_required
def export_csv(request, course_id): def export_csv(request, course_id):
course = get_object_or_404(Course, id=course_id, owner=request.user) """Export des résultats d'une course en CSV.
response = HttpResponse(content_type='text/csv') Supporte soit l'export direct depuis la base, soit l'export des lignes filtrées envoyées en POST."""
response['Content-Disposition'] = f'attachment; filename="course_{course_id}_resultats.csv"' course = get_object_or_404(Course, id=course_id, owner=request.user)
writer = csv.writer(response) response = HttpResponse(content_type='text/csv')
writer.writerow(['Rang', 'Nom', 'Classe', 'Temps']) response['Content-Disposition'] = f'attachment; filename="course_{course_id}_resultats.csv"'
import json writer = csv.writer(response)
rows_json = request.POST.get('rows') writer.writerow(['Rang', 'Nom', 'Classe', 'Temps'])
if request.method == "POST" and rows_json:
try: import json
rows = json.loads(rows_json) rows_json = request.POST.get('rows')
for row in rows: if request.method == "POST" and rows_json:
writer.writerow(row) try:
except Exception: rows = json.loads(rows_json)
pass for row in rows:
else: writer.writerow(row)
arrivees = course.arrivees.select_related('coureur').order_by('rang') except Exception:
for a in arrivees: pass
writer.writerow([a.rang, a.coureur.nom, a.coureur.classe, str(a.temps)]) else:
return response arrivees = course.arrivees.select_related('coureur').order_by('rang')
for a in arrivees:
writer.writerow([a.rang, a.coureur.nom, a.coureur.classe, str(a.temps)])
return response
@login_required @login_required
def export_pdf(request, course_id): def export_pdf(request, course_id):
course = get_object_or_404(Course, id=course_id, owner=request.user) """Export des résultats d'une course en PDF.
response = HttpResponse(content_type='application/pdf') Supporte soit l'export direct depuis la base, soit l'export des lignes filtrées envoyées en POST."""
response['Content-Disposition'] = f'attachment; filename="course_{course_id}_resultats.pdf"' course = get_object_or_404(Course, id=course_id, owner=request.user)
p = canvas.Canvas(response, pagesize=A4) response = HttpResponse(content_type='application/pdf')
width, height = A4 response['Content-Disposition'] = f'attachment; filename="course_{course_id}_resultats.pdf"'
y = height - 50 # Configuration du document PDF
p.setFont("Helvetica-Bold", 16) p = canvas.Canvas(response, pagesize=A4)
p.drawString(50, y, f"R\u00e9sultats - {course.nom} ({course.date})") width, height = A4
y -= 40 # En-tête
p.setFont("Helvetica", 12) y = height - 50
p.drawString(50, y, "Rang") p.setFont("Helvetica-Bold", 16)
p.drawString(100, y, "Nom") p.drawString(50, y, f"Résultats - {course.nom} ({course.date})")
p.drawString(300, y, "Classe")
p.drawString(400, y, "Temps") # En-tête du tableau
y -= 20 y -= 40
import json p.setFont("Helvetica", 12)
rows_json = request.POST.get('rows') p.drawString(50, y, "Rang")
if request.method == "POST" and rows_json: p.drawString(100, y, "Nom")
try: p.drawString(300, y, "Classe")
rows = json.loads(rows_json) p.drawString(400, y, "Temps")
for row in rows: y -= 20
p.drawString(50, y, str(row[0]))
p.drawString(100, y, str(row[1])) # Contenu : soit les lignes filtrées, soit toutes les arrivées
p.drawString(300, y, str(row[2])) import json
p.drawString(400, y, str(row[3])) rows_json = request.POST.get('rows')
y -= 20 if request.method == "POST" and rows_json:
if y < 50: try:
p.showPage() rows = json.loads(rows_json)
y = height - 50 for row in rows:
except Exception: p.drawString(50, y, str(row[0]))
pass p.drawString(100, y, str(row[1]))
else: p.drawString(300, y, str(row[2]))
arrivees = course.arrivees.select_related('coureur').order_by('rang') p.drawString(400, y, str(row[3]))
for a in arrivees: y -= 20
p.drawString(50, y, str(a.rang)) # Nouvelle page si nécessaire
p.drawString(100, y, a.coureur.nom) if y < 50:
p.drawString(300, y, a.coureur.classe) p.showPage()
p.drawString(400, y, str(a.temps)) y = height - 50
y -= 20 except Exception:
if y < 50: pass
p.showPage() else:
y = height - 50 arrivees = course.arrivees.select_related('coureur').order_by('rang')
p.save() for a in arrivees:
return response p.drawString(50, y, str(a.rang))
p.drawString(100, y, a.coureur.nom)
p.drawString(300, y, a.coureur.classe)
p.drawString(400, y, str(a.temps))
y -= 20
# Nouvelle page si nécessaire
if y < 50:
p.showPage()
y = height - 50
p.save()
return response
@login_required @login_required
def course_detail_view(request, course_id): def course_detail_view(request, course_id):

Loading…
Cancel
Save