#!/usr/bin/env python3
# seguridadsocial.py

"""

Descarga datos del transporte aereo: paro y ocupados

Con regresiones de las series

Exporta ficheros excel y carga base de datos

"""
from urllib import request
import matplotlib.pyplot as plt


import logging
import sys
import pathlib
import time
import filecmp
import shutil                                   
from datetime import datetime
import pandas as pd
import numpy as np
import statsmodels.api as sm
import sqlite3



#++++++ RASTREO - LOGGING - DEGUG - DEPURACION +++++++++++++

logging.basicConfig(
    format="%(asctime)s %(levelname)s:%(name)s: %(message)s",
    level=logging.INFO,  # DEBUG
    datefmt="%H:%M:%S",
    stream=sys.stderr,
)
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.disabled = False


#++++++++++++++ DOWNLOAD URL ++++++++++++++++

def download_url_excel (lg): # Descarga ficheros de la web
    ''' 
    Descarga los datos a excel de aeronaves (salidas y llegadas), 
    pasajeros y mercancías (salidas y llegadas) de la web: 
    https://www.ine.es/jaxiT3/dlgExport.htm?t=4247'
    '''
    
    logger = logging.getLogger('__' + sys._getframe().f_code.co_name)
    logger.disabled = lg
    
    urls = ['https://www.ine.es/jaxiT3/files/t/es/xlsx/4247.xlsx',
            'https://www.ine.es/jaxiT3/files/t/es/xlsx/3965.xlsx?nocab=1',
            'https://www.ine.es/jaxiT3/files/t/es/xlsx/3959.xlsx?nocab=1'
            ]
    ficheros =['TasaParo', 'Desempleo', 'Ocupados']
    salida = True
    try:
        logger.info('Initium ->')
        
        for url in urls:
            # Si existe el fichero hace una copia fichero_old
            fichero = 'd_' + ficheros[urls.index(url)] + '.xlsx'
            fichero_old = 'd_' + ficheros[urls.index(url)] + '_old.xlsx'
            path = pathlib.Path(fichero)
            existe = path.is_file()
            if existe:
                logger.info('{} EXISTE'.format(fichero))
                shutil.copy(fichero, fichero_old)
            # Baja a df y exporta a excell
            df = pd.read_excel(url, header=0)
            fichero = 'd_' + ficheros[urls.index(url)] + '.xlsx'
            df.to_excel(fichero, index = False)
            logger.info(fichero + '<-'+ url)
            # Comprueba si son iguales   
            # False: Deep comparison -content-, True:metadatos 
            iguales = filecmp.cmp(fichero, fichero_old, shallow = True) 
            logger.info('¿Iguales?: {}'.format(iguales))
            if not iguales: salida = False
            print(salida)
            
        logger.info('<- Finita est')
        
    except:  # catch *all* exceptions
        logger.exception('Error: [{}]'.format(sys.exc_info()))
    return salida


#+++++++++++++++++++ LOAD - CARGA  BD +++++++++++++++++++++++++++++++++++++

def carga_df_bd (df, bd, tabla, lg= False):
    ''' Carga el dataframe en tabla de la bd.sqlite3 '''
    
    logger = logging.getLogger('__' + sys._getframe().f_code.co_name)
    logger.disabled = lg
    
    try:

        logger.info(tabla)
        con = sqlite3.connect(bd) # Conexión
        #cur = con.cursor() # Cursor
        df.to_sql(tabla, con, if_exists = "replace") # df to bd
        con.close() # Cierre de conexion

    except:  # catch *all* exceptions
        logger.exception('Error: [{}]'.format(sys.exc_info()))


#++++++++++++++ TRANSFORMERS - DATA COLLECTION ++++++++++++++++


