From 0d5357793e9bd14ba4d99a9f3e25852c22c23dc8 Mon Sep 17 00:00:00 2001 From: Matthias Nagel Date: Wed, 1 Oct 2025 14:35:31 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Implementierung=20der=20'Spiel=20=C3=B6?= =?UTF-8?q?ffnen'-Funktionalit=C3=A4t=20und=20Fehlerbehebungen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- calendars/__pycache__/forms.cpython-313.pyc | Bin 2706 -> 3689 bytes calendars/__pycache__/models.cpython-313.pyc | Bin 4082 -> 4208 bytes calendars/__pycache__/urls.cpython-313.pyc | Bin 1218 -> 1317 bytes calendars/__pycache__/views.cpython-313.pyc | Bin 5545 -> 7814 bytes calendars/forms.py | 9 ++ .../migrations/0003_game_opened_for_teams.py | 19 +++ ...0003_game_opened_for_teams.cpython-313.pyc | Bin 0 -> 885 bytes calendars/models.py | 1 + calendars/templates/calendars/open_game.html | 21 ++++ calendars/urls.py | 1 + calendars/views.py | 49 +++++++- dashboard/__pycache__/views.cpython-313.pyc | Bin 3716 -> 4435 bytes dashboard/templates/dashboard/dashboard.html | 7 ++ dashboard/views.py | 17 ++- db.sqlite3 | Bin 282624 -> 303104 bytes docs/traceback/trace6.log | 71 ++++++++++++ docs/traceback/trace7.log | 108 ++++++++++++++++++ templates/base.html | 3 + 18 files changed, 301 insertions(+), 5 deletions(-) create mode 100644 calendars/migrations/0003_game_opened_for_teams.py create mode 100644 calendars/migrations/__pycache__/0003_game_opened_for_teams.cpython-313.pyc create mode 100644 calendars/templates/calendars/open_game.html create mode 100644 docs/traceback/trace6.log create mode 100644 docs/traceback/trace7.log diff --git a/calendars/__pycache__/forms.cpython-313.pyc b/calendars/__pycache__/forms.cpython-313.pyc index 2ae9b998f643176d313f0e59e455c7fd8e32282b..8af97fd8798c8c1158d1e483e74004508fa43fce 100644 GIT binary patch literal 3689 zcmds3-EUMy6rcOGd%GXq7AdKKT5A`(2rXi31QZOER?COj+trW)x3{~)a^dd1+nKxa z5kLB5N=yVE2t4YGNuSgw6QBGOwk4)y0`bA8CO)W((fH&!b9ZTr^()cDFxfpbbLQMR zAM=|tr=CtH2(<4Wzk6o4NyxW2X*Q!VsNaUcbz%`qIYdSkrgRZYwX{R(sK&HWo#~?n zGk`}d{ZMQ)&f;C9lXMfy*hs9{31zIUx#4m@s6}JeA|>M>Y5s*|LQ1xPWa2L*lTtDX zk|~hvT-Mqg-PfKjrE{%I$j&LkI32JuUs7np(YrGr6i;#ty}b?NY7y zE|taqg?&t`WtYZG8Jh&eCON1^x><{rVhN0dXpv42LmZay@PBR17W=a0zU=X?>?^yo zM__(@d$zniTO5~jp@$bM^w-v+6VlKv)9qeRaJ)HH#4wajC=4eI83#t%^()oTj@Vqp z&b!4D4TtA6wjT7b4j{s0bh_#?T3i*}hLNP+1u_o?ml^#K%GVVZvosbj={ZBR-glwz z2o0SjJe;|hFhV!n_(!00n*fi$hg!ia?xSi zpgnX8HipQZs)m$Ea|`nB?CR^0snVg)Xm7V0Th2V>hn)>JNlCN`T$EiaIlT zJF|W<)4h=CuI=1k8yc@Yo3EWJES{QPI5iC)TvA5yJ4-!|-Gq7$yD`Y;pk06b#N5fZv@!`4Ysb*f2mmT53BuV3_6-fq38TS5rsAmKZ(wQ<$f2PefrS}# z{jBk$O4b$C-zU*EVf6wkLV_XY|Lf#eR5AG#)d=@!^D5k9_QqEK5Xh)waP$CLlZp&s%#~a(~kC97uU06-5(Ns@5Q+&jLd1iqv$POG&2`%KLdv2XIs{7) zG@VoLw9p+^g7tIfv5$6cvF(EAa41PQTG0hg4ubqr)K6p+OlBg?(}Z^G&Pr6?Ad3sg`D-&9!4R`_u650KK|@nIk6>MOr0Ruk#iIw#1a2WJRHXat5X$I2q!6*; zz!_RNHx*p4s0R^ge!@mTgrQsVWfAFy>jer`mJOjk21^`4@NyJ^{V0kALBXI(Q!lsZ8gxrh54g~~ft zJnQ(SAgZ`xvR34>aAXK7hA0pzJbT{PSd^#RBE82H9#vWL5yP7+bjg&qcH<86EyEKO z=8Lcc6s~*O1WeH}ejLd6nxZIoN#{2tH&1eR$z%6)Lzz^r8aFc6GWQ6M@3l(K;EjFP k_APUY>TLDOdzars4y2IQm@=YV9lWvo+U}nSj-~3~0IFu|)&Kwi delta 820 zcma))&r4fD5XX0)?|1T|%@25(v}s~rGBOT~*=dM%yrirSL4e^fSTV=NiuFO#XPNCCc!8sxHandMr>mVVJH30SFa{@n zr<*H7*_hJ9e95K)($wiAcbJy>rlm7fG`tH(tyYZ>D k`bxd#bjhpphH*V2r92hknTU@>Ooh~J;)k$g!_ZCq0>OZXb^rhX diff --git a/calendars/__pycache__/models.cpython-313.pyc b/calendars/__pycache__/models.cpython-313.pyc index 0e5eafd99bb14f1da16cc741f5bebcfd76664355..e1c44e0811bbe64872eccc5dbe9ad9ccbaa07d36 100644 GIT binary patch delta 1036 zcmZ`&&rcIU6z-PYWlQ%*CAAF|XpItCD-t!3YKTf=KtUk3B{9KdS-WGYmR+6Mns~w! zG4VJa^rj(34_=5D<6q!S)B`w)M^9dia?*?QX7OOMHrcOlXWo2ozW2WUQT$#qe;P(l zg8!ELzpNy*P4jV5`t|xn^0y=v=!U$hAK-gL;!wgxx~!bg4)WNR@P7J4nUxOFugYX5 z%a@q8SiL;eLT!(4jjKhqz!&ilT~d$fgFF-*=Q#&eH&o!jDs_!My4XEBbp&3A0c?Py z0Am1W07P;TqRL;$@qj2C))P$DqKpcCmgt))+Q zVZEkfqiNVCUDGO)CMRHea2xr^bDHidB6bq@LX9*46rZ$Kd_x2`IsMh*?u>;cY9NBoakE@m8-cAPlRE@qrl7mX5BJ8T#wlS|$8X)~ zASVo(C|p4}?siO`L!pZuP7U;=o5Olyr%--qMQ!)^LR4kI&cv0$!VM?syji#)TI9kB z72rC6XqHMsH|!8B*y7Axhg5)J`pWd84%KPS8ovl`XUbgQp?1V7z5_84(OGfw-%o{U n6RUyOX`{3F7C#N%AXn)lYt&REBt010O~C)IN#9#V84U3U%tznb delta 912 zcmZ{iL2DCH5XX0u-OVQ1BwMtVgr-?)v`sCPNUbTAs-QtlqqQ166m3fKNCI(#Z+AU- zu!vBEo^(JgUW6c?ix)qG`T^vjM^9cvd-LMVZaoO=9)A0F{`+R;{pbC#eiqWdQmKT5 z&#_Nmy02n;>6a1t;k&ny-?E&Ap|WS>P;+F2#V4StmURo)C7B+6Ud#v9^X+bM!Sq$iHubEYCu_o^)a9|;^bzHBH$<$}9rRkv;mbI(>szxVMTFuxj0=@L zV77a35-sc`BIDV;`yShH2OZ{i84v4DD0Sv;@^ws2>75v2Tl$?VJ5ATK$wE*OgnIB6`p4nE{$SPsP#dc z^@FVbZS*JNJL~WK_rePO%)LR|>$7Fv!QFHdoLgX;bA}=>K0UQB;cMT3x>-;x)cyd9 C*R^i| diff --git a/calendars/__pycache__/urls.cpython-313.pyc b/calendars/__pycache__/urls.cpython-313.pyc index 149e62e5d363c8d951dd0cba9bb8fbb53269d334..c81d853e1f1138c1f7c85510afd52f738ca36890 100644 GIT binary patch delta 201 zcmX@axs*%&GcPX}0}!m1xtsBxiGkrUhyw$>P{!vy8`Y;XYQ-=rF$4?8Fb7G%6d>qe zkzh7UW_^w#-gHq-vCYRARTwp@B+?UeQ}t~!^Gd8htoY0nJN^8E)I9wvP7tLF67`fMq3!KUeLYK!cjNf5>k;VOkASXXl KBX^MqPz?YUS2585 delta 102 zcmZ3=b%;~_GcPX}0}vb*zMHX(iGkrUhyw#WP{!w38`Y;Xa_F-c@uUlDifn$tsKUtX or^!3ngn9AgN6ax?qCka=KwNxevIolno)7#S{7jA9MZ!QK08!@^j{pDw diff --git a/calendars/__pycache__/views.cpython-313.pyc b/calendars/__pycache__/views.cpython-313.pyc index 7bacaaa0dbddefb3b79a606ffcafcbdac42ed690..a9417b50e0d5c2b206ef1262feafd4bbeee51833 100644 GIT binary patch literal 7814 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 sM*1CRhV{UT6Tm&u z(Ut=8#=A2{rZ1-<>GB)Voj`{WdNLO)=wgZhd|IJ|eFO6+O&E-p+fKcs1p; zi+;jos3ta0PIHmtLfRSb5?L>Mgpw3>N`?f%U49_LtCH^X*FnW?>Rx0?Cn+#JmLVer z7K%po8m1^!E2u0QSL~|pbaGOpAHiNUEj?6q5Rw5xYO-Gtg+qj1_Hm)7RW^fACudv~ zIVH5z?UX2WDWUD0aHbDQdfRFGSTD+3)AYa6XWtYTViZfEg&scdxjs=Mm%t?Za0+lSoH0M~9EdFD3eEnI(%FqW$ z`-J|rvDrIe`i!ce0pC{P9T>X6-xzrJhK_a1ee-k}0{`ZPw{Y$)4Cd|(ZmbL%^B0Y^ z^Tyf-@u~UwIX-J7-=}Op7+P+_eY?Z%PS)IuZ0V;*m}X@&O)7ILOU7^2dmPU$DI~o| pW6QVLvP#w81Nfo}L(4FXUoJ4BA8yTX-v<}<)wBBQPoR>D{Tsb3@Y4VQ literal 0 HcmV?d00001 diff --git a/calendars/models.py b/calendars/models.py index 5ec7b39..a19db7f 100644 --- a/calendars/models.py +++ b/calendars/models.py @@ -27,6 +27,7 @@ class Game(Event): meeting_minutes_before_game = models.PositiveIntegerField(default=60) season = models.CharField(max_length=255, blank=True) min_players = models.PositiveIntegerField(default=9) + opened_for_teams = models.ManyToManyField('clubs.Team', related_name='opened_games', blank=True) class GameResult(models.Model): game = models.OneToOneField(Game, on_delete=models.CASCADE, related_name='result') diff --git a/calendars/templates/calendars/open_game.html b/calendars/templates/calendars/open_game.html new file mode 100644 index 0000000..215e075 --- /dev/null +++ b/calendars/templates/calendars/open_game.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+
+

Open Game for Other Teams

+
+
+

Select teams to open the game "{{ game.title }}" for.

+
+ {% csrf_token %} + {{ form.as_p }} + +
+
+
+
+
+{% endblock %} diff --git a/calendars/urls.py b/calendars/urls.py index 873c0cb..f87647a 100644 --- a/calendars/urls.py +++ b/calendars/urls.py @@ -9,4 +9,5 @@ urlpatterns = [ path('event//', views.EventUpdateView.as_view(), name='event-update'), path('event//delete/', views.EventDeleteView.as_view(), name='event-delete'), path('participation////', views.manage_participation, name='manage-participation'), + path('game//open/', views.open_game, name='open-game'), ] diff --git a/calendars/views.py b/calendars/views.py index c6e113f..340192d 100644 --- a/calendars/views.py +++ b/calendars/views.py @@ -4,8 +4,10 @@ from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.contrib.auth.decorators import login_required from django.urls import reverse_lazy 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 django.utils import timezone +import datetime def select_event_type(request): return render(request, 'calendars/select_event_type.html') @@ -87,8 +89,51 @@ def manage_participation(request, child_id, event_id, status): # Handle unauthorized access 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.status = status participation.save() - return redirect('dashboard') \ No newline at end of file + 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}) \ No newline at end of file diff --git a/dashboard/__pycache__/views.cpython-313.pyc b/dashboard/__pycache__/views.cpython-313.pyc index 541f399834a4102562e5a2da1baf668a8b40ef68..d9b5c1830b3499ecc61edd44fa27f854d8ad60f6 100644 GIT binary patch delta 2229 zcma)7O-vg{6rNe*KfL}0Y%mxvU`Wl6AwM992qA5V^G6EaZj{DJ-3{J=slBGN>!yhk z=%EMH9tzz`k$R~jwTgO6n@CNhs?90taW@>+vOtN{O09CJLWvah)*1isqoTAU?acFg zZ{EE3y>G@J2Uc1g8+Lm&z~g>!ZHA$j9rf_7&a?V&43FA*5n8M#0w8( zn>L3n%kr^M7#1vYDkEIWioz5uS&Oq3&j$Q$(B|(RAA;~}#YG*YM61FW_d6}DaR`dl z=f3PQ0}yLe=AeHe7TC7CEV3rn+)tH$#DcrnEv$9Z-m;6my1$&0Xya@(APSGS0wB^H z9mDI6p?n3IQ}%d!MakOyfV0A}%JHw2b-Wh<9KJ)jZ0Mlhge)aG zdx7ZUXk`@EDh~~{iUwu^TfK#QMN5u8&UFxfV&2JxUuhk z>|4v##P%wq#=5qB+g!^cOIMN->o`}eAN$oVaCNLbLRG3@z&P%u4KO6vfq#dH4_0)s zgB59OZQVqr2lFO2yk)6KqL-s$6}AxNWZA;eoP%?6F3!t3BF3$BqHr8TPZHV&FJzsS zN-FS1A#cI}!`O(j1S1X%#~&CDABMP|^L=g%z@ISB`k{}YZDL)RWetRJC54N1V}+k< z;QU*}JN=);do{5dxkj7?#!IB*qso0_VBw7l3R^4Z!(8zUQ8`Rzu}KVY^*hkn0;h?s z+0iA8ez8l2UHyGrJzKlO{}})miW?}-@PC`g)(E7$6iyfkwu%cErPrWBcxGwE;}BKNsqp5Y~4mJzB}+}8cjri@wJt(Ig;l2hW8 ztfZ_bp+mW6ZR;i}7Yx&aT%c0Pf{CBQv=rW!J2?-I-7ppEf=6EEh zrc!gfoXUz4B2OV=09XKskh4G&@DrsIB~(vT?j2ha8Ga6t2NsbM5qbR3o}yc^59RX{ zX$(ohT~ck&%TrQq&#pjY-Mi^zc`NE5(acjw;AJ6+g{&wGAIcKRlzH_)4P>izvvr8P z75IMvNF4?xV4Gj~-RxTRHr~uEXLN6;=Iy+5LiKj4-jPMys;}whjpZAVofBAxdHG*7qg9?;waEABzv9o5{?Urg%6`4x9ucc(RXTFqR4j`4#RsSN-;?@%xC zJWZ_^XKa|EC>3LdxJj=e?NS8L}QNV!KfCDs&vy+x?QKc zHM%?B(424W%ZCo+!y|ZJx0xc2m%wCqt_Oj)=|vx~I2Yxm^SYUNtURGx7ee9N{SmO z)0ks=a6$`CtOR3vkk^8|dTB@P?oJLC4{IW9O>V_u2JNu75heZmx2gK8n7m2DE2skJ{LO=gNxhuv&Ha z@4uoDw48wweulxLAeKyL!nE?p{$8p;CuezaIvdI1doGo34C&>!03VMDSM0N8IFn80 x(!vOO8|xtcB*OFO1_a_p> delta 1577 zcmZ`(OKclO7@pnrYuD??I*#o)an^aZjw~n0b%YBv0iiTiX_a~zgql!miMO$ZvvFrP zg;q+_Ly>w+GzZiZS3n2_xPXK}LPFwz(3%`<8KNQ~ap07Ys0dEX_=TZmr1|%s|2^M7 z|L%|8i+PVd9s!VbC2~-72)o`0v$%UI8(iTOdlY1-hAhOdITFs@y9<$3rX^`oo=}eL z<4^1#|MnciEa21Z*=TOC$E3|FcEyol@t$Llb1E)eXJZ3=rX5P>A>RiUPXN#bDCFXJ zhMUg2m&ZC$6d?+ryRTbJ*H2ug$(subiao=&_|t8f8UldC8*d+bKDkN!B^TyB(?ItU z3mg7`o5Ua5ha$RAs4hke=doaY_V;LhWMarFU$O2 z|7q95|8zYBLnPUng=w5mxOVK#hXGhc{@^2jpFMY zDk@BOxm3|yFDHi42(fQCi>jfTCPF;^K}a(KcDN^S#(jYi@wR&`O!GFlHO*|Wr9#7@ zRw|laKym!NJGLEdIFWW^Q!~tFC^SKA(Ij;cD`oYThVrJSt{aGU6HQS<$G^d=<#Jm? z2c}te!&_02Sz0YsRI{|98z@FK!<5L>N2HPRt*8bf(*_z6ep?J&q(1;ei$rvY8&VUS zymnL5O(S0}8D>N56`@gTq|iID-B?YUs7e#>-Lj+!OUl-y#j3RUZOFQ^yf3ZPr1h$_Zs}Wx0L-)3 z*kb?!dG-%Ni|kR85=Du;HLAGD-go?h&wIa^@l4jlWK~So2ZrmTGxhN^^~AX&-k$P4 z0(Ot@Xb^;kPNsm%ci-H(T64y$Sn-X{%H!{DJ=m(r>8hOmtpBU%=TS>eTk?xFS*gm3 z_3~9~<@&z7R+B4LxnkYe+L!O_h(F?AeekRggc65AFgaHroI0{|KF8hjCoUicc1~G? znY}mn-E)?0?$6(|VB|7mP!ztI9=RBFTuumh+y92r1m}G~?m+Nw0_S>meWS2h*3O|z rL`MI*27MKW9K$dNVC(>7AA;Ef5c`?&9kFRu<5>C1UwEg_b@h diff --git a/dashboard/templates/dashboard/dashboard.html b/dashboard/templates/dashboard/dashboard.html index e78447a..9f89bf6 100644 --- a/dashboard/templates/dashboard/dashboard.html +++ b/dashboard/templates/dashboard/dashboard.html @@ -18,6 +18,9 @@
Edit Delete + {% 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 %} + Open Game + {% endif %} +
Player Participation ({{ item.accepted_count }}/{{ item.required_players }})
diff --git a/dashboard/views.py b/dashboard/views.py index 29beb00..2ed8004 100644 --- a/dashboard/views.py +++ b/dashboard/views.py @@ -2,6 +2,7 @@ from django.shortcuts import render from django.contrib.auth.decorators import login_required from calendars.models import Event, EventParticipation from clubs.models import Team +from django.utils import timezone @login_required def dashboard(request): @@ -17,8 +18,11 @@ def dashboard(request): assisted_teams = user.assisted_teams.all() from itertools import chain all_teams = list(set(chain(player_teams, coached_teams, assisted_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: participations = event.eventparticipation_set.all() @@ -33,11 +37,14 @@ def dashboard(request): status = participation_map.get(player.id, 'maybe') player_participations.append({'player': player, 'status': status}) + days_until_event = (event.start_time - timezone.now()).days + events_with_participation.append({ 'event': event, 'accepted_count': accepted_count, 'required_players': required_players, - 'player_participations': player_participations + 'player_participations': player_participations, + 'days_until_event': days_until_event }) # Get children's events @@ -45,7 +52,10 @@ def dashboard(request): for child in user.children.all(): child_events_list = [] 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: participation, created = EventParticipation.objects.get_or_create(user=child, event=event) child_events_list.append({'event': event, 'participation': participation}) @@ -54,5 +64,6 @@ def dashboard(request): context = { 'events_with_participation': events_with_participation, 'children_events': children_events, + 'now': timezone.now() } return render(request, 'dashboard/dashboard.html', context) \ No newline at end of file diff --git a/db.sqlite3 b/db.sqlite3 index 6630a754a924f3eb087ad616107c80a9bc1716a1..94aeee36883dc4750109f2cc23fe5a0f1eb559d0 100644 GIT binary patch delta 1211 zcmZozAlPs~Xo9q0Ap--07ZAgM|3n>Q$wCIbszO$v5DWhskc21yZ~kxmANk*GRxBvz zm#}gJim6)y2{A1oAu7%bB&9gZCbJ6|GjWz}b`-GUba9emWsrAtOis*6%}YrvDmE}M zFpf`8%uS8YFG$TxO^Hv-FN!ZoP0TGeGB7gLH8jvQFjO!!vNE)^GBwpRHL^4|Fy6dN z(u<9mpD$=KyMineU(jYl1sy(3C4OdaPPn<)%~arHc4eG?aWb>a=IMG?jEqvvm-M$^ z(q}YMU>0DMn9d%+m^?W^U|qXJAmet4K&H2Rtc*(;7?)1xNMMrT;CaT-$1A}r0rW^Y z=lbF}(Y688=%j(6z+Rp05Z^QSU_bP7>&tLA#+!~xqIX1DsVb^3{ z$#kAEnIRphTY+^t|7>R4?Hd!A7BOzOn8bXYW7z`c4ci1*{_!)hFmF?U(hkfl0wNsj z3mEu1coTRUxR-HRaoTa@vo8Re(ZL?y=;OyOp6kunn46iGl3GE)>)=3*&rFH8NH#M! zHa9RQpx$fx#7WE&^;n|@!z?8Qe?J8!0wyXcXefbV7U&2i%|;(zVjOP)O$?Ce1iC)W z$kM>nC@qcDaOXq)AD?2LWMpn> zXc}Ldmsv=_FYeP7Q<=o3+fQNU#1S1Z4`DT(WZ&s1Afl-$)0azMk`Tq3rlvbYF`BY~q^572&a60n!Bl3h>0c9=M5jNS$t=$fbreMIKr)lj VcKNx?51Fh|D-gqg_e33I@jM2-szO$v5HtTrkc1WgZ~kxmA2%x&)bR_e zTLDGHw1I@EI4_WtvM?+b`)e8YwUfGGt9>4`585zBG|(UAsge<93Narnh{|3>z7yb0sjz za4~G;O=RffmEe__>?qK@y?rY4BF6UMB&O}bNzChX)wz8b1Q-r5aPxC{bFpze;h4?A z!}f^HoMj91L8jkK77PdYZt(u#l>_RXvAv^@rGjz0=5*%k9Mip~GTX8+Ffd-)>?m-K lk*hJ>i(S0jnX!p+`o~~K$>|^ZSlG8;=w{i$v}^(M1^|MSPxk-- diff --git a/docs/traceback/trace6.log b/docs/traceback/trace6.log new file mode 100644 index 0000000..66322c7 --- /dev/null +++ b/docs/traceback/trace6.log @@ -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 "", line 1387, in _gcd_import + File "", line 1360, in _find_and_load + File "", line 1331, in _find_and_load_unlocked + File "", line 935, in _load_unlocked + File "", line 1026, in exec_module + File "", line 488, in _call_with_frames_removed + File "/home/mnagel/Projekte/baseball_organisator/baseball_organisator/urls.py", line 25, in + 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 "", line 1387, in _gcd_import + File "", line 1360, in _find_and_load + File "", line 1331, in _find_and_load_unlocked + File "", line 935, in _load_unlocked + File "", line 1026, in exec_module + File "", line 488, in _call_with_frames_removed + File "/home/mnagel/Projekte/baseball_organisator/calendars/urls.py", line 2, in + from . import views + File "/home/mnagel/Projekte/baseball_organisator/calendars/views.py", line 7, in + from .forms import EventForm, TrainingForm, GameForm + File "/home/mnagel/Projekte/baseball_organisator/calendars/forms.py", line 1, in + class OpenGameForm(forms.Form): + ^^^^^ +NameError: name 'forms' is not defined. Did you mean: 'format'? + diff --git a/docs/traceback/trace7.log b/docs/traceback/trace7.log new file mode 100644 index 0000000..db95b21 --- /dev/null +++ b/docs/traceback/trace7.log @@ -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 + diff --git a/templates/base.html b/templates/base.html index 79edd80..140cc5d 100644 --- a/templates/base.html +++ b/templates/base.html @@ -14,6 +14,9 @@ .event-generic { border-left: 5px solid #6c757d; /* gray */ } + .support-game { + border: 2px solid #ffc107; /* yellow */ + }