feat: Implementierung der 'Spiel öffnen'-Funktionalität und Fehlerbehebungen

This commit is contained in:
Matthias Nagel 2025-10-01 14:35:31 +02:00
parent 84a37206fd
commit 0d5357793e
18 changed files with 301 additions and 5 deletions

View File

@ -1,5 +1,6 @@
from django import forms from django import forms
from .models import Event, Training, Game from .models import Event, Training, Game
from clubs.models import Team
class EventForm(forms.ModelForm): class EventForm(forms.ModelForm):
start_time = forms.DateTimeField(input_formats=['%d.%m.%Y %H:%M', '%Y-%m-%dT%H:%M'], widget=forms.DateTimeInput(format='%d.%m.%Y %H:%M', attrs={'type': 'datetime-local'})) start_time = forms.DateTimeField(input_formats=['%d.%m.%Y %H:%M', '%Y-%m-%dT%H:%M'], widget=forms.DateTimeInput(format='%d.%m.%Y %H:%M', attrs={'type': 'datetime-local'}))
@ -21,3 +22,11 @@ class GameForm(forms.ModelForm):
class Meta: class Meta:
model = Game model = Game
fields = ['title', 'description', 'start_time', 'end_time', 'location_address', 'team', 'opponent', 'meeting_minutes_before_game', 'season', 'min_players'] fields = ['title', 'description', 'start_time', 'end_time', 'location_address', 'team', 'opponent', 'meeting_minutes_before_game', 'season', 'min_players']
class OpenGameForm(forms.Form):
teams = forms.ModelMultipleChoiceField(queryset=Team.objects.none(), widget=forms.CheckboxSelectMultiple)
def __init__(self, *args, **kwargs):
club = kwargs.pop('club')
super().__init__(*args, **kwargs)
self.fields['teams'].queryset = Team.objects.filter(club=club)

View File

@ -0,0 +1,19 @@
# Generated by Django 5.2.6 on 2025-10-01 12:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('calendars', '0002_eventparticipation'),
('clubs', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='game',
name='opened_for_teams',
field=models.ManyToManyField(blank=True, related_name='opened_games', to='clubs.team'),
),
]

View File

@ -27,6 +27,7 @@ class Game(Event):
meeting_minutes_before_game = models.PositiveIntegerField(default=60) meeting_minutes_before_game = models.PositiveIntegerField(default=60)
season = models.CharField(max_length=255, blank=True) season = models.CharField(max_length=255, blank=True)
min_players = models.PositiveIntegerField(default=9) min_players = models.PositiveIntegerField(default=9)
opened_for_teams = models.ManyToManyField('clubs.Team', related_name='opened_games', blank=True)
class GameResult(models.Model): class GameResult(models.Model):
game = models.OneToOneField(Game, on_delete=models.CASCADE, related_name='result') game = models.OneToOneField(Game, on_delete=models.CASCADE, related_name='result')

View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h2>Open Game for Other Teams</h2>
</div>
<div class="card-body">
<p>Select teams to open the game "{{ game.title }}" for.</p>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Open Game</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -9,4 +9,5 @@ urlpatterns = [
path('event/<int:pk>/', views.EventUpdateView.as_view(), name='event-update'), path('event/<int:pk>/', views.EventUpdateView.as_view(), name='event-update'),
path('event/<int:pk>/delete/', views.EventDeleteView.as_view(), name='event-delete'), path('event/<int:pk>/delete/', views.EventDeleteView.as_view(), name='event-delete'),
path('participation/<int:child_id>/<int:event_id>/<str:status>/', views.manage_participation, name='manage-participation'), path('participation/<int:child_id>/<int:event_id>/<str:status>/', views.manage_participation, name='manage-participation'),
path('game/<int:game_id>/open/', views.open_game, name='open-game'),
] ]

View File