def transform_file_df (f, lg): # Transforma fichero a serie temporal

    
    ''' 
    Transforma datos del fichero excel original
    
    '''
    
    logger = logging.getLogger('__' + sys._getframe().f_code.co_name)
    logger.disabled = lg
    

    df = pd.read_excel(f, header=7)
    logger.info('File: ' + f)
    df = df.T # or df.transpose()
    #df.drop(df.columns[[0]], axis=1, inplace=True)
    df = df.iloc[: , 1:6]
    df = df.apply(lambda x: x.astype(str).str.upper())# todo df a mayusculas
    column_names = df.iloc[0].to_list()
    column_names = [c.replace(' ', '') for c in column_names]
    df = df.set_axis(column_names, axis = 1) # Cambia el nombre de las columnas
    #print(column_names)
    df=df.reset_index(drop=False) # Reset index
    df = df.drop(0)
    df=df.reset_index(drop=True) # Reset 
    df.rename(columns={'index':'PERIODO'},
               inplace=True)
    df[['TRIMESTRE','GRUPO']]=df.PERIODO.str.split(pat='.',expand=True)
    df.GRUPO = df.GRUPO.replace({None: 0})
    df['GRUPO'] = df['GRUPO'].astype(int)
    grupos = ['Total', 'De 16 a 19 años', 'De 20 a 24 años', 'De 25 a 54 años', '55 y más años']
    for i in range(len(grupos)):
        df.GRUPO = df.GRUPO.replace({i: grupos[i]})
    df.TRIMESTRE = df.TRIMESTRE.replace('T', 'Q', regex=True)
    df['FECHA'] = pd.to_datetime(df['TRIMESTRE']).dt.normalize()
    df['TOTAL'] = df['TOTAL'].astype(float)
    df = df.drop(['PERIODO'], axis = 1)
    df.rename(columns={'TRIMESTRE': 'PERIODO'},inplace=True)

    #Exporta fichero de datos transformados
    #fichero = f[2:]
    #df.to_excel('dt_'+ fichero, index = True)
      
    return df


    
def transform_files_dfvalor (export = False, lg = False): # Testing - prueba 
     
    '''
    Transforma los ficheros excel de Ocupación y Parados
    a dataframe sin y con categorías (solo una columna de valor)
    
    Exporta los 2 a excel
    
     
    Parameters
    ----------
    lg : TYPE: Boolean, False - activa el logging

    Returns
    -------
    Exporta 2 ficheros  con varias columnas de valores y otro con
    solo una columan de valor y categorias. 
    df con solo una columna de valor y categorías.
    
    '''
    logger = logging.getLogger('__' + sys._getframe().f_code.co_name)
    logger.disabled = lg
    
    try:
        logger.info('Initium ->')
        tipos = ['Ocupados', 'Desempleo']
        df0 = pd.DataFrame() # Dataframe vacio
        i = 0
        ntipos = len(tipos) # número de categorías
        
        # Carga los ficheros originales y los transforma en un df mesual y otro anual
        for tip in tipos:
            i = i +1
            logger.info('Tipos {}/{}: {}'.format(i, ntipos, tip) )   
            fichero = 'd_{}.xlsx'.format(tip)  
            df = transform_file_df(fichero, lg)
                
            # Concatea
            df['TIPO'] = tipos[tipos.index(tip)]
            frames = [df0, df]  # Para concatenar todas las categorias anualmente
            df1= pd.concat(frames, join="outer").reset_index(drop=True)
            df0 = df1
        
        #print (df0.columns)
        ''' De columnas a filas con categorías '''
        column_names = ['TOTAL', 'AGRICULTURA', 'INDUSTRIA', 'CONSTRUCCIÓN', 'SERVICIOS',
                        'PERIODO', 'GRUPO', 'FECHA', 'TIPO']
        df01 = df0.set_axis(column_names, axis = 1) # Cambia el nombre de las columnas, aquí no lo hacemos                        
        df0_fijo = df0.iloc[:, [7, 5, 8, 6]] # Parte fija para concatenar
        df0_valor = pd.DataFrame()
        columnas = df01.columns # Nombre de las columnas
        for col in range(0,5):
            columna = columnas[col]
            df_valor = df01.iloc[:,col] # Valor de una columna
            df_join = df0_fijo.join(df_valor) # Union con la parte fina
            df_join['CLASE'] = '{}'.format(columnas[col]) # Añado columna clase
            df_join['CATEGORIA'] = df_join['TIPO'] + ' - ' + df_join['CLASE'] + ' - ' + df_join['GRUPO'] # Añado columna categoría
            df_join.rename(columns={columna:'VALOR'},inplace=True) # Renombro la columana que añado
            df_join['VALOR'] = df_join['VALOR'].astype(float) # Convierto la columna a decimal
            frames = [df0_valor, df_join]  # Para concatenar todas las tipos anualmente
            df_cat= pd.concat(frames, join="outer").reset_index(drop=True) # Concateno al final
            df0_valor = df_cat # Añado al df la categoria
        
        # df categorias únicas
        categorias = pd.unique(df0_valor['CATEGORIA']).tolist()
        categorias.sort(reverse = True)
        df_categorias = pd.DataFrame()
        #df_categorias['COD'] = ['TA' + str(categorias.index(cat)+1) for cat in categorias]
        df_categorias['COD'] = [categorias.index(cat)+1 for cat in categorias]
        df_categorias['CATEGORIA'] = categorias
        
        # Añadimos columnas PERIODO y COD - codigo categoria para poder hacer select en sqlite
        df0_valor['COD'] = df0_valor['CATEGORIA'].apply(lambda x: df_categorias.loc[df_categorias['CATEGORIA'] == x].iloc[0,0])
        
        # Ordenamos los df
        df0 = df0.sort_values(by = ['TIPO', 'FECHA'], ascending = [True, True]).reset_index(drop=True)
        df0_valor = df0_valor.sort_values(by = ['CATEGORIA', 'FECHA'], ascending = [False, True]).reset_index(drop=True)
        
        # Info
        logger.info('Return df \n-> Filas =  {}  y campos: \n{}'.format(len(df0), df0.dtypes))
        logger.info('Return df_valor \n-> Filas =  {}  y campos: \n{}'.format(len(df0_valor), df0_valor.dtypes))
        # Exportamos los df a excel
          
        if export:
            fichero = 'd_ss.xlsx'
            logger.info(fichero) # Anual
            df0.to_excel(fichero, index = False)
            fichero = 'd_ss_valor.xlsx'
            logger.info(fichero) # Anual
            df0_valor.to_excel(fichero, index = False)
         
        salida = [df0_valor, df_categorias]
        return salida
        logger.info('<- Finita est')
        
    except:  # catch *all* exceptions
        logging.exception('Error: [{}]'.format(sys.exc_info()))    




