# -*- 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()