示例#1
0
def main():
    log = inicializar_log()

    try:
        cliente_mongo = repo.inicializar_cliente(cfg.URI_MONGODB,
                                                 cfg.NOMBRE_BD)
        repo_eventos = RepoEventos(cfg, cliente_mongo)
        gestion = Gestion(cfg, log)
        ejecucion_ok = True

        log.escribir(
            u'-- GESTIÓN DE EVENTOS INICIADO (v{0}) --'.format(
                version.__version__), log.INFO)

        # Procesar posibles eventos activos
        log.escribir(u'Buscar eventos activos...', log.INFO)
        eventos_activos = repo_eventos.obtener_eventos_activos()

        if eventos_activos:
            gestion.actualizar_datos_eventos(eventos_activos)
        else:
            log.escribir(u'No hay eventos activos', log.INFO)

    except Exception as ex:
        ejecucion_ok = False
        log.escribir(u'Se ha producido un error, {0}'.format(ex), log.ERROR)
    finally:
        repo.insertar_ejecucion(cliente_mongo, ejecucion_ok, 'Gestion')
        log.escribir(u'-- FIN GESTIÓN DE EVENTOS --', log.INFO)
 def __init__(self, cfg, log):
     self.cfg = cfg
     self.log = log
     self.cliente_mongo = repo.inicializar_cliente(cfg.URI_MONGODB, cfg.NOMBRE_BD)
     self.analisis = Analisis(self.cfg, self.log)
     self.gestion = Gestion(self.cfg, self.log)
     self.repo_eventos = RepoEventos(cfg, self.cliente_mongo)
     self.repo_nucleos = RepoNucleos(cfg, self.cliente_mongo)
     self.repo_tipos_evento = RepoTiposEvento(cfg, self.cliente_mongo)        
示例#3
0
 def __init__(self, cfg, log):
     self.cfg = cfg
     self.log = log
     self.twitter = Twitter(self.cfg, self.log)
     self.cliente_mongo = repo.inicializar_cliente(cfg.URI_MONGODB,
                                                   cfg.NOMBRE_BD)
     self.repo_estaciones = RepoEstaciones(cfg, self.cliente_mongo)
     self.repo_eventos = RepoEventos(cfg, self.cliente_mongo)
     self.tipo_json = 'json'
     self.tipo_binario = 'octet-stream'
     self.campos_analisis_foto = 'Categories, Tags, Description, Faces, ImageType, Adult'
示例#4
0
 def __init__(self, cfg, log):
     self.cfg = cfg
     self.log = log
     self.cliente_mongo = repo.inicializar_cliente(cfg.URI_MONGODB,
                                                   cfg.NOMBRE_BD)
     self.repo_confederaciones = RepoConfederaciones(
         cfg, self.cliente_mongo)
     self.repo_estaciones = RepoEstaciones(cfg, self.cliente_mongo)
     self.repo_eventos = RepoEventos(cfg, self.cliente_mongo)
     self.repo_variables = RepoVariables(cfg, self.cliente_mongo)
     self.repo_tipo_evento = RepoTiposEvento(cfg, self.cliente_mongo)
     self.twitter = Twitter(self.cfg, self.log)
示例#5
0
 def setUpClass(cls):
     cls.log = LogSodin(cfg, cfg.FICHERO_LOG_GESTOR, LogSodin.LOGGER_MONITOR)
     cls.log.configurar_log()
     cls.cliente_mongo = repo.inicializar_cliente(cfg.URI_MONGODB, cfg.NOMBRE_BD)
     cls.repo_eventos = RepoEventos(cfg, cls.cliente_mongo)
     cls.repo_tipos_evento = RepoTiposEvento(cfg, cls.cliente_mongo)
     cls.repo_estaciones = RepoEstaciones(cfg, cls.cliente_mongo)
     cls.analisis = Analisis(cfg, cls.log)
     cls.twitter = Twitter(cfg, cls.log)