def transform_df_serie (df, lg): # df a serie temporal
    '''
    Convierte un df a serie temporal: index + VALOR
    
    Parameters
    ----------
    local_log : TYPE: Boolean, False - activa el logging

    Returns
    -------
    df serie de tiempo
    st serie de tiempo

    '''

    logger = logging.getLogger('__' + sys._getframe().f_code.co_name)
    logger.disabled = lg
    
    try:
        
        logger.info('Initium ->')

        column_names = ['FECHA', 'VALOR']
        df = df.set_axis(column_names, axis = 1) # Cambia el nombre de las columnas
        df['FECHA'] = pd.to_datetime(df['FECHA']).apply(lambda x:datetime.strftime(x,'%Y-%m-%d'))
        #df = df.set_index('FECHA')
        df['VALOR'] = df['VALOR'].astype(float) # Valor como decimal
        df.set_index('FECHA', inplace=True)  # Serie de tiempo
        df.index = pd.to_datetime(df.index) # Indice de tiempo
        df.sort_index(ascending=True, inplace=True) # Ordena por fechas ascendente
        st = df.squeeze()
        #st = pd.Series(df['VALOR'], index = df.index) # Es lo mismo que squeeze
        #df0.index = df.index.to_period('M')
 
        #print(df.loc['2020':'2021'])
        #print(st.loc['2020'])
        logger.info('Dataframe-df0:\nlen: {}, type: {}'.format(len(df), type(df)))
        logger.info('Dataframe-Campos: \n{}'.format(df.dtypes))
        logger.info('Serie-st:\nlen: {}, type: {}'.format(len(st), type(st)))
        logger.info('Serie-Campos: \n{}'.format(st.dtypes))
        
        salida = [df, st]
        salida = [df]
        logger.info('<- Finita est')
        return salida

    except:  # catch *all* exceptions
        logging.exception('Error: [{}]'.format(sys.exc_info()))  
        



   
#+++++++++++++++++++++ REGRESIONES ++++++++++++++++++++++++++++++++++++


