381 lines
14 KiB
Python
381 lines
14 KiB
Python
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
|
|
from flask_login import login_required, current_user
|
|
from extensions import db, hash_password
|
|
from models import User, ROLES, ESPORT_GAMES, PlayerDisponibility, UserGamertag, GAME_PLATFORMS
|
|
from datetime import datetime, timedelta
|
|
|
|
users_bp = Blueprint('users', __name__, url_prefix='/users')
|
|
|
|
@users_bp.route('')
|
|
@login_required
|
|
def list_users():
|
|
if current_user.role != 'president':
|
|
flash('Only the president can manage users.', 'danger')
|
|
return redirect(url_for('main.dashboard'))
|
|
|
|
users = User.query.order_by(User.role, User.full_name).all()
|
|
return render_template('pages/users.html', users=users, roles=ROLES)
|
|
|
|
@users_bp.route('/<int:user_id>/edit', methods=['GET', 'POST'])
|
|
@login_required
|
|
def edit_user(user_id):
|
|
if current_user.role != 'president':
|
|
flash('Only the president can edit users.', 'danger')
|
|
return redirect(url_for('main.dashboard'))
|
|
|
|
user = User.query.get_or_404(user_id)
|
|
|
|
if request.method == 'POST':
|
|
full_name = request.form.get('full_name')
|
|
email = request.form.get('email')
|
|
phone = request.form.get('phone')
|
|
role = request.form.get('role')
|
|
is_active = request.form.get('is_active_account') == 'on'
|
|
|
|
if role not in ROLES:
|
|
flash('Invalid role selected.', 'danger')
|
|
return render_template('pages/edit_user.html', user=user, roles=ROLES, esport_games=ESPORT_GAMES, game_platforms=GAME_PLATFORMS)
|
|
|
|
selected_games = request.form.getlist('games')
|
|
discord_username = request.form.get('discord_username', '').strip()
|
|
league_os_profile = request.form.get('league_os_profile', '').strip()
|
|
|
|
user.full_name = full_name
|
|
user.email = email
|
|
user.phone = phone
|
|
user.role = role
|
|
user.is_active_account = is_active
|
|
user.games = ','.join(selected_games) if selected_games else None
|
|
user.discord_username = discord_username or None
|
|
user.league_os_profile = league_os_profile or None
|
|
|
|
# Handle gamertags - save or delete based on form input
|
|
# First, get all existing gamertags for this user
|
|
existing_gamertags = {gt.game: gt for gt in user.gamertags}
|
|
|
|
for game in selected_games:
|
|
gamertag = request.form.get(f'gamertag_{game}', '').strip()
|
|
platform = request.form.get(f'platform_{game}', '').strip() if GAME_PLATFORMS.get(game) else None
|
|
|
|
existing = existing_gamertags.get(game)
|
|
if gamertag:
|
|
if existing:
|
|
existing.gamertag = gamertag
|
|
existing.platform = platform
|
|
else:
|
|
gt = UserGamertag(user_id=user.id, game=game, gamertag=gamertag, platform=platform)
|
|
db.session.add(gt)
|
|
elif existing:
|
|
db.session.delete(existing)
|
|
|
|
# Remove gamertags for games that are no longer selected
|
|
for game in existing_gamertags:
|
|
if game not in selected_games:
|
|
db.session.delete(existing_gamertags[game])
|
|
|
|
password = request.form.get('password')
|
|
if password:
|
|
user.password_hash = hash_password(password)
|
|
|
|
db.session.commit()
|
|
flash(f'User {user.full_name} updated successfully!', 'success')
|
|
return redirect(url_for('users.list_users'))
|
|
|
|
user_gamertags = {gt.game: {'gamertag': gt.gamertag, 'platform': gt.platform} for gt in user.gamertags}
|
|
return render_template('pages/edit_user.html', user=user, roles=ROLES, esport_games=ESPORT_GAMES, game_platforms=GAME_PLATFORMS, user_gamertags=user_gamertags)
|
|
|
|
@users_bp.route('/<int:user_id>/delete', methods=['POST'])
|
|
@login_required
|
|
def delete_user(user_id):
|
|
if current_user.role != 'president':
|
|
flash('Only the president can delete users.', 'danger')
|
|
return redirect(url_for('main.dashboard'))
|
|
|
|
if current_user.id == user_id:
|
|
flash('You cannot delete your own account.', 'danger')
|
|
return redirect(url_for('users.list_users'))
|
|
|
|
user = User.query.get_or_404(user_id)
|
|
db.session.delete(user)
|
|
db.session.commit()
|
|
flash(f'User {user.full_name} has been removed.', 'success')
|
|
return redirect(url_for('users.list_users'))
|
|
|
|
@users_bp.route('/create', methods=['GET', 'POST'])
|
|
@login_required
|
|
def create_user():
|
|
if current_user.role != 'president':
|
|
flash('Only the president can create users.', 'danger')
|
|
return redirect(url_for('main.dashboard'))
|
|
|
|
if request.method == 'POST':
|
|
username = request.form.get('username')
|
|
email = request.form.get('email')
|
|
password = request.form.get('password')
|
|
full_name = request.form.get('full_name')
|
|
phone = request.form.get('phone')
|
|
role = request.form.get('role')
|
|
|
|
if role not in ROLES:
|
|
flash('Invalid role selected.', 'danger')
|
|
return render_template('pages/create_user.html', roles=ROLES)
|
|
|
|
if User.query.filter_by(username=username).first():
|
|
flash('Username already exists.', 'danger')
|
|
return render_template('pages/create_user.html', roles=ROLES)
|
|
|
|
if User.query.filter_by(email=email).first():
|
|
flash('Email already registered.', 'danger')
|
|
return render_template('pages/create_user.html', roles=ROLES)
|
|
|
|
hashed_password = hash_password(password)
|
|
user = User(
|
|
username=username,
|
|
password_hash=hashed_password,
|
|
role=role,
|
|
full_name=full_name,
|
|
email=email,
|
|
phone=phone
|
|
)
|
|
db.session.add(user)
|
|
db.session.commit()
|
|
flash(f'User {full_name} created as {role}!', 'success')
|
|
return redirect(url_for('users.list_users'))
|
|
|
|
return render_template('pages/create_user.html', roles=ROLES)
|
|
|
|
@users_bp.route('/profile')
|
|
@login_required
|
|
def profile():
|
|
return render_template('pages/profile.html', user=current_user)
|
|
|
|
@users_bp.route('/profile/edit', methods=['GET', 'POST'])
|
|
@login_required
|
|
def edit_profile():
|
|
if request.method == 'POST':
|
|
full_name = request.form.get('full_name')
|
|
email = request.form.get('email')
|
|
phone = request.form.get('phone')
|
|
|
|
selected_games = request.form.getlist('games')
|
|
discord_username = request.form.get('discord_username', '').strip()
|
|
league_os_profile = request.form.get('league_os_profile', '').strip()
|
|
|
|
if email != current_user.email and User.query.filter_by(email=email).first():
|
|
flash('Email already in use.', 'danger')
|
|
return render_template('pages/edit_profile.html', user=current_user, esport_games=ESPORT_GAMES, game_platforms=GAME_PLATFORMS)
|
|
|
|
current_user.full_name = full_name
|
|
current_user.email = email
|
|
current_user.phone = phone
|
|
current_user.games = ','.join(selected_games) if selected_games else None
|
|
current_user.discord_username = discord_username or None
|
|
current_user.league_os_profile = league_os_profile or None
|
|
|
|
# Handle gamertags - save or delete based on form input
|
|
# First, get all existing gamertags for this user
|
|
existing_gamertags = {gt.game: gt for gt in current_user.gamertags}
|
|
|
|
for game in selected_games:
|
|
gamertag = request.form.get(f'gamertag_{game}', '').strip()
|
|
platform = request.form.get(f'platform_{game}', '').strip() if GAME_PLATFORMS.get(game) else None
|
|
|
|
existing = existing_gamertags.get(game)
|
|
if gamertag:
|
|
if existing:
|
|
existing.gamertag = gamertag
|
|
existing.platform = platform
|
|
else:
|
|
gt = UserGamertag(user_id=current_user.id, game=game, gamertag=gamertag, platform=platform)
|
|
db.session.add(gt)
|
|
elif existing:
|
|
db.session.delete(existing)
|
|
|
|
# Remove gamertags for games that are no longer selected
|
|
for game in existing_gamertags:
|
|
if game not in selected_games:
|
|
db.session.delete(existing_gamertags[game])
|
|
|
|
password = request.form.get('password')
|
|
if password:
|
|
current_user.password_hash = hash_password(password)
|
|
|
|
db.session.commit()
|
|
flash('Profile updated successfully!', 'success')
|
|
return redirect(url_for('users.profile'))
|
|
|
|
user_gamertags = {gt.game: {'gamertag': gt.gamertag, 'platform': gt.platform} for gt in current_user.gamertags}
|
|
return render_template('pages/edit_profile.html', user=current_user, esport_games=ESPORT_GAMES, game_platforms=GAME_PLATFORMS, user_gamertags=user_gamertags)
|
|
|
|
|
|
# Day names for disponibility
|
|
DAY_NAMES = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
|
|
|
|
|
|
def add_30_minutes(t):
|
|
"""Add 30 minutes to a time object."""
|
|
return (datetime.combine(datetime.today(), t) + timedelta(minutes=30)).time()
|
|
|
|
|
|
@users_bp.route('/disponibilities')
|
|
@login_required
|
|
def get_disponibilities():
|
|
"""API endpoint to get all player disponibilities for scheduling."""
|
|
# Only managers and above can view disponibilities for scheduling
|
|
if not current_user.can_manage_teams() and not current_user.can_schedule_matches():
|
|
return jsonify({'error': 'Unauthorized'}), 403
|
|
|
|
players = User.query.filter_by(role='player', is_active_account=True).order_by(User.full_name).all()
|
|
result = {}
|
|
|
|
for player in players:
|
|
disponibilities = list(player.disponibilities)
|
|
result[player.id] = {
|
|
'full_name': player.full_name,
|
|
'disponibilities': [
|
|
{
|
|
'id': d.id,
|
|
'day_of_week': d.day_of_week,
|
|
'day_name': DAY_NAMES[d.day_of_week],
|
|
'start_time': d.start_time.strftime('%H:%M'),
|
|
'end_time': d.end_time.strftime('%H:%M')
|
|
}
|
|
for d in disponibilities
|
|
]
|
|
}
|
|
|
|
return jsonify(result)
|
|
|
|
|
|
@users_bp.route('/disponibilities/my')
|
|
@login_required
|
|
def get_my_disponibilities():
|
|
"""API endpoint for players to get their own disponibilities."""
|
|
disponibilities = PlayerDisponibility.query.filter_by(player_id=current_user.id).all()
|
|
|
|
# Group by day for easier display
|
|
result = {}
|
|
for d in disponibilities:
|
|
day = d.day_of_week
|
|
if day not in result:
|
|
result[day] = []
|
|
result[day].append({
|
|
'id': d.id,
|
|
'day_of_week': d.day_of_week,
|
|
'day_name': DAY_NAMES[d.day_of_week],
|
|
'start_time': d.start_time.strftime('%H:%M'),
|
|
'end_time': d.end_time.strftime('%H:%M')
|
|
})
|
|
|
|
return jsonify(result)
|
|
|
|
|
|
@users_bp.route('/disponibilities/add', methods=['POST'])
|
|
@login_required
|
|
def add_disponibility():
|
|
"""Add a disponibility block for the current player."""
|
|
day_of_week = request.form.get('day_of_week', type=int)
|
|
start_time_str = request.form.get('start_time')
|
|
|
|
if day_of_week is None or day_of_week < 0 or day_of_week > 6:
|
|
return jsonify({'error': 'Invalid day of week'}), 400
|
|
|
|
try:
|
|
start_time = datetime.strptime(start_time_str, '%H:%M').time()
|
|
except (ValueError, TypeError):
|
|
return jsonify({'error': 'Invalid time format'}), 400
|
|
|
|
end_time = add_30_minutes(start_time)
|
|
|
|
disponibility = PlayerDisponibility(
|
|
player_id=current_user.id,
|
|
day_of_week=day_of_week,
|
|
start_time=start_time,
|
|
end_time=end_time
|
|
)
|
|
|
|
db.session.add(disponibility)
|
|
db.session.commit()
|
|
|
|
return jsonify({
|
|
'id': disponibility.id,
|
|
'day_of_week': disponibility.day_of_week,
|
|
'day_name': DAY_NAMES[disponibility.day_of_week],
|
|
'start_time': disponibility.start_time.strftime('%H:%M'),
|
|
'end_time': disponibility.end_time.strftime('%H:%M')
|
|
})
|
|
|
|
|
|
@users_bp.route('/disponibilities/add_bulk', methods=['POST'])
|
|
@login_required
|
|
def add_disponibilities_bulk():
|
|
"""Add multiple disponibility blocks at once (for grid selection)."""
|
|
data = request.get_json()
|
|
slots = data.get('slots', []) # List of {day_of_week, start_time}
|
|
|
|
created = []
|
|
for slot in slots:
|
|
day_of_week = slot.get('day_of_week')
|
|
start_time_str = slot.get('start_time')
|
|
|
|
if day_of_week is None or day_of_week < 0 or day_of_week > 6:
|
|
continue
|
|
|
|
try:
|
|
start_time = datetime.strptime(start_time_str, '%H:%M').time()
|
|
except (ValueError, TypeError):
|
|
continue
|
|
|
|
end_time = add_30_minutes(start_time)
|
|
|
|
# Check if this slot already exists for this player
|
|
existing = PlayerDisponibility.query.filter_by(
|
|
player_id=current_user.id,
|
|
day_of_week=day_of_week,
|
|
start_time=start_time
|
|
).first()
|
|
|
|
if not existing:
|
|
disponibility = PlayerDisponibility(
|
|
player_id=current_user.id,
|
|
day_of_week=day_of_week,
|
|
start_time=start_time,
|
|
end_time=end_time
|
|
)
|
|
db.session.add(disponibility)
|
|
db.session.flush()
|
|
created.append({
|
|
'id': disponibility.id,
|
|
'day_of_week': disponibility.day_of_week,
|
|
'day_name': DAY_NAMES[disponibility.day_of_week],
|
|
'start_time': disponibility.start_time.strftime('%H:%M'),
|
|
'end_time': disponibility.start_time.strftime('%H:%M')
|
|
})
|
|
|
|
db.session.commit()
|
|
return jsonify({'success': True, 'created': created})
|
|
|
|
|
|
@users_bp.route('/disponibilities/clear', methods=['POST'])
|
|
@login_required
|
|
def clear_disponibilities():
|
|
"""Clear all disponibilities for the current player (for resetting)."""
|
|
PlayerDisponibility.query.filter_by(player_id=current_user.id).delete()
|
|
db.session.commit()
|
|
return jsonify({'success': True})
|
|
|
|
|
|
@users_bp.route('/disponibilities/<int:disponibility_id>/delete', methods=['POST'])
|
|
@login_required
|
|
def delete_disponibility(disponibility_id):
|
|
"""Delete a disponibility block."""
|
|
disponibility = PlayerDisponibility.query.get_or_404(disponibility_id)
|
|
|
|
# Only the owner can delete their disponibility
|
|
if disponibility.player_id != current_user.id:
|
|
return jsonify({'error': 'Unauthorized'}), 403
|
|
|
|
db.session.delete(disponibility)
|
|
db.session.commit()
|
|
|
|
return jsonify({'success': True}) |