From 4e7ecf0ab9e610531edf348bdbe3c83bd4a3d84d Mon Sep 17 00:00:00 2001 From: cedrick2711 Date: Tue, 14 Jul 2026 14:50:48 -0400 Subject: [PATCH] ajustements des calendriers, ajout de matchs jouer vs joueur --- .gitgnore | 1 + README.md | 1 + __pycache__/models.cpython-313.pyc | Bin 17396 -> 17586 bytes instance/team_tryouts.db | Bin 69632 -> 69632 bytes models.py | 2 + routes/__pycache__/matches.cpython-313.pyc | Bin 19632 -> 22820 bytes routes/__pycache__/tryouts.cpython-313.pyc | Bin 20563 -> 22509 bytes routes/matches.py | 107 ++++++++++++++++----- routes/tryouts.py | 30 +++++- static/css/style.css | 41 ++++++++ templates/pages/create_match.html | 69 +++++++++++-- templates/pages/edit_match.html | 83 +++++++++++++++- templates/pages/view_tryout.html | 45 +++++++-- 13 files changed, 338 insertions(+), 41 deletions(-) create mode 100644 .gitgnore create mode 100644 README.md diff --git a/.gitgnore b/.gitgnore new file mode 100644 index 0000000..4f509e5 --- /dev/null +++ b/.gitgnore @@ -0,0 +1 @@ +*.env \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e143bd6 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +### Plateforme centralisée de tryouts \ No newline at end of file diff --git a/__pycache__/models.cpython-313.pyc b/__pycache__/models.cpython-313.pyc index 2a51998a0eca546670abfe2f16b69253415aba59..8822d0cc02d3fe2466000eeb069dbe9c8131e28d 100644 GIT binary patch delta 206 zcmey;&bX0CX+c$ttZDiZDD*hS;+akFc+ihgreyc6Dz(l0GUNLK>Yxi CJU}l1 delta 168 zcmdng$@rz6k?%7vFBbz4?9L0zI<2{pFTkF$U~+-7=;SVm(8>H3I+MTJw}?xDRG@+w zz96~Didy2#0x@ip8}vmcznA0TDiR9jDH2ZS)#RJ(<5(al2h>u;4OWCPR&0LslO AuK)l5 diff --git a/instance/team_tryouts.db b/instance/team_tryouts.db index 926066b2c81976ed476fb4cf692ee0ecc251f0c4..9723cc83854f395b25bc7fcdaf5af3f32ffb9392 100644 GIT binary patch delta 608 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 delta 4321 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 diff --git a/routes/__pycache__/tryouts.cpython-313.pyc b/routes/__pycache__/tryouts.cpython-313.pyc index ecbf1a6c0723569ed3cf9d6abfe289115e033f80..b0599702d292934e1cab5b80e5a1371a6cf5e373 100644 GIT binary patch delta 2425 zcmbVNYiv_h96#swv8}i5y54qcZ`W-dYiYanvEiT{n_;{*H^xYti^HOAg(8KmY_|xm zAoZDmBnloM!x+aW!^424M*T*Xn21J0FPq-kEPjy~F)AIH;Pi{rqAC3V+TN?nOx7KjYPhO| zHdIZb$rG@nR)bE_F$6&L2mm1`n)jGDxQfoD7;=}ic36}GttHFdHRLSG0VR5_!K>-$ z#iWa=fi^VOtCJIBJB5C1Z$zUOSq;O{UC@3faVG^p1K^m17L7L6qscl(9;^r|{P1L6 zrwpkV=~p6Q1A|^&)+Et|+@xt7%ATBOluN4lN-=u92PCRFJp+2E-p2q))NnO{4s=q@ zpnXj;MGuTx0l=nCBzR0^csK<)s$xv{mkQop2Vqc&{cD!Qu0j7CTjbq(mlv)j8EBQY zj?pA34@goB=(XX-G@K@|p7`KOH1B25RAaSK<)*e!19(6*o=lQTXa=E&bXe@5$Xe*& zzpyU5Cu>mcmL?26aL|bP3js6y0UVVQJX9()uREePhUygx2#VLm#D4ln(nuDB_lX2x7%~70+tKzhrj?;71 zp}tZ~U=#YR!YrxrtC7KGc4>CuQ!1X+qEvBj$zjNJ*uI8op48)Uao~{VWcy>$k=EkU zmQxk9@aS-KJjN%BYTKf!DcZval6Oqk$=m6GxeAgNPR4h}uH!cfq^On#hm_w(%8X*j zX_wNn;Y4&KmMj`eaLQh5Nj zPyy;+@}~bY@qC4>R{g@Jyb`DxQDqiX=B#DKv}(ny|9;WmFZlaKKPULPY$Te+=zmo7 z?-KmGX8gOSYvWh_yWj1e96B{5nmsdSkKo^(K01)CjsL&gLh5(zYbSJ*##6@AT{Fhk z%d&6&qR-k^;7_6HW1I47fV=ed3BEqjw^{IQoW12+Q_Ec#FKfHO&?;rt?;7e}N9n^b-R6Yvx0M%zmI4J>{^?)4)OjX6!3{uQ?+^l5DPff(Nc z#E9HIjp*=dMdp=NFH-PLw5(%JehRz!NhDkCq2I>huO#}T#pul8)qOG?`N^86=t5&8upb`zLDmo^&E&)wB@u@_=Hg(}zD=rdR>$3JYzj|&aq+V0G;wHG9q&A0J9 zJcq7rY?7VFLCqOe&sW5Jt#1&%hVJYC5S~Ck_BYEeVCU*g?WQZRp1id@*&=_M0O^z` n1$aWhUqFh@X7~ZJZ~nGA1flnq1W+0Rw!D<+`BcU-*arUqhJ}{C delta 1055 zcmY+CZD?Cn7{|}KH|dhxBujcx!+d9hlYC;Jw}=6juV)(V4{d z-d5)erTU>*x)CN=@`$|@EjwW9sYWPA zMVIc@J%_0lhtshLO~6;NLwxPnLD+a9!qTC}INtUDF?MiQYbyGKVv3X`LljB&G9e|| zkl}-XZD%VAKT5BRBR|pYE6B+B< zZC2lFP{Uz34VNJpoZ&NqMy(+l(pdklLmPmmBNE(myM2Mfz8JB%CN@hvTu&%mJZL{m z{7U^5=gdUD`1H<_f#bTS#&vWs1cvu1%g%rmgq+wD2W!tb%?_!G6&Dn0H=a`j)-Xe}|(V z!?nS?HD}TP36!5p37@m@KAYBMf^URB^cGtg8g?>gK09BtgwE6Odv;Ek$8ZpF2Bwax z!cI}tXW`4EZJsY!SY^}t87s4kQg+Mm*F3xE*2pDVhPfAG!ZOQ?>_4==+pRWadvX?< z^4U7Lp!LutSk-={3$UOca9(8g7W)VN2JORdVdCRVe2s~1n^@6YMxfCQBO-_>L&fz> k@y*v}3lsh3CHTqsO&Ovzy~PtQfM}~aP*;cTtD_nEFJm?nv;Y7A diff --git a/routes/matches.py b/routes/matches.py index 480f9d7..e66ce79 100644 --- a/routes/matches.py +++ b/routes/matches.py @@ -1,7 +1,7 @@ from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify from flask_login import login_required, current_user from extensions import db -from models import User, Tryout, Match, MatchParticipant, Team, TeamMember, OrgTeam +from models import User, Tryout, Match, MatchParticipant, Team, TeamMember, OrgTeam, TryoutRegistration from datetime import datetime, time matches_bp = Blueprint('matches', __name__, url_prefix='/matches') @@ -97,30 +97,52 @@ def api_events_for_tryout(tryout_id): tryout = Tryout.query.get_or_404(tryout_id) # Check if user can view this tryout - if not current_user.can_manage_this_tryout(tryout): - # For players, check if they're registered or participating in the match - if current_user.role == 'player': - # Check if player is registered for this tryout - is_registered = TryoutRegistration.query.filter_by( - tryout_id=tryout_id, player_id=current_user.id - ).first() is not None - - # Check if player is participating in any matches for this tryout - match_participation = MatchParticipant.query.filter( - MatchParticipant.match_id == MatchParticipant.match_id - ).join(Match).filter(Match.tryout_id == tryout_id).all() - - player_in_match = any(mp.player_id == current_user.id for mp in match_participation) - - if not is_registered and not player_in_match: - return jsonify([]) + can_view = current_user.can_manage_this_tryout(tryout) + + # For players, check if they're registered or participating in a match + is_registered = False + player_in_match = False + if current_user.role == 'player': + is_registered = TryoutRegistration.query.filter_by( + tryout_id=tryout_id, player_id=current_user.id + ).first() is not None + + # Check if player is participating in any matches for this tryout + player_matches = Match.query.join(MatchParticipant).filter( + MatchParticipant.player_id == current_user.id, + Match.tryout_id == tryout_id + ).all() + player_in_match = len(player_matches) > 0 + + # Non-participating players cannot see the calendar + if not can_view and not is_registered and not player_in_match: + return jsonify([]) events = [] + # Add tryout date as an event (read-only, for context) + events.append({ + 'id': f'tryout_{tryout.id}', + 'title': f'Tryout: {tryout.title}', + 'date': tryout.date.strftime('%Y-%m-%d'), + 'type': 'tryout', + 'color': '#3b82f6', # Blue for tryouts + 'extendedProps': { + 'location': tryout.location or 'TBD', + 'status': tryout.status, + 'description': tryout.description or '', + 'tryout_id': tryout.id + } + }) + for match in tryout.matches: - match_color = '#10b981' if match.match_type == 'team_vs_team' else '#f59e0b' + # Determine color based on match type + if match.match_type == 'team_vs_team' or match.match_type == 'player_vs_player': + match_color = '#10b981' # Green for team matches + else: + match_color = '#f59e0b' # Orange for scrims - # Build participant string + # Build participant string with proper grouping participants_str = '' if match.match_type == 'team_vs_team': teams = [] @@ -129,10 +151,22 @@ def api_events_for_tryout(tryout_id): if match.team2: teams.append(match.team2.name) participants_str = f"{' vs '.join(teams)}" + elif match.match_type == 'player_vs_player': + # Get players grouped by team side + team1_players = [p.player.full_name for p in match.participants.filter_by(team_side=1).all()] + team2_players = [p.player.full_name for p in match.participants.filter_by(team_side=2).all()] + if team1_players and team2_players: + participants_str = f"{', '.join(team1_players)} vs {', '.join(team2_players)}" + else: + participants_str = 'TBD vs TBD' else: player_names = [p.player.full_name for p in match.participants.all()] participants_str = ', '.join(player_names) if player_names else 'No players' + # Include time for calendar display + start_time_str = match.start_time.strftime('%H:%M') if match.start_time else None + end_time_str = match.end_time.strftime('%H:%M') if match.end_time else None + events.append({ 'id': f'match_{match.id}', 'title': match.title + ' (' + participants_str + ')', @@ -144,7 +178,10 @@ def api_events_for_tryout(tryout_id): 'status': match.status, 'match_type': match.match_type, 'tryout_id': tryout.id, - 'match_id': match.id + 'match_id': match.id, + 'participants': participants_str, + 'start_time': start_time_str, + 'end_time': end_time_str } }) @@ -245,6 +282,17 @@ def create_match(tryout_id): match.team1_id = int(team1_id) if team1_id else None match.team2_id = int(team2_id) if team2_id else None + # Handle player vs player matches + elif match_type == 'player_vs_player': + team1_player_ids = request.form.getlist('team1_player_ids') + team2_player_ids = request.form.getlist('team2_player_ids') + for pid in team1_player_ids: + participant = MatchParticipant(match_id=match.id, player_id=int(pid), team_side=1) + db.session.add(participant) + for pid in team2_player_ids: + participant = MatchParticipant(match_id=match.id, player_id=int(pid), team_side=2) + db.session.add(participant) + # Handle player scrim matches elif match_type == 'player_scrim': player_ids = request.form.getlist('player_ids') @@ -273,6 +321,9 @@ def edit_match(match_id): teams = Team.query.filter_by(tryout_id=tryout.id).all() all_players = User.query.filter_by(role='player').order_by(User.full_name).all() current_player_ids = [p.player_id for p in match.participants.all()] + # Get players grouped by team side for player_vs_player matches + team1_player_ids = [p.player_id for p in match.participants.filter_by(team_side=1).all()] + team2_player_ids = [p.player_id for p in match.participants.filter_by(team_side=2).all()] if request.method == 'POST': match.title = request.form.get('title') @@ -308,6 +359,18 @@ def edit_match(match_id): match.team1_id = int(team1_id) if team1_id else None match.team2_id = int(team2_id) if team2_id else None + # Handle player vs player matches - update participants + elif match.match_type == 'player_vs_player': + MatchParticipant.query.filter_by(match_id=match.id).delete() + team1_player_ids = request.form.getlist('team1_player_ids') + team2_player_ids = request.form.getlist('team2_player_ids') + for pid in team1_player_ids: + participant = MatchParticipant(match_id=match.id, player_id=int(pid), team_side=1) + db.session.add(participant) + for pid in team2_player_ids: + participant = MatchParticipant(match_id=match.id, player_id=int(pid), team_side=2) + db.session.add(participant) + # Handle player scrim matches - update participants elif match.match_type == 'player_scrim': MatchParticipant.query.filter_by(match_id=match.id).delete() @@ -320,7 +383,7 @@ def edit_match(match_id): flash('Match updated successfully!', 'success') return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id)) - return render_template('pages/edit_match.html', match=match, tryout=tryout, teams=teams, all_players=all_players, current_player_ids=current_player_ids) + return render_template('pages/edit_match.html', match=match, tryout=tryout, teams=teams, all_players=all_players, current_player_ids=current_player_ids, team1_player_ids=team1_player_ids, team2_player_ids=team2_player_ids) @matches_bp.route('//delete', methods=['POST']) diff --git a/routes/tryouts.py b/routes/tryouts.py index 1142606..2fa670c 100644 --- a/routes/tryouts.py +++ b/routes/tryouts.py @@ -144,10 +144,22 @@ def view_tryout(tryout_id): # Determine if current user can edit this tryout can_edit = current_user.can_manage_this_tryout(tryout) - + + # Determine if current user can view the calendar (managers/coaches can always see it) + # Players need to be registered or participating in a match + can_view_calendar = can_edit + if current_user.role == 'player': + # Check if player is participating in any matches for this tryout + player_in_match = MatchParticipant.query.join(Match).filter( + MatchParticipant.player_id == current_user.id, + Match.tryout_id == tryout_id + ).first() is not None + + can_view_calendar = is_registered or player_in_match + # Get all players (for manager registration dropdown) all_players = User.query.filter_by(role='player').order_by(User.full_name).all() - + # Get matches for this tryout with participant info matches = Match.query.filter_by(tryout_id=tryout_id).order_by(Match.date).all() match_data = [] @@ -155,7 +167,18 @@ def view_tryout(tryout_id): if match.match_type == 'team_vs_team': participants = { 'team1': match.team1.name if match.team1 else 'TBD', - 'team2': match.team2.name if match.team2 else 'TBD' + 'team2': match.team2.name if match.team2 else 'TBD', + 'team1_players': [{'name': m.player.full_name, 'position': m.position} for m in match.team1.members.all()] if match.team1 else [], + 'team2_players': [{'name': m.player.full_name, 'position': m.position} for m in match.team2.members.all()] if match.team2 else [] + } + elif match.match_type == 'player_vs_player': + team1_players = [p.player.full_name for p in match.participants.filter_by(team_side=1).all()] + team2_players = [p.player.full_name for p in match.participants.filter_by(team_side=2).all()] + participants = { + 'team1': ', '.join(team1_players) if team1_players else 'TBD', + 'team2': ', '.join(team2_players) if team2_players else 'TBD', + 'team1_player_names': team1_players, + 'team2_player_names': team2_players } else: participants = [p.player.full_name for p in match.participants.all()] @@ -173,6 +196,7 @@ def view_tryout(tryout_id): registrations=registrations, team_data=team_data, can_edit=can_edit, + can_view_calendar=can_view_calendar, all_players=all_players, matches=matches, match_data=match_data, diff --git a/static/css/style.css b/static/css/style.css index a978463..341baad 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -949,6 +949,46 @@ a:hover { color: var(--primary-dark); } gap: 4px; } +/* Team Roster Display */ +.team-roster-display { + font-size: 0.85rem; + color: var(--gray-600); +} + +.team-roster-display small { + display: block; + margin-top: 2px; +} + +.team-rosters { + display: flex; + gap: 20px; + flex-wrap: wrap; +} + +.team-roster { + flex: 1; + min-width: 200px; +} + +.team-roster h5 { + font-size: 0.9rem; + margin-bottom: 8px; + color: var(--gray-700); +} + +.team-members-list { + list-style: none; + padding: 0; + margin: 0; + font-size: 0.85rem; +} + +.team-members-list li { + padding: 4px 0; + color: var(--gray-600); +} + @media (max-width: 768px) { .sidebar { width: var(--sidebar-collapsed); @@ -970,4 +1010,5 @@ a:hover { color: var(--primary-dark); } .content { padding: 16px; } .top-bar { padding: 12px 16px; } .flash-messages { padding: 0 16px; } + .team-rosters { flex-direction: column; } } diff --git a/templates/pages/create_match.html b/templates/pages/create_match.html index 0c6d45d..e17e922 100644 --- a/templates/pages/create_match.html +++ b/templates/pages/create_match.html @@ -12,10 +12,11 @@
-
+
@@ -79,10 +80,41 @@
+ + + + + +
+ {% if match.team1 %} +
+
{{ match.team1.name }} Players
+
    + {% for member in match.team1.members %} +
  • {{ member.player.full_name }}{% if member.position %} {{ member.position }}{% endif %}
  • + {% else %} +
  • No players assigned
  • + {% endfor %} +
