File size: 11,723 Bytes
ce639d9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# -*- coding: utf-8 -*-
"""Simulador_POC_UNAD_ECBTI_JHON_GONZALEZ.ipynb

Automatically generated by Colab.

Original file is located at
    https://colab.research.google.com/drive/18fQIhC6QTMEcoQY202hGIb2BH220X-PJ
"""

import pandas as pd
import ipywidgets as widgets
from IPython.display import display, clear_output, Markdown, Image
from google.colab import files
import os

# ============================
# Información de Autoría
# ============================

AUTHOR_INFO = """
**Autor:** Jhon Fredy González
**Programa:** ECBTI (Escuela de Ciencias Básicas, Tecnologías e Ingenierías)
**Institución:** UNAD (Universidad Nacional Abierta y a Distancia)
**Centro:** CIP (Centro de Innovación y Productividad) Dosquebradas
"""

LOGO_PATH = "/content/LOGOCIPDOQUEBRADAS.png"

# ============================
# Título y Descripción
# ============================

TITLE = "# 📚 Simulador de Pruebas UNAD 📚\n\n## **Exclusivamente para Directores de Curso**"

DESCRIPTION = """
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.
Por favor, complete el formulario de registro antes de continuar.
"""

# ============================
# Variables Globales
# ============================

user_data_file = "user_data.xlsx"  # Archivo para guardar los datos de los usuarios
visit_counter_file = "visit_counter.txt"  # Archivo para contar las visitas
visit_count = 0  # Contador de visitas inicializado en 0
PASSWORD = "9870548"  # Contraseña para descargar el archivo

# ============================
# Función para Registrar Datos
# ============================

def register_user_info():
    """
    Muestra un formulario para registrar información del usuario.
    """
    global visit_count

    # Incrementar el contador de visitas
    visit_count += 1
    save_visit_counter()

    display(Markdown("### Formulario de Registro"))

    # Crear widgets para el registro
    name_input = widgets.Text(description="Nombre:")
    document_input = widgets.Text(description="Documento:")
    course_code_input = widgets.Text(description="Código Curso:")
    course_name_input = widgets.Text(description="Nombre Curso:")
    evaluation_type_input = widgets.Text(description="Tipo Evaluación:")

    submit_button = widgets.Button(description="Enviar Registro ✅")

    def on_submit_clicked(b):
        # Guardar la información ingresada
        global user_info
        user_info = {
            "Nombre": name_input.value,
            "Documento": document_input.value,
            "Código Curso": course_code_input.value,
            "Nombre Curso": course_name_input.value,
            "Tipo Evaluación": evaluation_type_input.value
        }

        # Guardar los datos del usuario en un archivo Excel
        save_user_data(user_info)

        # Limpiar la salida y mostrar mensaje de confirmación
        clear_output(wait=True)
        display(Markdown(f"### ¡Registro Exitoso! ✅"))
        display(Markdown(f"- **Nombre:** {user_info['Nombre']}"))
        display(Markdown(f"- **Documento:** {user_info['Documento']}"))
        display(Markdown(f"- **Código Curso:** {user_info['Código Curso']}"))
        display(Markdown(f"- **Nombre Curso:** {user_info['Nombre Curso']}"))
        display(Markdown(f"- **Tipo Evaluación:** {user_info['Tipo Evaluación']}"))

        # Mostrar el número de la visita
        display(Markdown(f"--- Esta es la **visita número {visit_count}** ---"))

        # Continuar con el quiz
        questions = load_questions_from_excel()
        display_question(0, questions)

    # Mostrar los widgets y el botón
    display(name_input)
    display(document_input)
    display(course_code_input)
    display(course_name_input)
    display(evaluation_type_input)
    display(submit_button)

    # Asociar la función al botón
    submit_button.on_click(on_submit_clicked)


# ============================
# Funciones Principales
# ============================

def load_questions_from_excel():
    """
    Permite al usuario cargar un archivo Excel y lo convierte en una lista de preguntas.
    """
    display(Markdown("**Nota Importante:**"))
    display(Markdown("- El archivo Excel **debe estar en el formato F-7-4-2 de la UNAD**."))
    display(Markdown("- **No debe incluir encabezados** y debe tener al menos 16 columnas."))
    display(Markdown("- El archivo debe estar en formato **`.xlsx`**, no en formato `.xls` (versión antigua de Excel)."))
    print("Cargando archivo Excel...")
    uploaded = files.upload()

    # Verificar que se haya cargado exactamente un archivo
    if len(uploaded.keys()) != 1:
        raise ValueError("Debe cargar exactamente un archivo.")

    filename = list(uploaded.keys())[0]

    try:
        # Validar que el archivo tenga la extensión .xlsx
        if not filename.endswith(".xlsx"):
            raise ValueError("El archivo debe estar en formato `.xlsx`. Por favor, use un archivo moderno de Excel.")

        # Leer el archivo Excel sin encabezados
        df = pd.read_excel(filename, header=None)

        # Eliminar el archivo cargado para liberar espacio
        os.remove(filename)

        # Verificar que el archivo tenga suficientes columnas
        if df.shape[1] < 16:
            raise ValueError("El archivo no tiene el formato esperado. Debe tener al menos 16 columnas.")

        # Seleccionar solo las columnas relevantes (usando índices numéricos)
        df = df.iloc[:, [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]]

        # Renombrar columnas para facilitar el acceso
        df.columns = [
            "ENUNCIADO",
            "CLAVE",
            "OPCIÓN_DE_RESPUESTA_A",
            "REALIMENTACIÓN_A",
            "OPCIÓN_DE_RESPUESTA_B",
            "REALIMENTACIÓN_B",
            "OPCIÓN_DE_RESPUESTA_C",
            "REALIMENTACIÓN_C",
            "OPCIÓN_DE_RESPUESTA_D",
            "REALIMENTACIÓN_D"
        ]

        # Convertir todos los valores a cadenas de texto y reemplazar NaN con cadenas vacías
        df = df.astype(str).replace("nan", "")

        return df.to_dict(orient="records")
    except Exception as e:
        # Si ocurre un error, asegurarse de eliminar el archivo cargado
        if os.path.exists(filename):
            os.remove(filename)
        raise e