def hp_filter (df, lg = False): # Filtro de Hodrick-Prescott
    '''
    Separa la tendencia del ciclo y añade recesiones
    
    Parameters
    ----------
    local_log : TYPE: Boolean, False - activa el logging

    Returns
    -------
    df con  tendencia y recesiones

    '''

    logger = logging.getLogger('__' + sys._getframe().f_code.co_name)
    logger.disabled = lg
    
    try:
        
        logger.info('Initium ->')
        
        # Aplicamos logaritmo neperiano a los datos
        logger.info('log(df)')
        df['VALOR'] = np.log(df['VALOR'])  
        df.replace([np.inf, -np.inf,'',np.nan], 0, inplace = True)  # Pongo a O todas las singularidades    
        
        # Filtro de Hodrick-Prescott
        logger.info('Filtro de Hodrick-Prescott')
        hp_cycle, hp_trend = sm.tsa.filters.hpfilter(df, lamb=100)  
        ''' Con 1600 filtra bien SS y TA
         lamb sugerido 1600 para datos trimestrales, 6.5 (1600/4^4) anuales y 129600 (1600*3*3^4) mensuales
         Cuanto más bajo es lamb mayor rizado en la tendencia
        '''
        hp_trend = np.e**hp_trend # tendencia en totales
        hp_trend = hp_trend.round(0) # Redondea a cero decimales
        
        df_trend = pd.DataFrame(hp_trend) #
        df_trend_rc = recessions_add(df_trend, lg)
        #df_trend_rc['FECHA'] = pd.to_datetime(df_trend_rc['FECHA']).apply(lambda x:datetime.strftime(x,'%YM%m'))
        #df_trend_rc['FECHA'] = df_trend_rc.index
        #x_trend = df_trend_rc['PERIODO'].tolist()                                                                                             
        #y_trend = df_trend_rc['RA'].tolist()
        #y_trend_rc  =  df_trend_rc['RC'].tolist()
        
        salida = [df_trend_rc]
        #salida = [x_trend, y_trend, y_trend_rc]
        logger.info('<- Finita est')
        return salida

    except:  # catch *all* exceptions
        logging.exception('Error: [{}]'.format(sys.exc_info()))    


def date_recessions(local_log): # Recesiones USA
    '''
    
    Parameters
    ----------
    local_log : logging local disabled

    Returns
    -------
    dfyrc : dataframe, serie temporal, años de recesion
    dfmrc : dataframe, serie temporal, meses de recesion

    '''
    
    logger = logging.getLogger('&date_recessions')
    logger.disabled = local_log
    dict_recessions = { 1961 : 'Superávit - Sube: Tipos de Interés',
                       1970 : 'Guerra Vietnan - Sube: Deficit, Infraccón y Tipo de Interés',
                       1974 : 'Petróleo - se cuadriplica, Estanflacción',
                       1975 : 'Petróleo - se cuadriplica, Estanflacción',
                       1980 : 'W Doble Inmersión - Sube: Tipos de Interés',
                       1981 : 'Revolución Iraní - Sube: Petroleo',
                       1982 : 'Revolución Iraní - Sube: Petroleo',
                       1990 : 'Tras una larga expansión - Sube: Inflacción y Tipos de Interés, pesimismo consumidores',
                       1991 : 'Tras una larga expansión - Sube: Inflacción y Tipos de Interés, pesimismo consumidores',
                       2001 : 'Burbuja Puntocom y 11 S',
                       2007 : 'Burbuja Inmobiliaria, hipotecas subprime',
                       2008 : 'Burbuja Inmobiliaria, hipotecas subprime',
                       2020 : 'Coronavirus'
                       }
    
    try:
        logger.info('Años recesión USA')
        # YEAR recessions
        yrc = [1961, 1970, 1974, 1975, 1980, 1981, 1982, 1990, 1991, 2001, 2007, 2008, 2020]
        dfyrc = pd.DataFrame(yrc, columns=['PERIODO'])
        dfyrc['RC'] = 1
        dfyrc['PERIODO'] = pd.to_datetime(dfyrc['PERIODO'], format="%Y")
        dfyrc.set_index('PERIODO', inplace=True)  # Serie de tiempo
        # print(dfyrc)
        # MONTHS recessions
        mrc = []
        for y in yrc:
            for m in range(1, 13):
                e = [(str(y) + '-' + str(m))]
                mrc = mrc + e
        dfmrc = pd.DataFrame(mrc, columns=['PERIODO'])
        dfmrc['RC'] = 1
        dfmrc['PERIODO'] = pd.to_datetime(dfmrc['PERIODO'], format="%Y-%m")
        dfmrc.set_index('PERIODO', inplace=True)  # Serie de tiempo
        # print(dfmrc)
        return dfyrc, dfmrc, dict_recessions
    except ValueError as e:
        logging.exception('ValueError:', e)
    except:  # catch *all* exceptions
        e = sys.exc_info()
        logging.exception("Error: [{}]".format(e))


