First version
Browse files- app.py +78 -16
- requirements.txt +1 -0
- static/script.js +41 -9
- templates/client.html +3 -0
- templates/host.html +8 -3
app.py
CHANGED
|
@@ -1,17 +1,20 @@
|
|
| 1 |
from flask import Flask, render_template, request
|
| 2 |
from flask_socketio import SocketIO, emit, join_room, leave_room
|
|
|
|
|
|
|
|
|
|
| 3 |
import random
|
| 4 |
|
| 5 |
app = Flask(__name__)
|
| 6 |
app.config['SECRET_KEY'] = 'your_secret_key'
|
| 7 |
socketio = SocketIO(app)
|
| 8 |
|
| 9 |
-
# Store questions and participants
|
| 10 |
questions = [
|
| 11 |
-
{"question": "What is the capital of France?", "options": ["Paris", "London", "Berlin", "Rome"]},
|
| 12 |
-
{"question": "What is the largest planet?", "options": ["Earth", "Mars", "Jupiter", "Saturn"]}
|
| 13 |
]
|
| 14 |
-
|
|
|
|
| 15 |
participants = {}
|
| 16 |
|
| 17 |
@app.route('/')
|
|
@@ -29,32 +32,58 @@ def host():
|
|
| 29 |
@socketio.on('join')
|
| 30 |
def on_join(data):
|
| 31 |
username = data['username']
|
| 32 |
-
|
|
|
|
| 33 |
join_room('quiz')
|
| 34 |
-
emit('update_participants', participants, room='quiz')
|
| 35 |
-
print(f"{username} joined the quiz.")
|
| 36 |
|
| 37 |
@socketio.on('disconnect')
|
| 38 |
def on_leave():
|
| 39 |
if request.sid in participants:
|
| 40 |
-
username = participants[request.sid]
|
| 41 |
leave_room('quiz')
|
| 42 |
del participants[request.sid]
|
| 43 |
-
emit('update_participants', participants, room='quiz')
|
| 44 |
print(f"{username} left the quiz.")
|
| 45 |
|
| 46 |
-
@socketio.on('
|
| 47 |
-
def
|
|
|
|
|
|
|
| 48 |
index = current_question['index']
|
| 49 |
-
|
| 50 |
-
|
|
|
|
| 51 |
|
| 52 |
@socketio.on('submit_answer')
|
| 53 |
def receive_answer(data):
|
| 54 |
-
username = participants
|
| 55 |
answer = data['answer']
|
| 56 |
current_question['answers'][username] = answer
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
@socketio.on('next_question')
|
| 60 |
def next_question():
|
|
@@ -62,9 +91,42 @@ def next_question():
|
|
| 62 |
current_question['answers'] = {}
|
| 63 |
if current_question['index'] < len(questions):
|
| 64 |
question = questions[current_question['index']]
|
|
|
|
| 65 |
emit('new_question', question, room='quiz')
|
| 66 |
else:
|
| 67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
if __name__ == '__main__':
|
| 70 |
socketio.run(app, debug=True)
|
|
|
|
| 1 |
from flask import Flask, render_template, request
|
| 2 |
from flask_socketio import SocketIO, emit, join_room, leave_room
|
| 3 |
+
import matplotlib.pyplot as plt
|
| 4 |
+
import base64
|
| 5 |
+
from io import BytesIO
|
| 6 |
import random
|
| 7 |
|
| 8 |
app = Flask(__name__)
|
| 9 |
app.config['SECRET_KEY'] = 'your_secret_key'
|
| 10 |
socketio = SocketIO(app)
|
| 11 |
|
|
|
|
| 12 |
questions = [
|
| 13 |
+
{"question": "What is the capital of France?", "options": ["Paris", "London", "Berlin", "Rome"], "correct": "Paris"},
|
| 14 |
+
{"question": "What is the largest planet?", "options": ["Earth", "Mars", "Jupiter", "Saturn"], "correct": "Jupiter"}
|
| 15 |
]
|
| 16 |
+
initial_questions = questions.copy() # Keep a copy of the original questions to reset later
|
| 17 |
+
current_question = {"index": 0, "answers": {}, "started": False}
|
| 18 |
participants = {}
|
| 19 |
|
| 20 |
@app.route('/')
|
|
|
|
| 32 |
@socketio.on('join')
|
| 33 |
def on_join(data):
|
| 34 |
username = data['username']
|
| 35 |
+
user_id_number = random.randint(1000, 9999) # Generate a unique ID for the user
|
| 36 |
+
participants[request.sid] = {"user_id_number": user_id_number, "username": username, "score": 0}
|
| 37 |
join_room('quiz')
|
| 38 |
+
emit('update_participants', {"participants": participants, "count": len(participants)}, room='quiz')
|
| 39 |
+
print(f"{username} (ID: {user_id_number}) joined the quiz.")
|
| 40 |
|
| 41 |
@socketio.on('disconnect')
|
| 42 |
def on_leave():
|
| 43 |
if request.sid in participants:
|
| 44 |
+
username = participants[request.sid]["username"]
|
| 45 |
leave_room('quiz')
|
| 46 |
del participants[request.sid]
|
| 47 |
+
emit('update_participants', {"participants": participants, "count": len(participants)}, room='quiz')
|
| 48 |
print(f"{username} left the quiz.")
|
| 49 |
|
| 50 |
+
@socketio.on('start_quiz')
|
| 51 |
+
def start_quiz():
|
| 52 |
+
reset_quiz() # Reset the quiz state before starting
|
| 53 |
+
current_question['started'] = True
|
| 54 |
index = current_question['index']
|
| 55 |
+
if index < len(questions):
|
| 56 |
+
question = questions[index]
|
| 57 |
+
emit('new_question', question, room='quiz')
|
| 58 |
|
| 59 |
@socketio.on('submit_answer')
|
| 60 |
def receive_answer(data):
|
| 61 |
+
username = participants[request.sid]["username"]
|
| 62 |
answer = data['answer']
|
| 63 |
current_question['answers'][username] = answer
|
| 64 |
+
if len(current_question['answers']) == len(participants):
|
| 65 |
+
emit('all_answers_received', room='quiz')
|
| 66 |
+
|
| 67 |
+
@socketio.on('check_answers')
|
| 68 |
+
def check_answers():
|
| 69 |
+
index = current_question['index']
|
| 70 |
+
if index < len(questions):
|
| 71 |
+
question = questions[index]
|
| 72 |
+
correct_answer = question['correct']
|
| 73 |
+
results = {
|
| 74 |
+
"question": question["question"],
|
| 75 |
+
"answers": current_question["answers"],
|
| 76 |
+
"correct_answer": correct_answer
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
# Generate the chart and encode it as base64
|
| 80 |
+
chart_base64 = generate_chart(current_question["answers"], question["options"])
|
| 81 |
+
emit('display_results', {"results": results, "chart": chart_base64}, room='quiz')
|
| 82 |
+
|
| 83 |
+
# Update scores based on user_id_number
|
| 84 |
+
for sid, participant in participants.items():
|
| 85 |
+
if current_question['answers'].get(participant["username"]) == correct_answer:
|
| 86 |
+
participants[sid]["score"] += 1
|
| 87 |
|
| 88 |
@socketio.on('next_question')
|
| 89 |
def next_question():
|
|
|
|
| 91 |
current_question['answers'] = {}
|
| 92 |
if current_question['index'] < len(questions):
|
| 93 |
question = questions[current_question['index']]
|
| 94 |
+
emit('clear_results', room='quiz') # Clear previous results and plot
|
| 95 |
emit('new_question', question, room='quiz')
|
| 96 |
else:
|
| 97 |
+
final_results = calculate_final_results()
|
| 98 |
+
emit('quiz_end', final_results, room='quiz')
|
| 99 |
+
|
| 100 |
+
@socketio.on('restart_quiz')
|
| 101 |
+
def restart_quiz():
|
| 102 |
+
reset_quiz()
|
| 103 |
+
emit('quiz_reset', room='quiz')
|
| 104 |
+
|
| 105 |
+
def generate_chart(answers, options):
|
| 106 |
+
counts = [list(answers.values()).count(option) for option in options]
|
| 107 |
+
plt.figure(figsize=(6, 4))
|
| 108 |
+
plt.bar(options, counts)
|
| 109 |
+
plt.xlabel('Options')
|
| 110 |
+
plt.ylabel('Number of Votes')
|
| 111 |
+
plt.title('Results')
|
| 112 |
+
buf = BytesIO()
|
| 113 |
+
plt.savefig(buf, format='png')
|
| 114 |
+
buf.seek(0)
|
| 115 |
+
chart_base64 = base64.b64encode(buf.read()).decode('utf-8')
|
| 116 |
+
buf.close()
|
| 117 |
+
plt.close()
|
| 118 |
+
return chart_base64
|
| 119 |
+
|
| 120 |
+
def calculate_final_results():
|
| 121 |
+
results = {participant["username"]: participant["score"] for participant in participants.values()}
|
| 122 |
+
return results
|
| 123 |
+
|
| 124 |
+
def reset_quiz():
|
| 125 |
+
global questions, current_question
|
| 126 |
+
questions = initial_questions.copy()
|
| 127 |
+
current_question = {"index": 0, "answers": {}, "started": False}
|
| 128 |
+
for participant in participants.values():
|
| 129 |
+
participant["score"] = 0
|
| 130 |
|
| 131 |
if __name__ == '__main__':
|
| 132 |
socketio.run(app, debug=True)
|
requirements.txt
CHANGED
|
@@ -1,2 +1,3 @@
|
|
| 1 |
Flask
|
| 2 |
flask-socketio
|
|
|
|
|
|
| 1 |
Flask
|
| 2 |
flask-socketio
|
| 3 |
+
matplotlib
|
static/script.js
CHANGED
|
@@ -4,13 +4,21 @@ let username;
|
|
| 4 |
function joinQuiz() {
|
| 5 |
username = document.getElementById('username').value;
|
| 6 |
socket.emit('join', { username: username });
|
|
|
|
|
|
|
|
|
|
| 7 |
document.getElementById('quiz-content').style.display = 'block';
|
| 8 |
-
|
| 9 |
}
|
| 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
socket.on('new_question', (data) => {
|
|
|
|
| 12 |
document.getElementById('question-text').innerText = data.question;
|
| 13 |
-
const options = data.options.map((opt
|
| 14 |
`<button onclick="submitAnswer('${opt}')" class="btn btn-secondary">${opt}</button>`
|
| 15 |
).join('');
|
| 16 |
document.getElementById('options').innerHTML = options;
|
|
@@ -20,18 +28,42 @@ function submitAnswer(answer) {
|
|
| 20 |
socket.emit('submit_answer', { answer: answer });
|
| 21 |
}
|
| 22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
function nextQuestion() {
|
| 24 |
socket.emit('next_question');
|
| 25 |
}
|
| 26 |
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
}
|
| 32 |
-
document.getElementById('results').innerHTML =
|
| 33 |
});
|
| 34 |
|
| 35 |
-
socket.on('
|
| 36 |
-
|
|
|
|
|
|
|
| 37 |
});
|
|
|
|
| 4 |
function joinQuiz() {
|
| 5 |
username = document.getElementById('username').value;
|
| 6 |
socket.emit('join', { username: username });
|
| 7 |
+
document.getElementById('username').style.display = 'none';
|
| 8 |
+
document.querySelector('button').style.display = 'none';
|
| 9 |
+
document.getElementById('logged-user').textContent = username;
|
| 10 |
document.getElementById('quiz-content').style.display = 'block';
|
| 11 |
+
document.getElementById('waiting-message').style.display = 'block';
|
| 12 |
}
|
| 13 |
|
| 14 |
+
socket.on('update_participants', (data) => {
|
| 15 |
+
document.getElementById('participant-count').textContent = data.count;
|
| 16 |
+
});
|
| 17 |
+
|
| 18 |
socket.on('new_question', (data) => {
|
| 19 |
+
document.getElementById('waiting-message').style.display = 'none';
|
| 20 |
document.getElementById('question-text').innerText = data.question;
|
| 21 |
+
const options = data.options.map((opt) =>
|
| 22 |
`<button onclick="submitAnswer('${opt}')" class="btn btn-secondary">${opt}</button>`
|
| 23 |
).join('');
|
| 24 |
document.getElementById('options').innerHTML = options;
|
|
|
|
| 28 |
socket.emit('submit_answer', { answer: answer });
|
| 29 |
}
|
| 30 |
|
| 31 |
+
function startQuiz() {
|
| 32 |
+
socket.emit('start_quiz');
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
function checkAnswers() {
|
| 36 |
+
socket.emit('check_answers');
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
function nextQuestion() {
|
| 40 |
socket.emit('next_question');
|
| 41 |
}
|
| 42 |
|
| 43 |
+
function restartQuiz() {
|
| 44 |
+
socket.emit('restart_quiz');
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
socket.on('display_results', (data) => {
|
| 48 |
+
const img = `<img src="data:image/png;base64,${data.chart}" alt="Results Chart" />`;
|
| 49 |
+
const resultText = `<p>Correct Answer: ${data.results.correct_answer}</p>`;
|
| 50 |
+
document.getElementById('results').innerHTML = img + resultText;
|
| 51 |
+
});
|
| 52 |
+
|
| 53 |
+
socket.on('clear_results', () => {
|
| 54 |
+
document.getElementById('results').innerHTML = '';
|
| 55 |
+
});
|
| 56 |
+
|
| 57 |
+
socket.on('quiz_end', (finalResults) => {
|
| 58 |
+
let resultHtml = '<h3>Final Results</h3>';
|
| 59 |
+
for (let user in finalResults) {
|
| 60 |
+
resultHtml += `<p>${user}: ${finalResults[user]} correct answers</p>`;
|
| 61 |
}
|
| 62 |
+
document.getElementById('results').innerHTML = resultHtml;
|
| 63 |
});
|
| 64 |
|
| 65 |
+
socket.on('quiz_reset', () => {
|
| 66 |
+
document.getElementById('results').innerHTML = '';
|
| 67 |
+
document.getElementById('question-text').innerText = '';
|
| 68 |
+
document.getElementById('options').innerHTML = '';
|
| 69 |
});
|
templates/client.html
CHANGED
|
@@ -12,8 +12,11 @@
|
|
| 12 |
<input type="text" id="username" placeholder="Enter your name" class="form-control">
|
| 13 |
<button onclick="joinQuiz()" class="btn btn-primary mt-2">Join</button>
|
| 14 |
<div id="quiz-content" style="display: none;">
|
|
|
|
|
|
|
| 15 |
<h3 id="question-text"></h3>
|
| 16 |
<div id="options"></div>
|
|
|
|
| 17 |
</div>
|
| 18 |
</div>
|
| 19 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.0/socket.io.js"></script>
|
|
|
|
| 12 |
<input type="text" id="username" placeholder="Enter your name" class="form-control">
|
| 13 |
<button onclick="joinQuiz()" class="btn btn-primary mt-2">Join</button>
|
| 14 |
<div id="quiz-content" style="display: none;">
|
| 15 |
+
<h3>Logged in as: <span id="logged-user"></span></h3>
|
| 16 |
+
<h3 id="waiting-message" style="display: none;">Waiting for the Host...</h3>
|
| 17 |
<h3 id="question-text"></h3>
|
| 18 |
<div id="options"></div>
|
| 19 |
+
<div id="results" class="mt-4"></div>
|
| 20 |
</div>
|
| 21 |
</div>
|
| 22 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.0/socket.io.js"></script>
|
templates/host.html
CHANGED
|
@@ -9,9 +9,14 @@
|
|
| 9 |
<body>
|
| 10 |
<div class="container">
|
| 11 |
<h2>Quiz Host</h2>
|
| 12 |
-
<
|
| 13 |
-
<
|
| 14 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
</div>
|
| 16 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.0/socket.io.js"></script>
|
| 17 |
<script src="/static/script.js"></script>
|
|
|
|
| 9 |
<body>
|
| 10 |
<div class="container">
|
| 11 |
<h2>Quiz Host</h2>
|
| 12 |
+
<p>Participants connected: <span id="participant-count">0</span></p>
|
| 13 |
+
<button onclick="startQuiz()" class="btn btn-success">Start Quiz</button>
|
| 14 |
+
<button onclick="checkAnswers()" class="btn btn-primary mt-2">Check Answers</button>
|
| 15 |
+
<button onclick="nextQuestion()" class="btn btn-secondary mt-2">Next Question</button>
|
| 16 |
+
<button onclick="restartQuiz()" class="btn btn-danger mt-2">Start New Quiz</button>
|
| 17 |
+
<h3 id="question-text" class="mt-4"></h3>
|
| 18 |
+
<div id="options" class="mt-2"></div>
|
| 19 |
+
<div id="results" class="mt-4"></div>
|
| 20 |
</div>
|
| 21 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.0/socket.io.js"></script>
|
| 22 |
<script src="/static/script.js"></script>
|