@ -4,8 +4,10 @@ from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.urls import reverse_lazy from django.urls import reverse_lazy
from .models import Event, Training, Game, EventParticipation from .models import Event, Training, Game, EventParticipation
from .forms import EventForm, TrainingForm, GameForm from .forms import EventForm, TrainingForm, GameForm, OpenGameForm
from accounts.models import CustomUser # Import CustomUser for manage_participation view from accounts.models import CustomUser # Import CustomUser for manage_participation view
from django.utils import timezone
import datetime
def select_event_type(request): def select_event_type(request):
return render(request, 'calendars/select_event_type.html') return render(request, 'calendars/select_event_type.html')
@ -87,8 +89,51 @@ def manage_participation(request, child_id, event_id, status):
# Handle unauthorized access # Handle unauthorized access
return redirect('dashboard') return redirect('dashboard')
# Check for parallel events if accepting a support game
if status == 'attending' and hasattr(event, 'game') and child.team in event.game.opened_for_teams.all():
# A game's duration is defined as innings * 20 minutes + 1 hour travel.
# I will assume 9 innings for now.
game_duration = datetime.timedelta(minutes=(9 * 20 + 60))
event_start = event.start_time
event_end = event.start_time + game_duration
parallel_events = Event.objects.filter(
eventparticipation__user=child,
eventparticipation__status='attending'
).exclude(id=event.id)
for pe in parallel_events:
pe_duration = datetime.timedelta(minutes=(9 * 20 + 60)) # Assuming 9 innings for all games
pe_start = pe.start_time
pe_end = pe.start_time + pe_duration
# Check for overlap with a tolerance of +/- 2 hours
if (event_start < pe_end + datetime.timedelta(hours=2) and event_end > pe_start - datetime.timedelta(hours=2)):
# Handle parallel event conflict
return redirect('dashboard') # Or show an error message
participation, created = EventParticipation.objects.get_or_create(user=child, event=event) participation, created = EventParticipation.objects.get_or_create(user=child, event=event)
participation.status = status participation.status = status
participation.save() participation.save()
return redirect('dashboard') return redirect('dashboard')
@login_required
def open_game(request, game_id):
game = get_object_or_404(Game, id=game_id)
club = game.team.club
# Permission check: only head coach of the team's club can open the game
if not request.user.is_superuser and request.user not in club.administrators.all():
return redirect('dashboard')
if request.method == 'POST':
form = OpenGameForm(request.POST, club=club)
if form.is_valid():
teams = form.cleaned_data['teams']
game.opened_for_teams.add(*teams)
return redirect('dashboard')
else:
form = OpenGameForm(club=club)
return render(request, 'calendars/open_game.html', {'form': form, 'game': game})

View File

@ -18,6 +18,9 @@
<div class="list-group-item list-group-item-action flex-column align-items-start <div class="list-group-item list-group-item-action flex-column align-items-start
{% if item.event.game %} {% if item.event.game %}
event-game event-game
{% if user.team in item.event.game.opened_for_teams.all %}
support-game
{% endif %}
{% elif item.event.training %} {% elif item.event.training %}
event-training event-training
{% else %} {% else %}
@ -38,6 +41,10 @@
<a href="{% url 'event-update' item.event.pk %}" class="btn btn-warning btn-sm">Edit</a> <a href="{% url 'event-update' item.event.pk %}" class="btn btn-warning btn-sm">Edit</a>
<a href="{% url 'event-delete' item.event.pk %}" class="btn btn-danger btn-sm">Delete</a> <a href="{% url 'event-delete' item.event.pk %}" class="btn btn-danger btn-sm">Delete</a>
{% if item.event.game and item.days_until_event >= 0 and item.days_until_event < 7 and item.accepted_count < item.required_players and item.event.team.club.teams.count > 1 %}
<a href="{% url 'open-game' item.event.game.id %}" class="btn btn-info btn-sm">Open Game</a>
{% endif %}
<div class="mt-3"> <div class="mt-3">
<h6>Player Participation ({{ item.accepted_count }}/{{ item.required_players }})</h6> <h6>Player Participation ({{ item.accepted_count }}/{{ item.required_players }})</h6>
<div style="column-count: 3;"> <div style="column-count: 3;">

View File