def recessions_add(df, lg): # Añade recesiones USA a un df
    '''
    Añade una columna con recesiones. 
    Ojo, el df tiene que venir con indice y este llamarse PERIODO.
    '''
    
    logger = logging.getLogger('__' + sys._getframe().f_code.co_name)
    logger.disabled = lg
    
    try: 
        logger.info('Initium ->')
            
        column_names = ['RA']
        df = df.set_axis(column_names, axis = 1) # Cambia el nombre de las columnas
        
        # Años y meses de recesión en USA
        yrc, mrc, dict_recessions = date_recessions(lg)
        '''
        # Para imprimir las recesiones
        df_recessions = pd.DataFrame(list(dict_recessions.items()),
                        columns=['YEAR', 'RECESSION'])
        df_recessions.to_excel('RecesionesUSA.xlsx', index = False)
        '''

        # Añadimos la columna recesion RC al fichero index fecha, y solo los meses con recesion
        #df_rc = pd.merge(df, mrc, left_index = True, right_index = True) # Intersección
        
        df_rc = df.join(mrc) # Union
        
        # Relleno columna RC para que la pinte bien
        df_rc.fillna(0, inplace=True)  # Rellena los blancos con ceros
        # Rellena con el máximo valor y el mínimo para pintar bien las recesiones
        df_rc['RC'] = df_rc['RC'].map({1: df_rc['RA'].max(),
                                        0: df_rc['RA'].min()},
                                        na_action=None)        
        column_names = ['TENDENCIA', 'RECESION']
        df_rc = df_rc.set_axis(column_names, axis = 1) # Cambia el nombre de las columnas
        df_rc.sort_index(ascending=True, inplace=True) # Ordena por fechas ascendente
        
        logger.info('<- Finita est')
        return df_rc
    
    except:  # catch *all* exceptions
        logging.exception('Error: [{}]'.format(sys.exc_info()))    


