#!/usr/bin/env python #-*- coding: iso8859-15 -*- ########################################################## # SISTEMA DE ENVIO DE EMAILS v1.0 # ########################################################## # Autor: Juan Miguel Taboada Godoy # # Fecha: Szczecin, 09 de Octubre de 2006 # # Descripción: Envía las gráficas disponibles por email # # Versión: 2006100900 # # Interpreta los formatos de fichero: # # # # Codigo fuente bajo licencia GNU/GPL # # Centrologic (Computational Logistic Center) # # http://www.centrologic.com - info@centrologic.com # ########################################################## # Librerías que voy a usar {{{1 import os import sre from lib.EMAIL import sendEmail from lib.FILTER import filtro_regex, filtrar_texto from lib.INTERFACES import RUNABLE from lib.HERENCIA import BASE,DEFAULTCONFIG # }}}1 # ### CONFIG ### ###################################### ## Envío de emails: configuracion por defecto class CONFIG(DEFAULTCONFIG): # Constructor {{{1 def __init__(self): # Cargo las acciones del padre DEFAULTCONFIG.__init__(self,"EMAIL") # }}}1 # Config Cargador {{{1 # Fichero con la memoria {{{2 ## Fichero con la memoria ## \param self - ## \param arg Fichero con la memoria de la clase para recordar el trabajo pendiente de hacer def memoria(self,arg=None): if (arg!=None): self._memoria=arg return self._memoria # }}}2 # Directorio con las gráficas {{{2 ## Directorio con las graficas ## \param self - ## \param arg Directorio donde encontrar las graficas def dirgraph(self,arg=None): if (arg!=None): self._dirgraph=arg return self._dirgraph # }}}2 # Origen del email {{{2 ## Origen del email ## \param self - ## \param arg Remitente def source(self,arg=None): if (arg!=None): self._source=arg return self._source # }}}2 # Asunto del email {{{2 ## Asunto de email ## \param self - ## \param arg Asunto del email def subject(self,arg=None): if (arg!=None): self._subject=arg return self._subject # }}}2 # Asunto del email temporal {{{2 ## Asunto de email temporal ## \param self - ## \param arg Asunto del email temporal def subject_temp(self,arg=None): if (arg!=None): self._subject_temp=arg return self._subject_temp # }}}2 # Usuario para enviar los emails {{{2 ## Usuario para eniar los emails ## \param self - ## \param arg Usuario del servidor SMTP para enviar los emails def user(self,arg=None): if (arg!=None): self._user=arg return self._user # }}}2 # Clave para enviar los emails {{{2 ## Clave para eniar los emails ## \param self - ## \param arg Clave del usuario del servidor SMTP para enviar los emails def passwd(self,arg=None): if (arg!=None): self._passwd=arg return self._passwd # }}}2 # Host al que enviar los emails {{{2 ## Servidor SMTP al que enviar los emails ## \param self - ## \param arg Dirección IP o nombre del servidor SMTP def host(self,arg=None): if (arg!=None): self._host=arg return self._host # }}}2 # Puerto al que enviar los emails {{{2 ## Puerto del servidor SMTP ## \param self - ## \param arg Puerto del servidor SMTP def port(self,arg=None): if (arg!=None): self._port=arg return self._port # }}}2 # Firma de Likindoy {{{2 ## Firma que se incluirá al final de los correos ## \param self - ## \param arg Cadena de texto con la firma a incluir def firma(self,arg=None): if (arg!=None): self._firma=arg return self._firma # }}}2 # }}}1 # Checker {{{1 ## Comprueba que el conjunto minimo de datos requeridos por la clase han sido declarados por el usuario: \n ## - memoria() ## - dirgraph() ## - debug() ## - source() ## - subject() ## - user() ## - passwd() ## - host() ## - port() ## - firma() ## \param self - def check(self): self._check("memoria") self._check("dirgraph") self._check("debug") self._check("source") self._check("subject") self._check("subject_temp") self._check("user") self._check("passwd") self._check("host") self._check("port") self._check("firma") self.check_default() # }}}1 # ### ACTUADOR ### ################################### ## Envío de emails: actuador class ACTUADOR(BASE,RUNABLE): # Constructor {{{1 ## Constructor ## \param self - ## \param id Identificador del Actuador (ALFANUMERICO) def __init__(self,id): # Cargo las acciones del padre BASE.__init__(self,id,"EMAIL") # Lista de destinatarios self.__destinos=[] # Lista de graficas self.__graficas=[] # }}}1 # Guarda la config {{{1 ## Almacena la configuración por defecto ## \param self - ## \param config Corresponde a un objeto que contiene toda la configuración por defecto. El objeto es de la clase CONFIG. ## \param graficas Listado de graficas ## \attention Se usa el nombre de fichero que sufre un proceso de filtrado a expresiones regulares def config(self,config,graficas): # Configuración por defecto self.default_config(config) # Salva el listado de expresiones regulares de las graficas for elemento in graficas: # Extraigo el ID id=elemento.nombre() # Extraigo el filtro (extension,nombre,titulo,ancho,alto)=elemento.outvar() filtro=filtro_regex(nombre) self.__graficas.append((id,filtro)) # }}}1 # Configuraciones modificables {{{1 # Firma de Likindoy {{{2 ## Firma en el email ## \param self - ## \param arg Texto a indicar en la firma. El texto puede contener elementos interpretables por las cadenas de Python ('\\n','\\t',...) def firma(self,arg=None): if (arg!=None): self._firma=arg return self._firma # }}}2 # Asunto del email {{{2 ## Asunto del email ## \param self - ## \param arg Texto a indicar en el asunto del email. El texto es filtrado por la funcion FILTER::filtrar_texto(). def subject(self,arg=None): if (arg!=None): self._subject=arg return self._subject # }}}2 # Asunto del email temporal {{{2 ## Asunto del email temporal ## \param self - ## \param arg Texto a indicar en el asunto del email. El texto es filtrado por la funcion FILTER::filtrar_texto(). def subject_temp(self,arg=None): if (arg!=None): self._subject_temp=arg return self._subject_temp # }}}2 # }}}1 # All config {{{1 ## \internal ## \param self - ## \param confirmation Confirmation that we say to the library we know what we are doing def allconfig(self): dict={} dict["destinos"]=self.__destinos return dict # }}}1 # Añade un usuario a la lista de gráfica {{{1 ## Inserta un usuario a la lista de graficas ## \param self - ## \param nombre Nombre del usuario (Ej: nombre corto) ## \param detalles Detalles sobre el usuario (Ej: nombre completo) ## \param email Direccion de email del usuario (para mas de una usar la coma como separador) ## \param graficas Lista de graficas que van a ser enviadas al usuario ## \attention En este paso se comprueba si la grafica existe en la lista de graficas y se genera la expresion regular para recoger el fichero def add(self,nombre,detalles,email,graficas=[]): # Proceso la lista de graficas (añado los filtros) graficasfiltros=[] for elemento in graficas: # Busco el filtro que le corresponda encontrado=False for tmp in self.__graficas: # Compruebo si el nombre coincide if (tmp[0]==elemento): # Añado el filtro a la lista de graficasfiltros graficasfiltros.append((elemento,tmp[1])) # Indico que lo he encontrado encontrado=True # Salgo del bucle break # Si no se encontró emito un error if (not encontrado): raise IOError,"El usuario %s ha indicado en su lista la grafica %s, pero esta no existe en la lista de graficas. Compruebe la lista de graficas a entregar al usuario y si estan escritas correctamente. (Nombre completo: %s, Email: %s)" % (nombre,elemento,detalles,email) # Guardo las graficas con sus filtros self.__destinos.append((nombre,detalles,email,graficasfiltros)) # }}}1 # Renueva la memoria {{{1 ## Refresca la memoria de registros ## \param self - def renovar(self): # Obtengo la ruta al fichero de memoria ruta_memoria=self.valor("memoria") # Elimino el fichero de memoria os.unlink(ruta_memoria) # Abro el fichero de memoria FILE=open(ruta_memoria,'w') # Escribo a disco todos los trabajos primero=True for destino in self.__destinos: # Descompongo el destino (nombre,detalles,email,graficas)=destino # Añado una línea en blanco (si no es el primero) if (not primero): # Añado una línea en blanco FILE.write("\n") else: # Ya no es el primero primero=False # Escribo la cabecera FILE.write("%s\n" % (nombre)) # Escribo las gráficas pendientes for grafica in graficas: FILE.write("%s\n" % (grafica[0])) # Cierro el fichero de memoria FILE.close() # }}}1 # Obtiene el listado de graficas pendientes {{{1 ## Obtiene el listado de graficas pendientes ## \param self - def pendientes(self): # Leo la memoria {{{2 self.debug("Leyendo de memoria el trabajo por hacer") ruta_memoria=self.valor("memoria") memoria={} # Cargo a todos los usuarios a la memoria con lista vacía for elemento in self.__destinos: # Descompongo el elemento (nombre,detalles,email,graficasfiltros)=elemento # Añado al usuario a la memoria con lista vacía memoria[nombre]=[] # Abro el fichero de memoria FILE=open(ruta_memoria,'r') # Leo el fichero de memoria memoria_contenido=FILE.readlines() leo_usuario=True saltar=True for elemento_temp in memoria_contenido: elemento=elemento_temp[0:-1] # Si la línea está vacía if (elemento==""): leo_usuario=True else: # Si estoy leyendo el nombre de un usuario if (leo_usuario): # Lo cargo usuario=elemento # Si el usuario ya existía en la memoria no me salto sus datos if (usuario in memoria): saltar=False else: saltar=True # Anoto que lo que viene son datos leo_usuario=False else: # En caso contrario añado la gráfica a procesar a la lista de gráficas del usuario if (not saltar): memoria[usuario].append(elemento) # Cierro el fichero de memoria FILE.close() # }}}2 return memoria # }}}1 # Indica al actuador que comienze a trabajar {{{1 ## Indica al actuador que comienze a enviar emails con la informacion configurada ## \param self - ## \param ultima_ronda Indica si es la ultima ronda o no (Por defecto: False) def start(self,ultima_ronda=False): # Parametro {{{2 if (ultima_ronda!=True): ultima_ronda=False self.debug("Start %s" % (self.internal_id())) else: self.debug("Start %s [ULTIMA RONDA]" % (self.internal_id())) # }}}2 # Obtengo la lista de ficheros {{{2 ruta=self.valor("dirgraph") lista=os.listdir(ruta) lista.sort() # }}}2 # Leo la memoria {{{2 self.debug("Leyendo de memoria el trabajo por hacer") ruta_memoria=self.valor("memoria") memoria={} # Cargo a todos los usuarios a la memoria con lista vacía for elemento in self.__destinos: # Descompongo el elemento (nombre,detalles,email,graficasfiltros)=elemento # Añado al usuario a la memoria con lista vacía memoria[nombre]=[] # Abro el fichero de memoria FILE=open(ruta_memoria,'r') # Leo el fichero de memoria memoria_contenido=FILE.readlines() leo_usuario=True saltar=True for elemento_temp in memoria_contenido: elemento=elemento_temp[0:-1] # Si la línea está vacía if (elemento==""): leo_usuario=True else: # Si estoy leyendo el nombre de un usuario if (leo_usuario): # Lo cargo usuario=elemento # Si el usuario ya existía en la memoria no me salto sus datos if (usuario in memoria): saltar=False else: saltar=True # Anoto que lo que viene son datos leo_usuario=False else: # En caso contrario añado la gráfica a procesar a la lista de gráficas del usuario if (not saltar): memoria[usuario].append(elemento) # Cierro el fichero de memoria FILE.close() # }}}2 # Test de existencia de completitud {{{2 completitud={} for destino in self.__destinos: # Descompongo el destino (nombre,detalles,email,graficas)=destino # Inicializo el test de completitud para el usuario completitud[nombre]=True # Compruebo que tiene graficas para procesar if (len(memoria[nombre])>0): for grafica in graficas: # Descompongo el filtro (filtro_nombre,filtro_regex)=grafica # Miro si está pendiente de ser procesada if (filtro_nombre in memoria[nombre]): # Para cada fichero reviso el filtro adjuntar=False for fichero in lista: # Realizo la búsqueda buscador=sre.compile(filtro_regex) # Si el filtro da positivo if (len(buscador.findall(fichero))==1): adjuntar=True # Si no la ha encontrado if (not adjuntar): completitud[nombre]=False break # }}}2 # Proceso cada destino independientemente {{{2 trabajo_pendiente={} ficheros_usados=[] for destino in self.__destinos: # Descompongo el destino (nombre,detalles,email,graficas)=destino self.debug("Revisando ficheros para %s" % (nombre)) # Añado al usuario a trabajos pendientes con una lista vacía trabajo_pendiente[nombre]=[] if (len(memoria[nombre])>0): # Reviso los ficheros que puedo adjuntar adjuntos=[] graficausada=[] for grafica in graficas: # Descompongo el filtro (filtro_nombre,filtro_regex)=grafica # Miro si está pendiente de ser procesada if ((completitud[nombre]) or (ultima_ronda) or (filtro_nombre in memoria[nombre])): # Para cada fichero reviso el filtro adjuntar=False for fichero in lista: # Realizo la búsqueda buscador=sre.compile(filtro_regex) # Si el filtro da positivo if (len(buscador.findall(fichero))==1): adjuntar=True break # Compruebo si hay que adjuntar el fichero o no if (adjuntar): # Adjunto el fichero self.debug(" %s" % (fichero)) adjuntos.append((("%s%s" % (ruta,fichero)),"png")) # Anoto que el fichero ha sido usado if (not (fichero in ficheros_usados)): ficheros_usados.append(fichero) # Añado la grafica a la lista de graficas usados graficausada.append(filtro_nombre) else: # Anoto el trabajo como pendiente trabajo_pendiente[nombre].append(filtro_nombre) # Preparo los textos t="" t="%s%s" % (t,"Buenos dias %s, soy Likindoy:\n" % (detalles)) # Indico un error interno si existe error_email=None if ((len(graficausada)==0) and (len(trabajo_pendiente[nombre])==0)): # Informo del error self.debug("No se han enviado gráficas en este email, ni se reconoce una lista de graficas pendientes de ser enviadas, aunque la memoria del programa indica que tenia otros trabajos pendientes y que no han podido ser reconocidos. Esta situacion puede deberse a una desincronizacion temporal de los ficheros de configuracion y la memoria, posiblemente debido a un cambio de configuracion que aun no se ha reflejado en la memoria. Debido a que no es una situacion normal le recomendamos que informe de esta situacion al administrador de Likindoy, se adjunta una lista de esos trabajos:") for elemento in memoria[nombre]: self.debug(elemento) # Anoto que no se debe enviar el email error_email="No hay datos para procesar aunque la memoria indica que deberia haberlos" # Listado de gráficas adjuntas #if (len(graficausada)>0): # t="%s%s" % (t,"\n") # graficausada.sort() # t="%s%s" % (t,"Te adjunto las siguientes graficas:\n") # for grafica in graficausada: # t="%s%s" % (t,"%s\n" % (grafica)) # Listado de gráficas pendientes if (len(trabajo_pendiente[nombre])>0): if (len(graficausada)>0): t="%s%s" % (t,"\n") if (ultima_ronda): t="%s%s" % (t,"Las siguientes graficas no han podido ser procesadas y no se volvera a intentar:\n") else: t="%s%s" % (t,"Las siguientes graficas seran enviadas mas tarde:\n") trabajo_pendiente[nombre].sort() for pendiente in trabajo_pendiente[nombre]: t="%s%s" % (t,"%s\n" % (pendiente)) # Despedida t="%s%s" % (t,"\n") t="%s%s" % (t,"Atentamente,\n") t="%s%s" % (t,"\n") t="%s%s" % (t,"--\n") t="%s%s" % (t,"%s\n" % (self.valor("firma"))) t="%s%s" % (t,"Computational Logistic Center - http://www.centrologic.com - info@centrologic.com\n") etexto=t # Datos para enviar el email ede=self.valor("source") ehost=self.valor("host") eport=self.valor("port") euser=self.valor("user") epasswd=self.valor("passwd") earchivos=adjuntos self.debug(" - %s graficas enviadas, %s canceladas -" % (len(adjuntos),len(trabajo_pendiente[nombre]))) # Control de envio de emails vacíos if ((not ultima_ronda) and (len(earchivos)==0)): error_email="No hay nada que enviar" # Mando el email if (error_email==None): # Control de asunto if ((ultima_ronda) or (completitud[nombre])): easunto=filtrar_texto(self.valor("subject")) else: easunto=filtrar_texto(self.valor("subject_temp")) # Para cada direccion del usuario emails=email.split(",") for diremail in emails: self.debug(" Email destino: %s" % (diremail)) epara="%s <%s>" % (nombre,diremail) sendEmail(de=ede,para=epara,asunto=easunto,texto=etexto,host=ehost,port=eport,loginUserName=euser,loginPassword=epasswd,archivos=earchivos) else: self.debug(" NO SE ENVIA EMAIL AL USUARIO POR PETICION INTERNA DEL PROGRAMA. Razon: %s" % (error_email)) else: self.debug(" NO SE ENVIA EMAIL AL USUARIO POR NO HABER GRAFICAS PENDIENTES PARA EL") # }}}2 # Escribo la memoria {{{2 self.debug("Escribiendo a memoria el trabajo pendiente") # Elimino el fichero de memoria os.unlink(ruta_memoria) # Abro el fichero de memoria FILE=open(ruta_memoria,'w') # Escribo a disco el trabajo pendiente primero=True for elemento in trabajo_pendiente: # Extraigo la lista de gráficas pendientes graficas_pendientes=trabajo_pendiente[elemento] # Añado una línea en blanco (si no es el primero) if (not primero): # Añado una línea en blanco FILE.write("\n") else: # Ya no es el primero primero=False # Escribo la cabecera FILE.write("%s\n" % (elemento)) # Escribo las gráficas pendientes for tmp in graficas_pendientes: FILE.write("%s\n" % (tmp)) # Cierro el fichero de memoria FILE.close() # }}}2 # Borro los ficheros procesados {{{2 self.debug("Borrando los fichero procesados") for fichero in lista: # Borro el fichero self.debug(" Borrando: %s" % (fichero)) os.unlink("%s%s" % (ruta,fichero)) # }}}2 # Informo de errores {{{2 for elemento in lista: if (not (elemento in ficheros_usados)): self.debug("ATENCION: El fichero %s no ha sido usado en el envio de emails por ningun usuario" % (elemento)) # }}}2 # }}}1 # ### EXCEPCIONES ### ################################# # EXCEPTION CLASSES {{{1 # Excepciones básicas # Except (General Exception) {{{2 class Except(Exception): def __init__(self,string): self.string=string def __str__(self): return self.string #}}}2 # IOError (Error de entrada/salida) {{{2 class IOError(Exception): def __init__(self,string): self.string=string def __str__(self): return self.string # }}}2 # ExecutionError (Error de ejecucción) {{{2 class ExecutionError(Exception): def __init__(self,string): self.string=string def __str__(self): return self.string # }}}2 # }}}1