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 datetime import datetime, time matches_bp = Blueprint('matches', __name__, url_prefix='/matches') def can_schedule_match(): """Check if user can schedule matches (coaches and above).""" return current_user.role in ['president', 'manager', 'coach', 'scout'] @matches_bp.route('/calendar') @login_required def calendar(): """Calendar view showing tryouts and matches.""" return render_template('pages/calendar.html') @matches_bp.route('/api/events') @login_required def api_events(): """API endpoint returning calendar events.""" events = [] # Get tryouts based on user permissions tryouts = get_visible_tryouts_for_user() for tryout in tryouts: events.append({ 'id': f'tryout_{tryout.id}', 'title': 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 } }) # Add matches for this tryout for match in tryout.matches: match_color = '#10b981' if match.match_type == 'team_vs_team' else '#f59e0b' # Build match description with participants match_desc = match.description or '' participants_str = '' if match.match_type == 'team_vs_team': teams = [] if match.team1: teams.append(match.team1.name) if match.team2: teams.append(match.team2.name) participants_str = f"{' vs '.join(teams)}" match_desc = participants_str + (f"
{match.description}" if match.description else '') else: # Player scrim - show all participants player_names = [p.player.full_name for p in match.participants.all()] participants_str = ', '.join(player_names) if player_names else 'No players' match_desc = participants_str + (f"
{match.description}" if match.description else '') # 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 + ')', 'date': match.date.strftime('%Y-%m-%d'), 'type': 'match', 'color': match_color, 'extendedProps': { 'location': match.location or tryout.location or 'TBD', 'status': match.status, 'description': match_desc, 'match_type': match.match_type, 'tryout_id': tryout.id, 'match_id': match.id, 'start_time': start_time_str, 'end_time': end_time_str, 'participants': participants_str } }) return jsonify(events) @matches_bp.route('/api/events/') @login_required def api_events_for_tryout(tryout_id): """API endpoint returning calendar events for a specific tryout.""" 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([]) events = [] for match in tryout.matches: match_color = '#10b981' if match.match_type == 'team_vs_team' else '#f59e0b' # Build participant string participants_str = '' if match.match_type == 'team_vs_team': teams = [] if match.team1: teams.append(match.team1.name) if match.team2: teams.append(match.team2.name) participants_str = f"{' vs '.join(teams)}" else: player_names = [p.player.full_name for p in match.participants.all()] participants_str = ', '.join(player_names) if player_names else 'No players' events.append({ 'id': f'match_{match.id}', 'title': match.title + ' (' + participants_str + ')', 'date': match.date.strftime('%Y-%m-%d'), 'type': 'match', 'color': match_color, 'extendedProps': { 'location': match.location or tryout.location or 'TBD', 'status': match.status, 'match_type': match.match_type, 'tryout_id': tryout.id, 'match_id': match.id } }) return jsonify(events) def get_visible_tryouts_for_user(): """Get tryouts that the current user can see based on their role.""" if current_user.role == 'president': return Tryout.query.order_by(Tryout.date).all() elif current_user.role == 'manager': return Tryout.query.filter_by(created_by=current_user.id).order_by(Tryout.date).all() elif current_user.role == 'coach': org_team = OrgTeam.query.filter_by(coach_id=current_user.id).first() if org_team: return Tryout.query.filter_by(target_org_team_id=org_team.id).order_by(Tryout.date).all() return [] elif current_user.role == 'player': # Get tryouts player is registered for player_tryout_ids = [r.tryout_id for r in current_user.tryout_registrations.all()] tryouts = Tryout.query.filter(Tryout.id.in_(player_tryout_ids)).order_by(Tryout.date).all() if player_tryout_ids else [] # Also include matches where player is participating player_matches = Match.query.join(MatchParticipant).filter( MatchParticipant.player_id == current_user.id ).all() player_match_tryout_ids = list(set(m.tryout_id for m in player_matches)) additional_tryouts = Tryout.query.filter( Tryout.id.in_(player_match_tryout_ids) ).order_by(Tryout.date).all() if player_match_tryout_ids else [] # Combine and deduplicate all_tryouts = tryouts + [t for t in additional_tryouts if t.id not in player_tryout_ids] return all_tryouts else: # scout return Tryout.query.order_by(Tryout.date).all() @matches_bp.route('/create/', methods=['GET', 'POST']) @login_required def create_match(tryout_id): """Create a new match/scrimmage within a tryout.""" tryout = Tryout.query.get_or_404(tryout_id) # Check if user can manage this tryout (president, manager, or coach) if not current_user.can_manage_this_tryout(tryout): flash('You do not have permission to schedule matches for this tryout.', 'danger') return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_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() if request.method == 'POST': title = request.form.get('title') description = request.form.get('description') date_str = request.form.get('date') start_time_str = request.form.get('start_time') end_time_str = request.form.get('end_time') location = request.form.get('location') match_type = request.form.get('match_type') try: date_obj = datetime.strptime(date_str, '%Y-%m-%d').date() if date_str else tryout.date except (ValueError, TypeError): flash('Invalid date format.', 'danger') return render_template('pages/create_match.html', tryout=tryout, teams=teams, all_players=all_players) start_time = None end_time = None try: if start_time_str: start_time = datetime.strptime(start_time_str, '%H:%M').time() if end_time_str: end_time = datetime.strptime(end_time_str, '%H:%M').time() except ValueError: flash('Invalid time format.', 'danger') return render_template('pages/create_match.html', tryout=tryout, teams=teams, all_players=all_players) match = Match( tryout_id=tryout_id, title=title, description=description, date=date_obj, start_time=start_time, end_time=end_time, location=location, match_type=match_type, created_by=current_user.id ) db.session.add(match) db.session.flush() # Get match.id before commit # Handle team vs team matches if match_type == 'team_vs_team': team1_id = request.form.get('team1_id') team2_id = request.form.get('team2_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 scrim matches elif match_type == 'player_scrim': player_ids = request.form.getlist('player_ids') for pid in player_ids: participant = MatchParticipant(match_id=match.id, player_id=int(pid)) db.session.add(participant) db.session.commit() flash('Match scheduled successfully!', 'success') return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id)) return render_template('pages/create_match.html', tryout=tryout, teams=teams, all_players=all_players) @matches_bp.route('//edit', methods=['GET', 'POST']) @login_required def edit_match(match_id): """Edit an existing match.""" match = Match.query.get_or_404(match_id) tryout = match.tryout if not current_user.can_manage_this_tryout(tryout): flash('You do not have permission to edit this match.', 'danger') return redirect(url_for('matches.calendar')) 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()] if request.method == 'POST': match.title = request.form.get('title') match.description = request.form.get('description') date_str = request.form.get('date') start_time_str = request.form.get('start_time') end_time_str = request.form.get('end_time') location = request.form.get('location') status = request.form.get('status') try: match.date = datetime.strptime(date_str, '%Y-%m-%d').date() except (ValueError, TypeError): flash('Invalid date format.', 'danger') return render_template('pages/edit_match.html', match=match, tryout=tryout, teams=teams, all_players=all_players, current_player_ids=current_player_ids) try: if start_time_str: match.start_time = datetime.strptime(start_time_str, '%H:%M').time() if end_time_str: match.end_time = datetime.strptime(end_time_str, '%H:%M').time() except ValueError: pass match.location = location if status in ['scheduled', 'completed', 'cancelled']: match.status = status # Handle team vs team matches if match.match_type == 'team_vs_team': team1_id = request.form.get('team1_id') team2_id = request.form.get('team2_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 scrim matches - update participants elif match.match_type == 'player_scrim': MatchParticipant.query.filter_by(match_id=match.id).delete() player_ids = request.form.getlist('player_ids') for pid in player_ids: participant = MatchParticipant(match_id=match.id, player_id=int(pid)) db.session.add(participant) db.session.commit() 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) @matches_bp.route('//delete', methods=['POST']) @login_required def delete_match(match_id): """Delete a match.""" match = Match.query.get_or_404(match_id) tryout = match.tryout if not current_user.can_manage_this_tryout(tryout): flash('You do not have permission to delete this match.', 'danger') return redirect(url_for('matches.calendar')) db.session.delete(match) db.session.commit() flash('Match deleted successfully.', 'success') return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id))