def regression(df, lg = False): # Regresion UCARIMA
    '''
    Regresion por HPfilter, uc, uc-arima'

    Parameters
    ----------
    df : dataframe, serie tiempo

    Returns
    -------
    forecast = pronostico de 12 meses
    R2 : grado de ajuste
    hp_trend = tendencia de la serie

    '''
    
    logger = logging.getLogger('__' + sys._getframe().f_code.co_name)
    logger.disabled = lg
    
    try:
        logger.info('Initium ->')
                
        #Cambiamos los datos de 2020 ponderando con las medias anuales
        ratio_ponderacion = float(df.loc['2020'].mean()/df.loc['2019'].mean())
        df.loc['2020'] = df.loc['2019'].values * ratio_ponderacion
        
        # Aplicamos logaritmo neperiano a los datos
        logger.info('log(df)')
        df['VALOR'] = np.log(df['VALOR']) 
        # Pongo a O todas las singularidades e indeterminaciones 
        df.replace([np.inf, -np.inf,'',np.nan], 0, inplace = True)
         
        retardos = 4 # Numero de periodos de retardos para la autoregression
        
        # Regresion con UCARIMA con N retardos
        logger.info('UCARIMA-12')
        mod_ucarima = sm.tsa.UnobservedComponents(df, 'rwalk', autoregressive=retardos) 
        ''' Con 4 da mejor R2 que con 4
         Here the powell method is used, since it achieves a
         higher loglikelihood than the default L-BFGS method
        '''
        
        res_ucarima = mod_ucarima.fit(method='powell', disp=False)
        #print(res_ucarima.summary())
        
        # Calculo de R2        
        logger.info('UCARIMA - R2')
        y = res_ucarima.fittedvalues[retardos:]+res_ucarima.resid[retardos:]
        R2_UCARIMA = 1 - np.sum(res_ucarima.resid[retardos:]**2)/np.sum((y-y.mean())**2)
        R2 = round(R2_UCARIMA * 100, 2)
        logger.info('R2 % ... UCARIMA...{}'.format(R2))
        
        # Prediction
        logger.info('PREDICT')
        predict_ucarima = res_ucarima.predict(steps=12)
        df_predict = pd.DataFrame(predict_ucarima)
           
        df_predict.rename(index = {0:'FECHA'}, inplace = True) # Indice fecha
        df_predict = np.e**(np.e**df_predict) # Predict  UCARIMA en totales 
        df_predict.rename(columns = {'predicted_mean':'PREDICT'}, inplace = True)
        df_predict = df_predict.round(0)
        r2 = [R2]*len(df_predict) # array con el valor de R2 n veces
        df_predict['R2'] = r2 # Añado el array como columna al df

               
        # Perform forecasting n tetardos ************
        logger.info('FORECAST')
        fcast_ucarima = res_ucarima.get_forecast(steps=retardos, alpha=0.05).summary_frame()
        df_forecast = pd.DataFrame(fcast_ucarima)
        df_forecast = np.e**(np.e**df_forecast) # Forecast  UCARIMA en totales
        df_forecast = df_forecast.round(0) # Redondea a cero decimales
        #df_forecast = df_forecast.drop(['mean_se'], axis = 1, inplace = True) # Quito columna
        
        # Saco periodo TRIMESTRE
        df_forecast['FECHA'] = df_forecast.index
        trimestre = ['01','01','01','02','02','02','03','03','03','04','04','04']
        #df_forecast['PERIODO'] = df_forecast['FECHA'].apply(lambda x:datetime.strftime(x,'%YM%m'))
        df_forecast['PERIODO'] = df_forecast['FECHA'].apply(lambda x:datetime.strftime(x,'%YQ') + 
                                                    trimestre[int(datetime.strftime(x,'%m'))-1])
        
        #x = df_forecast['PERIODO'].tolist()[-retardos:] # Valores de x
        yt = df_forecast['mean'].tolist() # Valores de  y valor en el tiempo t
        '''
        yti = df_forecast['mean_ci_lower'].tolist() # Valores de  y error inferior
        yts = df_forecast['mean_ci_upper'].tolist() # Valores de  y error superior
        '''
        
        # Estadistica del forecast
        logger.info('FORECAST-Estadística vs t-{}'.format(retardos))
        df = np.e**(np.e**df) # df original en valores absolutos
        df = df.round(0)
        yt_N = df['VALOR'][-retardos:].tolist() # los útlimos N valores
        y_varN = [] # Variación en % de N periodos anteriores del forecast 
        for i in range(len(df_forecast)):
            var = round((yt[i]- yt_N[i])*100/yt_N[i], 2)
            y_varN.append(var)
        df_forecast['Vt_N'] = yt_N
        df_forecast['V_varN'] = y_varN
        
        df_fc = df_forecast[['FECHA', 'PERIODO', 'mean', 'mean_ci_lower', 'mean_ci_upper', 'Vt_N', 'V_varN']]
        column_names = ['FECHA','PERIODO','FCMEAN', 'FCMEAN_CI_LOWER', 'FCMEAN_CI_UPPER','VT_4', 'V_VAR4']
        df_fc = df_fc.set_axis(column_names, axis = 1) # Cambia el nombre de las columnas
        df_forecastsalida = df_fc[['FECHA','PERIODO','FCMEAN', 'FCMEAN_CI_LOWER', 'FCMEAN_CI_UPPER','VT_4', 'V_VAR4']]

        
        salida = [df_predict, df_forecastsalida]
        #salida = [x, yt, yt_N, y_varN, yti, yts, R2]
        logger.info('<- Finita est')
        return salida
        
    except ValueError as e:
        logging.exception('ValueError:', e)
    except:  # catch *all* exceptions
        e = sys.exc_info()
        logging.exception("Error: [{}]".format(e))


