diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc index 6b5d54a..c56d9c3 100644 Binary files a/__pycache__/app.cpython-313.pyc and b/__pycache__/app.cpython-313.pyc differ diff --git a/__pycache__/models.cpython-313.pyc b/__pycache__/models.cpython-313.pyc index 8822d0c..4e4b009 100644 Binary files a/__pycache__/models.cpython-313.pyc and b/__pycache__/models.cpython-313.pyc differ diff --git a/__pycache__/seed.cpython-313.pyc b/__pycache__/seed.cpython-313.pyc new file mode 100644 index 0000000..4c9bade Binary files /dev/null and b/__pycache__/seed.cpython-313.pyc differ diff --git a/app.py b/app.py index 736775d..051882c 100644 --- a/app.py +++ b/app.py @@ -32,11 +32,11 @@ def create_app(): with app.app_context(): import models - from models import User + from models import User, MatchParticipant try: # Check if the database schema is up to date by testing a query - # that uses all model columns - db.session.execute(text('SELECT games FROM users LIMIT 1')) + # that uses all model columns including team_side for match participants + db.session.execute(text('SELECT games, team_side FROM match_participants LIMIT 1')) db.create_all() except Exception: # If there's a schema mismatch, drop and recreate all tables diff --git a/c b/c deleted file mode 100644 index f9702d0..0000000 --- a/c +++ /dev/null @@ -1,147 +0,0 @@ -{% extends "layouts/base.html" %} -{% block title %}My Profile - TryoutPro{% endblock %} -{% block page_title %}My Profile{% endblock %} -{% block breadcrumb %}Home / Profile{% endblock %} - -{% block header_actions %} -
- - Edit Profile - -
-{% endblock %} - -{% block content %} -
-
-
-

Account Information

-
-
-
-
{{ user.full_name[:2] | upper }}
-
-

{{ user.full_name }}

- {{ user.role | capitalize }} -

Member since {% if user.created_at %}{{ user.created_at.strftime('%B %Y') }}{% else %}Unknown{% endif %}

-
-
-
-
- Username - {{ user.username }} -
-
- Email - {{ user.email }} -
-
- Phone - {{ user.phone or 'Not provided' }} -
-
- Account Status - - {% if user.is_active_account %} - Active - {% else %} - Inactive - {% endif %} - -
-
-
-
- - -
-
-

E-Sports Profile

-
-
-
-
- Games - - {% set games_list = user.get_games_list() %} - {% if games_list %} - {% for game in games_list %} - {{ game }} - {% endfor %} - {% else %} - Not specified - {% endif %} - -
-
- TRN (Tracker Network) - - {% if user.trn_username %} - - {{ user.trn_username }} - - {% else %} - Not connected - {% endif %} - -
-
- Discord - - {% if user.discord_username %} - {{ user.discord_username }} - {% else %} - Not connected - {% endif %} - -
-
- League OS - - {% if user.league_os_profile %} - - {{ user.league_os_profile }} - - {% else %} - Not connected - {% endif %} - -
-
-
-
- -
-
-

My Statistics

-
-
- {% if user.role == 'player' %} -
-
-

{{ user.tryout_registrations.count() }}

-

Tryouts Registered

-
-
-

{{ user.evaluations_received.count() }}

-

Evaluations Received

-
-
-

{{ user.team_assignments.count() }}

-

Team Assignments

-
-
- {% elif user.can_evaluate() %} -
-
-

{{ user.evaluations_given.count() }}

-

Evaluations Given

-
-
- {% else %} -

No statistics available for this role.

