├── AllTests.pdf
├── script automatico
├── .gitignore
├── README.md
└── addQuestion.py
├── README.md
├── index.html
├── export
└── export_html.py
├── style.css
└── script.js
/AllTests.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/martinBDev/DPPI_preguntator_2023/HEAD/AllTests.pdf
--------------------------------------------------------------------------------
/script automatico/.gitignore:
--------------------------------------------------------------------------------
1 | **.copy.json
2 | Test - Tema **
3 | **.cfg
4 | Scripts
5 | Lib
6 | Include
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DPPI_preguntator_2023
2 |
3 | App de tests para preparar el examen de DPPI
4 |
5 | - Autores: Martín Beltrán, Luis Vijande, Daniel Alvarez y Juan Torrente.
6 |
7 | # AGRADECIMIENTOS POR APORTACIONES #
8 | - Sara Fernandez Arias
9 | - Rosa García Lopez
10 | - Carlos Garriga Suarez
11 | - Jesus Alonso García
12 | - Diego Martín Fernandez
13 | - Joaquin Salustiano Britos Morales
14 | - Laís García Suarez
15 | - Laura Pernía Blanco
16 | - Mario Lombas Calderón de la Barca
17 | - Alejandro Alvarez Solís
18 | - Paula Suarez Prieto
19 | - Alvaro Rodriguez Gonzalez
20 | - Daniel Pascual
21 | - Pablo Diaz Rubio
22 | - Paula Puerta
23 | - Pablo Rodríguez Rodríguez
24 | - Stelian Adrian Stanci
25 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Test Online
7 |
8 |
9 |
10 |
11 |
12 |
13 |
Test Online
14 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
Ir a pregunta
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/script automatico/README.md:
--------------------------------------------------------------------------------
1 | # Script Automático para añadir las preguntas
2 | ## Como funciona
3 | Muy simple, vete a los cuestionarios y descargalos, en firefox es con ctrl + s, no tengo ni idea como va en otros buscadores.
4 | Cuando lo descargues, guardalo en este carpeta, no se va a añadir al repo, no te preocupes. Una vez que tengas los cuestionarios descargados, solo tienes que usar el script, debería ir automático. Tampoco te preocupes por colisiones, el achivo se encarga de eso también.
5 |
6 | **FIREFOZ DESCARGA LOS FICHEROS CON EXTENSION .HTM; CHROME CON EXTENSION .HTML. EL SCRIPT USA HTML, RECOMENDAMOS CAMBIAR LA EXTENSIÓN DE LOS FICHEROS A .HTML O CAMBIAR LA LINEA 67 DEL SCRIPT PARA QUE LEA .HTM, LO QUE EL USUARIO QUIERA**
7 |
8 | ## Resultados
9 | El archivo **NO** reemplaza el questions.json original, crea una copia en esta carpeta llamada questions.copy.json. Una vez que haya confirmado que el script ha funcionado, puedes reemplazar el json original. Muchas gracias
10 | ## **⚠⚠IMPORTANTE⚠⚠**
11 | **NECESITAS BeautifullSoup para que esto funcione.**
12 | ```bash
13 | python -m pip install beautifulsoup4
14 | ```
15 | Pon esto en la consola, y si tienes python metido en la ruta debería ir sin problemas. Si ves que te da error y no encuntra el módulo, haz lo siguiente:
16 |
17 | 1 - Entra en la carpeta del script
18 | 2 - Instala entorno virtual de python:
19 | ```bash
20 | python -m pip install --user virtualenv
21 | ```
22 | 3 - Crea el entorno DENTRO DE LA CARPETA DEL SCRIPT
23 | ```bash
24 | python -m venv .
25 | ```
26 | Importante incluir el punto "."
27 | 4 - Ejecuta el entorno:
28 | ```bash
29 | .\Scripts\activate
30 | ```
31 | 5 - Instala de nuevo BeautifullSoup
32 | ```bash
33 | pip install beautifulsoup4
34 | ```
35 |
36 | Ejecuta el script con los HTML en la carpeta (no les cambies el nombre):
37 | ```bash
38 | py .\addQuestion.py
39 | ```
--------------------------------------------------------------------------------
/script automatico/addQuestion.py:
--------------------------------------------------------------------------------
1 | import json
2 | from bs4 import BeautifulSoup
3 | import glob
4 | """
5 | Un script para añadir y detectar colisiones directamente al json
6 | """
7 |
8 | def cleanHtm(txt:str) -> str:
9 | return txt.replace('\t','').replace('\n',' ').replace(' ', ' ').strip()
10 |
11 |
12 |
13 |
14 | def parseHtm(htmFile:str) -> dict:
15 | with open(htmFile, 'r', encoding='utf-8') as f:
16 | htmlfile = f.read()
17 | soup = BeautifulSoup(htmlfile, 'html.parser')
18 |
19 | questions = []
20 | for questionField in soup.find_all('div', {'class': 'formulation'}):
21 | question = {
22 | 'question': cleanHtm(questionField.find('div').text),
23 | 'options': [],
24 | 'answer': -1,
25 | 'explanation': '...'
26 | }
27 | correctTag = questionField.parent.find('div', {'class': 'rightanswer'})
28 | correctText = cleanHtm(correctTag.text).replace('La respuesta correcta es: ','')
29 | # enumerar por preguntas
30 | for idx, answer in enumerate(questionField.find_all('div', {'data-region': 'answer-label'})):
31 | answerText = cleanHtm(answer.div.text)
32 | question['options'].append(answerText)
33 | if correctText == answerText:
34 | question['answer'] = idx
35 |
36 | questions.append(question)
37 | # conseguir retroalimentación
38 | feedback = questionField.parent.find('div', {'class': 'generalfeedback'})
39 | if feedback:
40 | question['explanation'] = f'{cleanHtm(feedback.text)}\n{correctText}'
41 |
42 | return questions
43 |
44 |
45 | def mergeData(data:dict, topic:str, questions: dict):
46 | theme = data[topic - 1]
47 |
48 | for question in questions:
49 | # buscar colisiones
50 | repeated = False
51 | for originalQuestion in theme['questions']:
52 | # hay preguntas con el mismo enunciado pero distintas opcions, si la pregunta y la explicación es la misma, está repetida
53 | if question['question'] == originalQuestion['question']:
54 | if len(set(question['options'] + originalQuestion['options'])) == len(question['options']):
55 | repeated = True
56 | if not repeated:
57 | theme['questions'].append(question)
58 |
59 | return data
60 |
61 |
62 |
63 | def main():
64 | # conseguir archivos
65 | htms = glob.glob('*.htm*')
66 | with open('../questions.json', 'r', encoding='utf-8') as f:
67 | data = json.loads(f.read())
68 |
69 | for htm in htms:
70 | # conseguir tema
71 | topic = int(htm.split(' ')[3].replace('.',''))
72 | # conseguir preguntas
73 | questions = parseHtm(htm)
74 | # juntar datos
75 | data = mergeData(data, topic, questions)
76 |
77 | # guardar datos
78 | with open('questions.copy.json', 'w', encoding='utf-8') as f:
79 | f.write(json.dumps(data, ensure_ascii=False, indent=2))
80 |
81 |
82 | if __name__ == '__main__':
83 | main()
--------------------------------------------------------------------------------
/export/export_html.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | def load_json(path):
4 | """
5 | Loads json in the following form:
6 |
7 | [
8 | {
9 | "topic": "Topic 1: title",
10 | "questions": [
11 | {
12 | "question": "¿What is the result of 3*3?",
13 | "options": [
14 | "2",
15 | "3",
16 | "9"
17 | ],
18 | "answer": 2,
19 | "explanation": "3 times 3 equals 9."
20 | },
21 | {}
22 | ]
23 | },
24 | {
25 | "topic": "Topic 2: title",
26 | "questions": []
27 | }
28 | ]
29 |
30 | """
31 | with open(path, 'r') as f:
32 | return json.load(f)
33 |
34 |
35 | def export_html(entries_json):
36 | html = """
37 |
38 |
39 | Tesst DPPI
40 |
77 |
78 |
79 | """
80 |
81 |
82 | for topic in entries_json:
83 | html += f"{topic['topic']}
"
84 | for question in topic['questions']:
85 | html += f"{question['question']}
"
86 | for i, option in enumerate(question['options']):
87 | if i == question['answer']:
88 | html += f"{i+1}. {option}
"
89 | else:
90 | html += f"{i+1}. {option}
"
91 | html += f"Answer: {question['answer'] + 1}
"
92 | html += f"Explanation: {question['explanation']}
"
93 | html += "
"
94 |
95 | html += ""
96 |
97 | with open('questionnaire.html', 'w') as f:
98 | f.write(html)
99 |
100 | def main():
101 | debug = True
102 |
103 |
104 | #load json file
105 | entries = load_json('../questions.json')
106 |
107 | #print every entry in entries (para que el copilot se entere)
108 | if debug:
109 | for entry in entries:
110 | print(entry["topic"])
111 |
112 | for question in entry["questions"]:
113 | print(question["question"])
114 | print(question["options"])
115 | print(question["answer"])
116 | print(question["explanation"])
117 |
118 | export_html(entries)
119 | if __name__ == '__main__':
120 | main()
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --c-app1 : #ffffff;
3 | --c-app2 : #dddddd;
4 | --c-btn1 : #007BFF;
5 | --c-btn2 : #0056b3;
6 | --c-main1 : #a1d5f8;
7 | --c-main2 : #8ccbf7;
8 | --c-correct : #5ce25c;
9 | --c-incorrect : #e25c5c;
10 | --c-background : #f4f4f4;
11 | --c-explanation1: #f8f8f0;
12 | --c-explanation2: #e8e8d0;
13 | --c-btn-disabled: #cccccc;
14 | }
15 | body {
16 | font-family: 'Arial', sans-serif;
17 | background-color: var(--c-background);
18 | color: #333;
19 | margin: 0;
20 | padding: 0;
21 | }
22 |
23 | #app {
24 | max-width: 45%;
25 | margin: 5px auto;
26 | padding: 20px;
27 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
28 | border-radius: 10px;
29 | position: relative;
30 | min-height: 100vh;
31 | background-color: var(--c-app1);
32 | }
33 |
34 | #traversal {
35 | position: fixed;
36 | left: 5px;
37 | top: 5px;
38 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
39 | border-radius: 10px;
40 | padding: 20px;
41 | min-height: 25vh;
42 | max-width: 18%;
43 | content: "";
44 | background-color: #fff;
45 |
46 | }
47 |
48 | #traversal-container {
49 | display: flex;
50 | flex-wrap: wrap;
51 | justify-content: center;
52 | }
53 |
54 | .traversal-element {
55 | width: 2em;
56 | margin: 2px;
57 | height: 2em;
58 | border: 1px solid black;
59 | border-radius: .5rem;
60 | align-items: center;
61 | transition: all 300ms ease; /* Actualizado */
62 | }
63 |
64 | .traversal-element:hover, .traversal-element:focus-within {
65 | transform: scale(1.02);
66 | background-color: var(--c-app2);
67 | }
68 |
69 | .traversal-button {
70 | width: 100%;
71 | height: 100%;
72 | background-color: var(--c-app);
73 | border-radius: .5rem;
74 | text-decoration: none;
75 | color: black;
76 | background-color: transparent;
77 | display: flex;
78 | align-items: center;
79 | }
80 |
81 | .traversal-text {
82 | display: inline-block;
83 | width: 100%;
84 | text-align: center;
85 | }
86 |
87 | .incorrect {
88 | background-color: var(--c-incorrect);
89 | }
90 |
91 | .correct {
92 | background-color: var(--c-correct);
93 | }
94 | .answered {
95 | background-color: var(--c-main1);
96 | }
97 |
98 | h1 {
99 | text-align: center;
100 | color: #333;
101 | padding-bottom: 20px;
102 | border-bottom: 1px solid var(--c-app2);
103 | }
104 |
105 | #topic-selector {
106 | width: 100%;
107 | margin: 20px 0;
108 | padding: 10px;
109 | border: 1px solid var(--c-app2);
110 | border-radius: 5px;
111 | font-size: 16px;
112 | background-color: var(--c-app1);
113 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
114 | }
115 |
116 | #question-container {
117 | margin-top: 20px;
118 | }
119 |
120 | .question {
121 | margin-top: 10px;
122 | padding-bottom: 5px;
123 | position: relative;
124 | }
125 |
126 | ul {
127 | margin: 0;
128 | }
129 |
130 | .question::before {
131 | position: absolute;
132 | content: "";
133 | width: 100%;
134 | bottom: 0;
135 | left: 0;
136 | height: 2px;
137 | background-color: var(--c-app2);
138 | }
139 |
140 | .question label {
141 | display: block;
142 | margin-bottom: 10px;
143 | font-size: 18px;
144 | }
145 |
146 | .question ul {
147 | list-style-type: none;
148 | padding: 0;
149 | }
150 |
151 | .answer {
152 | display: flex; /* Nuevo */
153 | align-items: center; /* Nuevo */
154 | margin-bottom: 10px;
155 | background-color: var(--c-main1);
156 | padding: 10px;
157 | border-radius: 5px;
158 | cursor: pointer;
159 | transition: background-color 300ms ease, transform 300ms ease; /* Actualizado */
160 | }
161 |
162 | .answer:hover, .answer:focus-within {
163 | background-color: var(--c-main2);
164 | transform: scale(1.005); /* Nuevo */
165 | }
166 |
167 | .answer label {
168 | pointer-events: none; /* Nuevo */
169 | margin-left: 10px; /* Nuevo */
170 | margin: 0; /* Nuevo */
171 | padding: 0; /* Nuevo */
172 | display: flex; /* Nuevo */
173 | align-items: center; /* Nuevo */
174 | }
175 |
176 |
177 | .answer input {
178 | margin-right: 10px;
179 | margin: 10px;
180 | }
181 |
182 | #buttons-container {
183 | display: flex;
184 | flex-direction: row;
185 | position: fixed;
186 | bottom: 0;
187 | right: 0;
188 | padding: 20px;
189 | border-top-left-radius: 10px;
190 | background: transparent;
191 | }
192 |
193 | /* Estilo para el contenedor del switch */
194 | .switch-container {
195 | position: fixed;
196 | bottom: 20px;
197 | left: 20px;
198 | background-color: #f5f5f5;
199 | padding: 10px 20px;
200 | border-radius: 20px;
201 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
202 | display: flex;
203 | align-items: center;
204 | justify-content: space-between;
205 | z-index: 1000;
206 | max-width:20%;
207 | }
208 |
209 | /* Estilo para la etiqueta del switch */
210 | .switch-container label {
211 | font-size: 16px;
212 | color: #333;
213 | margin-right: 10px;
214 | }
215 |
216 | /* Estilo para el switch */
217 | .switch-container .switch {
218 | position: relative;
219 | display: inline-block;
220 | width: 60px;
221 | height: 34px;
222 | }
223 |
224 | /* Estilo para el slider del switch */
225 | .switch-container .switch input {
226 | opacity: 0;
227 | width: 0;
228 | height: 0;
229 | }
230 |
231 | .switch-container .slider {
232 | position: absolute;
233 | cursor: pointer;
234 | top: 0;
235 | left: 0;
236 | right: 0;
237 | bottom: 0;
238 | background-color: #ccc;
239 | transition: .4s;
240 | border-radius: 34px;
241 | }
242 |
243 | .switch-container .slider:before {
244 | position: absolute;
245 | content: "";
246 | height: 26px;
247 | width: 26px;
248 | left: 4px;
249 | bottom: 4px;
250 | background-color: white;
251 | transition: .4s;
252 | border-radius: 50%;
253 | }
254 |
255 | .switch-container input:checked + .slider {
256 | background-color: #2196F3;
257 | }
258 |
259 | .switch-container input:checked + .slider:before {
260 | transform: translateX(26px);
261 | }
262 |
263 | /* Estilo para el slider del switch - versión redondeada */
264 | .switch-container .slider.round {
265 | border-radius: 34px;
266 | }
267 |
268 | .switch-container .slider.round:before {
269 | border-radius: 50%;
270 | }
271 |
272 | #scroll-to-top-button {
273 | display: none;
274 | align-self: right;
275 | }
276 |
277 | button.btn {
278 | padding: 10px 20px;
279 | border: none;
280 | background-color: var(--c-btn1);
281 | color: white;
282 | font-size: 16px;
283 | border-radius: 5px;
284 | margin: 10px;
285 | cursor: pointer;
286 | transition: background-color 0.3s ease;
287 | }
288 |
289 | button.btn:hover, button.btn:focus-within {
290 | background-color: var(--c-btn2);
291 | }
292 |
293 | button.btn:disabled {
294 | background-color: var(--c-btn-disabled);
295 | }
296 |
297 | .explanation {
298 | background-color: var(--c-explanation1);
299 | padding: 10px;
300 | border-radius: 5px;
301 | margin-top: 10px;
302 | border-left: 5px solid var(--c-explanation2);
303 | }
304 |
--------------------------------------------------------------------------------
/script.js:
--------------------------------------------------------------------------------
1 | document.addEventListener("DOMContentLoaded", () => {
2 | // Cargar preguntas
3 | fetch("questions.json")
4 | .then(response => response.json())
5 | .then(data => {
6 | populateTopics(data);
7 | createMockExam(data);
8 | loadQuestions(data[0].topic);
9 | loadTraversal(data[0].topic);
10 | });
11 |
12 | // Escuchar evento de cambio en el selector de temas
13 | const topicSelector = document.getElementById("topic-selector");
14 | topicSelector.addEventListener("change", (event) => {
15 | loadQuestions(event.target.value);
16 | // al cambiar de topic, asegurarse que el botón cambie de estado si se ha usado
17 | hideAnswers();
18 | loadTraversal(event.target.value);
19 | });
20 |
21 | // Escuchar evento de click en el botón de enviar
22 | const submitButton = document.getElementById("submit-button");
23 | submitButton.addEventListener("click", () => {
24 | // Aquí va la lógica para validar las respuestas
25 | });
26 |
27 | const viewAnswersButton = document.getElementById("view-answers-button");
28 | viewAnswersButton.addEventListener("click", () => {
29 | viewAnswers();
30 | });
31 |
32 |
33 |
34 | });
35 |
36 |
37 | let instantFeedback = false;
38 | const switchButton = document.getElementById('switch');
39 | const switchInput = document.getElementById('instant-feedback-switch');
40 | switchButton.onclick = function() {
41 | if (switchInput.checked === false) {
42 | console.log('checked');
43 | document.documentElement.setAttribute('data-theme', 'dark');
44 | switchInput.checked = true;
45 | updateInstantFeedback(true);
46 | } else {
47 | console.log('not checked');
48 | document.documentElement.setAttribute('data-theme', 'light');
49 | switchInput.checked = false;
50 | updateInstantFeedback(false);
51 | }
52 | }
53 | function calculateScore() {
54 | const selectedTopic = questionsData.find(q => q.topic === document.getElementById("topic-selector").value);
55 | const totalQuestions = selectedTopic.questions.length;
56 | const pointsPerQuestion = 10 / totalQuestions;
57 | let score = 0;
58 |
59 | selectedTopic.questions.forEach((question, index) => {
60 | const selectedOption = document.querySelector(`input[name="question-${index}"]:checked`);
61 |
62 | if (selectedOption) {
63 | const selectedIndex = parseInt(selectedOption.value);
64 |
65 | if (selectedIndex === question.answer) {
66 | score += pointsPerQuestion;
67 | } else {
68 | score -= pointsPerQuestion / totalQuestions;
69 | }
70 | }
71 | });
72 |
73 | // Asegurarse de que la nota esté en el rango de 0 a 10
74 | score = Math.min(Math.max(score, 0), 10);
75 | alert(`Tu nota es: ${score.toFixed(2)}`);
76 | }
77 |
78 | // Escuchar evento de click en el botón de enviar
79 | const submitButton = document.getElementById("submit-button");
80 | submitButton.addEventListener("click", () => {
81 | calculateScore();
82 | displayResults();
83 | });
84 |
85 | let questionsData = [];
86 |
87 | function populateTopics(questions) {
88 | questionsData = questions;
89 | const topicSelector = document.getElementById("topic-selector");
90 | questions.forEach(questionSet => {
91 | const option = document.createElement("option");
92 | option.value = questionSet.topic;
93 | option.textContent = questionSet.topic;
94 | topicSelector.appendChild(option);
95 | });
96 | }
97 |
98 | function displayResults() {
99 | const selectedTopic = questionsData.find(q => q.topic === document.getElementById("topic-selector").value);
100 |
101 | selectedTopic.questions.forEach((question, index) => {
102 | const selectedOption = document.querySelector(`input[name="question-${index}"]:checked`);
103 | const questionDiv = document.querySelector(`.question[data-index="${index}"]`);
104 | const traversalElement = document.querySelector(`.traversal-element[data-index="${index}"]`)
105 | if (selectedOption) {
106 | const selectedIndex = parseInt(selectedOption.value);
107 | const optionItem = selectedOption.parentElement;
108 |
109 | if (selectedIndex === question.answer) {
110 | optionItem.style.backgroundColor = "var(--c-correct)";
111 | traversalElement.className = 'traversal-element correct'
112 | } else {
113 | optionItem.style.backgroundColor = "var(--c-incorrect)";
114 | traversalElement.className = 'traversal-element incorrect'
115 | }
116 | }
117 |
118 | const explanation = document.createElement("p");
119 | explanation.className = "explanation";
120 | explanation.textContent = question.explanation;
121 | //check if the explanation is already displayed
122 | if(questionDiv.lastChild.className != "explanation"){
123 |
124 | questionDiv.appendChild(explanation);
125 | }
126 | });
127 | }
128 |
129 |
130 | function loadTraversal(topic) {
131 | // obtener los contenedores
132 | const traversalContainer = document.getElementById('traversal-container')
133 | // Vaciar los contenedores
134 | traversalContainer.innerHTML = '';
135 |
136 | const selectedTopic = questionsData.find(q => q.topic === topic)
137 | selectedTopic.questions.forEach((question, index) => {
138 | // populate traversal
139 | const traversalDiv = document.createElement('div')
140 | const traversalA = document.createElement('a')
141 | const traversalText = document.createElement('span')
142 |
143 | traversalDiv.className = 'traversal-element'
144 | traversalDiv.dataset.index = index
145 |
146 | traversalA.className = 'traversal-button'
147 | traversalA.href = `#q_${index}`
148 |
149 | traversalText.textContent = index+1
150 | traversalText.classList = 'traversal-text'
151 |
152 | traversalA.appendChild(traversalText)
153 | traversalDiv.appendChild(traversalA)
154 | traversalContainer.appendChild(traversalDiv)
155 | })
156 | }
157 |
158 | function loadQuestions(topic) {
159 | // obtener los contenedores
160 | const questionContainer = document.getElementById("question-container");
161 | // Vaciar los contenedores
162 | questionContainer.innerHTML = "";
163 |
164 | const selectedTopic = questionsData.find(q => q.topic === topic);
165 | selectedTopic.questions.forEach((question, index) => {
166 | const questionDiv = document.createElement("div");
167 | questionDiv.classList.add("question");
168 | // add unique index per question
169 | questionDiv.id = `q_${index}`
170 | questionDiv.dataset.index = index;
171 |
172 | const questionLabel = document.createElement("label");
173 | questionLabel.textContent = `${index + 1}. ${question.question}`;
174 | questionDiv.appendChild(questionLabel);
175 |
176 |
177 | const optionList = document.createElement("ul");
178 | question.options.forEach((option, optionIndex) => {
179 | const optionItem = document.createElement("li");
180 | optionItem.className = "answer"
181 |
182 |
183 | const optionInput = document.createElement("input");
184 | optionInput.type = "radio";
185 | optionInput.name = `question-${index}`;
186 | optionInput.value = optionIndex;
187 | optionInput.id = `question-${index}-${optionIndex}`;
188 |
189 | const optionLabel = document.createElement("label");
190 | optionLabel.textContent = option;
191 | optionLabel.htmlFor = `question-${index}-${optionIndex}`;
192 |
193 | optionItem.appendChild(optionInput);
194 | optionItem.appendChild(optionLabel);
195 | optionList.appendChild(optionItem);
196 |
197 | optionItem.addEventListener("click", (event) => {
198 | if(event.target !== optionInput) {
199 | optionInput.click();
200 | if(!switchInput.checked) {
201 | document.querySelector(`.traversal-element[data-index="${index}"]`).classList.add('answered')
202 | }
203 | }
204 | });
205 |
206 | });
207 | questionDiv.appendChild(optionList);
208 | questionContainer.appendChild(questionDiv);
209 | });
210 | }
211 |
212 | //Marks the correct answers and displays the explanation
213 | function viewAnswers(){
214 | const selectedTopic = questionsData.find(q => q.topic === document.getElementById("topic-selector").value);
215 |
216 | selectedTopic.questions.forEach((question, index) => {
217 | const questionDiv = document.querySelector(`.question[data-index="${index}"]`);
218 | const explanation = document.createElement("p");
219 | explanation.className = "explanation";
220 |
221 | explanation.textContent = question.explanation;
222 | if(questionDiv.lastChild.className != "explanation"){
223 | questionDiv.appendChild(explanation);
224 | }
225 |
226 | //Marks the correct answer
227 | const correctOption = document.getElementById(`question-${index}-${question.answer}`);
228 | const correctOptionItem = correctOption.parentElement;
229 | correctOptionItem.style.backgroundColor = "var(--c-correct)";
230 | correctOption.checked = true;
231 |
232 | });
233 |
234 | //change the button to hide answers
235 | const viewAnswersButton = document.getElementById("view-answers-button");
236 | viewAnswersButton.textContent = "Ocultar respuestas";
237 | viewAnswersButton.removeEventListener("click", viewAnswers);
238 | viewAnswersButton.addEventListener("click", hideAnswers);
239 | }
240 |
241 | function hideAnswers() {
242 | const explanationList = document.querySelectorAll(".explanation");
243 | explanationList.forEach(explanation => {
244 | explanation.remove();
245 | });
246 | // reset traversal
247 | const traversalList = document.querySelectorAll('.traversal-element')
248 | traversalList.forEach(traversal => {
249 | traversal.className = 'traversal-element'
250 | })
251 |
252 | const inputs = document.querySelectorAll("input");
253 | inputs.forEach(input => {
254 | input.checked = false;
255 | });
256 |
257 | const optionItems = document.querySelectorAll(".answer");
258 | optionItems.forEach(optionItem => {
259 | optionItem.style.backgroundColor = "#a1d5f8";
260 | optionItem.style.color = "black";
261 | });
262 |
263 | //change the button to view answers
264 | const viewAnswersButton = document.getElementById("view-answers-button");
265 | viewAnswersButton.textContent = "Ver respuestas";
266 | viewAnswersButton.removeEventListener("click", hideAnswers);
267 | viewAnswersButton.addEventListener("click", viewAnswers);
268 |
269 | }
270 |
271 | function createMockExam(data) {
272 | let mockExamQuestions = [];
273 | const numOfQuestionsPerTopic = 5;
274 |
275 | data.forEach(topic => {
276 | const questions = topic.questions;
277 | for(let i = 0; i < numOfQuestionsPerTopic; i++) {
278 | const randomIndex = Math.floor(Math.random() * questions.length);
279 | mockExamQuestions.push(questions[randomIndex]);
280 | questions.splice(randomIndex, 1); // Removemos la pregunta seleccionada para evitar duplicados
281 | }
282 | });
283 |
284 | shuffleArray(mockExamQuestions); // Mezcla las preguntas del simulacro de examen
285 |
286 | // Añadimos el nuevo tema de "Simulacro de examen" a los datos de las preguntas
287 | questionsData.push({
288 | topic: "Simulacro de examen",
289 | questions: mockExamQuestions
290 | });
291 |
292 | // Añadimos la opción de "Simulacro de examen" al selector de temas
293 | const topicSelector = document.getElementById("topic-selector");
294 | const option = document.createElement("option");
295 | option.value = "Simulacro de examen";
296 | option.textContent = "Simulacro de examen";
297 | topicSelector.appendChild(option);
298 | }
299 |
300 | function shuffleArray(array) {
301 | for (let i = array.length - 1; i > 0; i--) {
302 | const j = Math.floor(Math.random() * (i + 1));
303 | [array[i], array[j]] = [array[j], array[i]]; // Intercambia elementos
304 | }
305 | }
306 |
307 |
308 | const scrollToTopButton = document.getElementById('scroll-to-top-button')
309 | scrollToTopButton.addEventListener('click', () => {
310 | window.scrollTo({
311 | top: 0,
312 | left: 0,
313 | behavior: 'smooth'
314 | })
315 | })
316 |
317 | // Show or hide the button depending on the scroll position
318 | window.addEventListener('scroll', () => {
319 | if (window.scrollY > 1) {
320 | scrollToTopButton.style.display = 'block'
321 | } else {
322 | scrollToTopButton.style.display = 'none'
323 | }
324 | }
325 | )
326 |
327 |
328 | function updateInstantFeedback(enabled) {
329 | const allOptionInputs = document.querySelectorAll("input[type='radio']");
330 | allOptionInputs.forEach((input) => {
331 | if (enabled) {
332 | input.addEventListener("change", instantFeedbackHandler);
333 | } else {
334 | input.removeEventListener("change", instantFeedbackHandler);
335 | }
336 | });
337 | }
338 |
339 | function instantFeedbackHandler(event) {
340 | const input = event.target;
341 | const questionIndex = parseInt(input.name.split("-")[1]);
342 | const selectedOptionIndex = parseInt(input.value);
343 |
344 | const selectedTopic = questionsData.find(q => q.topic === document.getElementById("topic-selector").value);
345 | const question = selectedTopic.questions[questionIndex];
346 |
347 | const optionItem = input.parentElement;
348 |
349 | if (selectedOptionIndex === question.answer) {
350 | optionItem.style.backgroundColor = "var(--c-correct)";
351 | document.querySelector(`.traversal-element[data-index="${questionIndex}"]`).classList.add('correct')
352 | } else {
353 | optionItem.style.backgroundColor = "var(--c-incorrect)";
354 | document.querySelector(`.traversal-element[data-index="${questionIndex}"]`).classList.add('incorrect')
355 | }
356 |
357 | //explination
358 | const explanation = document.createElement("p");
359 | explanation.className = "explanation";
360 | explanation.textContent = question.explanation;
361 | let questionDiv = document.querySelector(`.question[data-index="${questionIndex}"]`);
362 | if(questionDiv.lastChild.className != "explanation"){
363 | questionDiv.appendChild(explanation);
364 | }
365 |
366 |
367 | }
--------------------------------------------------------------------------------