def regression_add (df, export = False, lg = False): # Añade estadisticos de regresion 
     
    '''
    Calcula las columnas predición, tendencia, recesion, forecast,
    vt-12 (desplazda 12 meses) y delta(vt - vt-12) para todas las categorias

    Parameters
    ----------
    df : Entrada de un df con categorías, fechas y valores.
    export : Si queremos que exporte ficheros excel (predicción y forecast)
    lg : TYPE: Boolean, False - activa el logging

    Returns
    -------
    df_predict 
    df_forecast
    
    '''
    logger = logging.getLogger('__' + sys._getframe().f_code.co_name)
    logger.disabled = lg
    
    try:
        logger.info('Initium ->')
        # Categorias únicas para procesar
        categorias = pd.unique(df['CATEGORIA']).tolist()
        #categorias = ['Pasajeros - TOTAL'] # Para pruebas
        ncategorias = len(categorias)
    
        df0_predict= pd.DataFrame()
        df0_forecast= pd.DataFrame()
        i = 0
        for cat in categorias:
            i= i + 1
            try:
                logger.info('>> Categoría {}/{}: {}'.format(i, ncategorias, cat) )
                df0 = df.loc[df['CATEGORIA'] == cat] # Filtro por categoria
                df0 = df0.reset_index(drop=True) # Reseteo el indice
                cod = df0['COD'][0] # Saco codigo de categoria
                # Añado columna desplazando 4 periodos Vt-4
                df['VALOR'] = df['VALOR'].astype(float) # Valor como decimal
                df0['VT_4'] = df0['VALOR'].shift(4).astype(float) # Valor como decimal
                # Añado columna con la Vt vs Vt-4 en %
                df0['V_VAR4'] = round((df0['VALOR']-df0['VT_4'])*100/df0['VT_4'],2)
                # Quita blancos, infinitos e indeterminaciones pasandolos a valor 0.
                df0.replace([np.inf, -np.inf, '',np.nan], 0, inplace = True)
                
                df01 = df0[['FECHA', 'VALOR']]
                dfst = transform_df_serie (df01, lg = True) # df a serie temporal
                dft = dfst[0].asfreq('QS', method = 'bfill') # Si falta algún periodo lo rellena MS-Mensual, QS-trimestral
                trend_rc = hp_filter (dft, lg= True) # Tendencia y recesiones                
                regre = regression(dft, lg = True) # Regresion

                # Predicción
                df_prerc = regre[0].join(trend_rc[0]) # Union prediccion-tendencia
                df_prerc = df_prerc.reset_index(drop=True) # Reseteo el indice
                df_pre = df0.join(df_prerc) # Union + trc:tendencia y recesion
                frames = [df0_predict, df_pre]  # Para concatenar todas las categorias
                df_cat = pd.concat(frames, join="outer").reset_index(drop=True) # Añade sin indice
                df0_predict = df_cat
                
                # Forecast
                df_forecast = regre[1].assign(CATEGORIA = cat) # Añade columna CATEGORIA
                df_forecast['COD'] = cod # Añade columna COD - codigo categoria
                frames = [df0_forecast, df_forecast]  # Para concatenar todas las categorias
                df_fcat = pd.concat(frames, join="outer").reset_index(drop=True) # Añade sin indice
                df0_forecast = df_fcat
                
            except:
                logging.exception('Error: [{}]'.format(sys.exc_info())) 
         
         
        # Ordenamos los df
        df0_predict = df0_predict.sort_values(by = ['COD', 'FECHA'], ascending = [True, True]).reset_index(drop=True)
        df0_forecast = df0_forecast.sort_values(by = ['COD','FECHA'], ascending = [True, True]).reset_index(drop=True)
        
        # Info
        logger.info('Return df_PREDICT \n {} filas  y campos: \n{}'.format(len(df0_predict), df0_predict.dtypes))
        logger.info('Return df_FORECAST \n {} filas  y campos: \n{}'.format(len(df0_forecast), df0_forecast.dtypes))
          
        # Exportamos los df a excel
            
        if export:
            fichero = 'd_ss_predict.xlsx'
            logger.info(fichero) # Anual
            df0_predict.to_excel(fichero, index = False)
            fichero = 'd_ss_forecast.xlsx'
            logger.info(fichero) # Forecast
            df0_forecast.to_excel(fichero, index = False)
            
               
        salida = df0_predict, df0_forecast
        logger.info('<- Finita est')
        return salida

    except:  # catch *all* exceptions
        logger.info('Categoría {}/{}: {} *** ERROR ***'.format(i, len(categorias), cat) ) 
        logging.exception('Error: [{}]'.format(sys.exc_info()))   
        
           

 
