Add right/wrong marking with steal mechanism and question stats
Adds Correct/Wrong buttons to admin panel that track question answer stats across games and implement a steal system where the next team gets one chance to answer if the first team is wrong. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -79,6 +79,9 @@ class Question(db.Model):
|
||||
start_time = db.Column(db.Integer, nullable=True) # Start time in seconds
|
||||
end_time = db.Column(db.Integer, nullable=True) # End time in seconds
|
||||
|
||||
times_correct = db.Column(db.Integer, default=0, nullable=False)
|
||||
times_incorrect = db.Column(db.Integer, default=0, nullable=False)
|
||||
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
|
||||
@@ -109,6 +112,8 @@ class Question(db.Model):
|
||||
'start_time': self.start_time,
|
||||
'end_time': self.end_time,
|
||||
'category': self.category,
|
||||
'times_correct': self.times_correct,
|
||||
'times_incorrect': self.times_incorrect,
|
||||
'created_at': self.created_at.isoformat() if self.created_at else None,
|
||||
'created_by': self.created_by,
|
||||
'creator_name': (self.creator.name or self.creator.preferred_username) if self.creator else None,
|
||||
@@ -157,6 +162,7 @@ class Game(db.Model):
|
||||
current_question_index = db.Column(db.Integer, default=0) # Track current question
|
||||
is_template = db.Column(db.Boolean, default=False) # Mark as reusable template
|
||||
current_turn_team_id = db.Column(db.Integer, db.ForeignKey('teams.id'), nullable=True) # Track whose turn it is
|
||||
steal_active = db.Column(db.Boolean, default=False) # True when a team got it wrong and next team can steal
|
||||
completed_at = db.Column(db.DateTime, nullable=True) # When game ended
|
||||
winners = db.Column(db.JSON, nullable=True) # [{"team_name": str, "score": int}, ...]
|
||||
|
||||
@@ -192,6 +198,7 @@ class Game(db.Model):
|
||||
'is_template': self.is_template,
|
||||
'current_turn_team_id': self.current_turn_team_id,
|
||||
'current_turn_team_name': self.current_turn_team.name if self.current_turn_team else None,
|
||||
'steal_active': self.steal_active,
|
||||
'completed_at': self.completed_at.isoformat() if self.completed_at else None,
|
||||
'winners': self.winners
|
||||
}
|
||||
|
||||
@@ -304,6 +304,43 @@ def seek_audio(game_id):
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@bp.route('/game/<int:game_id>/mark-correct', methods=['POST'])
|
||||
@require_auth
|
||||
def mark_correct(game_id):
|
||||
"""Mark current question as answered correctly by the current turn team"""
|
||||
game = Game.query.get_or_404(game_id)
|
||||
|
||||
try:
|
||||
team = game_service.mark_correct(game, socketio)
|
||||
if team:
|
||||
return jsonify({'message': 'Marked correct', 'team': team.to_dict()}), 200
|
||||
else:
|
||||
return jsonify({'error': 'No current question or team'}), 400
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@bp.route('/game/<int:game_id>/mark-wrong', methods=['POST'])
|
||||
@require_auth
|
||||
def mark_wrong(game_id):
|
||||
"""Mark current question as answered incorrectly by the current turn team"""
|
||||
game = Game.query.get_or_404(game_id)
|
||||
|
||||
try:
|
||||
result = game_service.mark_wrong(game, socketio)
|
||||
return jsonify({
|
||||
'message': 'Marked wrong',
|
||||
'steal_active': result['steal_active'],
|
||||
'both_failed': result['both_failed']
|
||||
}), 200
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@bp.route('/game/<int:game_id>/advance-turn', methods=['POST'])
|
||||
@require_auth
|
||||
def advance_turn(game_id):
|
||||
|
||||
@@ -19,7 +19,8 @@ def get_game_state(game):
|
||||
'is_active': game.is_active,
|
||||
'teams': [team.to_dict() for team in game.teams],
|
||||
'current_turn_team_id': game.current_turn_team_id,
|
||||
'current_turn_team_name': game.current_turn_team.name if game.current_turn_team else None
|
||||
'current_turn_team_name': game.current_turn_team.name if game.current_turn_team else None,
|
||||
'steal_active': game.steal_active
|
||||
}
|
||||
|
||||
if current_question:
|
||||
@@ -49,6 +50,7 @@ def start_game(game, socketio_instance):
|
||||
|
||||
game.is_active = True
|
||||
game.current_question_index = 0
|
||||
game.steal_active = False
|
||||
|
||||
# Set initial turn to the first team (by ID order)
|
||||
ordered_teams = get_ordered_teams(game)
|
||||
@@ -83,8 +85,10 @@ def next_question(game, socketio_instance):
|
||||
"""Move to next question"""
|
||||
if game.current_question_index < len(game.game_questions) - 1:
|
||||
game.current_question_index += 1
|
||||
game.steal_active = False
|
||||
db.session.commit()
|
||||
broadcast_question_change(game, socketio_instance)
|
||||
broadcast_steal_state(game, socketio_instance)
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -93,8 +97,10 @@ def previous_question(game, socketio_instance):
|
||||
"""Move to previous question"""
|
||||
if game.current_question_index > 0:
|
||||
game.current_question_index -= 1
|
||||
game.steal_active = False
|
||||
db.session.commit()
|
||||
broadcast_question_change(game, socketio_instance)
|
||||
broadcast_steal_state(game, socketio_instance)
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -254,6 +260,7 @@ def restart_game(game, socketio_instance):
|
||||
game.is_active = False
|
||||
game.current_question_index = 0
|
||||
game.current_turn_team_id = None # Reset turn
|
||||
game.steal_active = False
|
||||
|
||||
# Reset phone-a-friend lifelines for all teams
|
||||
for team in game.teams:
|
||||
@@ -365,6 +372,76 @@ def advance_turn(game, socketio_instance):
|
||||
return next_team
|
||||
|
||||
|
||||
def mark_correct(game, socketio_instance):
|
||||
"""Mark the current question as answered correctly by the current turn team.
|
||||
Awards 1 point, increments question stats, resets steal state."""
|
||||
current_question = game.get_current_question()
|
||||
if not current_question:
|
||||
return None
|
||||
|
||||
team = game.current_turn_team
|
||||
if not team:
|
||||
return None
|
||||
|
||||
# Increment question stats
|
||||
current_question.times_correct += 1
|
||||
|
||||
# Reset steal state
|
||||
game.steal_active = False
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Award 1 point
|
||||
award_points(game, team, 1, socketio_instance)
|
||||
|
||||
# Broadcast steal state reset
|
||||
broadcast_steal_state(game, socketio_instance)
|
||||
|
||||
return team
|
||||
|
||||
|
||||
def mark_wrong(game, socketio_instance):
|
||||
"""Mark the current question as answered incorrectly.
|
||||
If steal not active: enters steal mode, advances turn.
|
||||
If steal active: both teams failed, resets steal."""
|
||||
current_question = game.get_current_question()
|
||||
if not current_question:
|
||||
return {'steal_active': False, 'both_failed': True}
|
||||
|
||||
# Increment question stats
|
||||
current_question.times_incorrect += 1
|
||||
|
||||
if not game.steal_active:
|
||||
# First wrong answer - enter steal mode, advance turn
|
||||
game.steal_active = True
|
||||
db.session.commit()
|
||||
|
||||
advance_turn(game, socketio_instance)
|
||||
broadcast_steal_state(game, socketio_instance)
|
||||
|
||||
return {'steal_active': True, 'both_failed': False}
|
||||
else:
|
||||
# Second wrong answer - both teams failed
|
||||
game.steal_active = False
|
||||
db.session.commit()
|
||||
|
||||
broadcast_steal_state(game, socketio_instance)
|
||||
|
||||
return {'steal_active': False, 'both_failed': True}
|
||||
|
||||
|
||||
def broadcast_steal_state(game, socketio_instance):
|
||||
"""Broadcast steal state to all connected clients"""
|
||||
steal_data = {
|
||||
'steal_active': game.steal_active,
|
||||
'current_turn_team_id': game.current_turn_team_id,
|
||||
'current_turn_team_name': game.current_turn_team.name if game.current_turn_team else None,
|
||||
}
|
||||
|
||||
socketio_instance.emit('steal_state_changed', steal_data, room=f'game_{game.id}_contestant')
|
||||
socketio_instance.emit('steal_state_changed', steal_data, room=f'game_{game.id}_admin')
|
||||
|
||||
|
||||
def broadcast_turn_change(game, socketio_instance):
|
||||
"""Broadcast turn change to all connected clients"""
|
||||
ordered_teams = get_ordered_teams(game)
|
||||
|
||||
Reference in New Issue
Block a user