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