Fausto Busuito commited on
Commit
1677ecc
1 Parent(s): 04033e9

Application changes

Browse files
Dockerfile CHANGED
@@ -7,9 +7,4 @@ RUN pip install -r requirements.txt
7
 
8
  COPY . .
9
 
10
- # Ensure the flask_session directory exists and has the correct permissions
11
- RUN mkdir -p /app/flask_session && chmod -R 777 /app/flask_session
12
-
13
- EXPOSE 7860
14
-
15
- CMD ["python", "app.py"]
 
7
 
8
  COPY . .
9
 
10
+ CMD ["python", "app/app.py"]
 
 
 
 
 
app.py DELETED
@@ -1,115 +0,0 @@
1
- from flask import Flask, render_template, request, redirect, url_for, session
2
- from flask_session import Session
3
- import json
4
- import random
5
- import os
6
- import time
7
-
8
- app = Flask(__name__)
9
- app.secret_key = 'supersecretkey'
10
-
11
- # Configure Flask-Session
12
- app.config['SESSION_TYPE'] = 'filesystem'
13
- Session(app)
14
-
15
- QUESTIONS_FOLDER = 'questions'
16
-
17
- @app.route('/')
18
- def index():
19
- files = [f for f in os.listdir(QUESTIONS_FOLDER) if f.endswith('.json')]
20
- return render_template('index.html', files=files)
21
-
22
- @app.route('/start', methods=['POST'])
23
- def start():
24
- session['questions'] = []
25
- session['answers'] = []
26
- session['current_question'] = 0
27
- session['current_answers'] = []
28
- session['start_time'] = time.time()
29
-
30
- selected_file = request.form['file']
31
- session['selected_file'] = os.path.splitext(selected_file)[0] # Remove file extension
32
- file_path = os.path.join(QUESTIONS_FOLDER, selected_file)
33
- with open(file_path, 'r') as file:
34
- questions = json.load(file)
35
- random.shuffle(questions)
36
- session['questions'] = questions
37
- session['answers'] = [[] for _ in questions] # Initialize answers list for each question
38
- return redirect(url_for('quiz'))
39
-
40
- @app.route('/quiz', methods=['GET', 'POST'])
41
- def quiz():
42
- if 'questions' not in session or 'current_question' not in session:
43
- return redirect(url_for('index'))
44
-
45
- if 'user_answers' not in session:
46
- session['user_answers'] = [None] * len(session['questions'])
47
-
48
- if request.method == 'POST':
49
- action = request.form.get('action')
50
- if action == 'next':
51
- question = session['questions'][session['current_question']]
52
- multiple_selection = 'Choose two' in question['question'] or 'Select TWO' in question['question']
53
- answers = request.form.getlist('answer')
54
- if multiple_selection:
55
- required_answers = 2
56
- if len(answers) == required_answers:
57
- session['user_answers'][session['current_question']] = answers
58
- else:
59
- if answers:
60
- session['user_answers'][session['current_question']] = answers
61
- session['current_question'] += 1
62
- if session['current_question'] >= len(session['questions']):
63
- return redirect(url_for('results'))
64
- elif action == 'previous':
65
- session['current_question'] -= 1
66
- if session['current_question'] < 0:
67
- session['current_question'] = 0
68
- elif action == 'end':
69
- return redirect(url_for('results'))
70
-
71
- question = session['questions'][session['current_question']]
72
- multiple_selection = 'Choose two' in question['question'] or 'Select TWO' in question['question']
73
- elapsed_time = time.time() - session['start_time']
74
- elapsed_time_str = time.strftime('%H:%M:%S', time.gmtime(elapsed_time))
75
-
76
- previous_answers = session['user_answers'][session['current_question']] if session['user_answers'][session['current_question']] is not None else []
77
-
78
- # Debugging statements
79
- print(f"Current question index: {session['current_question']}")
80
- print(f"Previous answers: {previous_answers}")
81
- print(f"Session user answers: {session['user_answers']}")
82
-
83
- return render_template('quiz.html', question=question,
84
- question_number=session['current_question'] + 1,
85
- total_questions=len(session['questions']),
86
- selected_file=session['selected_file'],
87
- show_previous=session['current_question'] > 0,
88
- multiple_selection=multiple_selection,
89
- elapsed_time=elapsed_time_str,
90
- previous_answers=previous_answers)
91
-
92
- @app.route('/results')
93
- def results():
94
- if 'questions' not in session or 'answers' not in session:
95
- return redirect(url_for('index'))
96
-
97
- total_questions = len(session['questions'])
98
- session['score'] = 0 # Initialize score
99
-
100
- for i, question in enumerate(session['questions']):
101
- correct_answers = [str(ord(x) - ord('A')) for x in question['correct']]
102
- user_answers = session['answers'][i]
103
- if set(user_answers) == set(correct_answers):
104
- session['score'] += 1
105
-
106
- score_percentage = (session['score'] / total_questions) * 100
107
- elapsed_time = time.time() - session['start_time']
108
- elapsed_time_str = time.strftime('%H:%M:%S', time.gmtime(elapsed_time))
109
-
110
- return render_template('results.html', score=session['score'],
111
- total_questions=total_questions, score_percentage=score_percentage,
112
- elapsed_time=elapsed_time_str)
113
-
114
- if __name__ == '__main__':
115
- app.run(host='0.0.0.0', port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/script.js → app/__init__.py RENAMED
File without changes
app/app.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, jsonify
2
+ import os
3
+ import json
4
+ import random
5
+ from datetime import datetime
6
+
7
+ app = Flask(__name__)
8
+
9
+ # Load the list of question files
10
+ QUESTIONS_DIR = "app/questions"
11
+ question_files = [f for f in os.listdir(QUESTIONS_DIR) if f.endswith('.json')]
12
+
13
+ @app.route('/')
14
+ def index():
15
+ return render_template('index.html', files=question_files)
16
+
17
+ @app.route('/load_questions', methods=['POST'])
18
+ def load_questions():
19
+ file_name = request.json.get('file_name')
20
+ file_path = os.path.join(QUESTIONS_DIR, file_name)
21
+ if not os.path.exists(file_path):
22
+ return jsonify({"error": "File not found"}), 404
23
+
24
+ with open(file_path, 'r') as f:
25
+ questions = json.load(f)
26
+ random.shuffle(questions)
27
+ return jsonify(questions)
28
+
29
+ @app.route('/submit_results', methods=['POST'])
30
+ def submit_results():
31
+ data = request.json
32
+ questions = data.get('questions')
33
+ user_answers = data.get('user_answers')
34
+
35
+ correct_count = 0
36
+ total_questions = len(questions)
37
+ for q, ua in zip(questions, user_answers):
38
+ if set(q['correct']) == set(ua):
39
+ correct_count += 1
40
+
41
+ score = (correct_count / total_questions) * 100
42
+ return jsonify({"score": score})
43
+
44
+ if __name__ == '__main__':
45
+ app.run(host='0.0.0.0', port=7860)
{questions → app/questions}/Amazon.AIF-C01.v2024-09-02.json RENAMED
File without changes
{questions → app/questions}/Amazon.CLF-C02.v2024-10-14.json RENAMED
File without changes
{questions → app/questions}/Amazon.SAA-C03.v2024-10-25.json RENAMED
File without changes
app/static/script.js ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ let questions = [];
2
+ let currentIndex = 0;
3
+ let userAnswers = [];
4
+ let startTime;
5
+
6
+ document.getElementById('start-session').addEventListener('click', async () => {
7
+ const file = document.getElementById('file').value;
8
+ if (!file) return alert("Please select a file");
9
+
10
+ const response = await fetch('/load_questions', {
11
+ method: 'POST',
12
+ headers: { 'Content-Type': 'application/json' },
13
+ body: JSON.stringify({ file_name: file })
14
+ });
15
+
16
+ questions = await response.json();
17
+ userAnswers = Array(questions.length).fill([]);
18
+ startQuiz();
19
+ });
20
+
21
+ function startQuiz() {
22
+ document.getElementById('file-selection').style.display = 'none';
23
+ document.getElementById('quiz-container').style.display = 'block';
24
+ startTime = Date.now();
25
+ displayQuestion();
26
+ setInterval(updateTimer, 1000);
27
+ }
28
+
29
+ function displayQuestion() {
30
+ const question = questions[currentIndex];
31
+ document.getElementById('question').innerText = question.question;
32
+ const optionsDiv = document.getElementById('options');
33
+ optionsDiv.innerHTML = '';
34
+ question.options.forEach((option, i) => {
35
+ const btn = document.createElement('button');
36
+ btn.innerText = option;
37
+ btn.className = userAnswers[currentIndex].includes(i) ? 'selected' : '';
38
+ btn.addEventListener('click', () => selectAnswer(i));
39
+ optionsDiv.appendChild(btn);
40
+ });
41
+ }
42
+
43
+ function selectAnswer(optionIndex) {
44
+ const currentAnswers = userAnswers[currentIndex];
45
+ if (currentAnswers.includes(optionIndex)) {
46
+ userAnswers[currentIndex] = currentAnswers.filter(i => i !== optionIndex);
47
+ } else {
48
+ userAnswers[currentIndex].push(optionIndex);
49
+ }
50
+ displayQuestion();
51
+ }
52
+
53
+ document.getElementById('next').addEventListener('click', () => {
54
+ currentIndex = Math.min(currentIndex + 1, questions.length - 1);
55
+ displayQuestion();
56
+ });
57
+
58
+ document.getElementById('prev').addEventListener('click', () => {
59
+ currentIndex = Math.max(currentIndex - 1, 0);
60
+ displayQuestion();
61
+ });
62
+
63
+ document.getElementById('end-session').addEventListener('click', async () => {
64
+ const response = await fetch('/submit_results', {
65
+ method: 'POST',
66
+ headers: { 'Content-Type': 'application/json' },
67
+ body: JSON.stringify({ questions, user_answers: userAnswers })
68
+ });
69
+ const result = await response.json();
70
+ document.getElementById('quiz-container').style.display = 'none';
71
+ document.getElementById('results-container').style.display = 'block';
72
+ document.getElementById('score').innerText = `Your score: ${result.score.toFixed(2)}%`;
73
+ });
74
+
75
+ function updateTimer() {
76
+ const elapsedTime = Math.floor((Date.now() - startTime) / 1000);
77
+ document.getElementById('timer').innerText = `Time: ${elapsedTime}s`;
78
+ }
app/static/style.css ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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: #f5f5f5;
10
+ }
11
+
12
+ .container {
13
+ width: 600px;
14
+ background: white;
15
+ padding: 20px;
16
+ border-radius: 8px;
17
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
18
+ }
app/templates/index.html ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 App</title>
7
+ <link rel="stylesheet" href="/static/style.css">
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ <h1>Quiz Application</h1>
12
+ <div id="file-selection">
13
+ <label for="file">Select a quiz:</label>
14
+ <select id="file">
15
+ <option value="" disabled selected>Choose a file</option>
16
+ {% for file in files %}
17
+ <option value="{{ file }}">{{ file }}</option>
18
+ {% endfor %}
19
+ </select>
20
+ <button id="start-session">Start Session</button>
21
+ </div>
22
+ <div id="quiz-container" style="display: none;">
23
+ <div id="question-container">
24
+ <p id="timer">Time: 0s</p>
25
+ <h2 id="question"></h2>
26
+ <div id="options"></div>
27
+ <div id="navigation">
28
+ <button id="prev">Previous</button>
29
+ <button id="next">Next</button>
30
+ </div>
31
+ <button id="end-session">End Session</button>
32
+ </div>
33
+ </div>
34
+ <div id="results-container" style="display: none;">
35
+ <h2>Quiz Results</h2>
36
+ <p id="score"></p>
37
+ </div>
38
+ </div>
39
+ <script src="/static/script.js"></script>
40
+ </body>
41
+ </html>
static/styles.css DELETED
@@ -1,25 +0,0 @@
1
- body {
2
- font-family: Arial, sans-serif;
3
- margin: 20px;
4
- }
5
-
6
- h1 {
7
- color: #333;
8
- }
9
-
10
- form {
11
- margin-top: 20px;
12
- }
13
-
14
- label {
15
- display: block;
16
- margin-top: 10px;
17
- }
18
-
19
- input[type="text"], input[type="file"] {
20
- margin-top: 5px;
21
- }
22
-
23
- button {
24
- margin-top: 20px;
25
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/index.html DELETED
@@ -1,22 +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 App</title>
7
- <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
8
- </head>
9
- <body>
10
- <h1>Welcome to the Quiz App</h1>
11
- <form action="{{ url_for('start') }}" method="post">
12
- <label for="file">Select a JSON file:</label>
13
- <select id="file" name="file" required>
14
- {% for file in files %}
15
- <option value="{{ file }}">{{ file }}</option>
16
- {% endfor %}
17
- </select>
18
- <br>
19
- <button type="submit">Start Quiz</button>
20
- </form>
21
- </body>
22
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/quiz.html DELETED
@@ -1,48 +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</title>
7
- <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
8
- <script>
9
- function updateTimer() {
10
- const startTime = {{ session['start_time'] }} * 1000;
11
- const now = new Date().getTime();
12
- const elapsedTime = new Date(now - startTime);
13
- const hours = elapsedTime.getUTCHours().toString().padStart(2, '0');
14
- const minutes = elapsedTime.getUTCMinutes().toString().padStart(2, '0');
15
- const seconds = elapsedTime.getUTCSeconds().toString().padStart(2, '0');
16
- document.getElementById('timer').innerText = `${hours}:${minutes}:${seconds}`;
17
- }
18
- setInterval(updateTimer, 1000);
19
- </script>
20
- </head>
21
- <body onload="updateTimer()">
22
- <h1>Question {{ question_number }} of {{ total_questions }} - {{ selected_file }} <span id="timer"></span></h1>
23
- <p>{{ question.question }}</p>
24
- <form action="{{ url_for('quiz') }}" method="post">
25
- {% if multiple_selection %}
26
- {% for option in question.options %}
27
- <label style="display: flex; align-items: center;">
28
- <input type="checkbox" name="answer" value="{{ loop.index }}" {% if loop.index in previous_answers %}checked{% endif %}>
29
- <span>{{ option }}</span>
30
- </label><br>
31
- {% endfor %}
32
- {% else %}
33
- {% for option in question.options %}
34
- <label style="display: flex; align-items: center;">
35
- <input type="radio" name="answer" value="{{ loop.index }}" {% if loop.index in previous_answers %}checked{% endif %}>
36
- <span>{{ option }}</span>
37
- </label><br>
38
- {% endfor %}
39
- {% endif %}
40
- <br>
41
- {% if show_previous %}
42
- <button type="submit" name="action" value="previous">Previous</button>
43
- {% endif %}
44
- <button type="submit" name="action" value="next">Next</button>
45
- <button type="submit" name="action" value="end">End Session</button>
46
- </form>
47
- </body>
48
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/results.html DELETED
@@ -1,16 +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>Results</title>
7
- <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
8
- </head>
9
- <body>
10
- <h1>Results</h1>
11
- <p>Your score is {{ score }} out of {{ total_questions }}.</p>
12
- <p>That's {{ score_percentage }}%.</p>
13
- <p>Time Elapsed: {{ elapsed_time }}</p>
14
- <a href="{{ url_for('index') }}">Take another quiz</a>
15
- </body>
16
- </html>