"""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() managers = User.query.filter_by(role='manager', is_active_account=True).order_by(User.full_name).all() coaches = User.query.filter_by(role='coach', is_active_account=True).order_by(User.full_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') manager_id = request.form.get('manager_id') coach_id = request.form.get('coach_id') try: date_obj = datetime.strptime(date_str, '%Y-%m-%d').date() except (ValueError, TypeError): flash('Invalid date format.', 'danger') return render_template('pages/tryout_form.html', tryout=None, org_teams=org_teams, managers=managers, coaches=coaches, esport_games=ESPORT_GAMES) 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, manager_id=int(manager_id) if manager_id else None, coach_id=int(coach_id) if coach_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/tryout_form.html', tryout=None, org_teams=org_teams, managers=managers, coaches=coaches, esport_games=ESPORT_GAMES) @tryouts_bp.route('//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() managers = User.query.filter_by(role='manager', is_active_account=True).order_by(User.full_name).all() coaches = User.query.filter_by(role='coach', is_active_account=True).order_by(User.full_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') manager_id = request.form.get('manager_id') coach_id = request.form.get('coach_id') try: date_obj = datetime.strptime(date_str, '%Y-%m-%d').date() except (ValueError, TypeError): flash('Invalid date format.', 'danger') return render_template('pages/tryout_form.html', tryout=tryout, org_teams=org_teams, managers=managers, coaches=coaches, 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 tryout.manager_id = int(manager_id) if manager_id else None tryout.coach_id = int(coach_id) if coach_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/tryout_form.html', tryout=tryout, org_teams=org_teams, managers=managers, coaches=coaches, esport_games=ESPORT_GAMES) @tryouts_bp.route('/') @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('//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('//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('//registration//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('//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('//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('//team//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))