示例#6
0
 def setUpClass(cls):
     cls.chc = CHCantabrico(cfg)
     cls.cliente_mongo = repo.inicializar_cliente(cfg.URI_MONGODB, cfg.NOMBRE_BD)
     cls.repo_confederaciones = RepoConfederaciones(cfg, cls.cliente_mongo)
     cls.repo_estaciones = RepoEstaciones(cfg, cls.cliente_mongo)
     cls.repo_variables = RepoVariables(cfg, cls.cliente_mongo)
     cls.repo_eventos = RepoEventos(cfg, cls.cliente_mongo)
     cls.nombre_tipo_fluvial = 'Fluvial'
     cls.cod_tipo_fluvial = 1
     cls.cod_chc = 1 #CHCantabrico
示例#7
0
    def post_proceso_de_evento(self, evento):
        '''Analiza las fotos y textos, procesa las medidas de los sensores,
        extrae estadisticas y metadatos y guarda todo en un objeto PostEvento en la BD'''

        # Analisis ia de fotos y textos de los tweets
        self.log.escribir(u'2) Procesar tweets', self.log.INFO)
        tweets_post_procesados = self.analisis_tweets(evento['datosTwitter'])

        # Analizar datos del medio
        self.log.escribir(u'3) Procesar medidas de sensores', self.log.INFO)
        medidas_in_situ = evento['datosConfederaciones'] + evento[
            'datosPuertos']
        medidas_post_procesadas = self.procesar_medidas(medidas_in_situ)

        # Guardar PostEvento en BD
        self.log.escribir(u'4) Insertar PostEvento en BD', self.log.INFO)
        post_evento = self.crear_post_evento(evento, tweets_post_procesados,
                                             medidas_post_procesadas)
        repo_eventos = RepoEventos(self.cfg, self.cliente_mongo)
        return repo_eventos.insertar_post_evento(post_evento)
示例#8
0
 def setUpClass(cls):
     cls.log = LogSodin(cfg, cfg.FICHERO_LOG_MONITOR,
                        LogSodin.LOGGER_MONITOR)
     cls.log.configurar_log()
     cls.cliente_mongo = repo.inicializar_cliente(cfg.URI_MONGODB,
                                                  cfg.NOMBRE_BD)
     cls.repo_confederaciones = RepoConfederaciones(cfg, cls.cliente_mongo)
     cls.repo_estaciones = RepoEstaciones(cfg, cls.cliente_mongo)
     cls.repo_variables = RepoVariables(cfg, cls.cliente_mongo)
     cls.repo_eventos = RepoEventos(cfg, cls.cliente_mongo)
     cls.repo_tipos_evento = RepoTiposEvento(cfg, cls.cliente_mongo)
 def setUpClass(cls):
     cls.Chc = CHCantabrico(cfg)
     cls.log = LogSodin(cfg, cfg.FICHERO_LOG_MONITOR,
                        LogSodin.LOGGER_MONITOR)
     cls.log.configurar_log()
     cls.cliente_mongo = repo.inicializar_cliente(cfg.URI_MONGODB,
                                                  cfg.NOMBRE_BD)
     cls.repo_variables = RepoVariables(cfg, cls.cliente_mongo)
     cls.repo_eventos = RepoEventos(cfg, cls.cliente_mongo)
     cls.repo_estaciones = RepoEstaciones(cfg, cls.cliente_mongo)
     cls.repo_tipos_evento = RepoTiposEvento(cfg, cls.cliente_mongo)
     cls.CODIGO_CONFEDERACION_CHC = 1
