@ -31,8 +31,30 @@ def seconds_to_hms(delta: timedelta) -> str:
@@ -31,8 +31,30 @@ def seconds_to_hms(delta: timedelta) -> str:
seconds = total % 60
return f " { hours } : { minutes : 02d } : { seconds : 02d } "
@login_required
def main_view ( request ) :
courses = Course . objects . filter ( owner = request . user )
if request . method == ' POST ' and request . headers . get ( ' x-requested-with ' ) == ' XMLHttpRequest ' :
from django . http import JsonResponse
nom = request . POST . get ( ' nom ' )
date = timezone . localdate ( )
if not nom :
return JsonResponse ( { ' success ' : False , ' error ' : " Le nom de la course est requis. " } )
if Course . objects . filter ( nom = nom , date = date ) . exists ( ) :
return JsonResponse ( { ' success ' : False , ' error ' : " Une course avec ce nom existe d \u00e9 j \u00e0 aujourd ' hui. " } )
course = Course . objects . create ( nom = nom , date = date , owner = request . user )
return JsonResponse ( { ' success ' : True , ' course_id ' : course . id } )
form = CourseForm ( )
return render ( request , ' main.html ' , {
' title ' : ' Accueil ' ,
' courses ' : courses ,
' form ' : form ,
' now ' : timezone . localdate ( )
} )
@login_required
def export_csv ( request , course_id ) :
course = get_object_or_404 ( Course , id = course_id )
course = get_object_or_404 ( Course , id = course_id , owner = request . user )
response = HttpResponse ( content_type = ' text/csv ' )
response [ ' Content-Disposition ' ] = f ' attachment; filename= " course_ { course_id } _resultats.csv " '
writer = csv . writer ( response )
@ -52,8 +74,9 @@ def export_csv(request, course_id):
@@ -52,8 +74,9 @@ def export_csv(request, course_id):
writer . writerow ( [ a . rang , a . coureur . nom , a . coureur . classe , str ( a . temps ) ] )
return response
@login_required
def export_pdf ( request , course_id ) :
course = get_object_or_404 ( Course , id = course_id )
course = get_object_or_404 ( Course , id = course_id , owner = request . user )
response = HttpResponse ( content_type = ' application/pdf ' )
response [ ' Content-Disposition ' ] = f ' attachment; filename= " course_ { course_id } _resultats.pdf " '
p = canvas . Canvas ( response , pagesize = A4 )
@ -102,7 +125,7 @@ def export_pdf(request, course_id):
@@ -102,7 +125,7 @@ def export_pdf(request, course_id):
@login_required
def course_detail_view ( request , course_id ) :
course = get_object_or_404 ( Course , id = course_id )
course = get_object_or_404 ( Course , id = course_id , owner = request . user )
arrivees = course . arrivees . select_related ( ' coureur ' ) . order_by ( ' rang ' )
is_started = course . depart is not None
is_finished = course . fin is not None
@ -129,48 +152,30 @@ def course_detail_view(request, course_id):
@@ -129,48 +152,30 @@ def course_detail_view(request, course_id):
' is_started ' : is_started ,
' is_finished ' : is_finished
} )
@login_required
def main_view ( request ) :
courses = Course . objects . all ( )
if request . method == ' POST ' and request . headers . get ( ' x-requested-with ' ) == ' XMLHttpRequest ' :
from django . http import JsonResponse
nom = request . POST . get ( ' nom ' )
date = timezone . localdate ( )
if not nom :
return JsonResponse ( { ' success ' : False , ' error ' : " Le nom de la course est requis. " } )
if Course . objects . filter ( nom = nom , date = date ) . exists ( ) :
return JsonResponse ( { ' success ' : False , ' error ' : " Une course avec ce nom existe d \u00e9 j \u00e0 aujourd ' hui. " } )
course = Course . objects . create ( nom = nom , date = date )
return JsonResponse ( { ' success ' : True , ' course_id ' : course . id } )
form = CourseForm ( )
return render ( request , ' main.html ' , {
' title ' : ' Accueil ' ,
' courses ' : courses ,
' form ' : form ,
' now ' : timezone . localdate ( )
} )
@login_required
def scan_view ( request ) :
courses = Course . objects . filter ( depart__isnull = False , fin__isnull = True )
courses = Course . objects . filter ( owner = request . user , depart__isnull = False , fin__isnull = True )
result = None
error = None
course = None
if request . method == ' POST ' and request . headers . get ( ' x-requested-with ' ) == ' XMLHttpRequest ' :
course_id = request . POST . get ( ' course_id ' )
qrcode = request . POST . get ( ' qrcode ' )
if not course_id or not qrcode :
error = " Param \u00e8 tres manquants. "
elif qrcode . count ( ' ; ' ) != 1 :
error = " Format QR code invalide. "
else :
nom , classe = qrcode . split ( ' ; ' )
course = get_object_or_404 ( Course , id = course_id )
if not course . depart :
error = " La course n ' a pas d \u00e9 marr \u00e9 . "
else :
coureur , _ = Coureur . objects . get_or_create ( nom = nom . strip ( ) , classe = classe . strip ( ) )
# ensure user can only scan their own course
course = get_object_or_404 ( Course , id = course_id , owner = request . user )
# QR code now contains the unique Coureur id
coureur = None
try :
coureur = Coureur . objects . get ( id = qrcode . strip ( ) )
except Coureur . DoesNotExist :
error = " Coureur introuvable pour ce code QR. "
if coureur :
if Arrivee . objects . filter ( course = course , coureur = coureur ) . exists ( ) :
error = " Ce coureur a d \u00e9 j \u00e0 \u00e9 t \u00e9 scann \u00e9 . "
else :
@ -179,6 +184,7 @@ def scan_view(request):
@@ -179,6 +184,7 @@ def scan_view(request):
Arrivee . objects . create ( course = course , coureur = coureur , temps = temps , rang = rang )
result = {
' nom ' : coureur . nom ,
' prenom ' : coureur . prenom ,
' classe ' : coureur . classe ,
' rang ' : rang ,
' temps ' : str ( seconds_to_hms ( temps ) )
@ -191,14 +197,17 @@ def scan_view(request):
@@ -191,14 +197,17 @@ def scan_view(request):
' data ' : result
}
)
if result :
return render ( request , ' scan_result.html ' , { ' result ' : result } )
else :
return render ( request , ' scan_result.html ' , { ' error ' : error } )
else :
course_id = request . GET . get ( ' course_id ' )
if course_id :
course = get_object_or_404 ( Course , id = course_id )
course = get_object_or_404 ( Course , id = course_id , owner = request . user )
return render ( request , ' scan.html ' , {
' title ' : ' Scan course : ' + ( course . nom + " ( " + str ( course . date ) + " ) " if course else ' ' ) ,
' courses ' : courses ,
@ -219,11 +228,19 @@ def dossards_view(request):
@@ -219,11 +228,19 @@ def dossards_view(request):
rows = form . cleaned_data [ ' rows ' ]
cols = form . cleaned_data [ ' cols ' ]
try :
# Build or find Coureur objects for each line. Accept either 'nom;classe' or 'nom;prenom;classe'.
data = [ ]
for line in csv_file . read ( ) . decode ( ' utf-8 ' ) . splitlines ( ) :
if line . count ( ' ; ' ) == 1 :
nom , classe = line . split ( ' ; ' )
data . append ( ( nom . strip ( ) , classe . strip ( ) ) )
parts = [ p . strip ( ) for p in line . split ( ' ; ' ) if p . strip ( ) != ' ' ]
if len ( parts ) == 2 :
nom , classe = parts
prenom = ' '
elif len ( parts ) == 3 :
nom , prenom , classe = parts
else :
continue
coureur , _ = Coureur . objects . get_or_create ( nom = nom , prenom = prenom , classe = classe )
data . append ( coureur )
total = len ( data )
progress = f " G \u00e9 n \u00e9 ration des dossards : 0/ { total } ... "
buffer = generate_dossards_pdf ( data , rows , cols )
@ -258,6 +275,8 @@ def generate_dossards_pdf(data, rows, cols):
@@ -258,6 +275,8 @@ def generate_dossards_pdf(data, rows, cols):
x0 , y0 = margin , height - margin - label_h
qr_scale = 0.8 # 80% of the label area for the QR
for idx , ( nom , classe ) in enumerate ( data ) :
# data is now a list of Coureur objects
coureur = data [ idx ]
col = idx % cols
row = ( idx / / cols ) % rows
page = idx / / ( rows * cols )
@ -269,7 +288,8 @@ def generate_dossards_pdf(data, rows, cols):
@@ -269,7 +288,8 @@ def generate_dossards_pdf(data, rows, cols):
c . setStrokeColorRGB ( 0 , 0 , 0 )
c . rect ( x , y , label_w , label_h )
# Generate QR
qr = qrcode . make ( f " { nom } ; { classe } " )
# QR contains the unique coureur id
qr = qrcode . make ( f " { coureur . id } " )
qr_img = io . BytesIO ( )
qr . save ( qr_img , format = ' PNG ' )
qr_img . seek ( 0 )
@ -279,9 +299,11 @@ def generate_dossards_pdf(data, rows, cols):
@@ -279,9 +299,11 @@ def generate_dossards_pdf(data, rows, cols):
qr_y = y + ( label_h - qr_size ) / 2
c . drawInlineImage ( qr_pil , qr_x , qr_y , qr_size , qr_size )
c . setFont ( " Helvetica-Bold " , 12 )
c . drawCentredString ( x + label_w / 2 , qr_y - 10 , nom )
# Display nom, prenom, classe
full_name = f " { coureur . nom } { coureur . prenom } " . strip ( )
c . drawCentredString ( x + label_w / 2 , qr_y - 10 , full_name )
c . setFont ( " Helvetica " , 10 )
c . drawCentredString ( x + label_w / 2 , qr_y - 24 , classe )
c . drawCentredString ( x + label_w / 2 , qr_y - 24 , coureur . c lasse )
c . save ( )
buffer . seek ( 0 )
return buffer