@ -2,6 +2,7 @@ from django.shortcuts import render
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from calendars.models import Event, EventParticipation from calendars.models import Event, EventParticipation
from clubs.models import Team from clubs.models import Team
from django.utils import timezone
@login_required @login_required
def dashboard(request): def dashboard(request):
@ -17,8 +18,11 @@ def dashboard(request):
assisted_teams = user.assisted_teams.all() assisted_teams = user.assisted_teams.all()
from itertools import chain from itertools import chain
all_teams = list(set(chain(player_teams, coached_teams, assisted_teams))) all_teams = list(set(chain(player_teams, coached_teams, assisted_teams)))
if all_teams: if all_teams:
events = Event.objects.filter(team__in=all_teams).select_related('game', 'training').prefetch_related('team__players', 'eventparticipation_set__user').order_by('start_time') user_events = Event.objects.filter(team__in=all_teams)
opened_games = Event.objects.filter(game__opened_for_teams__in=all_teams)
events = (user_events | opened_games).distinct().select_related('game', 'training').prefetch_related('team__players', 'eventparticipation_set__user').order_by('start_time')
for event in events: for event in events:
participations = event.eventparticipation_set.all() participations = event.eventparticipation_set.all()
@ -33,11 +37,14 @@ def dashboard(request):
status = participation_map.get(player.id, 'maybe') status = participation_map.get(player.id, 'maybe')
player_participations.append({'player': player, 'status': status}) player_participations.append({'player': player, 'status': status})
days_until_event = (event.start_time - timezone.now()).days
events_with_participation.append({ events_with_participation.append({
'event': event, 'event': event,
'accepted_count': accepted_count, 'accepted_count': accepted_count,
'required_players': required_players, 'required_players': required_players,
'player_participations': player_participations 'player_participations': player_participations,
'days_until_event': days_until_event
}) })
# Get children's events # Get children's events
@ -45,7 +52,10 @@ def dashboard(request):
for child in user.children.all(): for child in user.children.all():
child_events_list = [] child_events_list = []
if child.team: if child.team:
child_events = Event.objects.filter(team=child.team).select_related('game', 'training').order_by('start_time') child_user_events = Event.objects.filter(team=child.team)
child_opened_games = Event.objects.filter(game__opened_for_teams=child.team)
child_events = (child_user_events | child_opened_games).distinct().select_related('game', 'training').order_by('start_time')
for event in child_events: for event in child_events:
participation, created = EventParticipation.objects.get_or_create(user=child, event=event) participation, created = EventParticipation.objects.get_or_create(user=child, event=event)
child_events_list.append({'event': event, 'participation': participation}) child_events_list.append({'event': event, 'participation': participation})
@ -54,5 +64,6 @@ def dashboard(request):
context = { context = {
'events_with_participation': events_with_participation, 'events_with_participation': events_with_participation,
'children_events': children_events, 'children_events': children_events,
'now': timezone.now()
} }
return render(request, 'dashboard/dashboard.html', context) return render(request, 'dashboard/dashboard.html', context)

Binary file not shown.

71
docs/traceback/trace6.log Normal file
View File

@ -0,0 +1,71 @@
Traceback (most recent call last):
File "/usr/lib64/python3.13/threading.py", line 1043, in _bootstrap_inner
self.run()
~~~~~~~~^^
File "/usr/lib64/python3.13/threading.py", line 994, in run
self._target(*self._args, **self._kwargs)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/utils/autoreload.py", line 64, in wrapper
fn(*args, **kwargs)
~~^^^^^^^^^^^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/management/commands/runserver.py", line 134, in inner_run
self.check(**check_kwargs)
~~~~~~~~~~^^^^^^^^^^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/management/base.py", line 492, in check
all_issues = checks.run_checks(
app_configs=app_configs,
...<2 lines>...
databases=databases,
)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/checks/registry.py", line 89, in run_checks
new_errors = check(app_configs=app_configs, databases=databases)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/checks/urls.py", line 16, in check_url_config
return check_resolver(resolver)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/checks/urls.py", line 26, in check_resolver
return check_method()
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/urls/resolvers.py", line 531, in check
for pattern in self.url_patterns:
^^^^^^^^^^^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/utils/functional.py", line 47, in __get__
res = instance.__dict__[self.name] = self.func(instance)
~~~~~~~~~^^^^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/urls/resolvers.py", line 718, in url_patterns
patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
^^^^^^^^^^^^^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/utils/functional.py", line 47, in __get__
res = instance.__dict__[self.name] = self.func(instance)
~~~~~~~~~^^^^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/urls/resolvers.py", line 711, in urlconf_module
return import_module(self.urlconf_name)
File "/usr/lib64/python3.13/importlib/__init__.py", line 88, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 1026, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "/home/mnagel/Projekte/baseball_organisator/baseball_organisator/urls.py", line 25, in <module>
path('calendars/', include('calendars.urls')),
~~~~~~~^^^^^^^^^^^^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/urls/conf.py", line 39, in include
urlconf_module = import_module(urlconf_module)
File "/usr/lib64/python3.13/importlib/__init__.py", line 88, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 1026, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "/home/mnagel/Projekte/baseball_organisator/calendars/urls.py", line 2, in <module>
from . import views
File "/home/mnagel/Projekte/baseball_organisator/calendars/views.py", line 7, in <module>
from .forms import EventForm, TrainingForm, GameForm
File "/home/mnagel/Projekte/baseball_organisator/calendars/forms.py", line 1, in <module>
class OpenGameForm(forms.Form):
^^^^^
NameError: name 'forms' is not defined. Did you mean: 'format'?