示例#10
0
 def setUpClass(cls):
     cls.log = LogSodin(cfg, cfg.FICHERO_LOG_MONITOR,
                        LogSodin.LOGGER_MONITOR)
     cls.log.configurar_log()
     cls.twitter = Twitter(cfg, cls.log)
     cls.cliente_mongo = repo.inicializar_cliente(cfg.URI_MONGODB,
                                                  cfg.NOMBRE_BD)
     cls.repo_eventos = RepoEventos(cfg, cls.cliente_mongo)
     cls.repo_estaciones = RepoEstaciones(cfg, cls.cliente_mongo)
     cls.repo_nucleos = RepoNucleos(cfg, cls.cliente_mongo)
     cls.repo_tipos_evento = RepoTiposEvento(cfg, cls.cliente_mongo)
     cls.repo_confederaciones = RepoConfederaciones(cfg, cls.cliente_mongo)
     cls.gestion = Gestion(cfg, cls.log)
     cls.coordinador_evento = CoordinadorEvento(cfg, cls.log)
 def setUpClass(cls):
     cls.log = LogSodin(cfg, cfg.FICHERO_LOG_GESTOR,
                        LogSodin.LOGGER_MONITOR)
     cls.log.configurar_log()
     cls.gestion = Gestion(cfg, cls.log)
     cls.cliente_mongo = repo.inicializar_cliente(cfg.URI_MONGODB,
                                                  cfg.NOMBRE_BD)
     cls.repo_confederaciones = RepoConfederaciones(cfg, cls.cliente_mongo)
     cls.repo_variables = RepoVariables(cfg, cls.cliente_mongo)
     cls.repo_estaciones = RepoEstaciones(cfg, cls.cliente_mongo)
     cls.repo_eventos = RepoEventos(cfg, cls.cliente_mongo)
     cls.repo_tipos_evento = RepoTiposEvento(cfg, cls.cliente_mongo)
     cls.cod_tipo_evento_fluvial = 1
     cls.Coordinador = CoordinadorEvento(cfg, cls.log)
     cls.id_estacion = 'N020'  #Pontenova
     cls.cod_chc = 1
     cls.nivel_rio = 2.14
