UNIVERSIDAD NACIONAL AUTÓNOMA DE NICARAGUA UNAN - LEÓN Ingeniería en Sistemas de Información Componente: Proyecto Integrador I Tercera Entrega Integradora Validación y mecanismo de excepciones Proyecto: Sistema de Agenda Personal Autores(as): Br. Gilder Talavera Triguero Br. Mario Trujillo Ugarte Tutor: Ing. William Martínez Orozco León, Nicaragua, octubre 2026 Índice Introducción ................................ ................................ ................................ ................................ .... 1 I. Validación y mecanismo de excepciones ................................ ................................ ..................... 1 1. Estructuras de datos ................................ ................................ ................................ ............. 1 2. Arreglos unidimensionales y bidimensionales ................................ ................................ ..... 1 3. Operaciones con arreglos ................................ ................................ ................................ ..... 1 Salida del proceso: Reporte de agenda ................................ ................................ .......................... 2 Código fuente ................................ ................................ ................................ ................................ .. 2 Evidencia de ejecución ................................ ................................ ................................ .................... 5 Conclusión ................................ ................................ ................................ ................................ ....... 7 1 Introducción En esta tercera entrega ampliamos nuestro Sistema de Agenda Personal para que revise los datos que se ingresan, maneje los errores sin cerrarse y muestre la salida más importante del proyecto, que en nuestro caso es el reporte de la agenda. Para lograrlo t rabajamos con estructuras de datos, arreglos de una y dos dimensiones y las operaciones básicas que se pueden hacer con ellos. I. Validación y mecanismo de excepciones Antes de guardar un contacto, el sistema revisa los datos con la función validar_contacto. Comprueba que el nombre no esté vacío, que el teléfono tenga exactamente 8 dígitos numéricos y que la fecha tenga el formato dd/mm/aaaa y sea una fecha real. Si algú n dato está mal, la función lanza un error de tipo ValueError con un mensaje que explica qué pasó. Los botones Agregar y Modificar usan un bloque try/except que atrapa ese error y muestra una ventana de aviso. De esta forma el programa no se cierra y la persona puede corregir el dato y volver a intentar. 1. Estructuras de datos Una estructura de datos es una forma de organizar la información para poder usarla después. En el proyecto usamos listas de Python para guardar la agenda en memoria. Cada contacto reúne cuatro datos: nombre, teléfono, fecha y nota. 2. Arreglos unidimensionales y bidimensionales Un arreglo unidimensional es una sola fila de datos. En nuestro proyecto cada contacto es uno, por ejemplo: ["Gilder", "87342269", "08/11/2004", "Hola"]. Un arreglo bidimensional es una matriz, es decir, un arreglo de arreglos. Nuestra variable agenda es una matriz donde cada fila es un contacto y cada columna es un dato: la columna 0 es el nombre, la 1 el teléfono, la 2 la fecha y la 3 la nota. 3. Operaciones con arreglos 2 Con la matriz de la agenda hicimos las cuatro operaciones que pide la ficha: lectura, escritura, asignación y actualización. Lectura: consiste en obtener un dato guardado, por ejemplo matriz[i] para tomar una fila y fila[0] para tomar el nombre. La usamos en generar_reporte y cuando se selecciona un contacto de la tabla. Escritura: es guardar un dato nuevo. En agregar_contacto usamos matriz.append([...]) para añadir una fila al final de la matriz. Asignación: es reemplazar el contenido de una posición. En editar_contacto hacemos matriz[indice] = [...] para cambiar toda la fila de un contacto. Actualización: es cambiar solo un elemento sin tocar el resto. La función actualizar_nota hace matriz[indice][3] = nueva_nota y modifica únicamente la nota de ese contacto. Salida del proceso: Reporte de agenda El proceso más importante de nuestro proyecto es consultar la agenda completa, por eso la salida que elegimos es el reporte de agenda. Al presionar el botón Generar reporte, la función generar_reporte recorre la matriz fila por fila y arma un texto con el número, el nombre, el teléfono, la fecha y la nota de cada contacto, más el total de contactos registrados. El reporte se muestra en una ventana nueva y además se guarda una copia en un archivo de texto. Código fuente import tkinter as tk from tkinter import ttk, messagebox from datetime import datetime # Arreglo bidimensional (matriz): cada fila es un contacto [nombre, telefono, fecha, nota] agenda = [] def formatear_telefono(telefono): return telefono.strip().replace(" - ", "").replace(" ", "") def validar_contacto(nombre, telefono, fecha): if not nombre.strip(): raise ValueError("El nombre es obligatorio.") if not telefono.isdigit() or len(telefono) != 8: 3 raise ValueError("El teléfono debe tener 8 dígitos numéricos.") try: datetime.strptime(fecha, "%d/%m/%Y") except ValueError: raise ValueError("La fecha no es válida. Use el formato dd/mm/aaaa (ej. 10/09/2026).") def agregar_contacto(matriz, nombre, telefono, fecha, nota): matriz.append([nombre, telefono, fecha, nota]) # escritura def editar_contacto(matriz, indice, nombre, telefono, fecha, nota): matriz[indice] = [nombre, telefono, fecha, nota] # asignación def actualizar_nota(matriz, indice, nueva_nota): matriz[indice][3] = nueva_nota # actualización de un elemento def eliminar_contacto(matriz, indice): matriz.pop(indice) def generar_reporte(matriz): lineas = ["REPORTE DE AGENDA PERSONAL", " - " * 70] lineas.append(f"{'N°':<4}{'Nombre':<22}{'Teléfono':<12}{'Fecha':<12}Nota") for i in range(len(matriz)): fila = matriz[i] # lectura lineas.append(f"{i + 1:<4}{fila[0]:<22}{fila[1]:<12}{fila[2]:<12}{fila[3]}") lineas.append(" - " * 70) lineas.append(f"Total de contactos: {len(matriz)}") return " \ n".join(lineas) class AgendaApp: def __init__(self, ventana): self.ventana = ventana self.ventana.title("Sistema de Agenda Personal") self.ventana.geometry("650x480") self.ventana.resizable(False, False) self.indice_seleccionado = None self.crear_widgets() def crear_widgets(self): marco_form = tk.LabelFrame(self.ventana, text="Datos del contacto", padx=10, pady=10) marco_form.pack(fill="x", padx=10, pady=10) etiquetas = ["Nombre:", "Teléfono (8 dígitos):", "Fecha (dd/mm/aaaa):", "Nota:"] self.entradas = [] for i, texto in enumerate(etiquetas): tk.Label(marco_form, text=texto).grid(row=i, column=0, sticky="w", pady=4) entrada = tk.Entry(marco_form, width=40) entrada.grid(row=i, column=1, pady=4) self.entradas.append(entrada) marco_botones = tk.Frame(self.ventana) marco_botones.pack(fill="x", padx=10) tk.Button(marco_botones, text="Agregar", width=11, command=self.accion_agregar).pack(side="left", padx=4) 4 tk.Button(marco_botones, text="Modificar", width=11, command=self.accion_modificar).pack(side="left", padx=4) tk.Button(marco_botones, text="Eliminar", width=11, command=self.accion_eliminar).pack(side="left", padx=4) tk.Button(marco_botones, text="Limpiar", width=11, command=self.limpiar_campos).pack(side="left", padx=4) tk.Button(marco_botones, text="Generar reporte", width=14, command=self.accion_reporte).pack(side="left", padx=4) marco_tabla = tk.LabelFrame(self.ventana, text="Contactos registrados", padx=10, pady=10) marco_tabla.pack(fill="both", expand=True, padx=10, pady=10) columnas = ("nombre", "telefono", "fecha", "nota") self.tabla = ttk.Treeview(marco_tabla, columns=columnas, show="headings", height=10) for col, ancho in zip(columnas, (150, 110, 100, 180)): self.tabla.heading(col, text=col.capitalize()) self.tabla.column(col, width=ancho) self.tabla.pack(fill="both", expand=True) self.tabla.bind("<<TreeviewSelect>>", self.seleccionar_fila) def leer_campos(self): nombre, telefono, fecha, nota = [e.get().strip() for e in self.entradas] return nombre, formatear_telefono(telefono), fecha, nota def accion_agregar(self): try: nombre, telefono, fecha, nota = self.leer_campos() validar_contacto(nombre, telefono, fecha) agregar_contacto(agenda, nombre, telefono, fecha, nota) except ValueError as error: messagebox.showerror("Dato inválido", f"{error} \ nVerifique los datos ingresados.") return self.actualizar_tabla() self.limpiar_campos() messagebox.showinfo("Éxito", f"Contacto '{nombre}' agregado correctamente.") def accion_modificar(self): if self.indice_seleccionado is None: messagebox.showwarning("Sin selección", "Seleccione un contacto de la tabla para modificar.") return try: nombre, telefono, fecha, nota = self.leer_campos() validar_contacto(nombre, telefono, fecha) editar_contacto(agenda, self.indice_seleccionado, nombre, telefono, fecha, nota) except ValueError as error: messagebox.showerror("Dato inválido", f"{error} \ nVerifique los datos ingresados.") return self.actualizar_tabla() self.limpiar_campos() messagebox.showinfo("Éxito", "Contacto modificado correctamente.") def accion_eliminar(self): if self.indice_seleccionado is None: messagebox.showwarning("Sin selección", "Seleccione un contacto de la tabla para eliminar.") return if messagebox.askyesno("Confirmar", "¿Desea eliminar este contacto?"): eliminar_contacto(agenda, self.indice_seleccionado) self.actualizar_tabla() self.limpiar_campos() def accion_reporte(self): if not agenda: messagebox.showwarning("Sin datos", "No hay contactos para generar el reporte.") return texto = generar_reporte(agenda) try: 5 with open("reporte_agenda.txt", "w", encoding="utf - 8") as archivo: archivo.write(texto) except OSError: messagebox.showerror("Error", "No se pudo guardar el archivo del reporte.") ventana_reporte = tk.Toplevel(self.ventana) ventana_reporte.title("Reporte de Agenda") caja = tk.Text(ventana_reporte, width=80, height=18, font=("Consolas", 10)) caja.insert("1.0", texto) caja.config(state="disabled") caja.pack(padx=10, pady=10) def seleccionar_fila(self, evento): seleccion = self.tabla.selection() if not seleccion: return self.indice_seleccionado = self.tabla.index(seleccion[0]) fila = agenda[self.indice_seleccionado] self.limpiar_campos(mantener_seleccion=True) for entrada, valor in zip(self.entradas, fila): entrada.insert(0, valor) def actualizar_tabla(self): for item in self.tabla.get_children(): self.tabla.delete(item) for fila in agenda: self.tabla.insert("", "end", values=fila) def limpiar_campos(self, mantener_seleccion=False): for entrada in self.entradas: entrada.delete(0, tk.END) if not mantener_seleccion: self.indice_seleccionado = None if __name__ == "__main__": ventana = tk.Tk() app = AgendaApp(ventana) ventana.mainloop() Evidencia de ejecución Estas son las capturas del programa funcionando en Visual Studio Code. 6 Figura 1. Mensaje de error cuando el teléfono no tiene 8 dígitos. Figura 2. Contacto agregado correctamente después de pasar las validaciones. Figura 3. Contacto modificado correctamente. 7 Figura 4. Reporte de agenda con los contactos registrados y el total. Conclusión Con esta entrega logramos que nuestra agenda sea más segura, porque ahora valida los datos y maneja los errores con try/except en lugar de cerrarse. También aprendimos a usar una matriz para guardar los contactos y a hacer sobre ella las operaciones de lec tura, escritura, asignación y actualización. Por último, generamos el reporte de agenda como la salida principal del proyecto, que junto con las validaciones deja nuestra aplicación más completa para las siguientes entregas.