ruslanmv commited on
Commit
45952cf
Β·
2 Parent(s): da650e0 d3dfe9c

Merge branch 'v4-extend-v2'

Browse files
README.md CHANGED
@@ -15,14 +15,19 @@ A Python-based real-time quiz application designed to mimic the functionality of
15
  ```bash
16
  Real-Time-Quiz-Application/
17
  β”‚
 
18
  β”œβ”€β”€ app.py # Main Flask application
 
19
  β”œβ”€β”€ templates/
20
  β”‚ β”œβ”€β”€ index.html # Main index page with client and host buttons
21
  β”‚ β”œβ”€β”€ client.html # Client interface
22
  β”‚ └── host.html # Host interface
23
  β”œβ”€β”€ static/
24
- β”‚ β”œβ”€β”€ style.css # Optional custom styles (Bootstrap already integrated)
25
  β”‚ └── script.js # JavaScript for real-time interactions
 
 
 
26
  β”œβ”€β”€ requirements.txt # Python dependencies
27
  └── README.md # Project documentation
28
  ```
@@ -57,12 +62,12 @@ pip install -r requirements.txt
57
  ```bash
58
  python app.py
59
  ```
60
- ![](assets/2024-10-21-12-08-37.png)
61
  4. **Access the application**:
62
  - **Host interface**: [http://127.0.0.1:5000/host](http://127.0.0.1:5000/host)
63
- ![](assets/2024-10-21-12-09-25.png)
64
  - **Client interface**: [http://127.0.0.1:5000/client](http://127.0.0.1:5000/client)
65
- ![](assets/2024-10-21-12-10-42.png)
66
  ## Application Overview
67
 
68
  ### Host Interface
 
15
  ```bash
16
  Real-Time-Quiz-Application/
17
  β”‚
18
+ β”‚
19
  β”œβ”€β”€ app.py # Main Flask application
20
+ β”œβ”€β”€ backend.py # Backend logic for loading and managing exams
21
  β”œβ”€β”€ templates/
22
  β”‚ β”œβ”€β”€ index.html # Main index page with client and host buttons
23
  β”‚ β”œβ”€β”€ client.html # Client interface
24
  β”‚ └── host.html # Host interface
25
  β”œβ”€β”€ static/
26
+ β”‚ β”œβ”€β”€ style.css # Custom styles to mimic the OnVUE exam appearance
27
  β”‚ └── script.js # JavaScript for real-time interactions
28
+ β”œβ”€β”€ questions/ # Folder containing exam JSON files
29
+ β”‚ β”œβ”€β”€ exam1.json # Example JSON file for exam 1
30
+ β”‚ β”œβ”€β”€ exam2.json # Example JSON file for exam 2
31
  β”œβ”€β”€ requirements.txt # Python dependencies
32
  └── README.md # Project documentation
33
  ```
 
62
  ```bash
63
  python app.py
64
  ```
65
+ ![](assets/2024-10-21-17-07-42.png)
66
  4. **Access the application**:
67
  - **Host interface**: [http://127.0.0.1:5000/host](http://127.0.0.1:5000/host)
68
+ ![](assets/2024-10-21-17-08-13.png)
69
  - **Client interface**: [http://127.0.0.1:5000/client](http://127.0.0.1:5000/client)
70
+ ![](assets/2024-10-21-17-09-15.png)
71
  ## Application Overview
72
 
73
  ### Host Interface
__pycache__/backend.cpython-312.pyc ADDED
Binary file (1.37 kB). View file
 
app.py CHANGED
@@ -1,5 +1,6 @@
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
@@ -9,11 +10,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
 
@@ -27,12 +25,12 @@ def client():
27
 
28
  @app.route('/host')
29
  def host():
30
- return render_template('host.html')
31
 
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')
@@ -47,28 +45,46 @@ def on_leave():
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"],
@@ -76,11 +92,9 @@ def check_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
@@ -89,23 +103,29 @@ def check_answers():
89
  def next_question():
90
  current_question['index'] += 1
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')
@@ -118,15 +138,14 @@ def generate_chart(answers, options):
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)
 
1
  from flask import Flask, render_template, request
2
  from flask_socketio import SocketIO, emit, join_room, leave_room
3
+ import backend # Import backend functions
4
  import matplotlib.pyplot as plt
5
  import base64
6
  from io import BytesIO
 
10
  app.config['SECRET_KEY'] = 'your_secret_key'
11
  socketio = SocketIO(app)
12
 
13
+ exams = backend.load_question_sets() # Load available exams
14
+ selected_questions = [] # Global variable to store the selected questions
 
 
 
15
  current_question = {"index": 0, "answers": {}, "started": False}
16
  participants = {}
17
 
 
25
 
26
  @app.route('/host')
27
  def host():
28
+ return render_template('host.html', exams=exams)
29
 
30
  @socketio.on('join')
31
  def on_join(data):
32
  username = data['username']
33
+ user_id_number = random.randint(1000, 9999)
34
  participants[request.sid] = {"user_id_number": user_id_number, "username": username, "score": 0}
35
  join_room('quiz')
36
  emit('update_participants', {"participants": participants, "count": len(participants)}, room='quiz')
 
45
  emit('update_participants', {"participants": participants, "count": len(participants)}, room='quiz')
46
  print(f"{username} left the quiz.")
47
 
48
+ @socketio.on('load_quiz')
49
+ def load_quiz(data):
50
+ global selected_questions
51
+ exam_name = data['exam_name']
52
+ start_question = data['start_question'] - 1 # Adjust for 0-based indexing
53
+ selected_questions = backend.select_exam(exam_name)
54
+ if selected_questions:
55
+ num_questions = len(selected_questions)
56
+ current_question['index'] = start_question
57
+ emit('quiz_loaded', {"success": True, "num_questions": num_questions, "start_question": start_question + 1}, room=request.sid)
58
+ else:
59
+ emit('quiz_loaded', {"success": False}, room=request.sid)
60
+
61
  @socketio.on('start_quiz')
62
  def start_quiz():
63
+ if participants and selected_questions:
64
+ current_question['started'] = True
65
+ emit('new_question', selected_questions[current_question['index']], room='quiz')
66
+ # Also emit the question to the host
67
+ emit('new_question', selected_questions[current_question['index']], room=request.sid)
68
+ emit('enable_end_quiz', room=request.sid) # Enable "End Quiz" for the host
69
+
70
+ @socketio.on('restart_quiz')
71
+ def restart_quiz():
72
+ reset_quiz()
73
+ emit('quiz_reset', room='quiz')
74
+ start_quiz()
75
 
76
  @socketio.on('submit_answer')
77
  def receive_answer(data):
78
  username = participants[request.sid]["username"]
79
  answer = data['answer']
80
  current_question['answers'][username] = answer
81
+ print(f"{username} submitted an answer: {answer}")
 
82
 
83
  @socketio.on('check_answers')
84
  def check_answers():
85
  index = current_question['index']
86
+ if index < len(selected_questions):
87
+ question = selected_questions[index]
88
  correct_answer = question['correct']
89
  results = {
90
  "question": question["question"],
 
92
  "correct_answer": correct_answer
93
  }
94
 
 
95
  chart_base64 = generate_chart(current_question["answers"], question["options"])
96
  emit('display_results', {"results": results, "chart": chart_base64}, room='quiz')
97
 
 
98
  for sid, participant in participants.items():
99
  if current_question['answers'].get(participant["username"]) == correct_answer:
100
  participants[sid]["score"] += 1
 
103
  def next_question():
104
  current_question['index'] += 1
105
  current_question['answers'] = {}
106
+ if current_question['index'] < len(selected_questions):
107
+ question = selected_questions[current_question['index']]
108
+ emit('clear_results', room='quiz')
109
  emit('new_question', question, room='quiz')
110
+ # Also emit the question to the host
111
+ emit('new_question', question, room=request.sid)
112
  else:
113
  final_results = calculate_final_results()
114
+ emit('display_final_results', final_results, room='quiz')
115
+
116
+ @socketio.on('end_quiz')
117
+ def end_quiz():
118
+ if current_question['started']: # Ensure the quiz has started before ending it
119
+ final_results = calculate_final_results()
120
+ emit('display_final_results', final_results, room='quiz')
121
+ reset_quiz() # Reset the quiz state
122
 
 
 
 
 
123
 
124
  def generate_chart(answers, options):
125
+ letters = [chr(65 + i) for i in range(len(options))] # Dynamically generate letters for options
126
  counts = [list(answers.values()).count(option) for option in options]
127
  plt.figure(figsize=(6, 4))
128
+ plt.bar(letters, counts)
129
  plt.xlabel('Options')
130
  plt.ylabel('Number of Votes')
131
  plt.title('Results')
 
138
  return chart_base64
139
 
140
  def calculate_final_results():
141
+ sorted_scores = sorted(participants.values(), key=lambda x: x['score'], reverse=True)
142
+ return [{"username": p["username"], "score": p["score"]} for p in sorted_scores]
143
 
144
  def reset_quiz():
145
+ global selected_questions, current_question
 
146
  current_question = {"index": 0, "answers": {}, "started": False}
147
  for participant in participants.values():
148
  participant["score"] = 0
149
 
150
  if __name__ == '__main__':
151
+ socketio.run(app, debug=True)
assets/2024-10-21-17-07-42.png ADDED
assets/2024-10-21-17-08-13.png ADDED
assets/2024-10-21-17-09-15.png ADDED
backend.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+
4
+ # Function to load question sets from the "questions" directory
5
+ def load_question_sets(directory='questions'):
6
+ question_sets = []
7
+ for root, dirs, files in os.walk(directory):
8
+ for file in files:
9
+ if file.endswith(".json"):
10
+ question_sets.append(file[:-5]) # Remove the ".json" extension
11
+ return question_sets
12
+
13
+ # Function to select and load the specified exam
14
+ def select_exam(exam_name):
15
+ file_path = os.path.join('questions', f'{exam_name}.json')
16
+ try:
17
+ with open(file_path, 'r') as f:
18
+ questions = json.load(f)
19
+ print(f"Loaded {len(questions)} questions from {exam_name}")
20
+ return questions
21
+ except FileNotFoundError:
22
+ print(f"File {file_path} not found.")
23
+ return [] # Return an empty list if the file is not found
questions/CLF-C02-v1.json ADDED
The diff for this file is too large to render. See raw diff
 
static/script.js CHANGED
@@ -12,21 +12,33 @@ function joinQuiz() {
12
  document.getElementById('join-title').style.display = 'none';
13
  }
14
 
15
- socket.on('update_participants', (data) => {
16
- document.getElementById('participant-count').textContent = data.count;
17
- });
 
 
 
 
 
 
 
18
 
19
- socket.on('new_question', (data) => {
20
- document.getElementById('waiting-message').style.display = 'none';
21
- document.getElementById('question-text').innerText = data.question;
22
- const options = data.options.map((opt) =>
23
- `<button onclick="submitAnswer('${opt}')" class="btn btn-secondary">${opt}</button>`
24
- ).join('');
25
- document.getElementById('options').innerHTML = options;
26
- });
 
 
 
27
 
28
- function submitAnswer(answer) {
29
- socket.emit('submit_answer', { answer: answer });
 
 
30
  }
31
 
32
  function startQuiz() {
@@ -41,30 +53,64 @@ function nextQuestion() {
41
  socket.emit('next_question');
42
  }
43
 
 
 
 
 
44
  function restartQuiz() {
45
  socket.emit('restart_quiz');
46
  }
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  socket.on('display_results', (data) => {
49
  const img = `<img src="data:image/png;base64,${data.chart}" alt="Results Chart" />`;
50
  const resultText = `<p>Correct Answer: ${data.results.correct_answer}</p>`;
51
  document.getElementById('results').innerHTML = img + resultText;
52
  });
53
 
 
 
 
 
54
  socket.on('clear_results', () => {
55
  document.getElementById('results').innerHTML = '';
56
  });
57
 
58
- socket.on('quiz_end', (finalResults) => {
59
- let resultHtml = '<h3>Final Results</h3>';
60
- for (let user in finalResults) {
61
- resultHtml += `<p>${user}: ${finalResults[user]} correct answers</p>`;
62
- }
63
- document.getElementById('results').innerHTML = resultHtml;
 
 
 
64
  });
65
 
66
  socket.on('quiz_reset', () => {
67
  document.getElementById('results').innerHTML = '';
68
  document.getElementById('question-text').innerText = '';
69
  document.getElementById('options').innerHTML = '';
70
- });
 
 
 
 
12
  document.getElementById('join-title').style.display = 'none';
13
  }
14
 
15
+ function submitForm(event) {
16
+ event.preventDefault();
17
+ const selectedOption = document.querySelector('input[name="answer"]:checked');
18
+ if (selectedOption) {
19
+ const answer = selectedOption.value;
20
+ socket.emit('submit_answer', { answer });
21
+ } else {
22
+ alert("Please select an option before submitting.");
23
+ }
24
+ }
25
 
26
+ function selectExam() {
27
+ const examName = document.getElementById('exam-selector').value;
28
+ const startQuestion = document.getElementById('start-question-number').value;
29
+ document.getElementById('question-start-display').textContent = `Starting from question ${startQuestion}.`;
30
+ }
31
+
32
+ function loadQuiz() {
33
+ const examName = document.getElementById('exam-selector').value;
34
+ const startQuestion = document.getElementById('start-question-number').value;
35
+ socket.emit('load_quiz', { exam_name: examName, start_question: parseInt(startQuestion) });
36
+ }
37
 
38
+ function updateSliderValue(value) {
39
+ document.getElementById('start-question').value = value;
40
+ document.getElementById('start-question-number').value = value;
41
+ document.getElementById('question-start-display').textContent = `Starting from question ${value}.`;
42
  }
43
 
44
  function startQuiz() {
 
53
  socket.emit('next_question');
54
  }
55
 
56
+ function endQuiz() {
57
+ socket.emit('end_quiz');
58
+ }
59
+
60
  function restartQuiz() {
61
  socket.emit('restart_quiz');
62
  }
63
 
64
+ socket.on('quiz_loaded', (data) => {
65
+ if (data.success) {
66
+ alert(`Quiz loaded with ${data.num_questions} questions, starting from question ${data.start_question}.`);
67
+ } else {
68
+ alert(`Failed to load quiz.`);
69
+ }
70
+ });
71
+
72
+ socket.on('new_question', (data) => {
73
+ document.getElementById('waiting-message').style.display = 'none';
74
+ document.getElementById('question-text').innerText = data.question;
75
+ // Dynamically generate letters for options (up to 'h')
76
+ const letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
77
+ const options = data.options.map((opt, index) =>
78
+ `<input type="radio" id="${letters[index]}" name="answer" value="${opt}">
79
+ <label for="${letters[index]}">${letters[index]}) ${opt}</label><br>`
80
+ ).join('');
81
+ document.getElementById('options').innerHTML = options;
82
+ });
83
+
84
  socket.on('display_results', (data) => {
85
  const img = `<img src="data:image/png;base64,${data.chart}" alt="Results Chart" />`;
86
  const resultText = `<p>Correct Answer: ${data.results.correct_answer}</p>`;
87
  document.getElementById('results').innerHTML = img + resultText;
88
  });
89
 
90
+ socket.on('enable_end_quiz', () => {
91
+ document.getElementById('end-quiz').disabled = false; // Enable the "End Quiz" button
92
+ });
93
+
94
  socket.on('clear_results', () => {
95
  document.getElementById('results').innerHTML = '';
96
  });
97
 
98
+ socket.on('display_final_results', (finalResults) => {
99
+ document.getElementById('quiz-content').style.display = 'none';
100
+ const resultsTable = document.getElementById('results-table');
101
+ resultsTable.innerHTML = '';
102
+ finalResults.forEach((participant) => {
103
+ const row = `<tr><td>${participant.username}</td><td>${participant.score}</td></tr>`;
104
+ resultsTable.innerHTML += row;
105
+ });
106
+ document.getElementById('final-results').style.display = 'block';
107
  });
108
 
109
  socket.on('quiz_reset', () => {
110
  document.getElementById('results').innerHTML = '';
111
  document.getElementById('question-text').innerText = '';
112
  document.getElementById('options').innerHTML = '';
113
+ document.getElementById('final-results').style.display = 'none';
114
+ document.getElementById('quiz-content').style.display = 'block';
115
+ document.getElementById('waiting-message').style.display = 'block';
116
+ });
static/style.css ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ background-color: #f4f4f4;
3
+ font-family: Arial, sans-serif;
4
+ }
5
+
6
+ .container {
7
+ max-width: 800px;
8
+ margin: 0 auto;
9
+ background-color: #fff;
10
+ padding: 20px;
11
+ border-radius: 8px;
12
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
13
+ }
14
+
15
+ h2, p {
16
+ font-size: 18px;
17
+ }
18
+
19
+ input[type="radio"] {
20
+ margin-right: 10px;
21
+ }
22
+
23
+ input[type="submit"] {
24
+ display: block;
25
+ margin-top: 20px;
26
+ }
templates/client.html CHANGED
@@ -5,6 +5,7 @@
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Quiz Client</title>
7
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
 
8
  </head>
9
  <body>
10
  <div class="container">
@@ -14,10 +15,28 @@
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>
23
  <script src="/static/script.js"></script>
 
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Quiz Client</title>
7
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
8
+ <link rel="stylesheet" href="/static/style.css">
9
  </head>
10
  <body>
11
  <div class="container">
 
15
  <div id="quiz-content" style="display: none;">
16
  <h3>Logged in as: <span id="logged-user"></span></h3>
17
  <h3 id="waiting-message" style="display: none;">Waiting for the Host...</h3>
18
+ <div id="question-section" class="mt-4">
19
+ <form id="quiz-form" onsubmit="submitForm(event)">
20
+ <p id="question-text"></p>
21
+ <div id="options"></div>
22
+ <input type="submit" value="Submit" class="btn btn-primary mt-2">
23
+ </form>
24
+ </div>
25
  <div id="results" class="mt-4"></div>
26
  </div>
27
+ <div id="final-results" style="display: none;" class="mt-4">
28
+ <h3>And the Winners are:</h3>
29
+ <table class="table mt-2">
30
+ <thead>
31
+ <tr>
32
+ <th>Participant</th>
33
+ <th>Score</th>
34
+ </tr>
35
+ </thead>
36
+ <tbody id="results-table">
37
+ </tbody>
38
+ </table>
39
+ </div>
40
  </div>
41
  <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.0/socket.io.js"></script>
42
  <script src="/static/script.js"></script>
templates/host.html CHANGED
@@ -5,17 +5,32 @@
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Quiz Host</title>
7
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
 
8
  </head>
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>
 
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Quiz Host</title>
7
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
8
+ <link rel="stylesheet" href="/static/style.css">
9
  </head>
10
  <body>
11
  <div class="container">
12
  <h2>Quiz Host</h2>
13
  <p>Participants connected: <span id="participant-count">0</span></p>
14
+ <select id="exam-selector" class="form-control" onchange="selectExam()">
15
+ <option value="" disabled selected>Select an exam</option>
16
+ {% for exam in exams %}
17
+ <option value="{{ exam }}">{{ exam }}</option>
18
+ {% endfor %}
19
+ </select>
20
+ <label for="start-question" class="mt-3">Select starting question:</label>
21
+ <input type="range" id="start-question" min="1" max="10" value="1" class="form-range mt-2 mb-2" oninput="updateSliderValue(this.value)">
22
+ <input type="number" id="start-question-number" min="1" max="10" value="1" class="form-control" oninput="updateSliderValue(this.value)">
23
+ <p id="question-start-display" class="mt-2">Starting from question 1.</p>
24
+ <button onclick="loadQuiz()" class="btn btn-info mt-3">Load Quiz</button><br><br>
25
+ <button onclick="startQuiz()" class="btn btn-success mt-3">Start Quiz</button><br><br>
26
+ <button onclick="restartQuiz()" class="btn btn-warning mt-3">Restart</button><br><br>
27
+ <button onclick="checkAnswers()" class="btn btn-primary mt-2">Check Answers</button><br><br>
28
+ <button onclick="nextQuestion()" class="btn btn-secondary mt-2">Next Question</button><br><br>
29
+ <button onclick="endQuiz()" id="end-quiz" class="btn btn-danger mt-2" disabled>End Quiz</button>
30
+ <div id="question-section" class="mt-4">
31
+ <p id="question-text"></p>
32
+ <div id="options"></div>
33
+ </div>
34
  <div id="results" class="mt-4"></div>
35
  </div>
36
  <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.0/socket.io.js"></script>