##############################################################################
# Parte del libro Introducción a la programación con Python
# Autor: Nilo Ney Coutinho Menezes
# Editora Novatec (c) 2015 - ISBN 978-85-7522-250-8
# Primera edición - Mayo/2016
# Sitio: http://www.librodepython.com
#
# Archivo: lista\capítulo 10\10.23 - Lista completa de la nueva agenda.py
# Descripción: Lista completa de la nueva agenda
##############################################################################
import sys
import pickle
from functools import total_ordering
def nulo_o_vacío(texto):
return texto == None or not texto.strip()
def valida_franja_entero(pregunta, inicio, fin, estándar=None):
while True:
try:
entrada = input(pregunta)
if nulo_o_vacío(entrada) and estándar != None:
entrada = estándar
valor = int(entrada)
if inicio <= valor <= fin:
return(valor)
except ValueError:
print("Valor inválido, favor digitar entre %d y %d" % (inicio, fin))
def valida_franja_entero_o_blanco(pregunta, inicio, fin):
while True:
try:
entrada = input(pregunta)
if nulo_o_vacío(entrada):
return None
valor = int(entrada)
if inicio <= valor <= fin:
return(valor)
except ValueError:
print("Valor inválido, favor digitar entre %d y %d" % (inicio, fin))
class ListaÚnica:
def __init__(self, elem_class):
self.lista = []
self.elem_class = elem_class
def __len__(self):
return len(self.lista)
def __iter__(self):
return iter(self.lista)
def __getitem__(self, p):
return self.lista[p]
def indiceVálido(self, i):
return i >= 0 and i < len(self.lista)
def adiciona(self, elem):
if self.investigación(elem) == -1:
self.lista.append(elem)
def remove(self, elem):
self.lista.remove(elem)
def investigación(self, elem):
self.verifica_tipo(elem)
try:
return self.lista.index(elem)
except ValueError:
return -1
def verifica_tipo(self, elem):
if type(elem) != self.elem_class:
raise TypeError("Tipo inválido")
def ordena(self, clave=None):
self.lista.sort(key=clave)
@total_ordering
class Nombre:
def __init__(self, nombre):
self.nombre = nombre
def __str__(self):
return self.nombre
def __repr__(self):
return "<Clase {3} en 0x{0:x} Nombre: {1} Clave: {2}>".format(
id(self), self.__nombre, self.__clave, type(self).__name__)
def __eq__(self, otro):
return self.nombre == otro.nombre
def __lt__(self, otro):
return self.nombre < otro.nombre
@property
def nombre(self):
return self.__nombre
@nombre.setter
def nombre(self, valor):
if nulo_o_vacío(valor):
raise ValueError("Nombre no puede ser nulo ni en blanco")
self.__nombre = valor
self.__clave = Nombre.CreaClave(valor)
@property
def clave(self):
return self.__clave
@staticmethod
def CreaClave(nombre):
return nombre.strip().lower()
@total_ordering
class TipoTeléfono:
def __init__(self, tipo):
self.tipo = tipo
def __str__(self):
return "({0})".format(self.tipo)
def __eq__(self, otro):
if otro is None:
return False
return self.tipo == otro.tipo
def __lt__(self, otro):
return self.tipo < otro.tipo
class Teléfono:
def __init__(self, número, tipo=None):
self.número = número
self.tipo = tipo
def __str__(self):
if self.tipo!=None:
tipo = self.tipo
else:
tipo = ""
return "{0} {1}".format(self.número, tipo)
def __eq__(self, otro):
return self.número == otro.número and (
(self.tipo == otro.tipo) or (
self.tipo == None or otro.tipo == None))
@property
def número(self):
return self.__número
@número.setter
def número(self, valor):
if nulo_o_vacío(valor):
raise ValueError("Número no puede ser None o en blanco")
self.__número = valor
class Teléfonos(ListaÚnica):
def __init__(self):
super().__init__(Teléfono)
class TiposTeléfono(ListaÚnica):
def __init__(self):
super().__init__(TipoTeléfono)
class DatoAgenda:
def __init__(self, nombre):
self.nombre = nombre
self.teléfonos = Teléfonos()
@property
def nombre(self):
return self.__nombre
@nombre.setter
def nombre(self, valor):
if type(valor) != Nombre:
raise TypeError("nombre debe ser una instancia de la clase Nombre")
self.__nombre = valor
def investigaciónTeléfono(self, teléfono):
posición = self.teléfonos.investigación(Teléfono(teléfono))
if posición == -1:
return None
else:
return self.teléfonos[posición]
class Agenda(ListaÚnica):
def __init__(self):
super().__init__(DatoAgenda)
self.tiposTeléfono = TiposTeléfono()
def adicionaTipo(self, tipo):
self.tiposTeléfono.adiciona(TipoTeléfono(tipo))
def investigaciónNombre(self, nombre):
if type(nombre) == str:
nombre = Nombre(nombre)
for datos in self.lista:
if datos.nombre == nombre:
return datos
else:
return None
def ordena(self):
super().ordena(lambda dato: str(dato.nombre))
class Menú:
def __init__(self):
self.opciones = [["Salir", None]]
def adicionaopción(self, nombre, función):
self.opciones.append([nombre, función])
def exhibe(self):
print("====")
print("Menú")
print("====\n")
for i, opción in enumerate(self.opciones):
print("[{0}] - {1}".format(i, opción[0]))
print()
def ejecute(self):
while True:
self.exhibe()
elija = valida_franja_entero("Elija una opción: ",
0, len(self.opciones) - 1)
if elija == 0:
break
self.opciones[elija][1]()
class AppAgenda:
@staticmethod
def pide_nombre():
return(input("Nombre: "))
@staticmethod
def pide_teléfono():
return(input("Teléfono: "))
@staticmethod
def muestra_datos(datos):
print("Nombre: %s" % datos.nombre)
for teléfono in datos.teléfonos:
print("Teléfono: %s" % teléfono)
print()
@staticmethod
def muestra_datos_teléfono(datos):
print("Nombre: %s" % datos.nombre)
for i, teléfono in enumerate(datos.teléfonos):
print("{0} - Teléfono: {1}".format(i, teléfono))
print()
@staticmethod
def pide_nombre_archivo():
return(input("Nombre del archivo: "))
def __init__(self):
self.agenda = Agenda()
self.agenda.adicionaTipo("Celular")
self.agenda.adicionaTipo("Residencia")
self.agenda.adicionaTipo("Trabajo")
self.agenda.adicionaTipo("Fax")
self.menú = Menú()
self.menú.adicionaopción("Nuevo", self.nuevo)
self.menú.adicionaopción("Altera", self.altera)
self.menú.adicionaopción("Borra", self.borra)
self.menú.adicionaopción("Lista", self.lista)
self.menú.adicionaopción("Graba", self.graba)
self.menú.adicionaopción("Lee", self.lee)
self.menú.adicionaopción("Ordena", self.ordena)
self.ultimo_nombre = None
def pide_tipo_teléfono(self, estándar=None):
for i, tipo in enumerate(self.agenda.tiposTeléfono):
print(" {0} - {1} ".format(i, tipo), end=None)
t = valida_franja_entero("Tipo: ", 0, len(self.agenda.tiposTeléfono) - 1, estándar)
return self.agenda.tiposTeléfono[t]
def investigación(self, nombre):
dato = self.agenda.investigaciónNombre(nombre)
return dato
def nuevo(self):
nuevo = AppAgenda.pide_nombre()
if nulo_o_vacío(nuevo):
return
nombre = Nombre(nuevo)
if self.investigación(nombre) != None:
print("¡Nombre ya existe!")
return
registro = DatoAgenda(nombre)
self.menú_teléfonos(registro)
self.agenda.adiciona(registro)
def borra(self):
if len(self.agenda) == 0:
print("Agenda vacía, nada a borrar")
nombre = AppAgenda.pide_nombre()
if(nulo_o_vacío(nombre)):
return
p = self.investigación(nombre)
if p != None:
self.agenda.remove(p)
print("Borrado. La agenda ahora tiene solo: %d registros" % len(self.agenda))
else:
print("Nombre no encontrado.")
def altera(self):
if len(self.agenda) == 0:
print("Agenda vacía, nada a alterar")
nombre = AppAgenda.pide_nombre()
if(nulo_o_vacío(nombre)):
return
p = self.investigación(nombre)
if p != None:
print("\nEncontrado:\n")
AppAgenda.muestra_datos(p)
print("Digite enter en caso de que no quiera alterar el nombre")
nuevo = AppAgenda.pide_nombre()
if not nulo_o_vacío(nuevo):
p.nombre = Nombre(nuevo)
self.menú_teléfonos(p)
else:
print("¡Nombre no encontrado!")
def menú_teléfonos(self, datos):
while True:
print("\nEditando teléfonos\n")
AppAgenda.muestra_datos_teléfono(datos)
if(len(datos.teléfonos) > 0):
print("\n[A] - alterar\n[D] - borrar\n", end="")
print("[N] - nuevo\n[S] - salir\n")
operación = input("Elija una operación: ")
operación = operación.lower()
if operación not in ["a", "d", "n", "s"]:
print("Operación inválida. Digite A, D, N o S")
continue
if operación == 'a' and len(datos.teléfonos) > 0:
self.altera_teléfonos(datos)
elif operación == 'd' and len(datos.teléfonos) > 0:
self.borra_teléfono(datos)
elif operación == 'n':
self.nuevo_teléfono(datos)
elif operación == "s":
break
def nuevo_teléfono(self, datos):
teléfono = AppAgenda.pide_teléfono()
if nulo_o_vacío(teléfono):
return
if datos.investigaciónTeléfono(teléfono) != None:
print("Teléfono ya existe")
tipo = self.pide_tipo_teléfono()
datos.teléfonos.adiciona(Teléfono(teléfono, tipo))
def borra_teléfono(self, datos):
t = valida_franja_entero_o_blanco(
"Digite la posición del número a borrar, enter para salir: ",
0, len(datos.teléfonos) - 1)
if t == None:
return
datos.teléfonos.remove(datos.teléfonos[t])
def altera_teléfonos(self, datos):
t = valida_franja_entero_o_blanco(
"Digite la posición del número a alterar, enter para salir: ",
0, len(datos.teléfonos) - 1)
if t == None:
return
teléfono = datos.teléfonos[t]
print("Teléfono: %s" % teléfono)
print("Digite enter en caso de que no quiera alterar el número")
nuevoteléfono = AppAgenda.pide_teléfono()
if not nulo_o_vacío(nuevoteléfono):
teléfono.número = nuevoteléfono
print("Digite enter en caso de que no quiera alterar el tipo")
teléfono.tipo = self.pide_tipo_teléfono(
self.agenda.tiposTeléfono.investigación(teléfono.tipo))
def lista(self):
print("\nAgenda")
print("-" * 60)
for e in self.agenda:
AppAgenda.muestra_datos(e)
print("-" * 60)
def lee(self, nombre_archivo=None):
if nombre_archivo == None:
nombre_archivo = AppAgenda.pide_nombre_archivo()
if nulo_o_vacío(nombre_archivo):
return
with open(nombre_archivo, "rb") as archivo:
self.agenda = pickle.load(archivo)
self.ultimo_nombre = nombre_archivo
def ordena(self):
self.agenda.ordena()
print("\nAgenda ordenada\n")
def graba(self):
if self.ultimo_nombre != None:
print("Último nombre utilizado fue '%s'" % self.ultimo_nombre)
print("Digite enter en caso de que quiera utilizar el mismo nombre")
nombre_archivo = AppAgenda.pide_nombre_archivo()
if nulo_o_vacío(nombre_archivo):
if self.ultimo_nombre != None:
nombre_archivo = self.ultimo_nombre
else:
return
with open(nombre_archivo, "wb") as archivo:
pickle.dump(self.agenda, archivo)
def ejecute(self):
self.menú.ejecute()
if __name__ == "__main__":
app = AppAgenda()
if len(sys.argv) > 1:
app.lee(sys.argv[1])
app.ejecute()