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>
|