diff --git a/app.py b/app.py index b30638d..7654874 100644 --- a/app.py +++ b/app.py @@ -9,7 +9,7 @@ from flask import Flask, render_template, request, session, redirect, url_for, a from flask_socketio import SocketIO, emit, join_room from dotenv import load_dotenv -# global store of all games, keyed by secret +# global store of all games, keyed by secret. Each entry will hold its own questions list. games = {} # Load environment vars @@ -19,18 +19,6 @@ app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'dev') # manage_session ensures Flask session is available in SocketIO handlers socketio = SocketIO(app, manage_session=True) -# Expect columns: question, answer1..answer4, correct (column name) -df = pd.read_excel('quizzes/questions.xlsx') -questions = [] -for _, row in df.iterrows(): - correct_col = row['correct'] # e.g. 'answer3' - correct_text = row[correct_col] - questions.append({ - 'question': row['question'], - 'options': [row[f'answer{i}'] for i in range(1,5)], # only four options - 'correct': correct_text - }) - # In-memory game state game = { 'players': {}, @@ -40,7 +28,7 @@ game = { 'started': False } -@app.route('/') +@app.route('/', methods=['GET']) def host(): session['role'] = 'host' # — manage per-game secret in session — @@ -48,14 +36,19 @@ def host(): if not secret: secret = uuid.uuid4().hex[:8] session['secret'] = secret - # ensure game exists + # ensure game exists (with questions placeholder) if secret not in games: games[secret] = { + 'questions': None, 'players': {}, 'order': [], 'current_q': 0, 'answered': [], 'started': False, 'finished': False, 'revealed': False } + game = games[secret] + # if no question file has been uploaded yet, show upload form + if not game.get('questions'): + return render_template('upload.html') # build join URL + QR join_url = url_for('player', secret=secret, _external=True) @@ -71,6 +64,41 @@ def host(): secret=secret ) +@app.route('/upload', methods=['POST']) +def upload(): + session['role'] = 'host' + secret = session.get('secret') + if not secret or secret not in games: + return redirect(url_for('host')) + game = games[secret] + # retrieve uploaded file + file = request.files.get('file') + if not file: + return render_template('upload.html', error='Keine Datei ausgewählt') + # try to read Excel + try: + df = pd.read_excel(file) + except Exception: + return render_template('upload.html', error='Ungültige Excel-Datei') + # mandatory columns + required = ['question','answer1','answer2','answer3','answer4','correct'] + if not all(col in df.columns for col in required): + return render_template('upload.html', error=f'Fehler in Datei: Fehlende Spalten. Erforderlich: {required}') + # validate that 'correct' references one of the answer columns + if not df['correct'].isin(['answer1','answer2','answer3','answer4']).all(): + return render_template('upload.html', error='Fehler in Datei: Antwortspalte "correct" muss einen der Werte "answer1", "answer2", "answer3" oder "answer4" enthalten.') + # build per-game question list + questions = [] + for _, row in df.iterrows(): + col = row['correct'] + questions.append({ + 'question': row['question'], + 'options': [row[f'answer{i}'] for i in range(1,5)], + 'correct': row[col] + }) + game['questions'] = questions + # once upload and validation succeeds, redirect to host view (which will show the QR) + return redirect(url_for('host')) @app.route('/new_game', methods=['POST']) def new_game(): @@ -185,8 +213,8 @@ def on_connect(): players = [game['players'][pid]['name'] for pid in game['order']] emit('update_roster', {'players': players}) - if game.get('started') and game['current_q'] < len(questions): - q = questions[game['current_q']] + if game.get('started') and game['current_q'] < len(game['questions']): + q = game['questions'][game['current_q']] emit('new_question', {'question': q['question']}, room='host') return @@ -223,7 +251,7 @@ def on_start_game(): game = games[secret] # if already through, reset - if game.get('started') or game['current_q'] >= len(questions): + if game.get('started') or game['current_q'] >= len(game['questions']): game.update({ 'current_q': 0, 'answered': [], @@ -274,7 +302,7 @@ def on_next_question(): game = games[secret] game['current_q'] += 1 - if game['current_q'] < len(questions): + if game['current_q'] < len(game['questions']): _send_question(secret) else: # mark finished & emit final boards @@ -314,7 +342,7 @@ def on_show_answer(): game = games[secret] game['revealed'] = True # find the correct answer text for current question - correct = questions[game['current_q']]['correct'] + correct = game['questions'][game['current_q']]['correct'] # emit to host so they can display it emit('answer_revealed', {'correct': correct}, room='host') # emit to all players so they can highlight it @@ -343,7 +371,7 @@ def _send_sync_state(pid, game): # question? else: idx = game['current_q'] - q = questions[idx] + q = game['questions'][idx] answered = any(pid == p for p,_ in game['answered']) emit('sync_state', { 'state': 'question', @@ -356,7 +384,7 @@ def _send_sync_state(pid, game): def _send_question(secret): game = games[secret] game['revealed'] = False - q = questions[game['current_q']] + q = game['questions'][game['current_q']] # clear previous answers game['answered'].clear() # broadcast @@ -378,7 +406,7 @@ def _score_and_emit(secret): per_q = [] # grab the current question’s correct answer - correct = questions[game['current_q']]['correct'] + correct = game['questions'][game['current_q']]['correct'] # take only the most‐recent submission pid, ans = game['answered'][-1] @@ -408,7 +436,7 @@ def _score_and_emit(secret): socketio.emit('overall_leader', {'board': board}, room='player') # if that was the last question, finish up - if game['current_q'] >= len(questions): + if game['current_q'] >= len(game['questions']): game['finished'] = True socketio.emit('game_over', {'board': board}, room='host') for psid, pdata in game['players'].items(): diff --git a/quizzes/questions.xlsx b/static/questions_example.xlsx similarity index 100% rename from quizzes/questions.xlsx rename to static/questions_example.xlsx diff --git a/templates/host.html b/templates/host.html index 1816bee..a206cc8 100644 --- a/templates/host.html +++ b/templates/host.html @@ -1,7 +1,7 @@ {% extends 'base.html' %} {% block content %} -

Bibelquiz

+

Public Quiz

diff --git a/templates/player.html b/templates/player.html index 03259d9..73c0951 100644 --- a/templates/player.html +++ b/templates/player.html @@ -22,7 +22,7 @@
+ class="form-control" placeholder="Deinen Name eingeben..." />
diff --git a/templates/upload.html b/templates/upload.html new file mode 100644 index 0000000..237b72f --- /dev/null +++ b/templates/upload.html @@ -0,0 +1,24 @@ +{% extends 'base.html' %} +{% block content %} +

Fragebogen hochladen

+ + + + +
+
+ + +
+ +
+ +{% if error %} +
{{ error }}
+{% endif %} + +{% endblock %} \ No newline at end of file