108
docs/traceback/trace7.log Normal file
View File

@ -0,0 +1,108 @@
Internal Server Error: /
Traceback (most recent call last):
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/smartif.py", line 180, in translate_token
op = OPERATORS[token]
~~~~~~~~~^^^^^^^
KeyError: '(item.event.start_time'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/contrib/auth/decorators.py", line 59, in _view_wrapper
return view_func(request, *args, **kwargs)
File "/home/mnagel/Projekte/baseball_organisator/dashboard/views.py", line 66, in dashboard
return render(request, 'dashboard/dashboard.html', context)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/shortcuts.py", line 25, in render
content = loader.render_to_string(template_name, context, request, using=using)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/loader.py", line 61, in render_to_string
template = get_template(template_name, using=using)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/loader.py", line 15, in get_template
return engine.get_template(template_name)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/backends/django.py", line 79, in get_template
return Template(self.engine.get_template(template_name), self)
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/engine.py", line 177, in get_template
template, origin = self.find_template(template_name)
~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/engine.py", line 159, in find_template
template = loader.get_template(name, skip=skip)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/loaders/cached.py", line 57, in get_template
template = super().get_template(template_name, skip)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/loaders/base.py", line 28, in get_template
return Template(
contents,
...<2 lines>...
self.engine,
)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 154, in __init__
self.nodelist = self.compile_nodelist()
~~~~~~~~~~~~~~~~~~~~~^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 196, in compile_nodelist
nodelist = parser.parse()
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 518, in parse
raise self.error(token, e)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 516, in parse
compiled_result = compile_func(self, token)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/loader_tags.py", line 299, in do_extends
nodelist = parser.parse()
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 518, in parse
raise self.error(token, e)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 516, in parse
compiled_result = compile_func(self, token)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/loader_tags.py", line 234, in do_block
nodelist = parser.parse(("endblock",))
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 518, in parse
raise self.error(token, e)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 516, in parse
compiled_result = compile_func(self, token)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/defaulttags.py", line 962, in do_if
nodelist = parser.parse(("elif", "else", "endif"))
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 518, in parse
raise self.error(token, e)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 516, in parse
compiled_result = compile_func(self, token)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/defaulttags.py", line 862, in do_for
nodelist_loop = parser.parse(
(
...<2 lines>...
)
)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 518, in parse
raise self.error(token, e)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 516, in parse
compiled_result = compile_func(self, token)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/defaulttags.py", line 962, in do_if
nodelist = parser.parse(("elif", "else", "endif"))
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 518, in parse
raise self.error(token, e)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 516, in parse
compiled_result = compile_func(self, token)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/defaulttags.py", line 961, in do_if
condition = TemplateIfParser(parser, bits).parse()
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/defaulttags.py", line 894, in __init__
super().__init__(*args, **kwargs)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/smartif.py", line 171, in __init__
mapped_tokens.append(self.translate_token(token))
~~~~~~~~~~~~~~~~~~~~^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/smartif.py", line 182, in translate_token
return self.create_var(token)
~~~~~~~~~~~~~~~^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/defaulttags.py", line 897, in create_var
return TemplateLiteral(self.template_parser.compile_filter(value), value)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 609, in compile_filter
return FilterExpression(token, self)
File "/home/mnagel/Projekte/baseball_organisator/venv/lib64/python3.13/site-packages/django/template/base.py", line 710, in __init__
raise TemplateSyntaxError(
...<2 lines>...
)
django.template.exceptions.TemplateSyntaxError: Could not parse the remainder: '(item.event.start_time' from '(item.event.start_time'
[01/Oct/2025 12:32:40] "GET / HTTP/1.1" 500 399373

View File

@ -14,6 +14,9 @@
.event-generic { .event-generic {
border-left: 5px solid #6c757d; /* gray */ border-left: 5px solid #6c757d; /* gray */
} }
.support-game {
border: 2px solid #ffc107; /* yellow */
}
</style> </style>
</head> </head>
<body> <body>