🚧 hromadne akcie na registraciach + zapisovanie casov + automaticke vytvaranie kol
This commit is contained in:
@@ -53,7 +53,7 @@ http://0.0.0.0:8234/admin/
|
|||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
- [x] casy (id_timeru, cas, id_karty)
|
- [x] casy (id_timeru, cas, id_karty)
|
||||||
- [ ] laps (star, end, id_karty)
|
- [x] laps (star, end, id_karty)
|
||||||
- [ ] bar zaznamy (id_karty, polozka, cas)
|
- [ ] bar zaznamy (id_karty, polozka, cas)
|
||||||
- [x] doplnit mysql kontajner a zmenit connetor v settings.py
|
- [x] doplnit mysql kontajner a zmenit connetor v settings.py
|
||||||
- [ ] doplnit phpmyadmin kontajner
|
- [ ] doplnit phpmyadmin kontajner
|
||||||
@@ -62,3 +62,4 @@ http://0.0.0.0:8234/admin/
|
|||||||
- [x] bezpecnostne kody pre email pre pristup k registraciam
|
- [x] bezpecnostne kody pre email pre pristup k registraciam
|
||||||
- [x] zoznam registracii pre email zabezpeceny bezpecnostnym kodom
|
- [x] zoznam registracii pre email zabezpeceny bezpecnostnym kodom
|
||||||
- [x] moznost priamej registracie bez mailoveho potvrdenia
|
- [x] moznost priamej registracie bez mailoveho potvrdenia
|
||||||
|
- [x] hromadne akcie v adminovi na registraciach
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin, messages
|
||||||
|
|
||||||
from events import models
|
from events import models
|
||||||
|
|
||||||
@@ -13,11 +13,43 @@ class EventAdmin(admin.ModelAdmin):
|
|||||||
list_display = ('start', 'name', 'is_actual', 'is_public', 'registrations_enabled')
|
list_display = ('start', 'name', 'is_actual', 'is_public', 'registrations_enabled')
|
||||||
|
|
||||||
|
|
||||||
|
def presentation(modeladmin, request, queryset):
|
||||||
|
for registration in queryset:
|
||||||
|
registration.is_presented = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
registration.save()
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(request, f"Registraciu {registration} sa nepodarilo zaprezentovat: {e}")
|
||||||
|
presentation.short_description = "Zaprezentovat registraciu"
|
||||||
|
|
||||||
|
def pay(modeladmin, request, queryset):
|
||||||
|
for registration in queryset:
|
||||||
|
registration.is_paid = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
registration.save()
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(request, f"Registraciu {registration} sa nepodarilo uhradit: {e}")
|
||||||
|
pay.short_description = "Oznacit registraciu ako uhradenu"
|
||||||
|
|
||||||
|
def finish(modeladmin, request, queryset):
|
||||||
|
for registration in queryset:
|
||||||
|
registration.is_finished = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
registration.save()
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(request, f"Registraciu {registration} sa nepodarilo dokoncit: {e}")
|
||||||
|
finish.short_description = "Dokoncit registraciu"
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.Registration)
|
@admin.register(models.Registration)
|
||||||
class RegistrationAdmin(admin.ModelAdmin):
|
class RegistrationAdmin(admin.ModelAdmin):
|
||||||
list_display = ('created', 'variable_symbol', 'event', 'email', 'first_name', 'last_name', 'is_confirmed' ,'is_paid', 'is_finished')
|
list_display = ('created', 'variable_symbol', 'event', 'email', 'first_name', 'last_name', 'is_confirmed' ,'is_paid', 'is_finished')
|
||||||
list_filter = ('event', 'is_confirmed', 'is_paid', 'is_finished', 'email')
|
list_filter = ('event', 'category', 'is_confirmed', 'is_paid', 'is_finished', 'email')
|
||||||
search_fields = ('email', 'first_name', 'last_name', 'variable_symbol')
|
search_fields = ('email', 'first_name', 'last_name', 'variable_symbol')
|
||||||
|
actions = [presentation, pay, finish]
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.HTMLSection)
|
@admin.register(models.HTMLSection)
|
||||||
|
|||||||
19
src/events/migrations/0003_alter_registration_event.py
Normal file
19
src/events/migrations/0003_alter_registration_event.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-03-06 18:51
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('events', '0002_category_mapy_cz_url'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='registration',
|
||||||
|
name='event',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='registrations', to='events.event', verbose_name='Event'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -118,7 +118,8 @@ class Category(models.Model):
|
|||||||
|
|
||||||
class Registration(TimeStampedModel):
|
class Registration(TimeStampedModel):
|
||||||
uid = models.UUIDField(blank=True, null=True, verbose_name=_('UUID'))
|
uid = models.UUIDField(blank=True, null=True, verbose_name=_('UUID'))
|
||||||
event = models.ForeignKey(Event, null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_('Event'))
|
event = models.ForeignKey(Event, null=True, blank=True, on_delete=models.SET_NULL,
|
||||||
|
related_name='registrations', verbose_name=_('Event'))
|
||||||
variable_symbol = models.CharField(max_length=10, blank=True, null=True, verbose_name=_('Variable symbol'))
|
variable_symbol = models.CharField(max_length=10, blank=True, null=True, verbose_name=_('Variable symbol'))
|
||||||
|
|
||||||
# data from form
|
# data from form
|
||||||
@@ -196,7 +197,8 @@ class Lap(TimeStampedModel):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if self.start and self.end:
|
if self.start and self.end:
|
||||||
return self.end - self.start
|
return (self.end - self.start)
|
||||||
|
|
||||||
return timedelta(0)
|
return timedelta(0)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ urlpatterns = [
|
|||||||
"my-registrations/<str:uid>/<str:email>/<str:security_code>/",
|
"my-registrations/<str:uid>/<str:email>/<str:security_code>/",
|
||||||
MyRegistrationsListView.as_view(),
|
MyRegistrationsListView.as_view(),
|
||||||
name="my_registrations_list",
|
name="my_registrations_list",
|
||||||
)
|
),
|
||||||
|
|
||||||
]
|
|
||||||
|
]
|
||||||
@@ -6,6 +6,7 @@ from django.urls import reverse_lazy
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import RedirectView, TemplateView
|
from django.views.generic import RedirectView, TemplateView
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
|
||||||
from core.mailer import Emailer
|
from core.mailer import Emailer
|
||||||
from core.utils import generate_random_string
|
from core.utils import generate_random_string
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<footer class="w3-center {{ actual_event.get_bg_color }} w3-padding-64">
|
<footer class="w3-center {{ actual_event.get_bg_color }} w3-padding-64">
|
||||||
<a href="#home" class="w3-button w3-light-grey">CHOD ZASA HORE</a>
|
<a href="#home" class="w3-button w3-light-grey">CHOD ZASA HORE</a>
|
||||||
|
|
||||||
<p>Powered by <a href="https://www.w3schools.com/w3css/default.asp" title="W3.CSS" target="_blank"
|
<p>Powered by <a href="https://www.w3schools.com/w3css/default.asp" title="W3.CSS" target="_blank"
|
||||||
class="w3-hover-text-green">w3.css</a></p>
|
class="w3-hover-text-green">w3.css</a></p>
|
||||||
</footer>
|
</footer>
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include 'menu/landing_page_menu.html' %}
|
{% include 'menu/landing_page_menu.html' %}
|
||||||
|
{% include 'partials/messages.html' %}
|
||||||
{% block landing_content %}{% endblock %}
|
{% block landing_content %}{% endblock %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Kalské Borec //25</title>
|
<title>Kalské Borec //25</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
@@ -15,10 +15,9 @@
|
|||||||
<link rel="manifest" href="{% static 'assets/favicon_io/site.webmanifest' %}">
|
<link rel="manifest" href="{% static 'assets/favicon_io/site.webmanifest' %}">
|
||||||
|
|
||||||
{% block extra_head %}{% endblock %}
|
{% block extra_head %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% include 'partials/messages.html' %}
|
{% block content %}{% endblock %}
|
||||||
{% block content %}{% endblock %}
|
{% block js %}{% endblock %}
|
||||||
{% block js %}{% endblock %}
|
</body>
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
@@ -5,7 +5,7 @@ from timer import models
|
|||||||
|
|
||||||
@admin.register(models.TimeRecord)
|
@admin.register(models.TimeRecord)
|
||||||
class TimeRecordAdmin(admin.ModelAdmin):
|
class TimeRecordAdmin(admin.ModelAdmin):
|
||||||
list_display = ('timer_id', 'card_id', 'time')
|
list_display = ('timer_id', 'chip_id', 'time')
|
||||||
search_fields = ('timer_id', 'card_id')
|
search_fields = ('timer_id', 'chip_id')
|
||||||
list_filter = ('timer_id', 'card_id')
|
list_filter = ('timer_id', 'chip_id')
|
||||||
list_display_links = ('timer_id', 'card_id')
|
list_display_links = ('timer_id', 'chip_id')
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-03-06 18:51
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('timer', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='timerecord',
|
||||||
|
name='card_id',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='timerecord',
|
||||||
|
name='chip_id',
|
||||||
|
field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Chip identifier'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -15,7 +15,7 @@ class TimeRecord(TimeStampedModel):
|
|||||||
TimeRecord database model holding the time records from the timer.
|
TimeRecord database model holding the time records from the timer.
|
||||||
"""
|
"""
|
||||||
timer_id = models.CharField(max_length=20, null=True, blank=True, verbose_name=_('Timer identifier'))
|
timer_id = models.CharField(max_length=20, null=True, blank=True, verbose_name=_('Timer identifier'))
|
||||||
card_id = models.CharField(max_length=100, null=True, blank=True, verbose_name=_('Card identifier'))
|
chip_id = models.CharField(max_length=100, null=True, blank=True, verbose_name=_('Chip identifier'))
|
||||||
time = models.DateTimeField(verbose_name=_('Time'))
|
time = models.DateTimeField(verbose_name=_('Time'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -23,4 +23,4 @@ class TimeRecord(TimeStampedModel):
|
|||||||
verbose_name_plural = _('Time Records')
|
verbose_name_plural = _('Time Records')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.timer_id} - {self.card_id}'
|
return f'{self.timer_id} - {self.chip_id}'
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from timer.views import WriteTimeApiView
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(
|
path(
|
||||||
"write/<str:timer_id>/<str:card_id>/<str:time>/",
|
"write/<str:timer_id>/<str:chip_id>/<str:time>/",
|
||||||
WriteTimeApiView.as_view(),
|
WriteTimeApiView.as_view(),
|
||||||
name="write_time",
|
name="write_time",
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from datetime import datetime
|
|||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
|
|
||||||
from events.models import Event
|
from events.models import Event, Lap
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -27,39 +27,47 @@ class WriteTimeApiView(View):
|
|||||||
|
|
||||||
# create new TimeRecord object and save it to the database
|
# create new TimeRecord object and save it to the database
|
||||||
TimeRecord(
|
TimeRecord(
|
||||||
card_id=kwargs['card_id'],
|
chip_id=kwargs['chip_id'],
|
||||||
timer_id=kwargs['timer_id'],
|
timer_id=kwargs['timer_id'],
|
||||||
time=record_time,
|
time=record_time,
|
||||||
).save()
|
).save()
|
||||||
|
|
||||||
# find the active event
|
# find the active event
|
||||||
event = Event.objects.filter(
|
event = Event.objects.filter(
|
||||||
is_active=True,
|
is_actual=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# if there is no active event, log error
|
# if there is no active event, log error
|
||||||
if not event:
|
if not event:
|
||||||
logger.error("No active event found")
|
logger.error("No active event found")
|
||||||
return JsonResponse({"error": "No active event found"}, status=http.HTTPStatus.NOT_FOUND, safe=False)
|
return JsonResponse({"status": "ok"}, status=http.HTTPStatus.OK, safe=False)
|
||||||
|
|
||||||
# find registration with the given card_id
|
# find registration with the given card_id
|
||||||
registration = event[0].registrations.filter(
|
registration = event[0].registrations.filter(
|
||||||
card_id=kwargs['card_id'],
|
chip_id=kwargs['chip_id'],
|
||||||
)
|
)
|
||||||
|
|
||||||
if not registration:
|
if not registration:
|
||||||
logger.error(f"No registration found for card_id: {kwargs['card_id']}")
|
logger.error(f"No registration found for chip_id: {kwargs['chip_id']}")
|
||||||
return JsonResponse({"status": "ok"}, status=http.HTTPStatus.OK ,safe=False)
|
return JsonResponse({"status": "ok"}, status=http.HTTPStatus.OK, safe=False)
|
||||||
|
|
||||||
# if there is more than one registration with the same card_id, log error
|
# if there is more than one registration with the same card_id, log error
|
||||||
if len(registration) > 1:
|
if len(registration) > 1:
|
||||||
logger.error(f"Multiple registrations found for card_id: {kwargs['card_id']}")
|
logger.error(f"Multiple registrations found for chip_id: {kwargs['chip_id']}")
|
||||||
|
|
||||||
# create new lap or update existing lap
|
# create new lap or update existing lap
|
||||||
registration[0].laps.filter(end = None).update(end = record_time)
|
laps = Lap.objects.filter(registration=registration[0])
|
||||||
|
open_lap = laps.filter(end=None)
|
||||||
|
if not open_lap:
|
||||||
|
registration[0].laps.create(
|
||||||
|
start=record_time,
|
||||||
|
number=len(laps) + 1,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
open_lap[0].end = record_time
|
||||||
|
open_lap[0].save()
|
||||||
|
|
||||||
return JsonResponse({"status": "ok"}, status=http.HTTPStatus.OK ,safe=False)
|
return JsonResponse({"status": "ok"}, status=http.HTTPStatus.OK ,safe=False)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error while getting prometheus data: {e}")
|
logger.error(f"Error while writing lap: {e}")
|
||||||
return JsonResponse({"error": f"Error while getting prometheus data: {e}"}, status=http.HTTPStatus.INTERNAL_SERVER_ERROR , safe=False)
|
return JsonResponse({"status": "ok"}, status=http.HTTPStatus.OK, safe=False)
|
||||||
Reference in New Issue
Block a user