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')
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']
# ermittelt, welche der benötigten Spalten nicht im DataFrame sind
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
valid_keys = ['answer1','answer2','answer3','answer4']
valid_keys = ['answer1', 'answer2', 'answer3', 'answer4']
invalid_mask = ~df['correct'].isin(valid_keys)
if invalid_mask.any():
# finde den ersten Invalid-Eintrag
idx = invalid_mask.idxmax() # DataFrame-Index
excel_line = idx + 2 # Header ist Zeile 1
idx = invalid_mask.idxmax()
excel_line = idx + 2 # Header is line 1
bad_value = df.at[idx, 'correct']
return render_template(
'upload.html',
@ -104,19 +114,23 @@ def upload():
f'"correct" enthält "{bad_value}", '
f'muss einer der Werte {valid_keys} sein.'
)
) # build per-game question list
)
# 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':
@ -419,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
@ -431,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

@ -4,27 +4,43 @@
<!-- Einführung -->
<p class="lead">
Willkommen beim interaktiven Gruppen-Quiz! Lade hier deinen eigenen Fragebogen hoch,
um ein spannendes Live-Quiz zu starten. Deine Teilnehmer bearbeiten die Fragen schnell
und wettbewerbsorientiert, direkt über ihre Smartphones.
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>
<!-- Beschreibung -->
<!-- Punktevergabe -->
<p>
Verwende die <strong>Beispieldatei</strong> als Vorlage und trage deine Fragen und Antwortoptionen
ein. Achte darauf, dass deine Excel-Datei folgende Spalten enthält:
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><code>question</code>: Der Fragentext (z.B. "Wer baute die Arche?")</li>
<li><code>answer1</code> bis <code>answer4</code>: Vier mögliche Antworten</li>
<li><code>correct</code>: Name der richtigen Spalte (<code>answer1</code><code>answer4</code>)</li>
<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 empfohlen.
Unterstützte Formate: <code>.xlsx</code>, <code>.xls</code>
Maximal 100 Fragen!
</p>
<h2 class="mb-4">Fragebogen:</h2>
<!-- Download-Link zur Beispiel-Datei -->
<div class="mb-4">
<a href="{{ url_for('static', filename='questions_example.xlsx') }}" class="btn btn-warning">
@ -45,7 +61,7 @@
required
>
</div>
<button type="submit" class="btn btn-success">Fragebogen hochladen</button>
<button type="submit" class="btn btn-success">Spiel beginnen</button>
</form>
<!-- Fehlermeldung -->