further description and improved point system
This commit is contained in:
parent
b3d4f4a198
commit
93f8563807
50
app.py
50
app.py
@ -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 question’s correct answer
|
# grab the current question’s 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 top‐4
|
# assign points only if korrekt
|
||||||
if ans == correct and rank <= 4:
|
if ans == correct:
|
||||||
pts = score_map[rank]
|
# für Platz 1–3 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'],
|
||||||
|
|||||||
@ -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 -->
|
||||||
@ -54,4 +70,4 @@
|
|||||||
{{ error }}
|
{{ error }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user