"""Organization team management routes. This module handles CRUD operations for organization teams and player assignments. """ 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 OrgTeam, User, Team, TeamMember, PersonalNote, TeamNote, Tryout, TeamPlayer teams_bp = Blueprint('teams', __name__, url_prefix='/teams') @teams_bp.route('') @login_required def list_teams(): """List all organization teams visible to the current user. Coaches and managers see only their assigned teams. President sees all teams. Returns: Response: Rendered teams list template. """ can_manage = current_user.can_manage_teams() if current_user.role == 'coach': teams = OrgTeam.query.filter( db.or_( OrgTeam.coaches.any(id=current_user.id), OrgTeam.coach_id == current_user.id ) ).order_by(OrgTeam.name).all() elif current_user.role == 'manager': teams = OrgTeam.query.filter( db.or_( OrgTeam.managers.any(id=current_user.id), OrgTeam.manager_id == current_user.id ) ).order_by(OrgTeam.name).all() elif current_user.role in ['player', 'scout']: flash('Use My Team(s) to view your teams.', 'info') return redirect(url_for('teams.my_teams')) else: flash('You do not have permission to view teams.', 'danger') return redirect(url_for('main.dashboard')) coaches = User.query.filter_by(role='coach', is_active_account=True).order_by(User.username).all() managers = User.query.filter_by(role='manager', is_active_account=True).order_by(User.username).all() all_players = User.query.filter_by(role='player').order_by(User.username).all() return render_template('pages/teams.html', teams=teams, coaches=coaches, managers=managers, all_players=all_players, can_manage=can_manage) @teams_bp.route('/my-teams') @login_required def my_teams(): """View the player's own teams with upcoming matches. Players can see their team rosters, coaches, managers, and upcoming team matches with presence confirmation toggles. Returns: Response: Rendered my_teams template. """ if current_user.role != 'player': flash('This page is for players.', 'info') return redirect(url_for('teams.list_teams')) from models import TeamMatch, TeamMatchParticipant from datetime import datetime player_teams = current_user.get_org_teams() team_data = [] now = datetime.utcnow() for org_team in player_teams: matches = TeamMatch.query.filter( TeamMatch.org_team_id == org_team.id, TeamMatch.status == 'scheduled' ).order_by(TeamMatch.date.asc(), TeamMatch.start_time.asc()).all() matches_data = [] for tm in matches: confirmed, total = tm.get_confirmed_count() participant = TeamMatchParticipant.query.filter_by( team_match_id=tm.id, player_id=current_user.id ).first() matches_data.append({ 'match': tm, 'participant_id': participant.id if participant else None, 'is_confirmed': participant.is_confirmed if participant else False, 'confirmed_count': confirmed, 'total_count': total }) team_data.append({ 'team': org_team, 'matches': matches_data, 'coaches': org_team.get_coaches(), 'managers': org_team.get_managers() }) return render_template('pages/my_teams.html', team_data=team_data, now=now) @teams_bp.route('/create', methods=['POST']) @login_required def create_team(): """Create a new organization team. Args: name: Team name from form. coach_id: Optional coach assignment from form. manager_id: Optional manager assignment from form. Returns: Response: Redirect to teams list with status message. """ if not current_user.can_manage_teams(): flash('You do not have permission to create teams.', 'danger') return redirect(url_for('teams.list_teams')) name = request.form.get('name') coach_id = request.form.get('coach_id') manager_id = request.form.get('manager_id') if not name: flash('Team name is required.', 'danger') return redirect(url_for('teams.list_teams')) existing = OrgTeam.query.filter_by(name=name).first() if existing: flash(f'Team "{name}" already exists.', 'danger') return redirect(url_for('teams.list_teams')) team = OrgTeam( name=name, coach_id=int(coach_id) if coach_id else None, manager_id=int(manager_id) if manager_id else None, created_by=current_user.id ) db.session.add(team) db.session.flush() if coach_id: coach_user = User.query.get(int(coach_id)) if coach_user: team.coaches.append(coach_user) if manager_id: manager_user = User.query.get(int(manager_id)) if manager_user: team.managers.append(manager_user) db.session.commit() flash(f'Team "{name}" created successfully!', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//edit', methods=['POST']) @login_required def edit_team(team_id): """Edit an existing organization team.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('You do not have permission to edit this team.', 'danger') return redirect(url_for('teams.list_teams')) name = request.form.get('name') coach_id = request.form.get('coach_id') manager_id = request.form.get('manager_id') if not name: flash('Team name is required.', 'danger') return redirect(url_for('teams.list_teams')) existing = OrgTeam.query.filter(OrgTeam.name == name, OrgTeam.id != team_id).first() if existing: flash(f'Team "{name}" already exists.', 'danger') return redirect(url_for('teams.list_teams')) team.name = name team.coach_id = int(coach_id) if coach_id else None team.manager_id = int(manager_id) if manager_id else None if coach_id: coach_user = User.query.get(int(coach_id)) if coach_user and not team.coaches.filter_by(id=coach_user.id).first(): team.coaches.append(coach_user) if manager_id: manager_user = User.query.get(int(manager_id)) if manager_user and not team.managers.filter_by(id=manager_user.id).first(): team.managers.append(manager_user) db.session.commit() flash(f'Team "{name}" updated successfully!', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//delete', methods=['POST']) @login_required def delete_team(team_id): """Delete an organization team.""" if not current_user.can_manage_teams(): flash('You do not have permission to delete teams.', 'danger') return redirect(url_for('teams.list_teams')) team = OrgTeam.query.get_or_404(team_id) name = team.name from models import Tryout tryouts = Tryout.query.filter_by(target_org_team_id=team_id).all() if tryouts: for t in tryouts: t.target_org_team_id = None db.session.commit() TeamPlayer.query.filter_by(org_team_id=team_id).delete() db.session.commit() db.session.delete(team) db.session.commit() flash(f'Team "{name}" deleted successfully.', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//add_coach', methods=['POST']) @login_required def add_coach(team_id): """Add a coach to an organization team (many-to-many).""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('Permission denied.', 'danger') return redirect(url_for('teams.list_teams')) coach_id = request.form.get('coach_id') if not coach_id: flash('Please select a coach.', 'danger') return redirect(url_for('teams.list_teams')) coach = User.query.get_or_404(int(coach_id)) if coach.role != 'coach': flash('Only coaches can be assigned as coach.', 'danger') return redirect(url_for('teams.list_teams')) if team.coaches.filter_by(id=coach.id).first(): flash(f'{coach.username} is already a coach of {team.name}.', 'info') return redirect(url_for('teams.list_teams')) team.coaches.append(coach) if not team.coach_id: team.coach_id = coach.id db.session.commit() flash(f'{coach.username} added as coach of {team.name}.', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//add_manager', methods=['POST']) @login_required def add_manager(team_id): """Add a manager to an organization team (many-to-many).""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('Permission denied.', 'danger') return redirect(url_for('teams.list_teams')) manager_id = request.form.get('manager_id') if not manager_id: flash('Please select a manager.', 'danger') return redirect(url_for('teams.list_teams')) manager = User.query.get_or_404(int(manager_id)) if manager.role != 'manager': flash('Only managers can be assigned as manager.', 'danger') return redirect(url_for('teams.list_teams')) if team.managers.filter_by(id=manager.id).first(): flash(f'{manager.username} is already a manager of {team.name}.', 'info') return redirect(url_for('teams.list_teams')) team.managers.append(manager) if not team.manager_id: team.manager_id = manager.id db.session.commit() flash(f'{manager.username} added as manager of {team.name}.', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//remove_coach', methods=['POST']) @login_required def remove_coach(team_id): """Remove a coach from an organization team.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('Permission denied.', 'danger') return redirect(url_for('teams.list_teams')) coach_id = request.form.get('coach_id') if coach_id: coach = User.query.get(int(coach_id)) if coach and team.coaches.filter_by(id=coach.id).first(): team.coaches.remove(coach) if team.coach_id == coach.id: team.coach_id = None else: team.coaches = [] team.coach_id = None db.session.commit() flash(f'Coach removed from {team.name}.', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//remove_manager', methods=['POST']) @login_required def remove_manager(team_id): """Remove a manager from an organization team.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('Permission denied.', 'danger') return redirect(url_for('teams.list_teams')) manager_id = request.form.get('manager_id') if manager_id: manager = User.query.get(int(manager_id)) if manager and team.managers.filter_by(id=manager.id).first(): team.managers.remove(manager) if team.manager_id == manager.id: team.manager_id = None else: team.managers = [] team.manager_id = None db.session.commit() flash(f'Manager removed from {team.name}.', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//add_player', methods=['POST']) @login_required def add_player(team_id): """Add a player to an organization team.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('Permission denied.', 'danger') return redirect(url_for('teams.list_teams')) player_id = request.form.get('player_id') status = request.form.get('status', 'starter') if not player_id: flash('Please select a player.', 'danger') return redirect(url_for('teams.list_teams')) player = User.query.get_or_404(int(player_id)) if player.role != 'player': flash('Can only assign players to teams.', 'danger') return redirect(url_for('teams.list_teams')) existing = TeamPlayer.query.filter_by(player_id=player.id, org_team_id=team.id).first() if existing: flash(f'{player.username} is already on {team.name}.', 'info') return redirect(url_for('teams.list_teams')) tp = TeamPlayer( player_id=player.id, org_team_id=team.id, status=status ) db.session.add(tp) db.session.commit() flash(f'{player.username} added to {team.name}!', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//remove_player/', methods=['POST']) @login_required def remove_player(team_id, player_id): """Remove a player from an organization team.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('Permission denied.', 'danger') return redirect(url_for('teams.list_teams')) player = User.query.get_or_404(player_id) tp = TeamPlayer.query.filter_by(player_id=player_id, org_team_id=team_id).first() if not tp: flash(f'{player.username} is not on {team.name}.', 'danger') return redirect(url_for('teams.list_teams')) db.session.delete(tp) db.session.commit() flash(f'{player.username} removed from {team.name}.', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//toggle_status/', methods=['POST']) @login_required def toggle_player_status(team_id, player_id): """Toggle a player's status between starter and substitute.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): return jsonify({'error': 'Permission denied'}), 403 tp = TeamPlayer.query.filter_by(player_id=player_id, org_team_id=team_id).first() if not tp: return jsonify({'error': 'Player not found on this team'}), 404 tp.status = 'substitute' if tp.status == 'starter' else 'starter' db.session.commit() return jsonify({ 'success': True, 'player_id': player_id, 'new_status': tp.status, 'player_name': tp.player.username }) @teams_bp.route('//add-team-note', methods=['POST']) @login_required def add_team_note(team_id): """Add a team improvement note from the team page (for coaches).""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('You do not have permission to add notes to this team.', 'danger') return redirect(url_for('teams.list_teams')) content = request.form.get('content', '').strip() if content: note = TeamNote( org_team_id=team_id, coach_id=current_user.id, content=content ) db.session.add(note) db.session.commit() flash('Team notes added successfully!', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//add-player-note/', methods=['POST']) @login_required def add_player_note(team_id, player_id): """Add a personal note for a player from the team page (for coaches).""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('You do not have permission to add notes to this team.', 'danger') return redirect(url_for('teams.list_teams')) player = User.query.get_or_404(player_id) if player.role != 'player': flash('Can only add notes for players.', 'danger') return redirect(url_for('teams.list_teams')) tp = TeamPlayer.query.filter_by(player_id=player_id, org_team_id=team_id).first() if not tp: flash(f'{player.username} is not on {team.name}.', 'danger') return redirect(url_for('teams.list_teams')) content = request.form.get('content', '').strip() if content: note = PersonalNote( player_id=player_id, coach_id=current_user.id, content=content ) db.session.add(note) db.session.commit() flash(f'Note added for {player.username}!', 'success') return redirect(url_for('teams.list_teams'))