Compare commits

...

2 Commits

  1. 1
      main/templates/course_detail.html
  2. 14
      main/templates/dossards.html
  3. 4
      main/templates/scan.html
  4. 123
      main/views.py

1
main/templates/course_detail.html

@ -20,6 +20,7 @@
<button type="button" id="btnFinish" class="btn btn-danger">Fin course <button type="button" id="btnFinish" class="btn btn-danger">Fin course
<i class="fa-solid fa-stop"></i> <i class="fa-solid fa-stop"></i>
</button> </button>
<a href="{% url 'scan' %}?course_id={{ course.id }}" class="btn btn-info ml-2">Accès au scan <i class="fas fa-qrcode"></i></a>
<!-- Modal confirmation fin de course --> <!-- Modal confirmation fin de course -->
<div class="modal fade" id="finishModal" tabindex="-1" role="dialog" aria-labelledby="finishModalLabel" aria-hidden="true"> <div class="modal fade" id="finishModal" tabindex="-1" role="dialog" aria-labelledby="finishModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">

14
main/templates/dossards.html

@ -91,14 +91,22 @@
</div> </div>
</div> </div>
{% block extra_js %} {% block extra_js %}
<script src="{% static 'jquery/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'jquery/jquery.dataTables.min.js' %}"></script> <script src="{% static 'jquery/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'bootstrap/dataTables.bootstrap4.min.js' %}"></script> <script src="{% static 'bootstrap/dataTables.bootstrap4.min.js' %}"></script>
<script src="{% static 'jquery/datatables.fr.js' %}"></script> <script src="{% static 'jquery/datatables.fr.js' %}"></script>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
var table = $('#coureursTable').DataTable({ // Empêche la double initialisation de DataTables
pageLength: 50, var table;
}); if (!$.fn.DataTable.isDataTable('#coureursTable')) {
table = $('#coureursTable').DataTable({
pageLength: 50,
});
} else {
table = $('#coureursTable').DataTable();
}
function updateMainDossardsCount() { function updateMainDossardsCount() {
var count = table.rows({search:'applied'}).count(); var count = table.rows({search:'applied'}).count();
$('#mainDossardsCountText').text('Générer ' + count + ' dossards'); $('#mainDossardsCountText').text('Générer ' + count + ' dossards');

4
main/templates/scan.html

@ -9,6 +9,9 @@
<h6 class="m-0 font-weight-bold text-primary">Scanner un coureur</h6> <h6 class="m-0 font-weight-bold text-primary">Scanner un coureur</h6>
</div> </div>
<div class="card-body"> <div class="card-body">
{% if course and course.fin %}
<div class="alert alert-danger text-center mb-0">Cette course est terminée. Le scan n'est plus possible.</div>
{% else %}
<div id="reader" style="width:100%; max-width:400px; margin:auto;"></div> <div id="reader" style="width:100%; max-width:400px; margin:auto;"></div>
<div class="mt-4 position-relative" style="max-width:400px; margin:auto;"> <div class="mt-4 position-relative" style="max-width:400px; margin:auto;">
<label id="manualCoureurLabel">Saisie manuelle :</label> <label id="manualCoureurLabel">Saisie manuelle :</label>
@ -22,6 +25,7 @@
{% if error %} {% if error %}
<div class="alert alert-danger mt-3">{{ error }}</div> <div class="alert alert-danger mt-3">{{ error }}</div>
{% endif %} {% endif %}
{% endif %}
</div> </div>
</div> </div>
<div class="card shadow mb-4"> <div class="card shadow mb-4">

123
main/views.py

@ -273,6 +273,7 @@ def scan_view(request):
error = None error = None
course = None course = None
if request.method == 'POST' and request.headers.get('x-requested-with') == 'XMLHttpRequest': if request.method == 'POST' and request.headers.get('x-requested-with') == 'XMLHttpRequest':
course_id = request.POST.get('course_id') course_id = request.POST.get('course_id')
qrcode = request.POST.get('qrcode') qrcode = request.POST.get('qrcode')
@ -281,72 +282,74 @@ def scan_view(request):
else: else:
# S'assurer que l'utilisateur scanne uniquement ses propres courses # S'assurer que l'utilisateur scanne uniquement ses propres courses
course = get_object_or_404(Course, id=course_id, owner=request.user) course = get_object_or_404(Course, id=course_id, owner=request.user)
if course.fin:
# Le QR code contient maintenant l'identifiant unique du Coureur error = "Cette course est terminée. Le scan n'est plus possible."
coureur = None else:
try: # Le QR code contient maintenant l'identifiant unique du Coureur
coureur = Coureur.objects.get(id=qrcode.strip()) coureur = None
except Coureur.DoesNotExist: try:
error = "Coureur introuvable pour ce code QR." coureur = Coureur.objects.get(id=qrcode.strip())
except Coureur.DoesNotExist:
if coureur: error = "Coureur introuvable pour ce code QR."
# Si la course est de type unique : comportement existant (1 arrivée par coureur)
if course.type == Course.TYPE_UNIQUE: if coureur:
if Arrivee.objects.filter(course=course, coureur=coureur).exists(): # Si la course est de type unique : comportement existant (1 arrivée par coureur)
error = "Ce coureur a déjà été scanné." if course.type == Course.TYPE_UNIQUE:
if Arrivee.objects.filter(course=course, coureur=coureur).exists():
error = "Ce coureur a déjà été scanné."
else:
temps = timezone.now() - course.depart
rang = Arrivee.objects.filter(course=course).count() + 1
arr = Arrivee.objects.create(course=course, coureur=coureur, temps=temps, rang=rang, tour=1)
temps_str = seconds_to_hms(temps)
# nombre total de scans pour cette course
scan_count = Arrivee.objects.filter(course=course).count()
result = {
'nom': coureur.nom,
'prenom': coureur.prenom,
'classe': coureur.classe,
'rang': arr.rang,
'temps': temps_str,
'tour': arr.tour,
'scan_count': scan_count
}
# Pour les courses multi, autoriser plusieurs scans par coureur et calculer le tour/temps de tour
else: else:
temps = timezone.now() - course.depart # Compter les arrivées précédentes pour ce coureur sur cette course
previous = Arrivee.objects.filter(course=course, coureur=coureur).order_by('tour', 'date_arrivee')
last_arr = previous.last()
if last_arr:
# prochain tour = dernier tour + 1
next_tour = last_arr.tour + 1
lap_time = timezone.now() - last_arr.date_arrivee
else:
# premier scan pour ce coureur : tour 1, temps depuis le départ de la course
next_tour = 1
lap_time = timezone.now() - course.depart
# rang = nombre d'arrivées global + 1
rang = Arrivee.objects.filter(course=course).count() + 1 rang = Arrivee.objects.filter(course=course).count() + 1
arr = Arrivee.objects.create(course=course, coureur=coureur, temps=temps, rang=rang, tour=1) arr = Arrivee.objects.create(course=course, coureur=coureur, temps=lap_time, rang=rang, tour=next_tour)
temps_str = seconds_to_hms(temps) lap_str = seconds_to_hms(lap_time)
# nombre total de scans pour cette course
scan_count = Arrivee.objects.filter(course=course).count() scan_count = Arrivee.objects.filter(course=course).count()
result = { result = {
'nom': coureur.nom, 'nom': coureur.nom,
'prenom': coureur.prenom, 'prenom': coureur.prenom,
'classe': coureur.classe, 'classe': coureur.classe,
'rang': arr.rang, 'rang': arr.rang,
'temps': temps_str, 'temps': lap_str,
'tour': arr.tour, 'tour': arr.tour,
'lap_seconds': int(lap_time.total_seconds()),
'scan_count': scan_count 'scan_count': scan_count
} }
# Pour les courses multi, autoriser plusieurs scans par coureur et calculer le tour/temps de tour # Diffuser via le canal websocket
else: channel_layer = get_channel_layer()
# Compter les arrivées précédentes pour ce coureur sur cette course async_to_sync(channel_layer.group_send)(
previous = Arrivee.objects.filter(course=course, coureur=coureur).order_by('tour', 'date_arrivee') f'course_{course.id}',
last_arr = previous.last() {
if last_arr: 'type': 'send_arrivee',
# prochain tour = dernier tour + 1 'data': result
next_tour = last_arr.tour + 1 }
lap_time = timezone.now() - last_arr.date_arrivee )
else:
# premier scan pour ce coureur : tour 1, temps depuis le départ de la course
next_tour = 1
lap_time = timezone.now() - course.depart
# rang = nombre d'arrivées global + 1
rang = Arrivee.objects.filter(course=course).count() + 1
arr = Arrivee.objects.create(course=course, coureur=coureur, temps=lap_time, rang=rang, tour=next_tour)
lap_str = seconds_to_hms(lap_time)
scan_count = Arrivee.objects.filter(course=course).count()
result = {
'nom': coureur.nom,
'prenom': coureur.prenom,
'classe': coureur.classe,
'rang': arr.rang,
'temps': lap_str,
'tour': arr.tour,
'lap_seconds': int(lap_time.total_seconds()),
'scan_count': scan_count
}
# Diffuser via le canal websocket
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
f'course_{course.id}',
{
'type': 'send_arrivee',
'data': result
}
)
if result: if result:
return render(request, 'scan_result.html', {'result': result, 'course_type': course.type if course else None}) return render(request, 'scan_result.html', {'result': result, 'course_type': course.type if course else None})
@ -357,17 +360,19 @@ def scan_view(request):
course_id = request.GET.get('course_id') course_id = request.GET.get('course_id')
if course_id: if course_id:
course = get_object_or_404(Course, id=course_id, owner=request.user) course = get_object_or_404(Course, id=course_id, owner=request.user)
if course.fin:
error = "Cette course est terminée. Le scan n'est plus possible."
# Formatage de la date pour affichage JJ/MM/AAAA # Formatage de la date pour affichage JJ/MM/AAAA
date_str = format_date(course.date) if course else '' date_str = format_date(course.date) if course_id and 'course' in locals() else ''
# nombre actuel de scans pour cette course # nombre actuel de scans pour cette course
scan_count = Arrivee.objects.filter(course=course).count() if course else 0 scan_count = Arrivee.objects.filter(course=course).count() if course_id and 'course' in locals() else 0
return render(request, 'scan.html', { return render(request, 'scan.html', {
'title': f'Scan course : {course.nom} ({date_str})' if course else '', 'title': f'Scan course : {course.nom} ({date_str})' if course_id and 'course' in locals() else '',
'courses': courses, 'courses': courses,
'result': result, 'result': result,
'error': error, 'error': error,
'course': course, 'course': course if course_id and 'course' in locals() else None,
'scan_count': scan_count 'scan_count': scan_count
}) })

Loading…
Cancel
Save