JHONGONZALEZ commited on
Commit
ce639d9
·
1 Parent(s): 0c05110

Actualizar README.md y otros archivos

Browse files
Files changed (4) hide show
  1. LOGOCIPDOQUEBRADAS.png +0 -0
  2. README.md +39 -2
  3. app.py +336 -0
  4. requirements.txt +5 -0
LOGOCIPDOQUEBRADAS.png ADDED
README.md CHANGED
@@ -7,7 +7,44 @@ sdk: gradio
7
  sdk_version: 5.23.1
8
  app_file: app.py
9
  pinned: false
10
- short_description: ' Simulador de Pruebas UNAD '
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  sdk_version: 5.23.1
8
  app_file: app.py
9
  pinned: false
10
+ short_description: 'Simulador de Pruebas UNAD'
11
  ---
12
 
13
+ # 📚 Simulador de Pruebas UNAD
14
+
15
+ Este simulador está diseñado exclusivamente para los directores de curso de la Universidad Nacional Abierta y a Distancia (UNAD). Permite cargar preguntas en formato F-7-4-2 desde un archivo Excel y simular evaluaciones con retroalimentación interactiva. Es una herramienta ideal para probar y validar preguntas antes de implementarlas en cursos oficiales.
16
+
17
+ ## 🌟 Características principales
18
+ - 📋 **Formulario de registro**: Los usuarios deben completar un formulario con sus datos antes de comenzar.
19
+ - 📂 **Carga de preguntas**: Las preguntas se cargan desde archivos Excel en formato `.xlsx`.
20
+ - 💬 **Retroalimentación inmediata**: Recibe retroalimentación instantánea después de responder cada pregunta.
21
+ - 🔒 **Acceso seguro**: Solo los administradores pueden descargar el archivo de registros usando una contraseña.
22
+ - 🎉 **Interfaz intuitiva**: Diseñada para ser fácil de usar, incluso para usuarios sin experiencia técnica.
23
+
24
+ ## 🛠️ Instrucciones de uso
25
+ 1. **Registro inicial**:
26
+ - Completa el formulario de registro con tus datos básicos.
27
+ 2. **Cargar preguntas**:
28
+ - Sube un archivo Excel con las preguntas en el formato requerido (F-7-4-2).
29
+ 3. **Responder preguntas**:
30
+ - Responde las preguntas y recibe retroalimentación inmediata.
31
+ 4. **Descargar registros (solo administradores)**:
32
+ - Usa la contraseña proporcionada para descargar el archivo de registros.
33
+
34
+ ## 📝 Requisitos técnicos
35
+ - El archivo Excel debe estar en formato `.xlsx` y seguir el formato F-7-4-2 de la UNAD.
36
+ - No incluir encabezados en el archivo Excel.
37
+ - Asegúrate de tener una conexión estable a internet para usar la aplicación.
38
+
39
+ ## 📄 Licencia
40
+ Este proyecto está bajo la licencia [MIT](https://opensource.org/licenses/MIT). Puedes usarlo, modificarlo y distribuirlo libremente.
41
+
42
+ ## 🙌 Autor
43
+ Desarrollado por:
44
+ **Jhon Fredy González**
45
+ Programa: ECBTI (Escuela de Ciencias Básicas, Tecnologías e Ingenierías)
46
+ Institución: UNAD (Universidad Nacional Abierta y a Distancia)
47
+ Centro: CIP (Centro de Innovación y Productividad) Dosquebradas
48
+
49
+ Contacto: [Enlace a tu perfil o correo]
50
+ GitHub: [Enlace a tu GitHub]
app.py ADDED
@@ -0,0 +1,336 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """Simulador_POC_UNAD_ECBTI_JHON_GONZALEZ.ipynb
3
+
4
+ Automatically generated by Colab.
5
+
6
+ Original file is located at
7
+ https://colab.research.google.com/drive/18fQIhC6QTMEcoQY202hGIb2BH220X-PJ
8
+ """
9
+
10
+ import pandas as pd
11
+ import ipywidgets as widgets
12
+ from IPython.display import display, clear_output, Markdown, Image
13
+ from google.colab import files
14
+ import os
15
+
16
+ # ============================
17
+ # Información de Autoría
18
+ # ============================
19
+
20
+ AUTHOR_INFO = """
21
+ **Autor:** Jhon Fredy González
22
+ **Programa:** ECBTI (Escuela de Ciencias Básicas, Tecnologías e Ingenierías)
23
+ **Institución:** UNAD (Universidad Nacional Abierta y a Distancia)
24
+ **Centro:** CIP (Centro de Innovación y Productividad) Dosquebradas
25
+ """
26
+
27
+ LOGO_PATH = "/content/LOGOCIPDOQUEBRADAS.png"
28
+
29
+ # ============================
30
+ # Título y Descripción
31
+ # ============================
32
+
33
+ TITLE = "# 📚 Simulador de Pruebas UNAD 📚\n\n## **Exclusivamente para Directores de Curso**"
34
+
35
+ DESCRIPTION = """
36
+ Este simulador está diseñado para ayudar a los directores de curso a probar y validar preguntas en el formato F-7-4-2 de la UNAD.
37
+ Por favor, complete el formulario de registro antes de continuar.
38
+ """
39
+
40
+ # ============================
41
+ # Variables Globales
42
+ # ============================
43
+
44
+ user_data_file = "user_data.xlsx" # Archivo para guardar los datos de los usuarios
45
+ visit_counter_file = "visit_counter.txt" # Archivo para contar las visitas
46
+ visit_count = 0 # Contador de visitas inicializado en 0
47
+ PASSWORD = "9870548" # Contraseña para descargar el archivo
48
+
49
+ # ============================
50
+ # Función para Registrar Datos
51
+ # ============================
52
+
53
+ def register_user_info():
54
+ """
55
+ Muestra un formulario para registrar información del usuario.
56
+ """
57
+ global visit_count
58
+
59
+ # Incrementar el contador de visitas
60
+ visit_count += 1
61
+ save_visit_counter()
62
+
63
+ display(Markdown("### Formulario de Registro"))
64
+
65
+ # Crear widgets para el registro
66
+ name_input = widgets.Text(description="Nombre:")
67
+ document_input = widgets.Text(description="Documento:")
68
+ course_code_input = widgets.Text(description="Código Curso:")
69
+ course_name_input = widgets.Text(description="Nombre Curso:")
70
+ evaluation_type_input = widgets.Text(description="Tipo Evaluación:")
71
+
72
+ submit_button = widgets.Button(description="Enviar Registro ✅")
73
+
74
+ def on_submit_clicked(b):
75
+ # Guardar la información ingresada
76
+ global user_info
77
+ user_info = {
78
+ "Nombre": name_input.value,
79
+ "Documento": document_input.value,
80
+ "Código Curso": course_code_input.value,
81
+ "Nombre Curso": course_name_input.value,
82
+ "Tipo Evaluación": evaluation_type_input.value
83
+ }
84
+
85
+ # Guardar los datos del usuario en un archivo Excel
86
+ save_user_data(user_info)
87
+
88
+ # Limpiar la salida y mostrar mensaje de confirmación
89
+ clear_output(wait=True)
90
+ display(Markdown(f"### ¡Registro Exitoso! ✅"))
91
+ display(Markdown(f"- **Nombre:** {user_info['Nombre']}"))
92
+ display(Markdown(f"- **Documento:** {user_info['Documento']}"))
93
+ display(Markdown(f"- **Código Curso:** {user_info['Código Curso']}"))
94
+ display(Markdown(f"- **Nombre Curso:** {user_info['Nombre Curso']}"))
95
+ display(Markdown(f"- **Tipo Evaluación:** {user_info['Tipo Evaluación']}"))
96
+
97
+ # Mostrar el número de la visita
98
+ display(Markdown(f"--- Esta es la **visita número {visit_count}** ---"))
99
+
100
+ # Continuar con el quiz
101
+ questions = load_questions_from_excel()
102
+ display_question(0, questions)
103
+
104
+ # Mostrar los widgets y el botón
105
+ display(name_input)
106
+ display(document_input)
107
+ display(course_code_input)
108
+ display(course_name_input)
109
+ display(evaluation_type_input)
110
+ display(submit_button)
111
+
112
+ # Asociar la función al botón
113
+ submit_button.on_click(on_submit_clicked)
114
+
115
+
116
+ # ============================
117
+ # Funciones Principales
118
+ # ============================
119
+
120
+ def load_questions_from_excel():
121
+ """
122
+ Permite al usuario cargar un archivo Excel y lo convierte en una lista de preguntas.
123
+ """
124
+ display(Markdown("**Nota Importante:**"))
125
+ display(Markdown("- El archivo Excel **debe estar en el formato F-7-4-2 de la UNAD**."))
126
+ display(Markdown("- **No debe incluir encabezados** y debe tener al menos 16 columnas."))
127
+ display(Markdown("- El archivo debe estar en formato **`.xlsx`**, no en formato `.xls` (versión antigua de Excel)."))
128
+ print("Cargando archivo Excel...")
129
+ uploaded = files.upload()
130
+
131
+ # Verificar que se haya cargado exactamente un archivo
132
+ if len(uploaded.keys()) != 1:
133
+ raise ValueError("Debe cargar exactamente un archivo.")
134
+
135
+ filename = list(uploaded.keys())[0]
136
+
137
+ try:
138
+ # Validar que el archivo tenga la extensión .xlsx
139
+ if not filename.endswith(".xlsx"):
140
+ raise ValueError("El archivo debe estar en formato `.xlsx`. Por favor, use un archivo moderno de Excel.")
141
+
142
+ # Leer el archivo Excel sin encabezados
143
+ df = pd.read_excel(filename, header=None)
144
+
145
+ # Eliminar el archivo cargado para liberar espacio
146
+ os.remove(filename)
147
+
148
+ # Verificar que el archivo tenga suficientes columnas
149
+ if df.shape[1] < 16:
150
+ raise ValueError("El archivo no tiene el formato esperado. Debe tener al menos 16 columnas.")
151
+
152
+ # Seleccionar solo las columnas relevantes (usando índices numéricos)
153
+ df = df.iloc[:, [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]]
154
+
155
+ # Renombrar columnas para facilitar el acceso
156
+ df.columns = [
157
+ "ENUNCIADO",
158
+ "CLAVE",
159
+ "OPCIÓN_DE_RESPUESTA_A",
160
+ "REALIMENTACIÓN_A",
161
+ "OPCIÓN_DE_RESPUESTA_B",
162
+ "REALIMENTACIÓN_B",
163
+ "OPCIÓN_DE_RESPUESTA_C",
164
+ "REALIMENTACIÓN_C",
165
+ "OPCIÓN_DE_RESPUESTA_D",
166
+ "REALIMENTACIÓN_D"
167
+ ]
168
+
169
+ # Convertir todos los valores a cadenas de texto y reemplazar NaN con cadenas vacías
170
+ df = df.astype(str).replace("nan", "")
171
+
172
+ return df.to_dict(orient="records")
173
+ except Exception as e:
174
+ # Si ocurre un error, asegurarse de eliminar el archivo cargado
175
+ if os.path.exists(filename):
176
+ os.remove(filename)
177
+ raise e
178
+
179
+
180
+ def display_question(index, questions):
181
+ """
182
+ Muestra una pregunta específica con opciones de respuesta.
183
+ """
184
+ global current_question_index
185
+ current_question_index = index
186
+ question = questions[index]
187
+
188
+ # Mostrar la pregunta con formato matemático
189
+ display(Markdown(f"--- Pregunta {index + 1} --- 📝"))
190
+ display(Markdown(question["ENUNCIADO"]))
191
+ display(Markdown("\n**Opciones:**"))
192
+
193
+ options = {
194
+ "A": question["OPCIÓN_DE_RESPUESTA_A"],
195
+ "B": question["OPCIÓN_DE_RESPUESTA_B"],
196
+ "C": question["OPCIÓN_DE_RESPUESTA_C"],
197
+ "D": question["OPCIÓN_DE_RESPUESTA_D"]
198
+ }
199
+ for key, value in options.items():
200
+ if value.strip(): # Solo mostrar opciones no vacías
201
+ display(Markdown(f"{key}) {value}"))
202
+
203
+ # Widget para seleccionar la respuesta
204
+ radio_buttons = widgets.RadioButtons(
205
+ options=["A", "B", "C", "D"],
206
+ description="Respuesta:",
207
+ disabled=False
208
+ )
209
+ submit_button = widgets.Button(description="Enviar Respuesta ✅")
210
+
211
+ def on_submit_clicked(b):
212
+ selected_answer = radio_buttons.value
213
+ correct_answer = question["CLAVE"]
214
+
215
+ # Obtener la retroalimentación correspondiente
216
+ feedback = ""
217
+ if selected_answer == "A":
218
+ feedback = question["REALIMENTACIÓN_A"]
219
+ elif selected_answer == "B":
220
+ feedback = question["REALIMENTACIÓN_B"]
221
+ elif selected_answer == "C":
222
+ feedback = question["REALIMENTACIÓN_C"]
223
+ elif selected_answer == "D":
224
+ feedback = question["REALIMENTACIÓN_D"]
225
+
226
+ # Mostrar retroalimentación
227
+ clear_output(wait=True)
228
+ if selected_answer == correct_answer:
229
+ display(Markdown(f"\n**¡Correcto! ✅ {feedback}**"))
230
+ else:
231
+ display(Markdown(f"\n**¡Incorrecto! ❌ {feedback}**"))
232
+
233
+ # Botón para avanzar a la siguiente pregunta
234
+ if index < len(questions) - 1:
235
+ next_button = widgets.Button(description="Siguiente Pregunta ➡️")
236
+ display(next_button)
237
+ next_button.on_click(lambda b: display_question(index + 1, questions))
238
+ else:
239
+ display(Markdown("\n**🎉 ¡Has completado todas las preguntas! 🎉**"))
240
+
241
+ display(radio_buttons)
242
+ display(submit_button)
243
+ submit_button.on_click(on_submit_clicked)
244
+
245
+
246
+ # ============================
247
+ # Funciones para Guardar Datos
248
+ # ============================
249
+
250
+ def save_user_data(user_info):
251
+ """
252
+ Guarda los datos del usuario en un archivo Excel.
253
+ """
254
+ try:
255
+ # Si el archivo ya existe, leerlo
256
+ if os.path.exists(user_data_file):
257
+ df = pd.read_excel(user_data_file)
258
+ else:
259
+ # Crear un DataFrame vacío si el archivo no existe
260
+ df = pd.DataFrame(columns=["Nombre", "Documento", "Código Curso", "Nombre Curso", "Tipo Evaluación"])
261
+
262
+ # Agregar los nuevos datos
263
+ new_row = pd.DataFrame([user_info])
264
+ df = pd.concat([df, new_row], ignore_index=True)
265
+
266
+ # Guardar el archivo
267
+ df.to_excel(user_data_file, index=False)
268
+ except Exception as e:
269
+ print(f"Error al guardar los datos del usuario: {e}")
270
+
271
+
272
+ def save_visit_counter():
273
+ """
274
+ Guarda el contador de visitas en un archivo de texto.
275
+ """
276
+ global visit_count
277
+ try:
278
+ # Si el archivo ya existe, leer el contador
279
+ if os.path.exists(visit_counter_file):
280
+ with open(visit_counter_file, "r") as f:
281
+ visit_count = int(f.read().strip() or 0)
282
+
283
+ # Incrementar el contador
284
+ visit_count += 1
285
+
286
+ # Guardar el contador en el archivo
287
+ with open(visit_counter_file, "w") as f:
288
+ f.write(str(visit_count))
289
+ except Exception as e:
290
+ print(f"Error al guardar el contador de visitas: {e}")
291
+
292
+
293
+ def download_user_data_with_password():
294
+ """
295
+ Descarga el archivo Excel con los datos de los usuarios después de verificar la contraseña.
296
+ """
297
+ password_input = widgets.Password(description="Contraseña:")
298
+ download_button = widgets.Button(description="Descargar Registros 🔒")
299
+ output = widgets.Output()
300
+
301
+ def on_download_clicked(b):
302
+ with output:
303
+ if password_input.value == PASSWORD:
304
+ if os.path.exists(user_data_file):
305
+ files.download(user_data_file)
306
+ display(Markdown("### 📥 Archivo descargado exitosamente: `user_data.xlsx`"))
307
+ else:
308
+ display(Markdown("### ❌ No hay datos disponibles para descargar."))
309
+ else:
310
+ display(Markdown("### ❌ Contraseña incorrecta. Inténtalo de nuevo."))
311
+
312
+ download_button.on_click(on_download_clicked)
313
+ display(password_input, download_button, output)
314
+
315
+
316
+ # ============================
317
+ # Ejecución Principal
318
+ # ============================
319
+
320
+ if __name__ == "__main__":
321
+ # Mostrar el logo
322
+ display(Image(filename=LOGO_PATH, width=300))
323
+
324
+ # Mostrar la información de autoría
325
+ display(Markdown(TITLE))
326
+ display(Markdown(AUTHOR_INFO))
327
+
328
+ # Mostrar la descripción
329
+ display(Markdown(DESCRIPTION))
330
+
331
+ # Botón para descargar los datos con contraseña
332
+ display(Markdown("### Para el creador: Usa el botón a continuación para descargar los registros."))
333
+ download_user_data_with_password()
334
+
335
+ # Registrar la información del usuario
336
+ register_user_info()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ pandas
2
+ ipywidgets
3
+ gradio
4
+ matplotlib
5
+ google-colab