Compare commits

..

2 Commits

  1. 33
      main/templates/course_detail.html
  2. 45
      main/views.py

33
main/templates/course_detail.html

@ -157,10 +157,13 @@ socket.onmessage = function(e) {
dt.row.add(rowData).draw(false); dt.row.add(rowData).draw(false);
}; };
// Modal confirmation fin de course // Modal confirmation fin de course (attach handler only if button exists)
document.getElementById('btnFinish').onclick = function() { var finishBtn = document.getElementById('btnFinish');
if (finishBtn) {
finishBtn.addEventListener('click', function() {
$('#finishModal').modal('show'); $('#finishModal').modal('show');
}; });
}
// Initialisation DataTables au chargement // Initialisation DataTables au chargement
$(document).ready(function() { $(document).ready(function() {
@ -192,9 +195,12 @@ $(document).ready(function() {
// Keep the current ordering so we can restore it // Keep the current ordering so we can restore it
var storedOrder = table.order(); var storedOrder = table.order();
// Expose grouping state to the global scope so exports can read it
window.courseGroupBy = groupBy;
$('#btnGroup').on('click', function(){ $('#btnGroup').on('click', function(){
groupBy = !groupBy; groupBy = !groupBy;
window.courseGroupBy = groupBy;
$(this).text(groupBy ? 'Désactiver le groupement' : 'Grouper par coureur'); $(this).text(groupBy ? 'Désactiver le groupement' : 'Grouper par coureur');
if (groupBy) { if (groupBy) {
// Sort by Nom (col 1) then Prénom (col 2) to make grouping contiguous // Sort by Nom (col 1) then Prénom (col 2) to make grouping contiguous
@ -210,6 +216,27 @@ $(document).ready(function() {
function getVisibleRows() { function getVisibleRows() {
var dt = $('#arriveesTable').DataTable(); var dt = $('#arriveesTable').DataTable();
var rows = dt.rows({search: 'applied'}).data().toArray(); var rows = dt.rows({search: 'applied'}).data().toArray();
// If grouping is active, inject group-header marker rows into the exported data
try {
if (window.courseGroupBy) {
var output = [];
var lastKey = null;
for (var i = 0; i < rows.length; i++) {
var name = rows[i][1] || '';
var prenom = rows[i][2] || '';
var key = name + '|' + prenom;
if (key !== lastKey) {
// marker row: first cell '__GROUP__', second cell holds the group label
output.push(['__GROUP__', name + ' ' + prenom, '', '', '']);
lastKey = key;
}
output.push(rows[i]);
}
return JSON.stringify(output);
}
} catch (e) {
// fallback to default rows if anything goes wrong
}
return JSON.stringify(rows); return JSON.stringify(rows);
} }

45
main/views.py

@ -134,7 +134,7 @@ def export_csv(request, course_id):
response = HttpResponse(content_type='text/csv') response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = f'attachment; filename="course_{course_id}_resultats.csv"' response['Content-Disposition'] = f'attachment; filename="course_{course_id}_resultats.csv"'
writer = csv.writer(response) writer = csv.writer(response)
writer.writerow(['Rang', 'Nom', 'Classe', 'Temps']) writer.writerow(['Rang', 'Nom', 'Prénom', 'Classe', 'Temps'])
import json import json
rows_json = request.POST.get('rows') rows_json = request.POST.get('rows')
@ -164,18 +164,22 @@ def export_pdf(request, course_id):
p = canvas.Canvas(response, pagesize=A4) p = canvas.Canvas(response, pagesize=A4)
width, height = A4 width, height = A4
# Choisit l'étiquette de la première colonne selon le type de course
first_col_label = 'Tour' if course.type == 'multi' else 'Rang'
# En-tête du document # En-tête du document
y = height - 50 y = height - 50
p.setFont("Helvetica-Bold", 16) p.setFont("Helvetica-Bold", 16)
p.drawString(50, y, f"Résultats - {course.nom} ({course.date})") p.drawString(50, y, f"Résultats - {course.nom} ({course.date})")
# En-tête du tableau # En-tête du tableau (colonnes: premier, Nom, Prénom, Classe, Temps)
y -= 40 y -= 40
p.setFont("Helvetica", 12) p.setFont("Helvetica", 12)
p.drawString(50, y, "Rang") p.drawString(50, y, first_col_label)
p.drawString(100, y, "Nom") p.drawString(100, y, "Nom")
p.drawString(300, y, "Classe") p.drawString(220, y, "Prénom")
p.drawString(400, y, "Temps") p.drawString(340, y, "Classe")
p.drawString(460, y, "Temps")
y -= 20 y -= 20
# Contenu : soit les lignes filtrées envoyées en POST, soit toutes les arrivées en base # Contenu : soit les lignes filtrées envoyées en POST, soit toutes les arrivées en base
@ -185,10 +189,24 @@ def export_pdf(request, course_id):
try: try:
rows = json.loads(rows_json) rows = json.loads(rows_json)
for row in rows: for row in rows:
p.drawString(50, y, str(row[0])) # If we receive a group marker row (injected client-side when grouping), render it as a header
p.drawString(100, y, str(row[1])) if isinstance(row, list) and len(row) > 0 and row[0] == '__GROUP__':
p.drawString(300, y, str(row[2])) # Render group header spanning the table width
p.drawString(400, y, str(row[3])) p.setFont("Helvetica-Bold", 12)
p.drawString(50, y, str(row[1]))
p.setFont("Helvetica", 12)
else:
# Standard row: expected format [first, nom, prenom, classe, temps]
first = row[0] if len(row) > 0 else ''
nom = row[1] if len(row) > 1 else ''
prenom = row[2] if len(row) > 2 else ''
classe = row[3] if len(row) > 3 else ''
temps_val = row[4] if len(row) > 4 else ''
p.drawString(50, y, str(first))
p.drawString(100, y, str(nom))
p.drawString(220, y, str(prenom))
p.drawString(340, y, str(classe))
p.drawString(460, y, str(temps_val))
y -= 20 y -= 20
# Nouvelle page si nécessaire # Nouvelle page si nécessaire
if y < 50: if y < 50:
@ -199,10 +217,13 @@ def export_pdf(request, course_id):
else: else:
arrivees = course.arrivees.select_related('coureur').order_by('rang') arrivees = course.arrivees.select_related('coureur').order_by('rang')
for a in arrivees: for a in arrivees:
p.drawString(50, y, str(a.rang)) # Affiche le numéro de tour pour les courses multi, sinon le rang
first_value = a.tour if course.type == 'multi' else a.rang
p.drawString(50, y, str(first_value))
p.drawString(100, y, a.coureur.nom) p.drawString(100, y, a.coureur.nom)
p.drawString(300, y, a.coureur.classe) p.drawString(220, y, a.coureur.prenom)
p.drawString(400, y, str(a.temps)) p.drawString(340, y, a.coureur.classe)
p.drawString(460, y, str(a.temps))
y -= 20 y -= 20
# Nouvelle page si nécessaire # Nouvelle page si nécessaire
if y < 50: if y < 50:

Loading…
Cancel
Save