further description and improved point system

This commit is contained in:
lelo 2025-07-06 14:42:30 +00:00
parent b3d4f4a198
commit 93f8563807
2 changed files with 65 additions and 29 deletions

50
app.py
View File

@ -70,32 +70,42 @@ def upload():
secret = session.get('secret') secret = session.get('secret')
if not secret or secret not in games: if not secret or secret not in games:
return redirect(url_for('host')) return redirect(url_for('host'))
game = games[secret] game = games[secret]
# retrieve uploaded file # retrieve uploaded file
file = request.files.get('file') file = request.files.get('file')
if not file: if not file:
return render_template('upload.html', error='Keine Datei ausgewählt') return render_template('upload.html', error='Keine Datei ausgewählt')
# try to read Excel # try to read Excel
try: try:
df = pd.read_excel(file) df = pd.read_excel(file)
except Exception: except Exception:
return render_template('upload.html', error='Ungültige Excel-Datei') return render_template('upload.html', error='Ungültige Excel-Datei')
# enforce maximum of 100 questions
max_questions = 100
if df.shape[0] > max_questions:
return render_template(
'upload.html',
error=f'Maximal dürfen {max_questions} Fragen hochgeladen werden. Ihre Datei enthält {df.shape[0]}.')
# mandatory columns # mandatory columns
required = ['question','answer1','answer2','answer3','answer4','correct'] required = ['question', 'answer1', 'answer2', 'answer3', 'answer4', 'correct']
# ermittelt, welche der benötigten Spalten nicht im DataFrame sind
missing = [col for col in required if col not in df.columns] missing = [col for col in required if col not in df.columns]
if missing: if missing:
return render_template( return render_template(
'upload.html', 'upload.html',
error=f"Fehler in Datei: Fehlende Spalte(n): {', '.join(missing)}" error=f"Fehler in Datei: Fehlende Spalte(n): {', '.join(missing)}"
) )
# validate that 'correct' references one of the answer columns # validate that 'correct' references one of the answer columns
valid_keys = ['answer1','answer2','answer3','answer4'] valid_keys = ['answer1', 'answer2', 'answer3', 'answer4']
invalid_mask = ~df['correct'].isin(valid_keys) invalid_mask = ~df['correct'].isin(valid_keys)
if invalid_mask.any(): if invalid_mask.any():
# finde den ersten Invalid-Eintrag idx = invalid_mask.idxmax()
idx = invalid_mask.idxmax() # DataFrame-Index excel_line = idx + 2 # Header is line 1
excel_line = idx + 2 # Header ist Zeile 1
bad_value = df.at[idx, 'correct'] bad_value = df.at[idx, 'correct']
return render_template( return render_template(
'upload.html', 'upload.html',
@ -104,19 +114,23 @@ def upload():
f'"correct" enthält "{bad_value}", ' f'"correct" enthält "{bad_value}", '
f'muss einer der Werte {valid_keys} sein.' f'muss einer der Werte {valid_keys} sein.'
) )
) # build per-game question list )
# build per-game question list
questions = [] questions = []
for _, row in df.iterrows(): for _, row in df.iterrows():
col = row['correct'] col = row['correct']
questions.append({ questions.append({
'question': row['question'], 'question': str(row['question']),
'options': [row[f'answer{i}'] for i in range(1,5)], 'options': [str(row[f'answer{i}']) for i in range(1, 5)],
'correct': row[col] 'correct': str(row[col])
}) })
game['questions'] = questions game['questions'] = questions
# once upload and validation succeeds, redirect to host view (which will show the QR) # redirect to host view, which will show the QR code
return redirect(url_for('host')) return redirect(url_for('host'))
@app.route('/new_game', methods=['POST']) @app.route('/new_game', methods=['POST'])
def new_game(): def new_game():
if session.get('role') != 'host': if session.get('role') != 'host':
@ -419,7 +433,12 @@ def _send_question(secret):
def _score_and_emit(secret): def _score_and_emit(secret):
game = games[secret] game = games[secret]
score_map = {1: 4, 2: 3, 3: 2, 4: 1} # Punkteverteilung:
# 1. richtige Antwort → 5 Punkte
# 2. richtige Antwort → 4 Punkte
# 3. richtige Antwort → 3 Punkte
# alle weiteren richtigen Antworten → 1 Punkt
score_map = {1: 5, 2: 4, 3: 3}
per_q = [] per_q = []
# grab the current questions correct answer # grab the current questions correct answer
@ -431,9 +450,10 @@ def _score_and_emit(secret):
prev_correct = sum(1 for p, a in game['answered'][:-1] if a == correct) prev_correct = sum(1 for p, a in game['answered'][:-1] if a == correct)
rank = prev_correct + 1 rank = prev_correct + 1
# assign points only if this one is correct and within top4 # assign points only if korrekt
if ans == correct and rank <= 4: if ans == correct:
pts = score_map[rank] # für Platz 13 nach Mapping, ab Platz 4 immer 1 Punkt
pts = score_map.get(rank, 1)
game['players'][pid]['score'] += pts game['players'][pid]['score'] += pts
per_q.append({ per_q.append({
'name': game['players'][pid]['name'], 'name': game['players'][pid]['name'],

View File

@ -4,27 +4,43 @@
<!-- Einführung --> <!-- Einführung -->
<p class="lead"> <p class="lead">
Willkommen beim interaktiven Gruppen-Quiz! Lade hier deinen eigenen Fragebogen hoch, Willkommen zum interaktiven Gruppenquiz! Lade deinen eigenen Fragebogen hoch und starte ein spannendes Live-Quiz.
um ein spannendes Live-Quiz zu starten. Deine Teilnehmer bearbeiten die Fragen schnell Deine Teilnehmer beantworten die Fragen schnell und wettbewerbsorientiert direkt über ihre Smartphones.
und wettbewerbsorientiert, direkt über ihre Smartphones.
</p> </p>
<!-- Beschreibung --> <!-- Punktevergabe -->
<p> <p>
Verwende die <strong>Beispieldatei</strong> als Vorlage und trage deine Fragen und Antwortoptionen Schnelle und richtige Antworten werden mit mehr Punkten belohnt. Jeder Spieler hat pro Frage nur einen Versuch.
ein. Achte darauf, dass deine Excel-Datei folgende Spalten enthält: Ein guter Mix aus Schnelligkeit und Genauigkeit zahlt sich aus!
</p>
<p class="text-muted">
<strong>Hinweis:</strong> Die ersten drei richtigen Antworten erhalten höhere Punktwerte, alle weiteren richtigen Antworten bringen jeweils 1 Punkt.
</p> </p>
<ul> <ul>
<li><code>question</code>: Der Fragentext (z.B. "Wer baute die Arche?")</li> <li>erste richtige Antwort → 5 Punkte</li>
<li><code>answer1</code> bis <code>answer4</code>: Vier mögliche Antworten</li> <li>zweite richtige Antwort → 4 Punkte</li>
<li><code>correct</code>: Name der richtigen Spalte (<code>answer1</code><code>answer4</code>)</li> <li>dritte richtige Antwort → 3 Punkte</li>
<li>jeder weitere richtige Antwort → 1 Punkt</li>
</ul>
<p>Sobald die richtige Antwort für alle anzeigt wird, werden keine weiteren Punkte verteilt.</p>
<!-- Dateivorlage -->
<h2 class="mb-4">Fragebogen erstellen</h2>
<p>
Nutze die <strong>Beispieldatei</strong> als Vorlage und trage deine Fragen sowie die Antwortoptionen ein.
Deine Excel-Datei sollte folgende Spalten enthalten:
</p>
<ul>
<li><code>question</code>: Text der Frage (z. B. „Wer baute die Arche?“)</li>
<li><code>answer1</code> bis <code>answer4</code>: Vier Antwortmöglichkeiten</li>
<li><code>correct</code>: Spaltenname der richtigen Antwort (<code>answer1</code>-<code>answer4</code>)</li>
</ul> </ul>
<p class="text-muted"> <p class="text-muted">
Unterstützte Formate: <code>.xlsx</code>, <code>.xls</code>. Maximal 100 Fragen empfohlen. Unterstützte Formate: <code>.xlsx</code>, <code>.xls</code>
Maximal 100 Fragen!
</p> </p>
<h2 class="mb-4">Fragebogen:</h2>
<!-- Download-Link zur Beispiel-Datei --> <!-- Download-Link zur Beispiel-Datei -->
<div class="mb-4"> <div class="mb-4">
<a href="{{ url_for('static', filename='questions_example.xlsx') }}" class="btn btn-warning"> <a href="{{ url_for('static', filename='questions_example.xlsx') }}" class="btn btn-warning">
@ -45,7 +61,7 @@
required required
> >
</div> </div>
<button type="submit" class="btn btn-success">Fragebogen hochladen</button> <button type="submit" class="btn btn-success">Spiel beginnen</button>
</form> </form>
<!-- Fehlermeldung --> <!-- Fehlermeldung -->