示例#12
0
class Gestion(object):
    """Clase para gestionar los eventos mientras permanecen activos"""
    def __init__(self, cfg, log):
        self.cfg = cfg
        self.log = log
        self.cliente_mongo = repo.inicializar_cliente(cfg.URI_MONGODB,
                                                      cfg.NOMBRE_BD)
        self.repo_confederaciones = RepoConfederaciones(
            cfg, self.cliente_mongo)
        self.repo_estaciones = RepoEstaciones(cfg, self.cliente_mongo)
        self.repo_eventos = RepoEventos(cfg, self.cliente_mongo)
        self.repo_variables = RepoVariables(cfg, self.cliente_mongo)
        self.repo_tipo_evento = RepoTiposEvento(cfg, self.cliente_mongo)
        self.twitter = Twitter(self.cfg, self.log)

    def actualizar_datos_eventos(self, eventos_activos):
        '''Obtiene los ultimos datos y los guarda en el evento'''
        for evento in eventos_activos:
            self.guardar_datos_en_evento(evento)
            self.repo_eventos.actualizar_evento(evento)
            self.log.escribir(u'OK - Evento actualizado con los últimos datos',
                              self.log.INFO)

    def guardar_datos_en_evento(self, evento):
        estacion = self.repo_estaciones.obtener_estacion(evento['idEstacion'])
        tipo = self.repo_eventos.obtener_tipo_evento(evento['codigoTipo'])
        self.log.escribir(
            u'EVENTO {0} activo en {1}. Fecha: {2}'.format(
                tipo['nombre'], estacion['nombre'], evento['fechaInicio']),
            self.log.INFO)
        if tipo == self.repo_tipo_evento.obtener_tipo_evento_fluvial():
            # datos de estaciones saih para eventos fluviales
            self.log.escribir(
                u'1) Obtener últimos datos de estaciones SAIH para el evento',
                self.log.INFO)
            self.guardar_datos_de_saih(evento, estacion)
        else:
            # datos de boyas de puertos del estado para eventos costeros
            self.log.escribir(
                u'1) Obtener últimos datos de boyas para el evento',
                self.log.INFO)
            self.guardar_datos_de_boyas(evento, estacion)

        # datos twitter
        self.log.escribir(u'2) Buscar nuevos tweets sobre el evento',
                          self.log.INFO)
        self.guardar_nuevos_tweets_evento(evento, estacion)

        # datos aemet?

    def guardar_datos_de_saih(self, evento, estacion):
        confederacion = self.repo_confederaciones.obtener_confederacion(
            estacion['codigoConfederacion'])
        proveedor_confe = self.repo_confederaciones.obtener_proveedor_saih(
            confederacion)

        for variable_estacion in estacion['variables']:
            variable = self.repo_variables.obtener_variable(
                variable_estacion['codigo'])
            try:
                ultima_medida = proveedor_confe.ultima_medida_de_estacion(
                    estacion, variable)
                Gestion._agregar_medida_a_evento(evento,
                                                 'datosConfederaciones',
                                                 ultima_medida)
                self.log.escribir(
                    u'Último dato de {0} añadido al evento'.format(
                        variable['nombre']), self.log.INFO)
            except ValueError as verr:
                self.log.escribir(
                    u'No se ha podido obtener la medida. {0}'.format(
                        verr.message), self.log.WARNING)
                continue

    def guardar_datos_de_boyas(self, evento, estacion):
        proveedor_boyas = self.repo_confederaciones.obtener_proveedor_boyas()
        for variable_estacion in estacion['variables']:
            variable = self.repo_variables.obtener_variable(
                variable_estacion['codigo'])
            try:
                ultima_medida = proveedor_boyas.ultima_medida_de_estacion(
                    estacion, variable)
                Gestion._agregar_medida_a_evento(evento, 'datosPuertos',
                                                 ultima_medida)
                self.log.escribir(
                    u'Último dato de {0} añadido al evento'.format(
                        variable['nombre']), self.log.INFO)
            except ValueError as verr:
                self.log.escribir(
                    u'No se ha podido obtener la medida. {0}'.format(
                        verr.message), self.log.WARNING)
                continue

    def guardar_nuevos_tweets_evento(self, evento, estacion):
        '''Busca y guarda nuevos tweets relacionados con un evento concreto'''

        # Configurar peticion de busqueda en twitter
        props = self.propiedades_peticion_twitter(evento, estacion)

        # Buscar nuevos tweets por lotes
        tweets = self.twitter.busqueda_tweets_por_lotes(
            props, config_twitter.NUM_LOTES)
        self.log.escribir(u'Filtro avanzado de tweets', self.log.INFO)
        tweets_filtrados = self.twitter.filtro_avanzado_tweets_evento(
            tweets, evento)

        # Guardar nuevos tweets en evento de BD
        if not tweets_filtrados:
            self.log.escribir(
                u'No se han encontrado nuevos tweets sobre el evento',
                self.log.INFO)

        evento['datosTwitter'].extend(tweets_filtrados)
        self.log.escribir(
            u'{0} nuevos tweets agregados al evento'.format(
                str(len(tweets_filtrados))), self.log.INFO)

    @staticmethod
    def _agregar_medida_a_evento(evento, campo_datos, medida):
        '''Agrega una medida de una estación a un evento'''
        evento[campo_datos].append(medida.to_json_obj())

    def propiedades_peticion_twitter(self, evento, estacion):
        tipo_fluvial = self.repo_tipo_evento.obtener_tipo_evento_fluvial()
        toponimos = evento['toponimos']
        query_toponimos = u'"{0}"'.format(toponimos[0])

        for toponimo in toponimos[1:]:
            query_toponimos += u' OR "{0}"'.format(toponimo)

        if evento['codigoTipo'] == tipo_fluvial['codigo']:
            query = self.twitter.crear_query_twitter(
                query_toponimos, config_twitter.PALABRAS_CLAVE_FLUVIAL,
                config_twitter.PALABRAS_EXCLUIDAS,
                config_twitter.CUENTAS_OFICIALES)
        else:
            query = self.twitter.crear_query_twitter(
                query_toponimos, config_twitter.PALABRAS_CLAVE_COSTERO,
                config_twitter.PALABRAS_EXCLUIDAS,
                config_twitter.CUENTAS_OFICIALES)

        #Activar para filtrar espacialmente la búsqueda de tweets
        #coords = Gestion.obtener_centroide_estacion(estacion)
        #query_geo = Twitter.crear_query_geo(coords[0], coords[1])
        query_geo = ''

        id_ultimo_tweet = self.obtener_id_ultimo_tweet_evento(evento)

        return {
            'query': query,
            'query_geo': query_geo,
            'id_ultimo': id_ultimo_tweet,
            'next_max_id': None,
            'max_num_tweets': config_twitter.MAX_TWEETS_PETICION
        }

    def obtener_id_ultimo_tweet_evento(self, evento):
        '''Obtiene la id del último tweet de un evento guardado en la BD'''
        id_ultimo_tweet = 0

        try:
            id_ultimo_tweet = evento['datosTwitter'][0]['id']
        except IndexError:
            self.log.escribir(u'Aun no hay tweets en el evento',
                              self.log.DEBUG)
        finally:
            return id_ultimo_tweet

    @staticmethod
    def obtener_centroide_estacion(estacion_evento):
        try:
            return (estacion_evento['coordenadas']['lon'],
                    estacion_evento['coordenadas']['lat'])
        except AttributeError as atterr:
            raise AttributeError(
                u'Error obteniendo centroide de un evento. {0}'.format(
                    atterr.message))