def display_question(index, questions):
    """
    Muestra una pregunta específica con opciones de respuesta.
    """
    global current_question_index
    current_question_index = index
    question = questions[index]

    # Mostrar la pregunta con formato matemático
    display(Markdown(f"--- Pregunta {index + 1} --- 📝"))
    display(Markdown(question["ENUNCIADO"]))
    display(Markdown("\n**Opciones:**"))

    options = {
        "A": question["OPCIÓN_DE_RESPUESTA_A"],
        "B": question["OPCIÓN_DE_RESPUESTA_B"],
        "C": question["OPCIÓN_DE_RESPUESTA_C"],
        "D": question["OPCIÓN_DE_RESPUESTA_D"]
    }
    for key, value in options.items():
        if value.strip():  # Solo mostrar opciones no vacías
            display(Markdown(f"{key}) {value}"))

    # Widget para seleccionar la respuesta
    radio_buttons = widgets.RadioButtons(
        options=["A", "B", "C", "D"],
        description="Respuesta:",
        disabled=False
    )
    submit_button = widgets.Button(description="Enviar Respuesta ✅")

    def on_submit_clicked(b):
        selected_answer = radio_buttons.value
        correct_answer = question["CLAVE"]

        # Obtener la retroalimentación correspondiente
        feedback = ""
        if selected_answer == "A":
            feedback = question["REALIMENTACIÓN_A"]
        elif selected_answer == "B":
            feedback = question["REALIMENTACIÓN_B"]
        elif selected_answer == "C":
            feedback = question["REALIMENTACIÓN_C"]
        elif selected_answer == "D":
            feedback = question["REALIMENTACIÓN_D"]

        # Mostrar retroalimentación
        clear_output(wait=True)
        if selected_answer == correct_answer:
            display(Markdown(f"\n**¡Correcto! ✅ {feedback}**"))
        else:
            display(Markdown(f"\n**¡Incorrecto! ❌ {feedback}**"))

        # Botón para avanzar a la siguiente pregunta
        if index < len(questions) - 1:
            next_button = widgets.Button(description="Siguiente Pregunta ➡️")
            display(next_button)
            next_button.on_click(lambda b: display_question(index + 1, questions))
        else:
            display(Markdown("\n**🎉 ¡Has completado todas las preguntas! 🎉**"))

    display(radio_buttons)
    display(submit_button)
    submit_button.on_click(on_submit_clicked)


# ============================
# Funciones para Guardar Datos
# ============================

def save_user_data(user_info):
    """
    Guarda los datos del usuario en un archivo Excel.
    """
    try:
        # Si el archivo ya existe, leerlo
        if os.path.exists(user_data_file):
            df = pd.read_excel(user_data_file)
        else:
            # Crear un DataFrame vacío si el archivo no existe
            df = pd.DataFrame(columns=["Nombre", "Documento", "Código Curso", "Nombre Curso", "Tipo Evaluación"])

        # Agregar los nuevos datos
        new_row = pd.DataFrame([user_info])
        df = pd.concat([df, new_row], ignore_index=True)

        # Guardar el archivo
        df.to_excel(user_data_file, index=False)
    except Exception as e:
        print(f"Error al guardar los datos del usuario: {e}")


def save_visit_counter():
    """
    Guarda el contador de visitas en un archivo de texto.
    """
    global visit_count
    try:
        # Si el archivo ya existe, leer el contador
        if os.path.exists(visit_counter_file):
            with open(visit_counter_file, "r") as f:
                visit_count = int(f.read().strip() or 0)

        # Incrementar el contador
        visit_count += 1

        # Guardar el contador en el archivo
        with open(visit_counter_file, "w") as f:
            f.write(str(visit_count))
    except Exception as e:
        print(f"Error al guardar el contador de visitas: {e}")


def download_user_data_with_password():
    """
    Descarga el archivo Excel con los datos de los usuarios después de verificar la contraseña.
    """
    password_input = widgets.Password(description="Contraseña:")
    download_button = widgets.Button(description="Descargar Registros 🔒")
    output = widgets.Output()

    def on_download_clicked(b):
        with output:
            if password_input.value == PASSWORD:
                if os.path.exists(user_data_file):
                    files.download(user_data_file)
                    display(Markdown("### 📥 Archivo descargado exitosamente: `user_data.xlsx`"))
                else:
                    display(Markdown("### ❌ No hay datos disponibles para descargar."))
            else:
                display(Markdown("### ❌ Contraseña incorrecta. Inténtalo de nuevo."))

    download_button.on_click(on_download_clicked)
    display(password_input, download_button, output)


# ============================
# Ejecución Principal
# ============================

if __name__ == "__main__":
    # Mostrar el logo
    display(Image(filename=LOGO_PATH, width=300))

    # Mostrar la información de autoría
    display(Markdown(TITLE))
    display(Markdown(AUTHOR_INFO))

    # Mostrar la descripción
    display(Markdown(DESCRIPTION))

    # Botón para descargar los datos con contraseña
    display(Markdown("### Para el creador: Usa el botón a continuación para descargar los registros."))
    download_user_data_with_password()

    # Registrar la información del usuario
    register_user_info()