#++++++++++++++ FUNCIÓN TEST - PRUEBAS +++++++++++++++++++++++++++++++++++++
    
def test (lg = False): # Testing - prueba 
     
    '''
         
    Parameters
    ----------
    lg : TYPE: Boolean, False - activa el logging

    Returns
    -------
    None
    
    '''
    logger = logging.getLogger('__' + sys._getframe().f_code.co_name)
    logger.disabled = lg
    
    try:
        logger.info('Initium ->')
        export = False
        
        #download_url_excel (lg)
        f = 'd_Ocupados.xlsx'
        df = transform_files_dfvalor (export = export, lg = lg)
        print(df[0])
        # Dataframes con predicción y forecast - datos mensuales 
        salida = regression_add (df[0], export = export, lg = lg)
        print(salida)
        
        
        
        salida = '<<< TEST, missa est >>>'
        logger.info('<- Finita est')
        return salida
        
    except:  # catch *all* exceptions
        logging.exception('Error: [{}]'.format(sys.exc_info()))    



#++++++++++++++ FUNCIÓN PRINCIPAL +++++++++++++++++++++++++++++++++++++

def main (lg = False): # Main 
     
    '''
    Parameters
    ----------
    lg : TYPE: Boolean, False - activa el logging

    Returns
    -------
    None.

    '''
    logger = logging.getLogger('__' + sys._getframe().f_code.co_name)
    
    # Variables
    logger.disabled = lg # False activa el logging - rastreo
    export = True # True exporta ficheros excel
    bd = '/var/www/html/lartica/db.sqlite3' # Path base de datos entorno producción
    #bd = '/home/ja/Documentos/test/lartica/db.sqlite3' # Path base de datos entorno pruebas
       
    
    try:
        logger.info('Initium ->')
        
        # Bajamos los ficheros excel de la url
        iguales = download_url_excel (lg)
   
        # Carga los ficheros originales y los transforma en un df mesual y otro anual
        if not iguales: # Solo transformamos si no son iguales
           
            # Dataframes con solo una columna valor
            df, dfc = transform_files_dfvalor (export = export, lg = lg)
           
            # Dataframes con predicción y forecast - datos mensuales 
            df_predict, df_forecast = regression_add (df, export = export, lg = lg)
     
            # Carga dataframes en base de datos
            carga_df_bd (dfc, bd = bd, tabla = 'SSCategorias', lg=lg)
            carga_df_bd (df_predict, bd = bd, tabla = 'SSPredict', lg=lg )
            carga_df_bd (df_forecast, bd = bd, tabla = 'SSForecast', lg=lg )
            
        salida = '*** MAIN, missa est ***'
        logger.info('<- Finita est')
        return salida

    except:  # catch *all* exceptions
        logging.exception('Error: [{}]'.format(sys.exc_info()))    

if __name__ == "__main__": # Main
    start = time.perf_counter()
    assert sys.version_info >= (3, 7),"Script requires Python 3.7+."
    here = pathlib.Path(__file__).parent
    logger.disabled = False
    logger.info(here)
    logger.info('Initium novum')
    # ********** INICIO **********
    
    # local log disabled
    lgd = False # False activa el logging - rastreo
    #salida = test(lgd) # Testing
    salida = main(lgd) # Producción
    logger.info(salida)
    
    
    # ********** FIN **********
    logger.info('Ave verum')
    elapsed = time.perf_counter() - start
    logger.info(f"Program completed in {elapsed:0.5f} seconds = {elapsed/60:0.2f} min.")