示例#13
0
class Analisis(object):
    """Clase encargada del analisis y postproceso de un evento de inundación"""
    def __init__(self, cfg, log):
        self.cfg = cfg
        self.log = log
        self.twitter = Twitter(self.cfg, self.log)
        self.cliente_mongo = repo.inicializar_cliente(cfg.URI_MONGODB,
                                                      cfg.NOMBRE_BD)
        self.repo_estaciones = RepoEstaciones(cfg, self.cliente_mongo)
        self.repo_eventos = RepoEventos(cfg, self.cliente_mongo)
        self.tipo_json = 'json'
        self.tipo_binario = 'octet-stream'
        self.campos_analisis_foto = 'Categories, Tags, Description, Faces, ImageType, Adult'

    def post_proceso_de_evento(self, evento):
        '''Analiza las fotos y textos, procesa las medidas de los sensores,
        extrae estadisticas y metadatos y guarda todo en un objeto PostEvento en la BD'''

        # Analisis ia de fotos y textos de los tweets
        self.log.escribir(u'2) Procesar tweets', self.log.INFO)
        tweets_post_procesados = self.analisis_tweets(evento['datosTwitter'])

        # Analizar datos del medio
        self.log.escribir(u'3) Procesar medidas de sensores', self.log.INFO)
        medidas_in_situ = evento['datosConfederaciones'] + evento[
            'datosPuertos']
        medidas_post_procesadas = self.procesar_medidas(medidas_in_situ)

        # Guardar PostEvento en BD
        self.log.escribir(u'4) Insertar PostEvento en BD', self.log.INFO)
        post_evento = self.crear_post_evento(evento, tweets_post_procesados,
                                             medidas_post_procesadas)
        repo_eventos = RepoEventos(self.cfg, self.cliente_mongo)
        return repo_eventos.insertar_post_evento(post_evento)

    def analisis_tweets(self, tweets):
        tweets_postevento = []
        for tweet in tweets:
            try:
                tweets_postevento.append(self.procesar_tweet(tweet))
            except Exception as ex:
                self.log.escribir(
                    u'Se ha producido un error analizando el tweet. {0}'.
                    format(ex.message), self.log.WARNING)
                continue
        return tweets_postevento

    def procesar_tweet(self, tweet):
        self.log.escribir(u'   Analizar tweet:', self.log.INFO)
        foto_procesada = self.analizar_foto_del_tweet(tweet)
        texto_procesado = self.analizar_texto_del_tweet(tweet)
        metadatos = self.extraer_metadatos_del_tweet(tweet)
        tweet_postevento = {
            'id_tweet': str(tweet['id']),
            'metadatos': metadatos,
            'datos_foto': foto_procesada,
            'datos_texto': texto_procesado
        }
        return tweet_postevento

    def analizar_foto_del_tweet(self, tweet):
        self.log.escribir(u'   - Buscar fotos asociadas', self.log.INFO)
        try:
            foto = None
            url_foto = self.twitter.obtener_url_foto_de_tweet(tweet)
            if url_foto is not None:
                self.log.escribir(
                    u'      Ejecución de análisis i.a. de la foto',
                    self.log.INFO)
                datos_analisis = self.analizar_foto_externa(
                    self.campos_analisis_foto, url_foto)
                foto = self.crear_objeto_foto(self.campos_analisis_foto,
                                              datos_analisis, url_foto)
            else:
                self.log.escribir(u'      Tweet sin foto asociada',
                                  self.log.INFO)
        except Exception as ex:
            self.log.escribir(
                u'No se ha podido hacer el análisis de imagen. {0}'.format(
                    ex.message), self.log.WARNING)
        finally:
            return foto

    def analizar_foto_externa(self, campos_busqueda, url_foto):
        (cabeceras,
         parametros) = self.config_peticion_api_vision(campos_busqueda,
                                                       self.tipo_json)
        datos_analisis = cpu_vision.process_request({'url': url_foto}, None,
                                                    cabeceras, parametros)
        return datos_analisis

    def analizar_foto_en_disco(self, campos_busqueda, ruta_en_disco):
        with open(ruta_en_disco, 'rb') as fichero:
            datos = fichero.read()
        (cabeceras,
         parametros) = self.config_peticion_api_vision(campos_busqueda,
                                                       self.tipo_binario)
        datos_analisis = cpu_vision.process_request(None, datos, cabeceras,
                                                    parametros)
        return datos_analisis

    def config_peticion_api_vision(self, campos_busqueda, tipo):
        '''Genera las cabeceras y parametros necesarios para la peticion a la api'''
        cabeceras = dict()
        cabeceras[
            'Ocp-Apim-Subscription-Key'] = self.cfg.COMPUTER_VISION_API_KEY
        cabeceras['Content-Type'] = 'application/{0}'.format(tipo)

        parametros = {
            'visualFeatures': campos_busqueda,
            'language': 'en',
            'details': 'Landmarks'
        }
        return (cabeceras, parametros)

    # pylint: disable=R0201
    def crear_objeto_foto(self, campos, datos_analisis, url_foto):
        foto_procesada = {}
        for campo in campos.replace(' ', '').split(','):
            campo_camelcase = campo[:1].lower() + campo[1:]
            foto_procesada[campo] = datos_analisis[campo_camelcase]
        # Añadir la url original de la foto
        foto_procesada['url'] = url_foto
        return foto_procesada

    def analizar_texto_del_tweet(self, tweet):
        try:
            self.log.escribir(u'   - Análisis semántico del texto',
                              self.log.INFO)
            (palabras_clave, sentimiento) = text_analytics.analisis_texto(
                tweet['id'], tweet['text'])
            texto_procesado = {
                'texto': tweet['text'],
                'palabras_clave': palabras_clave,
                'sentimiento': sentimiento
            }
        except Exception as ex:
            self.log.escribir(
                u'No se ha podido hacer el análisis del texto. {0}'.format(
                    ex.message), self.log.WARNING)
            texto_procesado = {
                'texto': tweet['text'],
                'palabras_clave': [],
                'sentimiento': 1
            }
        finally:
            return texto_procesado

    def extraer_metadatos_del_tweet(self, tweet):
        self.log.escribir(u'   - Extracción de metadatos del tweet',
                          self.log.INFO)
        return {
            'coordenadas': tweet.get('coordinates'),
            'hashtags': tweet.get('entities').get('hashtags', []),
            'n_retweets': tweet.get('retweet_count', 0),
            'n_likes': tweet.get('favorite_count', 0),
            #'sensible': tweet['possibly_sensitive'],
            'fecha_creacion': tweet.get('created_at'),
            'verified': tweet.get('user').get('verified'),
            'account': tweet.get('user').get('screen_name')
            #todo: guardar si tiene enlaces o no. links:true
        }

    def procesar_medidas(self, medidas):
        '''Estandarizacion de los datos de los sensores'''
        self.log.escribir(u'   - Estandarización de datos', self.log.INFO)
        # Pasar las fechas internas de las medidas a cadena para poder insertar
        # en BD
        for medida in medidas:
            medida['fecha'] = util.convertir_fecha_a_cadena(
                medida['fecha'], self.cfg.FORMATO_FECHA)
        return medidas

    def crear_post_evento(self, evento, tweets_procesados, medidas_procesadas):
        tipo = self.repo_eventos.obtener_tipo_evento(evento['codigoTipo'])
        estacion = self.repo_estaciones.obtener_estacion(evento['idEstacion'])

        try:
            nuevo_post_evento = PostEvento({
                'idEvento':
                str(evento['_id']),
                'tipo':
                tipo['nombre'],
                'lugar':
                estacion['nombre'],
                'idEstacion':
                evento['idEstacion'],
                'coords':
                estacion['coordenadas'],
                'medidas':
                medidas_procesadas,
                'tweets':
                tweets_procesados,
                'fechaInicio':
                util.convertir_fecha_a_cadena(evento['fechaInicio'],
                                              self.cfg.FORMATO_FECHA),
                'fechaFin':
                util.convertir_fecha_a_cadena(evento['fechaFin'],
                                              self.cfg.FORMATO_FECHA)
            })
            return nuevo_post_evento
        except AttributeError as aterr:
            raise AttributeError(u'Error creando post evento. {0}'.format(
                aterr.message))
