Compare commits

...

3 Commits

Author SHA1 Message Date
93f8563807 further description and improved point system 2025-07-06 14:42:30 +00:00
b3d4f4a198 add detailed error messages 2025-07-06 14:15:27 +00:00
b4f7c8eae0 add description 2025-07-06 14:15:06 +00:00
2 changed files with 116 additions and 30 deletions

63
app.py
View File

@ -70,36 +70,67 @@ def upload():
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')
# 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
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}')
required = ['question', 'answer1', 'answer2', 'answer3', 'answer4', 'correct']
missing = [col for col in required if col not in df.columns]
if missing:
return render_template(
'upload.html',
error=f"Fehler in Datei: Fehlende Spalte(n): {', '.join(missing)}"
)
# 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.')
valid_keys = ['answer1', 'answer2', 'answer3', 'answer4']
invalid_mask = ~df['correct'].isin(valid_keys)
if invalid_mask.any():
idx = invalid_mask.idxmax()
excel_line = idx + 2 # Header is line 1
bad_value = df.at[idx, 'correct']
return render_template(
'upload.html',
error=(
f'Fehler in Zeile {excel_line}: '
f'"correct" enthält "{bad_value}", '
f'muss einer der Werte {valid_keys} sein.'
)
)
# 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]
'question': str(row['question']),
'options': [str(row[f'answer{i}']) for i in range(1, 5)],
'correct': str(row[col])
})
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'))
@app.route('/new_game', methods=['POST'])
def new_game():
if session.get('role') != 'host':
@ -402,7 +433,12 @@ def _send_question(secret):
def _score_and_emit(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 = []
# grab the current questions correct answer
@ -414,9 +450,10 @@ def _score_and_emit(secret):
prev_correct = sum(1 for p, a in game['answered'][:-1] if a == correct)
rank = prev_correct + 1
# assign points only if this one is correct and within top4
if ans == correct and rank <= 4:
pts = score_map[rank]
# assign points only if korrekt
if ans == correct:
# für Platz 13 nach Mapping, ab Platz 4 immer 1 Punkt
pts = score_map.get(rank, 1)
game['players'][pid]['score'] += pts
per_q.append({
'name': game['players'][pid]['name'],

View File

@ -1,24 +1,73 @@
{% extends 'base.html' %}
{% block content %}
<h1 class="mb-4">Fragebogen hochladen</h1>
<h1 class="mb-4">Public Quiz</h1>
<!-- Download-Link zur Beispiel-Datei -->
<div class="mb-3">
<a href="{{ url_for('static', filename='questions_example.xlsx') }}" class="btn btn-outline-secondary">
Beispiel-Excel-Datei herunterladen
</a>
</div>
<!-- Einführung -->
<p class="lead">
Willkommen zum interaktiven Gruppenquiz! Lade deinen eigenen Fragebogen hoch und starte ein spannendes Live-Quiz.
Deine Teilnehmer beantworten die Fragen schnell und wettbewerbsorientiert direkt über ihre Smartphones.
</p>
<form action="{{ url_for('upload') }}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label for="file" class="form-label">Excel-Datei auswählen</label>
<input class="form-control" type="file" id="file" name="file" accept=".xlsx,.xls">
<!-- Punktevergabe -->
<p>
Schnelle und richtige Antworten werden mit mehr Punkten belohnt. Jeder Spieler hat pro Frage nur einen Versuch.
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>
<ul>
<li>erste richtige Antwort → 5 Punkte</li>
<li>zweite richtige Antwort → 4 Punkte</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>
<p class="text-muted">
Unterstützte Formate: <code>.xlsx</code>, <code>.xls</code>
Maximal 100 Fragen!
</p>
<!-- Download-Link zur Beispiel-Datei -->
<div class="mb-4">
<a href="{{ url_for('static', filename='questions_example.xlsx') }}" class="btn btn-warning">
Beispieldatei Fragebogen (Excel)
</a>
</div>
<button type="submit" class="btn btn-primary">Hochladen</button>
</form>
{% if error %}
<div class="alert alert-danger mt-3">{{ error }}</div>
{% endif %}
<!-- Upload-Form -->
<form action="{{ url_for('upload') }}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label for="file" class="form-label">Fragebogen hochladen</label>
<input
type="file"
id="file"
name="file"
class="form-control"
accept=".xlsx, .xls"
required
>
</div>
<button type="submit" class="btn btn-success">Spiel beginnen</button>
</form>
{% endblock %}
<!-- Fehlermeldung -->
{% if error %}
<div class="alert alert-danger mt-3" role="alert">
{{ error }}
</div>
{% endif %}
{% endblock %}