- {% endif %} -
-
-
-{% endblock %} \ No newline at end of file diff --git a/instance/team_tryouts.db b/instance/team_tryouts.db index 9723cc8..8721583 100644 Binary files a/instance/team_tryouts.db and b/instance/team_tryouts.db differ diff --git a/models.py b/models.py index 1021da4..4de4e87 100644 --- a/models.py +++ b/models.py @@ -217,6 +217,20 @@ class Match(db.Model): return [p.player_id for p in self.participants.all()] +class PlayerDisponibility(db.Model): + """Player availability in 30-minute time blocks for scheduling matches.""" + __tablename__ = 'player_disponibilities' + id = db.Column(db.Integer, primary_key=True) + player_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) + day_of_week = db.Column(db.Integer, nullable=False) # 0=Monday, 6=Sunday + start_time = db.Column(db.Time, nullable=False) + end_time = db.Column(db.Time, nullable=False) # Always 30 minutes after start_time + created_at = db.Column(db.DateTime, default=datetime.utcnow) + updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + + player = db.relationship('User', backref='disponibilities') + + class MatchParticipant(db.Model): """Players participating in player scrimmage matches.""" __tablename__ = 'match_participants' diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8c8f4a9 Binary files /dev/null and b/requirements.txt differ diff --git a/routes/__pycache__/matches.cpython-313.pyc b/routes/__pycache__/matches.cpython-313.pyc index 1a5d5a1..36f6716 100644 Binary files a/routes/__pycache__/matches.cpython-313.pyc and b/routes/__pycache__/matches.cpython-313.pyc differ diff --git a/routes/__pycache__/users.cpython-313.pyc b/routes/__pycache__/users.cpython-313.pyc index 56c426d..a0aac6d 100644 Binary files a/routes/__pycache__/users.cpython-313.pyc and b/routes/__pycache__/users.cpython-313.pyc differ diff --git a/routes/matches.py b/routes/matches.py index e66ce79..ed0f6ec 100644 --- a/routes/matches.py +++ b/routes/matches.py @@ -1,8 +1,8 @@ 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, TryoutRegistration -from datetime import datetime, time +from models import User, Tryout, Match, MatchParticipant, Team, TeamMember, OrgTeam, TryoutRegistration, PlayerDisponibility +from datetime import datetime, time, timedelta matches_bp = Blueprint('matches', __name__, url_prefix='/matches') @@ -60,7 +60,10 @@ def api_events(): 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()] + player_names = [] + for p in match.participants.all(): + player_name = p.player.full_name if p.player else 'Unknown Player' + player_names.append(player_name) participants_str = ', '.join(player_names) if player_names else 'No players' match_desc = participants_str + (f"
{match.description}" if match.description else '') @@ -153,14 +156,23 @@ def api_events_for_tryout(tryout_id): 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()] + team1_players = [] + for p in match.participants.filter_by(team_side=1).all(): + if p.player: + team1_players.append(p.player.full_name) + team2_players = [] + for p in match.participants.filter_by(team_side=2).all(): + if p.player: + team2_players.append(p.player.full_name) 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()] + player_names = [] + for p in match.participants.all(): + player_name = p.player.full_name if p.player else 'Unknown Player' + player_names.append(player_name) participants_str = ', '.join(player_names) if player_names else 'No players' # Include time for calendar display @@ -244,6 +256,11 @@ def create_match(tryout_id): location = request.form.get('location') match_type = request.form.get('match_type') + # Start time is now mandatory + if not start_time_str: + flash('Start time is required. Please select a time slot.', 'danger') + return render_template('pages/create_match.html', tryout=tryout, teams=teams, all_players=all_players) + try: date_obj = datetime.strptime(date_str, '%Y-%m-%d').date() if date_str else tryout.date except (ValueError, TypeError): @@ -253,10 +270,15 @@ def create_match(tryout_id): start_time = None end_time = None try: - if start_time_str: - start_time = datetime.strptime(start_time_str, '%H:%M').time() + start_time = datetime.strptime(start_time_str, '%H:%M').time() + # Auto-calculate end time if not provided (start + 30 minutes) if end_time_str: end_time = datetime.strptime(end_time_str, '%H:%M').time() + else: + # Auto-calculate end time as start + 30 minutes + start_dt = datetime.combine(date_obj, start_time) + end_dt = start_dt + timedelta(minutes=30) + end_time = end_dt.time() except ValueError: flash('Invalid time format.', 'danger') return render_template('pages/create_match.html', tryout=tryout, teams=teams, all_players=all_players) @@ -340,11 +362,21 @@ def edit_match(match_id): 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) + # Start time is now mandatory + if not start_time_str: + flash('Start time is required. Please select a time slot.', '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() + match.start_time = datetime.strptime(start_time_str, '%H:%M').time() + # Auto-calculate end time if not provided (start + 30 minutes) if end_time_str: match.end_time = datetime.strptime(end_time_str, '%H:%M').time() + else: + # Auto-calculate end time as start + 30 minutes + start_dt = datetime.combine(match.date, match.start_time) + end_dt = start_dt + timedelta(minutes=30) + match.end_time = end_dt.time() except ValueError: pass @@ -400,4 +432,64 @@ def delete_match(match_id): db.session.delete(match) db.session.commit() flash('Match deleted successfully.', 'success') - return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id)) \ No newline at end of file + return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id)) + + +def get_players_available_at_time(date_str, time_str): + """Get list of player IDs available at a specific date and time. + + Args: + date_str: Date in YYYY-MM-DD format + time_str: Time in HH:MM format + + Returns: + List of player IDs who are available at that time + """ + try: + date_obj = datetime.strptime(date_str, '%Y-%m-%d').date() + time_obj = datetime.strptime(time_str, '%H:%M').time() + except (ValueError, TypeError): + return [] + + # Calculate day of week (Python: 0=Monday, 6=Sunday) + # JavaScript: 0=Sunday, 6=Saturday, so we convert + date_parts = date_str.split('-') + date_for_day = datetime(int(date_parts[0]), int(date_parts[1]), int(date_parts[2])) + js_day = date_for_day.weekday() + + # Convert Python weekday (Mon=0) to our format (Mon=0) + day_of_week = js_day + + # Get all active players + players = User.query.filter_by(role='player', is_active_account=True).all() + + available_players = [] + for player in players: + # Check if player has disponibility at this time + disponibilities = PlayerDisponibility.query.filter_by( + player_id=player.id, + day_of_week=day_of_week + ).all() + + for disp in disponibilities: + # Check if time falls within disponibility block + disp_start = disp.start_time.hour * 60 + disp.start_time.minute + disp_end = disp.end_time.hour * 60 + disp.end_time.minute + match_time = time_obj.hour * 60 + time_obj.minute + + if disp_start <= match_time < disp_end: + available_players.append(player.id) + break + + return available_players + + +@matches_bp.route('/api/available_players//