Fausto Busuito
commited on
Commit
·
d353aa8
1
Parent(s):
937ef12
Application changes
Browse files- Dockerfile +12 -21
- requirements.txt +1 -4
- routes.py +53 -0
- static/script.js +61 -131
- static/style.css +22 -14
- templates/client.html +0 -44
- templates/host.html +0 -39
- templates/index.html +11 -8
- templates/quiz.html +20 -0
- templates/results.html +13 -0
Dockerfile
CHANGED
|
@@ -1,29 +1,20 @@
|
|
| 1 |
-
# Use the official Python image
|
| 2 |
-
FROM python:3.9
|
| 3 |
-
|
| 4 |
-
# Create a new user to run the application
|
| 5 |
-
RUN useradd -m -u 1000 user
|
| 6 |
-
|
| 7 |
-
# Set the user for subsequent commands
|
| 8 |
-
USER user
|
| 9 |
-
|
| 10 |
-
# Update the PATH environment variable
|
| 11 |
-
ENV PATH="/home/user/.local/bin:$PATH"
|
| 12 |
|
| 13 |
# Set the working directory
|
| 14 |
WORKDIR /app
|
| 15 |
|
| 16 |
-
# Copy the requirements
|
| 17 |
-
COPY
|
| 18 |
|
| 19 |
-
# Install the dependencies
|
| 20 |
-
RUN pip install --no-cache-dir
|
| 21 |
|
| 22 |
-
# Copy the
|
| 23 |
-
COPY
|
| 24 |
|
| 25 |
-
# Expose the
|
| 26 |
-
EXPOSE
|
| 27 |
|
| 28 |
-
#
|
| 29 |
-
CMD ["python", "
|
|
|
|
| 1 |
+
# Use the official Python image from the Docker Hub
|
| 2 |
+
FROM python:3.9-slim
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
# Set the working directory
|
| 5 |
WORKDIR /app
|
| 6 |
|
| 7 |
+
# Copy the requirements file into the container
|
| 8 |
+
COPY requirements.txt .
|
| 9 |
|
| 10 |
+
# Install the dependencies
|
| 11 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 12 |
|
| 13 |
+
# Copy the rest of the application code into the container
|
| 14 |
+
COPY . .
|
| 15 |
|
| 16 |
+
# Expose the port the app runs on
|
| 17 |
+
EXPOSE 5000
|
| 18 |
|
| 19 |
+
# Run the application
|
| 20 |
+
CMD ["python", "run.py"]
|
requirements.txt
CHANGED
|
@@ -1,4 +1 @@
|
|
| 1 |
-
Flask
|
| 2 |
-
flask-socketio
|
| 3 |
-
eventlet
|
| 4 |
-
matplotlib
|
|
|
|
| 1 |
+
Flask==2.0.1
|
|
|
|
|
|
|
|
|
routes.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import render_template, request, redirect, url_for, jsonify
|
| 2 |
+
from app import create_app
|
| 3 |
+
import json
|
| 4 |
+
import random
|
| 5 |
+
|
| 6 |
+
app = create_app()
|
| 7 |
+
|
| 8 |
+
questions = []
|
| 9 |
+
user_answers = []
|
| 10 |
+
|
| 11 |
+
@app.route('/')
|
| 12 |
+
def index():
|
| 13 |
+
return render_template('index.html')
|
| 14 |
+
|
| 15 |
+
@app.route('/start', methods=['POST'])
|
| 16 |
+
def start():
|
| 17 |
+
global questions
|
| 18 |
+
file = request.files['file']
|
| 19 |
+
questions = json.load(file)
|
| 20 |
+
random.shuffle(questions)
|
| 21 |
+
return redirect(url_for('quiz'))
|
| 22 |
+
|
| 23 |
+
@app.route('/quiz')
|
| 24 |
+
def quiz():
|
| 25 |
+
return render_template('quiz.html')
|
| 26 |
+
|
| 27 |
+
@app.route('/questions')
|
| 28 |
+
def get_questions():
|
| 29 |
+
return jsonify(questions)
|
| 30 |
+
|
| 31 |
+
@app.route('/submit', methods=['POST'])
|
| 32 |
+
def submit():
|
| 33 |
+
global user_answers
|
| 34 |
+
user_answers = request.json
|
| 35 |
+
return jsonify({'status': 'success'})
|
| 36 |
+
|
| 37 |
+
def results():
|
| 38 |
+
correct_answers = 0
|
| 39 |
+
total_questions = len(questions)
|
| 40 |
+
|
| 41 |
+
for user_answer in user_answers:
|
| 42 |
+
question_index = user_answer['questionIndex']
|
| 43 |
+
selected_options = user_answer['answers']
|
| 44 |
+
correct_options = questions[question_index]['correct']
|
| 45 |
+
|
| 46 |
+
if set(selected_options) == set(correct_options):
|
| 47 |
+
correct_answers += 1
|
| 48 |
+
|
| 49 |
+
score = (correct_answers / total_questions) * 100
|
| 50 |
+
return render_template('results.html', score=score, total_questions=total_questions, correct_answers=correct_answers)
|
| 51 |
+
|
| 52 |
+
if __name__ == '__main__':
|
| 53 |
+
app.run(debug=True)
|
static/script.js
CHANGED
|
@@ -1,139 +1,69 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 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 |
|
| 33 |
-
function
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
| 40 |
}
|
| 41 |
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
document.getElementById('start-question').value = value;
|
| 49 |
-
document.getElementById('start-question-number').value = value;
|
| 50 |
-
document.getElementById('question-start-display').textContent = `Starting from question ${value}.`;
|
| 51 |
-
}
|
| 52 |
-
|
| 53 |
-
function startQuiz() {
|
| 54 |
-
socket.emit('start_quiz');
|
| 55 |
-
}
|
| 56 |
-
|
| 57 |
-
function checkAnswers() {
|
| 58 |
-
socket.emit('check_answers');
|
| 59 |
-
}
|
| 60 |
-
|
| 61 |
-
function nextQuestion() {
|
| 62 |
-
socket.emit('next_question');
|
| 63 |
-
}
|
| 64 |
-
|
| 65 |
-
function endQuiz() {
|
| 66 |
-
socket.emit('end_quiz');
|
| 67 |
-
}
|
| 68 |
-
|
| 69 |
-
function restartQuiz() {
|
| 70 |
-
socket.emit('restart_quiz');
|
| 71 |
-
}
|
| 72 |
-
|
| 73 |
-
socket.on('quiz_loaded', (data) => {
|
| 74 |
-
if (data.success) {
|
| 75 |
-
alert(`Quiz loaded with ${data.num_questions} questions.`);
|
| 76 |
-
const startQuestionInput = document.getElementById('start-question');
|
| 77 |
-
const startQuestionNumber = document.getElementById('start-question-number');
|
| 78 |
-
startQuestionInput.max = data.num_questions;
|
| 79 |
-
startQuestionNumber.max = data.num_questions;
|
| 80 |
-
startQuestionInput.value = data.start_question;
|
| 81 |
-
startQuestionNumber.value = data.start_question;
|
| 82 |
-
document.getElementById('question-start-display').textContent = `Starting from question ${data.start_question}.`;
|
| 83 |
-
document.getElementById('start-question-label').style.display = 'block';
|
| 84 |
-
startQuestionInput.style.display = 'block';
|
| 85 |
-
startQuestionNumber.style.display = 'block';
|
| 86 |
-
document.getElementById('question-start-display').style.display = 'block';
|
| 87 |
-
} else {
|
| 88 |
-
alert("Failed to load quiz. Please try again.");
|
| 89 |
-
}
|
| 90 |
-
});
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
socket.on('new_question', (data) => {
|
| 94 |
-
document.getElementById('waiting-message').style.display = 'none';
|
| 95 |
-
document.getElementById('question-text').innerText = `Question ${data.index}: ${data.question}`;
|
| 96 |
-
const letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
|
| 97 |
-
const options = data.options.map((opt, index) =>
|
| 98 |
-
`<input type="radio" id="${letters[index]}" name="answer" value="${opt}">
|
| 99 |
-
<label for="${letters[index]}">${letters[index]}) ${opt}</label><br>`
|
| 100 |
-
).join('');
|
| 101 |
-
document.getElementById('options').innerHTML = options;
|
| 102 |
-
});
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
socket.on('display_results', (data) => {
|
| 108 |
-
const img = `<img src="data:image/png;base64,${data.chart}" alt="Results Chart" />`;
|
| 109 |
-
const resultText = `<p>Correct Answer: ${data.results.correct_answer}</p>`;
|
| 110 |
-
document.getElementById('results').innerHTML = img + resultText;
|
| 111 |
-
});
|
| 112 |
-
|
| 113 |
-
socket.on('enable_end_quiz', () => {
|
| 114 |
-
document.getElementById('end-quiz').disabled = false; // Enable the "End Quiz" button
|
| 115 |
-
});
|
| 116 |
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
|
|
|
|
|
|
|
|
|
| 120 |
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
});
|
| 129 |
-
document.getElementById('final-results').style.display = 'block';
|
| 130 |
-
});
|
| 131 |
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
document.getElementById('quiz-content').style.display = 'block';
|
| 138 |
-
document.getElementById('waiting-message').style.display = 'block';
|
| 139 |
-
});
|
|
|
|
| 1 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 2 |
+
const questionElement = document.getElementById('question');
|
| 3 |
+
const optionsElement = document.getElementById('options');
|
| 4 |
+
const prevButton = document.getElementById('prev');
|
| 5 |
+
const nextButton = document.getElementById('next');
|
| 6 |
+
const submitButton = document.getElementById('submit');
|
| 7 |
+
|
| 8 |
+
let questions = [];
|
| 9 |
+
let currentQuestionIndex = 0;
|
| 10 |
+
let userAnswers = [];
|
| 11 |
+
|
| 12 |
+
function loadQuestions(data) {
|
| 13 |
+
questions = data;
|
| 14 |
+
showQuestion(currentQuestionIndex);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
+
function showQuestion(index) {
|
| 18 |
+
const question = questions[index];
|
| 19 |
+
questionElement.textContent = question.question;
|
| 20 |
+
optionsElement.innerHTML = '';
|
| 21 |
+
question.options.forEach((option, i) => {
|
| 22 |
+
const optionElement = document.createElement('div');
|
| 23 |
+
optionElement.innerHTML = `<input type="checkbox" id="option${i}" name="option${i}" value="${String.fromCharCode(65 + i)}">
|
| 24 |
+
<label for="option${i}">${option}</label>`;
|
| 25 |
+
optionsElement.appendChild(optionElement);
|
| 26 |
+
});
|
| 27 |
}
|
| 28 |
|
| 29 |
+
prevButton.addEventListener('click', () => {
|
| 30 |
+
if (currentQuestionIndex > 0) {
|
| 31 |
+
currentQuestionIndex--;
|
| 32 |
+
showQuestion(currentQuestionIndex);
|
| 33 |
+
}
|
| 34 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
+
nextButton.addEventListener('click', () => {
|
| 37 |
+
if (currentQuestionIndex < questions.length - 1) {
|
| 38 |
+
currentQuestionIndex++;
|
| 39 |
+
showQuestion(currentQuestionIndex);
|
| 40 |
+
}
|
| 41 |
+
});
|
| 42 |
|
| 43 |
+
submitButton.addEventListener('click', () => {
|
| 44 |
+
const selectedOptions = Array.from(document.querySelectorAll('input[type="checkbox"]:checked')).map(input => input.value);
|
| 45 |
+
userAnswers.push({ questionIndex: currentQuestionIndex, answers: selectedOptions });
|
| 46 |
+
if (currentQuestionIndex < questions.length - 1) {
|
| 47 |
+
currentQuestionIndex++;
|
| 48 |
+
showQuestion(currentQuestionIndex);
|
| 49 |
+
} else {
|
| 50 |
+
// Submit the quiz
|
| 51 |
+
fetch('/submit', {
|
| 52 |
+
method: 'POST',
|
| 53 |
+
headers: {
|
| 54 |
+
'Content-Type': 'application/json'
|
| 55 |
+
},
|
| 56 |
+
body: JSON.stringify(userAnswers)
|
| 57 |
+
})
|
| 58 |
+
.then(response => response.json())
|
| 59 |
+
.then(data => {
|
| 60 |
+
window.location.href = '/results';
|
| 61 |
+
});
|
| 62 |
+
}
|
| 63 |
});
|
|
|
|
|
|
|
| 64 |
|
| 65 |
+
// Fetch questions from the server
|
| 66 |
+
fetch('/questions')
|
| 67 |
+
.then(response => response.json())
|
| 68 |
+
.then(data => loadQuestions(data));
|
| 69 |
+
});
|
|
|
|
|
|
|
|
|
static/style.css
CHANGED
|
@@ -1,26 +1,34 @@
|
|
| 1 |
body {
|
| 2 |
-
background-color: #f4f4f4;
|
| 3 |
font-family: Arial, sans-serif;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
}
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 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 |
-
|
| 16 |
-
|
|
|
|
|
|
|
| 17 |
}
|
| 18 |
|
| 19 |
-
|
| 20 |
-
|
| 21 |
}
|
| 22 |
|
| 23 |
-
|
| 24 |
-
display: block;
|
| 25 |
margin-top: 20px;
|
| 26 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
body {
|
|
|
|
| 2 |
font-family: Arial, sans-serif;
|
| 3 |
+
margin: 0;
|
| 4 |
+
padding: 0;
|
| 5 |
+
display: flex;
|
| 6 |
+
justify-content: center;
|
| 7 |
+
align-items: center;
|
| 8 |
+
height: 100vh;
|
| 9 |
+
background-color: #f0f0f0;
|
| 10 |
}
|
| 11 |
|
| 12 |
+
h1 {
|
| 13 |
+
text-align: center;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
}
|
| 15 |
|
| 16 |
+
form {
|
| 17 |
+
display: flex;
|
| 18 |
+
flex-direction: column;
|
| 19 |
+
align-items: center;
|
| 20 |
}
|
| 21 |
|
| 22 |
+
#quiz-container {
|
| 23 |
+
text-align: center;
|
| 24 |
}
|
| 25 |
|
| 26 |
+
#options {
|
|
|
|
| 27 |
margin-top: 20px;
|
| 28 |
}
|
| 29 |
+
|
| 30 |
+
button {
|
| 31 |
+
margin: 10px;
|
| 32 |
+
padding: 10px 20px;
|
| 33 |
+
font-size: 16px;
|
| 34 |
+
}
|
templates/client.html
DELETED
|
@@ -1,44 +0,0 @@
|
|
| 1 |
-
<!DOCTYPE html>
|
| 2 |
-
<html lang="en">
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="UTF-8">
|
| 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">
|
| 12 |
-
<h2 id="join-title">Join the Quiz</h2>
|
| 13 |
-
<input type="text" id="username" placeholder="Enter your name" class="form-control">
|
| 14 |
-
<button onclick="joinQuiz()" class="btn btn-primary mt-2">Join</button>
|
| 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>
|
| 43 |
-
</body>
|
| 44 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
templates/host.html
DELETED
|
@@ -1,39 +0,0 @@
|
|
| 1 |
-
<!DOCTYPE html>
|
| 2 |
-
<html lang="en">
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="UTF-8">
|
| 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" id="start-question-label" class="mt-3" style="display: none;">Select starting question:</label>
|
| 21 |
-
<input type="range" id="start-question" min="1" max="10" value="1" class="form-range mt-2 mb-2" style="display: none;" oninput="updateSliderValue(this.value)">
|
| 22 |
-
<input type="number" id="start-question-number" min="1" max="10" value="1" class="form-control" style="display: none;" oninput="updateSliderValue(this.value)">
|
| 23 |
-
<p id="question-start-display" class="mt-2" style="display: none;">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>
|
| 37 |
-
<script src="/static/script.js"></script>
|
| 38 |
-
</body>
|
| 39 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
templates/index.html
CHANGED
|
@@ -3,14 +3,17 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
-
<title>Quiz
|
| 7 |
-
<link href="
|
| 8 |
</head>
|
| 9 |
<body>
|
| 10 |
-
<
|
| 11 |
-
|
| 12 |
-
<
|
| 13 |
-
<
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
| 15 |
</body>
|
| 16 |
-
</html>
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Quiz App</title>
|
| 7 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
| 8 |
</head>
|
| 9 |
<body>
|
| 10 |
+
<h1>Welcome to the Quiz App</h1>
|
| 11 |
+
<form action="/start" method="post" enctype="multipart/form-data">
|
| 12 |
+
<label for="name">Enter your name:</label>
|
| 13 |
+
<input type="text" id="name" name="name" required>
|
| 14 |
+
<label for="file">Upload JSON file:</label>
|
| 15 |
+
<input type="file" id="file" name="file" required>
|
| 16 |
+
<button type="submit">Start Quiz</button>
|
| 17 |
+
</form>
|
| 18 |
</body>
|
| 19 |
+
</html>
|
templates/quiz.html
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Quiz</title>
|
| 7 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
| 8 |
+
</head>
|
| 9 |
+
<body>
|
| 10 |
+
<h1>Quiz</h1>
|
| 11 |
+
<div id="quiz-container">
|
| 12 |
+
<div id="question"></div>
|
| 13 |
+
<div id="options"></div>
|
| 14 |
+
<button id="prev">Previous</button>
|
| 15 |
+
<button id="next">Next</button>
|
| 16 |
+
<button id="submit">Submit</button>
|
| 17 |
+
</div>
|
| 18 |
+
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
|
| 19 |
+
</body>
|
| 20 |
+
</html>
|
templates/results.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Results</title>
|
| 7 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
| 8 |
+
</head>
|
| 9 |
+
<body>
|
| 10 |
+
<h1>Results</h1>
|
| 11 |
+
<div id="results"></div>
|
| 12 |
+
</body>
|
| 13 |
+
</html>
|