Spaces:
Build error
Build error
| # -*- 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() |