class CoordinadorEvento(object):
    """Clase que coordina el ciclo de vida de un evento de inundacion"""

    def __init__(self, cfg, log):
        self.cfg = cfg
        self.log = log
        self.cliente_mongo = repo.inicializar_cliente(cfg.URI_MONGODB, cfg.NOMBRE_BD)
        self.analisis = Analisis(self.cfg, self.log)
        self.gestion = Gestion(self.cfg, self.log)
        self.repo_eventos = RepoEventos(cfg, self.cliente_mongo)
        self.repo_nucleos = RepoNucleos(cfg, self.cliente_mongo)
        self.repo_tipos_evento = RepoTiposEvento(cfg, self.cliente_mongo)        

    def gestionar_alerta(self, estacion, variable, hay_alerta):
        '''Comprueba el estado de las alertas en una estacion,
        gestionando un nuevo evento o actualizando uno existente'''
        evento_estacion = self.obtener_evento_en_estacion(estacion, variable['codigoTipoEvento'])
        if not hay_alerta and evento_estacion:
            #Si no hay alerta y existe un evento, hay que desactivarlo            
            if not self.evento_mas_24h(evento_estacion['fechaInicio']):
                #Si no han pasado 24h el evento se mantiene activo
                self.log.escribir(u' - Valores normalizados en las primeras 24h, el EVENTO SIGUE ACTIVO', self.log.INFO)
                return
            
            self.desactivar_evento(evento_estacion)
            self.iniciar_postproceso_evento(evento_estacion)
            return

        if hay_alerta and evento_estacion is None:
            #Si hay alerta y no existe un evento en BD hay que crear uno
            toponimos = self.crear_lista_toponimos(estacion)
            res_evento = self.guardar_evento_nuevo(estacion['id'], variable['codigoTipoEvento'], toponimos)
            self.log.escribir(u' - Valores superan umbral, NUEVO EVENTO ACTIVADO', self.log.INFO)
            #Insertar datos iniciales en el nuevo evento
            nuevo_evento = self.repo_eventos.obtener_evento(res_evento)
            self.gestion.actualizar_datos_eventos([nuevo_evento])            
            return res_evento

        if hay_alerta and evento_estacion:
            self.log.escribir(u' - Valores superan umbral, el EVENTO SIGUE ACTIVO', self.log.INFO)
            return
        
    def evento_mas_24h(self, fecha_inicio_evento):                  
        utc = pytz.timezone(u'UTC')
        fecha_utc = utc.localize(fecha_inicio_evento)
        ahora = datetime.now(pytz.utc)
        ayer = ahora + relativedelta(days=-1)

        return fecha_utc < ayer

    def obtener_evento_en_estacion(self, estacion, cod_tipo):
        '''Devuelve el evento activo en la estacion o None si no hay ninguno'''
        eventos_activos = self.repo_eventos.obtener_eventos_activos_de_tipo(cod_tipo)
        return util.filtrar_lista(eventos_activos, 'idEstacion', estacion['id'])

    def desactivar_evento(self, evento):
        evento['activo'] = False
        evento['fechaFin'] = util.fecha_actual(self.cfg.FORMATO_FECHA)
        res = self.repo_eventos.actualizar_evento(evento)
        if res.modified_count == 1:
            self.log.escribir(u' - Valores normalizados, EVENTO DESACTIVADO', self.log.INFO)
        else:
            self.log.escribir(u' - No se ha podido desactivar el evento. Error actualizando la BD', self.log.WARNING)

    def iniciar_postproceso_evento(self, evento):
        self.log.escribir(u' Iniciando Análisis y PostProceso del Evento', self.log.INFO)
        res = self.analisis.post_proceso_de_evento(evento)
        if res.acknowledged:
            self.log.escribir(u' PostEvento guardado con éxito', self.log.INFO)
        else:
            self.log.escribir(u' No se ha podido almacenar el PostEvento', self.log.WARNING)

    def guardar_evento_nuevo(self, id_estacion, cod_tipo_evento, toponimos):
        evento = self.crear_evento(id_estacion, cod_tipo_evento, toponimos)
        res = self.repo_eventos.insertar_evento(evento)
        return res.inserted_id

    def crear_evento(self, id_estacion, cod_tipo, toponimos):
        try:
            nuevo_evento = Evento({
                'fechaInicio' : util.fecha_actual_str(self.cfg.FORMATO_FECHA),
                'fechaFin': None,
                'codigoTipo' : cod_tipo,
                'idEstacion' : id_estacion,
                'activo': True,
                'toponimos': toponimos,
                'datosAemet': [],
                'datosPuertos':[],
                'datosConfederaciones':[],
                'datosTwitter':[]
                })
            return nuevo_evento
        except AttributeError as aterr:
            raise AttributeError(u'Error creando nuevo evento. {0}'.format(aterr.message))


    def crear_lista_toponimos(self, estacion):
        # Crear query con un máximo de 200 caracteres.  La busqueda en twitter
        # está limitada #
        tipo_fluvial = self.repo_tipos_evento.obtener_tipo_evento_fluvial()
        lista_toponimos = []        

        # Topónimos de la estación fluvial / costera
        lista_toponimos.extend([estacion['nombre'], estacion['rio_costa'], estacion['sistema']])
        
        if estacion['codigoTipo'] == tipo_fluvial['codigo']:
            # Topónimos de núcleos cercanos a la estación fluvial
            distancia_max = 4000 #4km
            nombres_nucleos_cercanos = self.repo_nucleos.obtener_nucleos_cercanos(estacion['coordenadas'], distancia_max)            
        else:
            # Topónimos de núcleos costeros cercanos a la boya
            distancia_max = 40000 #40km
            nombres_nucleos_cercanos = self.repo_nucleos.obtener_nucleos_costeros_cercanos(estacion['coordenadas'], distancia_max)

        for nombre in nombres_nucleos_cercanos:
            #Hay que limitar los caracteres de la lista de toponimos a un
            #máximo de 200 por si acaso.
            #La query final no puede superar los 500 (max de la api de twitter)
            if nombre.find(".") != -1: #Controlar que no entren nombres con caracteres que rompan la api de twitter
                continue
                
            lista_toponimos.append(nombre)            
            if len(str(lista_toponimos)) > 175:
                break            
            
        return lista_toponimos