305 lines
9.0 KiB
Python
305 lines
9.0 KiB
Python
from flask import Blueprint, request, jsonify
|
|
from backend.models import db, Game, Team
|
|
from backend.services import game_service
|
|
from backend.app import socketio
|
|
from backend.auth.middleware import require_auth
|
|
|
|
bp = Blueprint('admin', __name__, url_prefix='/api/admin')
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/start', methods=['POST'])
|
|
@require_auth
|
|
def start_game(game_id):
|
|
"""Start/activate a game"""
|
|
game = Game.query.get_or_404(game_id)
|
|
|
|
try:
|
|
game_service.start_game(game, socketio)
|
|
return jsonify({'message': 'Game started successfully', 'game': game.to_dict()}), 200
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/next', methods=['POST'])
|
|
@require_auth
|
|
def next_question(game_id):
|
|
"""Move to next question"""
|
|
game = Game.query.get_or_404(game_id)
|
|
|
|
try:
|
|
if game_service.next_question(game, socketio):
|
|
return jsonify({'message': 'Moved to next question', 'current_index': game.current_question_index}), 200
|
|
else:
|
|
return jsonify({'error': 'Already at last question'}), 400
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/prev', methods=['POST'])
|
|
@require_auth
|
|
def previous_question(game_id):
|
|
"""Move to previous question"""
|
|
game = Game.query.get_or_404(game_id)
|
|
|
|
try:
|
|
if game_service.previous_question(game, socketio):
|
|
return jsonify({'message': 'Moved to previous question', 'current_index': game.current_question_index}), 200
|
|
else:
|
|
return jsonify({'error': 'Already at first question'}), 400
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/award', methods=['POST'])
|
|
@require_auth
|
|
def award_points(game_id):
|
|
"""Award points to a team
|
|
|
|
Expected JSON: { "team_id": int, "points": int }
|
|
"""
|
|
game = Game.query.get_or_404(game_id)
|
|
data = request.get_json()
|
|
|
|
if not data or 'team_id' not in data or 'points' not in data:
|
|
return jsonify({'error': 'team_id and points are required'}), 400
|
|
|
|
team_id = data['team_id']
|
|
points = data['points']
|
|
|
|
team = Team.query.get_or_404(team_id)
|
|
|
|
# Verify team belongs to this game
|
|
if team.game_id != game_id:
|
|
return jsonify({'error': 'Team does not belong to this game'}), 400
|
|
|
|
try:
|
|
game_service.award_points(game, team, points, socketio)
|
|
return jsonify({'message': 'Points awarded successfully', 'team': team.to_dict()}), 200
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/current', methods=['GET'])
|
|
@require_auth
|
|
def get_current_state(game_id):
|
|
"""Get current game state with answer (admin only)"""
|
|
game = Game.query.get_or_404(game_id)
|
|
|
|
try:
|
|
state = game_service.get_admin_game_state(game)
|
|
return jsonify(state), 200
|
|
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/toggle-answer', methods=['POST'])
|
|
@require_auth
|
|
def toggle_answer_visibility(game_id):
|
|
"""Toggle answer visibility on contestant screen
|
|
|
|
Expected JSON: { "show_answer": bool }
|
|
"""
|
|
game = Game.query.get_or_404(game_id)
|
|
data = request.get_json()
|
|
|
|
if not data or 'show_answer' not in data:
|
|
return jsonify({'error': 'show_answer is required'}), 400
|
|
|
|
show_answer = data['show_answer']
|
|
|
|
try:
|
|
game_service.toggle_answer_visibility(game, show_answer, socketio)
|
|
return jsonify({'message': 'Answer visibility toggled', 'show_answer': show_answer}), 200
|
|
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/pause-timer', methods=['POST'])
|
|
@require_auth
|
|
def pause_timer(game_id):
|
|
"""Pause or resume the timer
|
|
|
|
Expected JSON: { "paused": bool }
|
|
"""
|
|
game = Game.query.get_or_404(game_id)
|
|
data = request.get_json()
|
|
|
|
if not data or 'paused' not in data:
|
|
return jsonify({'error': 'paused is required'}), 400
|
|
|
|
paused = data['paused']
|
|
|
|
try:
|
|
game_service.toggle_timer_pause(game, paused, socketio)
|
|
return jsonify({'message': 'Timer pause state updated', 'paused': paused}), 200
|
|
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/reset-timer', methods=['POST'])
|
|
@require_auth
|
|
def reset_timer(game_id):
|
|
"""Reset the timer to 30 seconds"""
|
|
game = Game.query.get_or_404(game_id)
|
|
|
|
try:
|
|
game_service.reset_timer(game, socketio)
|
|
return jsonify({'message': 'Timer reset'}), 200
|
|
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/end', methods=['POST'])
|
|
@require_auth
|
|
def end_game(game_id):
|
|
"""End/deactivate a game"""
|
|
game = Game.query.get_or_404(game_id)
|
|
|
|
try:
|
|
game_service.end_game(game, socketio)
|
|
return jsonify({'message': 'Game ended successfully', 'game': game.to_dict()}), 200
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/restart', methods=['POST'])
|
|
@require_auth
|
|
def restart_game(game_id):
|
|
"""Restart a game (clear scores and reset to waiting state)"""
|
|
game = Game.query.get_or_404(game_id)
|
|
|
|
try:
|
|
game_service.restart_game(game, socketio)
|
|
return jsonify({'message': 'Game restarted successfully', 'game': game.to_dict()}), 200
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/team/<int:team_id>/use-lifeline', methods=['POST'])
|
|
@require_auth
|
|
def use_lifeline(game_id, team_id):
|
|
"""Use a phone-a-friend lifeline for a team"""
|
|
game = Game.query.get_or_404(game_id)
|
|
team = Team.query.get_or_404(team_id)
|
|
|
|
# Verify team belongs to this game
|
|
if team.game_id != game_id:
|
|
return jsonify({'error': 'Team does not belong to this game'}), 400
|
|
|
|
if team.phone_a_friend_count <= 0:
|
|
return jsonify({'error': 'No lifelines remaining'}), 400
|
|
|
|
try:
|
|
team.phone_a_friend_count -= 1
|
|
db.session.commit()
|
|
|
|
# Broadcast lifeline update
|
|
game_service.broadcast_lifeline_update(game, team, socketio)
|
|
|
|
return jsonify({'message': 'Lifeline used', 'team': team.to_dict()}), 200
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/team/<int:team_id>/add-lifeline', methods=['POST'])
|
|
@require_auth
|
|
def add_lifeline(game_id, team_id):
|
|
"""Add a phone-a-friend lifeline to a team"""
|
|
game = Game.query.get_or_404(game_id)
|
|
team = Team.query.get_or_404(team_id)
|
|
|
|
# Verify team belongs to this game
|
|
if team.game_id != game_id:
|
|
return jsonify({'error': 'Team does not belong to this game'}), 400
|
|
|
|
try:
|
|
team.phone_a_friend_count += 1
|
|
db.session.commit()
|
|
|
|
# Broadcast lifeline update
|
|
game_service.broadcast_lifeline_update(game, team, socketio)
|
|
|
|
return jsonify({'message': 'Lifeline added', 'team': team.to_dict()}), 200
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/audio/play', methods=['POST'])
|
|
@require_auth
|
|
def play_audio(game_id):
|
|
"""Admin controls audio playback for contestants"""
|
|
game = Game.query.get_or_404(game_id)
|
|
|
|
try:
|
|
game_service.broadcast_audio_play(game, socketio)
|
|
return jsonify({'message': 'Audio play command sent'}), 200
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/audio/pause', methods=['POST'])
|
|
@require_auth
|
|
def pause_audio(game_id):
|
|
"""Pause audio for all contestants"""
|
|
game = Game.query.get_or_404(game_id)
|
|
|
|
try:
|
|
game_service.broadcast_audio_pause(game, socketio)
|
|
return jsonify({'message': 'Audio pause command sent'}), 200
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/audio/stop', methods=['POST'])
|
|
@require_auth
|
|
def stop_audio(game_id):
|
|
"""Stop and reset audio for all contestants"""
|
|
game = Game.query.get_or_404(game_id)
|
|
|
|
try:
|
|
game_service.broadcast_audio_stop(game, socketio)
|
|
return jsonify({'message': 'Audio stop command sent'}), 200
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/game/<int:game_id>/audio/seek', methods=['POST'])
|
|
@require_auth
|
|
def seek_audio(game_id):
|
|
"""Seek audio to specific position
|
|
|
|
Expected JSON: { "position": float }
|
|
"""
|
|
game = Game.query.get_or_404(game_id)
|
|
data = request.get_json()
|
|
|
|
if not data or 'position' not in data:
|
|
return jsonify({'error': 'position is required'}), 400
|
|
|
|
try:
|
|
position = float(data['position'])
|
|
game_service.broadcast_audio_seek(game, position, socketio)
|
|
return jsonify({'message': f'Audio seeked to {position}s'}), 200
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|