+
+ {% endif %} + {% if match.team2 %} +
+
{{ match.team2.name }} Players
+
    + {% for member in match.team2.members %} +
  • {{ member.player.full_name }}{% if member.position %} {{ member.position }}{% endif %}
  • + {% else %} +
  • No players assigned
  • + {% endfor %} +
+
+ {% endif %} +
+ + + {% elif match.match_type == 'player_vs_player' %} +
+

Select Players

+ +
+
+ +
+ {% for player in all_players %} + + {% endfor %} +
+
+
+ +
+ {% for player in all_players %} + + {% endfor %} +
+
+
+ + {% else %}
-

Players

+

Select Players

@@ -105,4 +166,22 @@
-{% endblock %} \ No newline at end of file +{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/templates/pages/view_tryout.html b/templates/pages/view_tryout.html index abf3e1e..89bee35 100644 --- a/templates/pages/view_tryout.html +++ b/templates/pages/view_tryout.html @@ -231,10 +231,10 @@ - {% if matches %} + {% if can_view_calendar %}
-

Scheduled Matches

+

Schedule

{% if can_edit %} Schedule Match @@ -242,9 +242,18 @@ {% endif %}
+ {% if matches %}
+ {% else %} + +
+ +

No matches scheduled yet. Check back later!

+
+ {% endif %} +{% if matches %}
@@ -264,19 +273,42 @@ {% for item in match_data %} {% set m = item.match %} + {% set participants = item.participants %}
{{ m.title }} - - {{ 'Team vs Team' if m.match_type == 'team_vs_team' else 'Player Scrim' }} + + {% if m.match_type == 'team_vs_team' %} + Team vs Team + {% elif m.match_type == 'player_vs_player' %} + Player vs Player + {% else %} + Player Scrim + {% endif %} {{ m.date.strftime('%m/%d/%Y') }} {% if m.match_type == 'team_vs_team' %} - {{ item.participants.team1 }} vs {{ item.participants.team2 }} + {{ participants.team1 }} vs {{ participants.team2 }} + {% if participants.team1_players or participants.team2_players %} +
+ {% if participants.team1_players %} + {{ m.team1.name if m.team1 else 'Team 1' }}: + {% for pl in participants.team1_players %}{{ pl.name }}{% if not loop.last %}, {% endif %}{% endfor %} +
+ {% endif %} + {% if participants.team2_players %} + {{ m.team2.name if m.team2 else 'Team 2' }}: + {% for pl in participants.team2_players %}{{ pl.name }}{% if not loop.last %}, {% endif %}{% endfor %} + + {% endif %} +
+ {% endif %} + {% elif m.match_type == 'player_vs_player' %} + {{ participants.team1 }} vs {{ participants.team2 }} {% else %} - {{ item.participants | join(', ') }} + {{ participants | join(', ') }} {% endif %}
@@ -301,6 +333,7 @@
+ {% endif %}
{% endif %}