Advertisement
LinuxAIO

Agenda.py (Proyecto Final)

Dec 24th, 2018
157
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 17.64 KB | None | 0 0
  1. import sys
  2. import sqlite3
  3. import os.path
  4. from functools import total_ordering
  5.  
  6. BANCO = """
  7. create table tipos(id integer primary key autoincrement,
  8.                                     descripción text);
  9. create table nombres(id integer primary key autoincrement,
  10.                                         nombre text);
  11. create table teléfonos(id integer primary key autoincrement,
  12.                                             id_nombre integer,
  13.                                             número text,
  14.                                             id_tipo integer);
  15. insert into tipos(descripción) values ("Celular");
  16. insert into tipos(descripción) values ("Fijo");
  17. insert into tipos(descripción) values ("Fax");
  18. insert into tipos(descripción) values ("Casa");
  19. insert into tipos(descripción) values ("Trabajo");
  20. """
  21.  
  22.  
  23. def nulo_o_vacío(texto):
  24.     return texto == None or not texto.strip()
  25.  
  26.  
  27. def valida_franja_entero(pregunta, inicio, fin, estándar=None):
  28.     while True:
  29.         try:
  30.             entrada = input(pregunta)
  31.             if nulo_o_vacío(entrada) and estándar != None:
  32.                 entrada = estándar
  33.             valor = int(entrada)
  34.             if inicio <= valor <= fin:
  35.                 return(valor)
  36.         except ValueError:
  37.             print("Valor inválido, favor digitar entre %d y %d" %
  38.                   (inicio, fin))
  39.  
  40.  
  41. def valida_franja_entero_o_blanco(pregunta, inicio, fin):
  42.     while True:
  43.         try:
  44.             entrada = input(pregunta)
  45.             if nulo_o_vacío(entrada):
  46.                 return None
  47.             valor = int(entrada)
  48.             if inicio <= valor <= fin:
  49.                 return(valor)
  50.         except ValueError:
  51.             print("Valor inválido, favor digitar entre %d y %d" %
  52.                   (inicio, fin))
  53.  
  54.  
  55. class ListaÚnica:
  56.     def __init__(self, elem_class):
  57.         self.lista = []
  58.         self.elem_class = elem_class
  59.  
  60.     def __len__(self):
  61.         return len(self.lista)
  62.  
  63.     def __iter__(self):
  64.         return iter(self.lista)
  65.  
  66.     def __getitem__(self, p):
  67.         return self.lista[p]
  68.  
  69.     def indiceVálido(self, i):
  70.         return i >= 0 and i < len(self.lista)
  71.  
  72.     def adiciona(self, elem):
  73.         if self.investigación(elem) == -1:
  74.             self.lista.append(elem)
  75.  
  76.     def remove(self, elem):
  77.         self.lista.remove(elem)
  78.  
  79.     def investigación(self, elem):
  80.         self.verifica_tipo(elem)
  81.         try:
  82.             return self.lista.index(elem)
  83.         except ValueError:
  84.             return -1
  85.  
  86.     def verifica_tipo(self, elem):
  87.         if type(elem) != self.elem_class:
  88.             raise TypeError("Tipo inválido")
  89.  
  90.     def ordena(self, clave=None):
  91.             self.lista.sort(key=clave)
  92.  
  93.  
  94. class DBListaÚnica(ListaÚnica):
  95.     def __init__(self, elem_class):
  96.         super().__init__(elem_class)
  97.         self.borrados = []
  98.  
  99.     def remove(self, elem):
  100.         if elem.id is not None:
  101.             self.borrados.append(elem.id)
  102.             super().remove(elem)
  103.  
  104.     def limpia(self):
  105.             self.borrados = []
  106.  
  107.  
  108. @total_ordering
  109. class Nombre:
  110.     def __init__(self, nombre):
  111.         self.nombre = nombre
  112.  
  113.     def __str__(self):
  114.         return self.nombre
  115.  
  116.     def __repr__(self):
  117.         return "<Clase {3} en 0x{0:x} Nombre: {1} Clave: {2}>".format(
  118.             id(self), self.__nombre, self.__clave, type(self).__name__)
  119.  
  120.     def __eq__(self, otro):
  121.         return self.nombre == otro.nombre
  122.  
  123.     def __lt__(self, otro):
  124.         return self.nombre < otro.nombre
  125.  
  126.     @property
  127.     def nombre(self):
  128.         return self.__nombre
  129.  
  130.     @nombre.setter
  131.     def nombre(self, valor):
  132.         if nulo_o_vacío(valor):
  133.             raise ValueError("Nombre no puede ser nulo ni en blanco")
  134.         self.__nombre = valor
  135.         self.__clave = Nombre.CreaClave(valor)
  136.  
  137.     @property
  138.     def clave(self):
  139.         return self.__clave
  140.  
  141.     @staticmethod
  142.     def CreaClave(nombre):
  143.         return nombre.strip().lower()
  144.  
  145.  
  146. class DBNome(Nombre):
  147.     def __init__(self, nombre, id_=None):
  148.         super().__init__(nombre)
  149.         self.id = id_
  150.  
  151.  
  152. @total_ordering
  153. class TipoTeléfono:
  154.     def __init__(self, tipo):
  155.         self.tipo = tipo
  156.  
  157.     def __str__(self):
  158.         return "({0})".format(self.tipo)
  159.  
  160.     def __eq__(self, otro):
  161.         if otro is None:
  162.             return False
  163.         return self.tipo == otro.tipo
  164.  
  165.     def __lt__(self, otro):
  166.         return self.tipo < otro.tipo
  167.  
  168.  
  169. class DBTipoTeléfono(TipoTeléfono):
  170.     def __init__(self, id_, tipo):
  171.         super().__init__(tipo)
  172.         self.id = id_
  173.  
  174.  
  175. class Teléfono:
  176.     def __init__(self, número, tipo=None):
  177.         self.número = número
  178.         self.tipo = tipo
  179.  
  180.     def __str__(self):
  181.         if self.tipo != None:
  182.             tipo = self.tipo
  183.         else:
  184.             tipo = ""
  185.         return "{0} {1}".format(self.número, tipo)
  186.  
  187.     def __eq__(self, otro):
  188.         return self.número == otro.número and (
  189.             (self.tipo == otro.tipo) or (
  190.              self.tipo == None or otro.tipo == None))
  191.  
  192.     @property
  193.     def número(self):
  194.         return self.__número
  195.  
  196.     @número.setter
  197.     def número(self, valor):
  198.         if nulo_o_vacío(valor):
  199.             raise ValueError("Número no puede ser None o en blanco")
  200.         self.__número = valor
  201.  
  202.  
  203. class DBTeléfono(Teléfono):
  204.     def __init__(self, número, tipo=None, id_=None, id_nombre=None):
  205.         super().__init__(número, tipo)
  206.         self.id = id_
  207.         self.id_nombre = id_nombre
  208.  
  209.  
  210. class DBTeléfonos(DBListaÚnica):
  211.     def __init__(self):
  212.         super().__init__(DBTeléfono)
  213.  
  214.  
  215. class DBTiposTeléfono(ListaÚnica):
  216.     def __init__(self):
  217.         super().__init__(DBTipoTeléfono)
  218.  
  219.  
  220. class DBDadoAgenda:
  221.     def __init__(self, nombre):
  222.         self.nombre = nombre
  223.         self.teléfonos = DBTeléfonos()
  224.  
  225.     @property
  226.     def nombre(self):
  227.         return self.__nombre
  228.  
  229.     @nombre.setter
  230.     def nombre(self, valor):
  231.         if type(valor) != DBNome:
  232.             raise TypeError("nombre debe ser una instancia de la clase DBNome")
  233.         self.__nombre = valor
  234.  
  235.     def investigaciónTeléfono(self, teléfono):
  236.         posición = self.teléfonos.investigación(DBTeléfono(teléfono))
  237.         if posición == -1:
  238.             return None
  239.         else:
  240.             return self.teléfonos[posición]
  241.  
  242.  
  243. class DBAgenda:
  244.     def __init__(self, banco):
  245.         self.tiposTeléfono = DBTiposTeléfono()
  246.         self.banco = banco
  247.         nuevo = not os.path.isfile(banco)
  248.         self.conexión = sqlite3.connect(banco)
  249.         self.conexión.row_factory = sqlite3.Row
  250.         if nuevo:
  251.             self.crea_banco()
  252.         self.cargaTipos()
  253.  
  254.     def cargaTipos(self):
  255.         for tipo in self.conexión.execute("select * from tipos"):
  256.             id_ = tipo["id"]
  257.             descripción = tipo["descripción"]
  258.             self.tiposTeléfono.adiciona(DBTipoTeléfono(id_, descripción))
  259.  
  260.     def crea_banco(self):
  261.         self.conexión.executescript(BANCO)
  262.  
  263.     def investigaciónNombre(self, nombre):
  264.         if not isinstance(nombre, DBNome):
  265.             raise TypeError("nombre debe ser del tipo DBNome")
  266.         encontrado = self.conexión.execute("""select count(*)
  267.                                             from nombres where nombre = ?""",
  268.                                            (nombre.nombre,)).fetchone()
  269.         if(encontrado[0] > 0):
  270.             return self.carga_por_nombre(nombre)
  271.         else:
  272.             return None
  273.  
  274.     def carga_por_id(self, id):
  275.         consulta = self.conexión.execute(
  276.             "select * from nombres where id = ?", (id,))
  277.         return carga(consulta.fetchone())
  278.  
  279.     def carga_por_nombre(self, nombre):
  280.         consulta = self.conexión.execute(
  281.             "select * from nombres where nombre = ?", (nombre.nombre,))
  282.         return self.carga(consulta.fetchone())
  283.  
  284.     def carga(self, consulta):
  285.         if consulta is None:
  286.             return None
  287.         nuevo = DBDadoAgenda(DBNome(consulta["nombre"], consulta["id"]))
  288.         for teléfono in self.conexión.execute(
  289.                 "select * from teléfonos where id_nombre = ?",
  290.                 (nuevo.nombre.id,)):
  291.             ntel = DBTeléfono(teléfono["número"], None,
  292.                               teléfono["id"], teléfono["id_nombre"])
  293.         for tipo in self.tiposTeléfono:
  294.             if tipo.id == teléfono["id_tipo"]:
  295.                 ntel.tipo = tipo
  296.                 break
  297.         nuevo.teléfonos.adiciona(ntel)
  298.         return nuevo
  299.  
  300.     def lista(self):
  301.         consulta = self.conexión.execute(
  302.             "select * from nombres order by nombre")
  303.         for registro in consulta:
  304.             yield self.carga(registro)
  305.  
  306.     def nuevo(self, registro):
  307.         try:
  308.             cur = self.conexión.cursor()
  309.             cur.execute("insert into nombres(nombre) values (?)",
  310.                         (str(registro.nombre),))
  311.             registro.nombre.id = cur.lastrowid
  312.             for teléfono in registro.teléfonos:
  313.                 cur.execute("""insert into teléfonos(número,
  314.                                                     id_tipo, id_nombre) values (?,?,?)""",
  315.                             (teléfono.número, teléfono.tipo.id,
  316.                              registro.nombre.id))
  317.                 teléfono.id = cur.lastrowid
  318.             self.conexión.commit()
  319.         except:
  320.             self.conexión.rollback()
  321.             raise
  322.         finally:
  323.             cur.close()
  324.  
  325.     def actualiza(self, registro):
  326.         try:
  327.             cur = self.conexión.cursor()
  328.             cur.execute("update nombres set nombre=? where id = ?",
  329.                         (str(registro.nombre), registro.nombre.id))
  330.             for teléfono in registro.teléfonos:
  331.                 if teléfono.id is None:
  332.                     cur.execute("""insert into teléfonos(número,
  333.                                                         id_tipo, id_nombre)
  334.                                                         values (?,?,?)""",
  335.                                 (teléfono.número, teléfono.tipo.id,
  336.                                  registro.nombre.id))
  337.                     teléfono.id = cur.lastrowid
  338.                 else:
  339.                     cur.execute("""update teléfonos set número=?,
  340.                                                        id_tipo=?, id_nombre=?
  341.                                                    where id = ?""",
  342.                                 (teléfono.número, teléfono.tipo.id,
  343.                                  registro.nombre.id, teléfono.id))
  344.                     for borrado in registro.teléfonos.borrados:
  345.                         cur.execute("delete from teléfonos where id = ?", (borrado,))
  346.                     self.conexión.commit()
  347.                     registro.teléfonos.limpia()
  348.         except:
  349.             self.conexión.rollback()
  350.             raise
  351.         finally:
  352.             cur.close()
  353.  
  354.     def borra(self, registro):
  355.         try:
  356.             cur = self.conexión.cursor()
  357.             cur.execute("delete from teléfonos where id_nombre = ?",
  358.                         (registro.nombre.id,))
  359.             cur.execute("delete from nombres where id = ?",
  360.                         (registro.nombre.id,))
  361.             self.conexión.commit()
  362.         except:
  363.             self.conexión.rollback()
  364.             raise
  365.         finally:
  366.             cur.close()
  367.  
  368.  
  369. class Menú:
  370.     def __init__(self):
  371.         self.opciones = [["Salir", None]]
  372.  
  373.     def adicionaopción(self, nombre, función):
  374.         self.opciones.append([nombre, función])
  375.  
  376.     def exhibe(self):
  377.         print("====")
  378.         print("Menú")
  379.         print("====\n")
  380.         for i, opción in enumerate(self.opciones):
  381.             print("[{0}] - {1}".format(i, opción[0]))
  382.         print()
  383.  
  384.     def ejecute(self):
  385.         while True:
  386.             self.exhibe()
  387.             elija = valida_franja_entero("Elija una opción: ",
  388.                                          0, len(self.opciones) - 1)
  389.             if elija == 0:
  390.                 break
  391.             self.opciones[elija][1]()
  392.  
  393.  
  394. class AppAgenda:
  395.     @staticmethod
  396.     def pide_nombre():
  397.         return(input("Nombre: "))
  398.  
  399.     @staticmethod
  400.     def pide_teléfono():
  401.         return(input("Teléfono: "))
  402.  
  403.     @staticmethod
  404.     def muestra_datos(datos):
  405.         print("Nombre: %s" % datos.nombre)
  406.         for teléfono in datos.teléfonos:
  407.             print("Teléfono: %s" % teléfono)
  408.         print()
  409.  
  410.     @staticmethod
  411.     def muestra_datos_teléfono(datos):
  412.         print("Nombre: %s" % datos.nombre)
  413.         for i, teléfono in enumerate(datos.teléfonos):
  414.             print("{0} - Teléfono: {1}".format(i, teléfono))
  415.         print()
  416.  
  417.     def __init__(self, banco):
  418.         self.agenda = DBAgenda(banco)
  419.         self.menú = Menú()
  420.         self.menú.adicionaopción("Nuevo", self.nuevo)
  421.         self.menú.adicionaopción("Altera", self.altera)
  422.         self.menú.adicionaopción("Borra", self.borra)
  423.         self.menú.adicionaopción("Lista", self.lista)
  424.         self.ultimo_nombre = None
  425.  
  426.     def pide_tipo_teléfono(self, estándar=None):
  427.         for i, tipo in enumerate(self.agenda.tiposTeléfono):
  428.             print(" {0} - {1} ".format(i, tipo), end=None)
  429.             t = valida_franja_entero("Tipo: ", 0,
  430.                                      len(self.agenda.tiposTeléfono) - 1, estándar)
  431.         return self.agenda.tiposTeléfono[t]
  432.  
  433.     def investigación(self, nombre):
  434.         if type(nombre) == str:
  435.             nombre = DBNome(nombre)
  436.         dato = self.agenda.investigaciónNombre(nombre)
  437.         return dato
  438.  
  439.     def nuevo(self):
  440.         nuevo = AppAgenda.pide_nombre()
  441.         if nulo_o_vacío(nuevo):
  442.             return
  443.         nombre = DBNome(nuevo)
  444.         if self.investigación(nombre) != None:
  445.             print("¡Nombre ya existe!")
  446.             return
  447.         registro = DBDadoAgenda(nombre)
  448.         self.menú_teléfonos(registro)
  449.         self.agenda.nuevo(registro)
  450.  
  451.     def borra(self):
  452.         nombre = AppAgenda.pide_nombre()
  453.         if(nulo_o_vacío(nombre)):
  454.             return
  455.         p = self.investigación(nombre)
  456.         if p != None:
  457.             self.agenda.borra(p)
  458.         else:
  459.             print("Nombre no encontrado.")
  460.  
  461.     def altera(self):
  462.         nombre = AppAgenda.pide_nombre()
  463.         if(nulo_o_vacío(nombre)):
  464.             return
  465.         p = self.investigación(nombre)
  466.         if p != None:
  467.             print("\nEncontrado:\n")
  468.             AppAgenda.muestra_datos(p)
  469.             print("Digite enter en caso de que no quiera alterar el nombre")
  470.             nuevo = AppAgenda.pide_nombre()
  471.             if not nulo_o_vacío(nuevo):
  472.                     p.nombre.nombre = nuevo
  473.             self.menú_teléfonos(p)
  474.             self.agenda.actualiza(p)
  475.         else:
  476.                 print("¡Nombre no encontrado!")
  477.  
  478.     def menú_teléfonos(self, datos):
  479.         while True:
  480.             print("\nEditando teléfonos\n")
  481.             AppAgenda.muestra_datos_teléfono(datos)
  482.             if(len(datos.teléfonos) >= 0):
  483.                 print("\n[A] - alterar\n[D] - borrar\n", end="")
  484.                 print("[N] - nuevo\n[S] - salir\n")
  485.                 operación = input("Elija una operación: ")
  486.                 operación = operación.lower()
  487.                 if operación not in ["a", "d", "n", "s"]:
  488.                         print("Operación inválida. Digite A, D, N o S")
  489.                         continue
  490.                 if operación == 'a' and len(datos.teléfonos) > 0:
  491.                     self.altera_teléfonos(datos)
  492.                 elif operación == 'd' and len(datos.teléfonos) > 0:
  493.                     self.borra_teléfono(datos)
  494.                 elif operación == 'n':
  495.                     self.nuevo_teléfono(datos)
  496.                 elif operación == "s":
  497.                     break
  498.  
  499.     def nuevo_teléfono(self, datos):
  500.         teléfono = AppAgenda.pide_teléfono()
  501.         if nulo_o_vacío(teléfono):
  502.             return
  503.         if datos.investigaciónTeléfono(teléfono) != None:
  504.             print("Teléfono ya existe")
  505.         tipo = self.pide_tipo_teléfono()
  506.         datos.teléfonos.adiciona(DBTeléfono(teléfono, tipo))
  507.  
  508.     def borra_teléfono(self, datos):
  509.         t = valida_franja_entero_o_blanco(
  510.             "Digite la posición del número a borrar, enter para salir: ",
  511.             0, len(datos.teléfonos) - 1)
  512.         if t == None:
  513.             return
  514.         datos.teléfonos.remove(datos.teléfonos[t])
  515.  
  516.     def altera_teléfonos(self, datos):
  517.         t = valida_franja_entero_o_blanco(
  518.             "Digite la posición del número a alterar, enter para salir: ",
  519.             0, len(datos.teléfonos) - 1)
  520.         if t == None:
  521.             return
  522.         teléfono = datos.teléfonos[t]
  523.         print("Teléfono: %s" % teléfono)
  524.         print("Digite enter en caso de que no quiera alterar el número")
  525.         nuevoteléfono = AppAgenda.pide_teléfono()
  526.         if not nulo_o_vacío(nuevoteléfono):
  527.             teléfono.número = nuevoteléfono
  528.         print("Digite enter en caso de que no quiera alterar el tipo")
  529.         teléfono.tipo = self.pide_tipo_teléfono(
  530.             self.agenda.tiposTeléfono.investigación(teléfono.tipo))
  531.  
  532.     def lista(self):
  533.         print("\nAgenda")
  534.         print("-" * 60)
  535.         for e in self.agenda.lista():
  536.             AppAgenda.muestra_datos(e)
  537.         print("-" * 60)
  538.  
  539.     def ejecute(self):
  540.         self.menú.ejecute()
  541.  
  542.  
  543. if __name__ == "__main__":
  544.     if len(sys.argv) > 1:
  545.         app = AppAgenda(sys.argv[1])
  546.         app.ejecute()
  547. else:
  548.     print("Error: nombre del banco de datos no informado")
  549.     print("           agenda.py nombre_del_banco")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement