Fausto Busuito commited on
Commit
d353aa8
·
1 Parent(s): 937ef12

Application changes

Browse files
Dockerfile CHANGED
@@ -1,29 +1,20 @@
1
- # Use the official Python image with version 3.9
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.txt file into the container
17
- COPY --chown=user ./requirements.txt requirements.txt
18
 
19
- # Install the dependencies listed in requirements.txt
20
- RUN pip install --no-cache-dir --upgrade -r requirements.txt
21
 
22
- # Copy the entire project into the container
23
- COPY --chown=user . /app
24
 
25
- # Expose the application port (7860) for Hugging Face Spaces
26
- EXPOSE 7860
27
 
28
- # Set the command to run the application using Flask-SocketIO and eventlet
29
- CMD ["python", "app.py"]
 
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
- const socket = io();
2
- let username;
3
-
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
- 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
 
33
- function loadQuiz() {
34
- const examName = document.getElementById('exam-selector').value;
35
- const startQuestion = parseInt(document.getElementById('start-question-number').value, 10);
36
-
37
- if (!examName) {
38
- alert("Please select an exam first.");
39
- return;
 
 
 
40
  }
41
 
42
- socket.emit('load_quiz', { exam_name: examName, start_question: startQuestion });
43
- }
44
-
45
-
46
-
47
- function updateSliderValue(value) {
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
- socket.on('clear_results', () => {
118
- document.getElementById('results').innerHTML = '';
119
- });
 
 
 
120
 
121
- socket.on('display_final_results', (finalResults) => {
122
- document.getElementById('quiz-content').style.display = 'none';
123
- const resultsTable = document.getElementById('results-table');
124
- resultsTable.innerHTML = '';
125
- finalResults.forEach((participant) => {
126
- const row = `<tr><td>${participant.username}</td><td>${participant.score}</td></tr>`;
127
- resultsTable.innerHTML += row;
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  });
129
- document.getElementById('final-results').style.display = 'block';
130
- });
131
 
132
- socket.on('quiz_reset', () => {
133
- document.getElementById('results').innerHTML = '';
134
- document.getElementById('question-text').innerText = '';
135
- document.getElementById('options').innerHTML = '';
136
- document.getElementById('final-results').style.display = 'none';
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
- .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
  }
 
 
 
 
 
 
 
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 Application</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 text-center">
11
- <h1 class="mt-5">Real-Time Quiz Application</h1>
12
- <button onclick="window.location.href='/client'" class="btn btn-primary mt-3">Join as Client</button>
13
- <button onclick="window.location.href='/host'" class="btn btn-success mt-3">Join as Host</button>
14
- </div>
 
 
 
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>