Files
team-tryouts/routes/tryouts.py
T

496 lines
19 KiB
Python

"""Tryout management routes for creating, viewing, and managing tryout events.
This module handles CRUD operations for tryouts and player registrations.
"""
from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_required, current_user
from extensions import db
from models import User, Tryout, TryoutRegistration, Evaluation, Team, TeamMember, OrgTeam, Match, MatchParticipant, ESPORT_GAMES, GAME_POSITIONS
from datetime import datetime
tryouts_bp = Blueprint('tryouts', __name__, url_prefix='/tryouts')
def can_manage():
"""Check if current user can manage tryouts.
Returns:
bool: True if user is president or manager.
"""
return current_user.role in ['president', 'manager']
@tryouts_bp.route('')
@login_required
def list_tryouts():
"""List all tryouts visible to the current user.
Shows tryouts filtered by user's role:
- President: All tryouts
- Manager: Only their created tryouts
- Coach: Tryouts targeting their org team
- Player: Only tryouts they are registered for or participating in
Returns:
Response: Rendered tryouts list template.
"""
if current_user.role == 'president':
tryouts = Tryout.query.order_by(Tryout.date.desc()).all()
elif current_user.role == 'manager':
tryouts = Tryout.query.filter_by(created_by=current_user.id).order_by(Tryout.date.desc()).all()
elif current_user.role == 'coach':
# Coaches see tryouts targeting their org team
org_team = OrgTeam.query.filter_by(coach_id=current_user.id).first()
if org_team:
tryouts = Tryout.query.filter_by(target_org_team_id=org_team.id).order_by(Tryout.date.desc()).all()
else:
tryouts = []
elif current_user.role == 'player':
# Players only see tryouts they are registered for or participating in matches
from routes.matches import get_visible_tryouts_for_user
tryouts = get_visible_tryouts_for_user()
else:
tryouts = Tryout.query.order_by(Tryout.date.desc()).all()
return render_template('pages/tryouts.html', tryouts=tryouts, now=datetime.utcnow())
@tryouts_bp.route('/create', methods=['GET', 'POST'])
@login_required
def create_tryout():
"""Create a new tryout event.
GET: Render the tryout creation form.
POST: Create a tryout with the submitted details.
Requires president or manager role.
Returns:
Response: Create form or redirect to the new tryout.
"""
if not can_manage():
flash('You do not have permission to create tryouts.', 'danger')
return redirect(url_for('tryouts.list_tryouts'))
org_teams = OrgTeam.query.order_by(OrgTeam.name).all()
if request.method == 'POST':
title = request.form.get('title')
description = request.form.get('description')
game = request.form.get('game')
date_str = request.form.get('date')
location = request.form.get('location')
max_players = request.form.get('max_players')
target_org_team_id = request.form.get('target_org_team_id')
try:
date_obj = datetime.strptime(date_str, '%Y-%m-%d').date()
except (ValueError, TypeError):
flash('Invalid date format.', 'danger')
return render_template('pages/create_tryout.html', org_teams=org_teams)
tryout = Tryout(
title=title,
description=description,
game=game,
date=date_obj,
location=location,
max_players=int(max_players) if max_players else None,
created_by=current_user.id,
status='upcoming',
target_org_team_id=int(target_org_team_id) if target_org_team_id else None
)
db.session.add(tryout)
db.session.commit()
flash('Tryout created successfully!', 'success')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id))
return render_template('pages/create_tryout.html', org_teams=org_teams, esport_games=ESPORT_GAMES)
@tryouts_bp.route('/<int:tryout_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_tryout(tryout_id):
"""Edit an existing tryout event.
GET: Render the tryout edit form with current data.
POST: Update the tryout with submitted changes.
Permission based on can_manage_this_tryout check.
Args:
tryout_id: The ID of the tryout to edit.
Returns:
Response: Edit form or redirect to tryout view.
"""
tryout = Tryout.query.get_or_404(tryout_id)
# Permission: president, manager (own tryouts), or coach (targets their team)
if not current_user.can_manage_this_tryout(tryout):
flash('You do not have permission to edit this tryout.', 'danger')
return redirect(url_for('tryouts.list_tryouts'))
org_teams = OrgTeam.query.order_by(OrgTeam.name).all()
if request.method == 'POST':
title = request.form.get('title')
description = request.form.get('description')
game = request.form.get('game')
date_str = request.form.get('date')
location = request.form.get('location')
max_players = request.form.get('max_players')
target_org_team_id = request.form.get('target_org_team_id')
try:
date_obj = datetime.strptime(date_str, '%Y-%m-%d').date()
except (ValueError, TypeError):
flash('Invalid date format.', 'danger')
return render_template('pages/edit_tryout.html', tryout=tryout, org_teams=org_teams, esport_games=ESPORT_GAMES)
tryout.title = title
tryout.description = description
tryout.game = game
tryout.date = date_obj
tryout.location = location
tryout.max_players = int(max_players) if max_players else None
tryout.target_org_team_id = int(target_org_team_id) if target_org_team_id else None
db.session.commit()
flash('Tryout updated successfully!', 'success')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id))
return render_template('pages/edit_tryout.html', tryout=tryout, org_teams=org_teams, esport_games=ESPORT_GAMES)
@tryouts_bp.route('/<int:tryout_id>')
@login_required
def view_tryout(tryout_id):
"""View a specific tryout with all details.
Displays tryout information, registered players, evaluations, teams,
matches, and evaluation status information.
Args:
tryout_id: The ID of the tryout to view.
Returns:
Response: Rendered tryout detail template.
"""
tryout = Tryout.query.get_or_404(tryout_id)
# Check if user has permission to view this tryout
can_view = False
if current_user.role == 'president':
can_view = True
elif current_user.role == 'manager' and tryout.created_by == current_user.id:
can_view = True
elif current_user.role == 'coach':
org_team = OrgTeam.query.filter_by(coach_id=current_user.id).first()
if org_team and tryout.target_org_team_id == org_team.id:
can_view = True
elif current_user.role == 'player':
is_registered = TryoutRegistration.query.filter_by(
tryout_id=tryout_id, player_id=current_user.id
).first() is not None
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 = is_registered or player_in_match
elif current_user.role == 'scout':
can_view = True
if not can_view:
flash('You do not have permission to view this tryout.', 'danger')
return redirect(url_for('tryouts.list_tryouts'))
registrations = TryoutRegistration.query.filter_by(tryout_id=tryout_id).all()
registered_players = [User.query.get(r.player_id) for r in registrations if r.player_id]
evaluations = Evaluation.query.filter_by(tryout_id=tryout_id).all()
evaluator_ids = set(e.evaluator_id for e in evaluations)
player_ids_with_eval = set(e.player_id for e in evaluations)
# Check if current user has evaluated each player
player_eval_status = {}
if current_user.can_evaluate():
for p in registered_players:
existing = Evaluation.query.filter_by(
tryout_id=tryout_id,
player_id=p.id,
evaluator_id=current_user.id
).first()
player_eval_status[p.id] = existing is not None
is_registered = TryoutRegistration.query.filter_by(
tryout_id=tryout_id, player_id=current_user.id
).first() is not None
teams = Team.query.filter_by(tryout_id=tryout_id).all()
team_data = []
for team in teams:
members = TeamMember.query.filter_by(team_id=team.id).all()
team_data.append({
'team': team,
'members': [{'player': User.query.get(m.player_id), 'position': m.position} for m in members]
})
# 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
# Only expose all_players to users who can manage players in this tryout
all_players = None
if can_edit:
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, Match.start_time).all()
match_data = []
for match in matches:
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',
'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 = [{'name': p.player.full_name, 'position': p.position} for p in match.participants.filter_by(team_side=1).all() if p.player]
team2_players = [{'name': p.player.full_name, 'position': p.position} for p in match.participants.filter_by(team_side=2).all() if p.player]
participants = {
'team1': 'Team 1',
'team2': 'Team 2',
'team1_players': team1_players,
'team2_players': team2_players
}
else:
participants = [p.player.full_name for p in match.participants.all()]
match_data.append({
'match': match,
'participants': participants
})
return render_template('pages/view_tryout.html',
tryout=tryout,
registered_players=registered_players,
evaluations=evaluations,
player_eval_status=player_eval_status,
is_registered=is_registered,
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,
game_positions=GAME_POSITIONS,
now=datetime.utcnow())
@tryouts_bp.route('/<int:tryout_id>/register', methods=['POST'])
@login_required
def register_for_tryout(tryout_id):
"""Register a player for a tryout.
Allows players to register for tryouts. Validates that the tryout
is accepting registrations and not at capacity.
Args:
tryout_id: The ID of the tryout to register for.
Returns:
Response: Redirect to tryout view with status message.
"""
tryout = Tryout.query.get_or_404(tryout_id)
if current_user.role != 'player':
flash('Only players can register for tryouts.', 'danger')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
if tryout.status not in ['upcoming', 'in_progress']:
flash('This tryout is not accepting registrations.', 'danger')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
existing = TryoutRegistration.query.filter_by(tryout_id=tryout_id, player_id=current_user.id).first()
if existing:
flash('You are already registered for this tryout.', 'info')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
if tryout.max_players:
count = TryoutRegistration.query.filter_by(tryout_id=tryout_id).count()
if count >= tryout.max_players:
flash('This tryout is full.', 'danger')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
registration = TryoutRegistration(tryout_id=tryout_id, player_id=current_user.id)
db.session.add(registration)
db.session.commit()
flash('Successfully registered for tryout!', 'success')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
@tryouts_bp.route('/<int:tryout_id>/status', methods=['POST'])
@login_required
def update_status(tryout_id):
"""Update the status of a tryout.
Changes tryout status between upcoming, in_progress, and completed.
Args:
tryout_id: The ID of the tryout to update.
Returns:
Response: Redirect to tryout view.
"""
tryout = Tryout.query.get_or_404(tryout_id)
if not current_user.can_manage_this_tryout(tryout):
flash('Permission denied.', 'danger')
return redirect(url_for('tryouts.list_tryouts'))
new_status = request.form.get('status')
if new_status in ['upcoming', 'in_progress', 'completed']:
tryout.status = new_status
db.session.commit()
flash(f'Tryout status updated to {new_status}.', 'success')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
@tryouts_bp.route('/<int:tryout_id>/registration/<int:player_id>/status', methods=['POST'])
@login_required
def update_registration_status(tryout_id, player_id):
"""Update the attendance status of a tryout registration.
Args:
tryout_id: The ID of the tryout.
player_id: The ID of the player whose status to update.
Returns:
Response: Redirect to tryout view.
"""
tryout = Tryout.query.get_or_404(tryout_id)
if not current_user.can_manage_this_tryout(tryout):
flash('Permission denied.', 'danger')
return redirect(url_for('tryouts.list_tryouts'))
registration = TryoutRegistration.query.filter_by(tryout_id=tryout_id, player_id=player_id).first_or_404()
new_status = request.form.get('status')
if new_status in ['registered', 'attended', 'no_show']:
registration.status = new_status
db.session.commit()
flash('Registration status updated.', 'success')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
@tryouts_bp.route('/<int:tryout_id>/register_player', methods=['POST'])
@login_required
def register_player(tryout_id):
"""Manually register a player for a tryout (by managers/coaches).
Allows authorized users to register players on their behalf.
Args:
tryout_id: The ID of the tryout.
Returns:
Response: Redirect to tryout view with status message.
"""
tryout = Tryout.query.get_or_404(tryout_id)
if not current_user.can_manage_this_tryout(tryout):
flash('Permission denied.', 'danger')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
player_id = request.form.get('player_id')
if not player_id:
flash('Please select a player.', 'danger')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
player = User.query.get_or_404(int(player_id))
if player.role != 'player':
flash('Can only register players.', 'danger')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
existing = TryoutRegistration.query.filter_by(tryout_id=tryout_id, player_id=player.id).first()
if existing:
flash(f'{player.full_name} is already registered for this tryout.', 'info')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
if tryout.max_players:
count = TryoutRegistration.query.filter_by(tryout_id=tryout_id).count()
if count >= tryout.max_players:
flash('This tryout is full.', 'danger')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
registration = TryoutRegistration(tryout_id=tryout_id, player_id=player.id)
db.session.add(registration)
db.session.commit()
flash(f'{player.full_name} registered for tryout!', 'success')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
@tryouts_bp.route('/<int:tryout_id>/team/create', methods=['POST'])
@login_required
def create_team(tryout_id):
"""Create a tryout-specific team.
Args:
tryout_id: The ID of the tryout to create the team for.
Returns:
Response: Redirect to tryout view with status message.
"""
tryout = Tryout.query.get_or_404(tryout_id)
if not current_user.can_manage_this_tryout(tryout):
flash('Permission denied.', 'danger')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
team_name = request.form.get('team_name')
if team_name:
team = Team(tryout_id=tryout_id, name=team_name, created_by=current_user.id)
db.session.add(team)
db.session.commit()
flash(f'Team "{team_name}" created!', 'success')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
@tryouts_bp.route('/<int:tryout_id>/team/<int:team_id>/add', methods=['POST'])
@login_required
def add_to_team(tryout_id, team_id):
"""Add a player to a tryout team.
Args:
tryout_id: The ID of the tryout.
team_id: The ID of the team to add the player to.
Returns:
Response: Redirect to tryout view with status message.
"""
team = Team.query.get_or_404(team_id)
tryout = Tryout.query.get_or_404(tryout_id)
if not current_user.can_manage_this_tryout(tryout):
flash('Permission denied.', 'danger')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
player_id = request.form.get('player_id')
position = request.form.get('position', '')
existing = TeamMember.query.filter_by(team_id=team_id, player_id=player_id).first()
if existing:
flash('Player is already on this team.', 'info')
else:
member = TeamMember(team_id=team_id, player_id=int(player_id), position=position)
db.session.add(member)
db.session.commit()
flash('Player added to team!', 'success')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))