#!/usr/bin/env python3
# turismo_download.py

"""Descarga de Datos de turismo"""

# Librerias generales
import logging
import sys
import time
import pathlib


# Librerias del proyecto

import requests
import json
import pandas as pd
import numpy as np
import sqlite3
from datetime import datetime, date
import statsmodels.api as sm
import filecmp
import shutil


#++++++++++ 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 ++++++++++++++++

def download_url_json(lg = False):
    ''' 
    Descarga un fichero json de una url
    
    Parameters
    ----------
    lg : TYPE: Boolean, False - activa el logging

    Returns
    -------
    path : nombre del fichero json de salida

    '''
    logger = logging.getLogger('__' + sys._getframe().f_code.co_name)
    logger.disabled = lg
    

    path = 'data.json'  # data download file
    url = 'https://servicios.ine.es/wstempus/js/es/DATOS_TABLA/2074?tip=AM&'
    path_old = 'data_old.json'
    
    try:
        logger.info('Initium ->')
        # Si existe data.json hace una copia data_old.json
        p = pathlib.Path(path)
        existe = p.is_file()
        if existe:
            logger.info('data.json existe')
            shutil.copy(path, path_old)
        # Baja el fichero de la url
        data = requests.get(url)
        output = json.loads(data.text)  # dict
        with open(path, 'w') as outfile:
            json.dump(output, outfile, sort_keys=False, indent=4)
        # Comprueba si son iguales   
        # False: Deep comparison -content-, True:metadatos 
        iguales = filecmp.cmp(path, path_old, shallow = True) 
        logger.info('¿Iguales?: {}'.format(iguales))
        
        salida = iguales
        logger.info('<- Finita est')
        return salida
        
    except:  # catch *all* exceptions
        logger.exception('Error: [{}]'.format(sys.exc_info()))
    
    

#+++++++++++++++++++ LOAD - CARGA +++++++++++++++++++++++++++++++++++++

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 ++++++++++++++++

def transform_json_df(f, export=False, lg = False):
    ''' Toma un fichero json (f) de entrada.
    Exporta dos excel datos y categorias
    
    Parameters
    ----------
    - f: nombre fichero json
    - export : boolean, True exporta los fichero
    - lg: boolean, False imprime logging - depuración
    
    
    Return:
    -------
    - Dataframe: datos
    - Dataframe: categorias
    
    '''
    logger = logging.getLogger('__' + sys._getframe().f_code.co_name)
    logger.disabled = lg

    try:
        logger.info('Initium ->')
        df0 = pd.DataFrame()
        
        # Reading the json as a dict
        with open(f, 'r', encoding='utf-8') as json_data:
            data = json.load(json_data)  # dict
        df = pd.DataFrame(data)
        
        '''Datos: categorias'''
        
        df_categorias = df[['COD', 'Nombre']]
        column_names = ['COD', 'CATEGORIA']
        df_categorias = df_categorias.set_axis(column_names, axis = 1) # Cambia el nombre de las columnas
        # Elimino texto redundante
        df_categorias['CATEGORIA'] = df_categorias['CATEGORIA'].replace({' Total categorías.':''}, regex = True)
        df_categorias['CATEGORIA'] = df_categorias['CATEGORIA'].replace({' Dato base.':''}, regex = True)
        # Quito los blancos de los nombres de la columnas                  
        df_categorias.columns = df_categorias.columns.str.strip()
        # Quito los blancos del inicio y del final de todos los datos de la tabla.
        df_categorias[df_categorias.columns] = df_categorias.apply(lambda x: x.str.strip())                     
        # Divido la columna en varias
        df_categorias_expand = df_categorias['CATEGORIA'].str.split('.', expand = True)
        # Tomo las tres primeras columnas
        df_categorias_expand = df_categorias_expand.iloc[:,[0,1,2]]
        # Renombro las columnas
        df_categorias_expand.columns = ['NACIONAL', 'VIAJEROS', 'TOTAL']
        # Concateno la columna dividida con el df original
        df_categorias = pd.concat([df_categorias, df_categorias_expand], axis = 1).reset_index(drop=True)
        # Quito los blancos del inicio y del final de todos los datos de la tabla.
        #df_categorias['VIAJEROS'] = df_categorias['VIAJEROS'].str.strip()
        df_categorias[df_categorias.columns] = df_categorias.apply(lambda x: x.str.strip()) 
        # Variable con todos los codigosde categorias
        categorias_all = df_categorias['COD'].tolist()
        # Exporta el fichero de categorias
        path = 'd_categorias.xlsx'
        if export:
            df_categorias.to_excel(path, index = False)
            
        ''' Datos: Serie temporal '''
        
        #df_data = df['Data'].apply(pd.Series)
        df_data = pd.DataFrame(df.pop('Data').values.tolist())  # más rápido
        # Seleccionamos las categorias a procesar
        categorias = ['EOT1', 'EOT9', 'EOT17',  # Viajeros nacional
                      'EOT25', 'EOT33', 'EOT41',  # pernoctaciones nacional
                      'EOT1568', 'EOT1569', 'EOT1570',  # pernoctaciones cyl
                      'EOT1622', 'EOT1623']  # pernoctaciones zamora
        
        categorias = ['EOT1', 'EOT9', 'EOT17']  # Viajeros nacional
        categorias = ['EOT1', 'EOT1471']  # Daban error
        categorias = categorias_all # Todas las categorias
        
        # Añadimos las categorias al dataframe df0
        for cat in categorias:
            i = df.index[df['COD'] == cat].tolist()[0]  # Saca el index
            df_cat = df_data.iloc[i, :]
            df_cat = pd.json_normalize(df_cat)
            logger.info('\nCategoria: {}-{}-{}({} periodos)'.format(i,
                                               df.iloc[i, 0],
                                               df.iloc[i, 1],
                                               len(df_cat)
                                               ))            
            df_cat['COD'] = df.iloc[i, 0]  # Añade el codigo
            df_cat['NOMBRE'] = df.iloc[i, 1]  # Añade el nombre
            df_cat = df_cat.sort_values('Fecha')
            df_cat.replace([np.inf, -np.inf, '',np.nan], 0, inplace = True)
            df_cat = df_cat.loc[df_cat['Fecha'] != 0, :]
            df_cat['Fecha'] = pd.to_datetime(df_cat['Fecha']).apply(lambda x:datetime.strftime(x,'%Y-%m-%d'))
            
            # Para rellenar las fechas que faltan con la fila anterior.
            
            #fdesde = datetime.strptime(df_cat['Fecha'].values[0], '%Y-%m-%d')
            #fhasta = datetime.strptime(df_cat['Fecha'].values[-1], '%Y-%m-%d')
            #Filamasreciente = df_cat.asof(start) # the most recent
            
            #print(df_cat.index)
            # Convierte la columna Fecha(string) to datatime types
            datetime_series = pd.to_datetime(df_cat['Fecha'])
            # Creamos el datetime index pasando del datetime series
            datetime_index = pd.DatetimeIndex(datetime_series.values)
            df2 = df_cat.set_index(datetime_index)
            # Borramos la columna Fecha
            df2.drop('Fecha', axis = 1, inplace = True)
            df2.sort_index(inplace=True) # Hay que ordenar para rellenar
            df2 = df2[~df2.index.duplicated(keep='first')] # Elimina duplicados
            # Rellena con el primero de mes y valores fila anterior la fecha que falta
            df2 = df2.asfreq('MS', method = 'bfill') 
            df2 = df2.reset_index() # Quita el indice y lo pasa a columna
            df2 = df2.rename({'index':'Fecha'}, axis = 1) # Renombra la columna index
        
            # Rellena el periodo y el año
            df2['PERIODO'] = df2['Fecha'].apply(lambda x:datetime.strftime(x,'%YM%m'))
            df2['Anyo'] = df2['Fecha'].apply(lambda x:datetime.strftime(x,'%Y'))
            # Columna desplazando 12 periodos Vt-12
            df2['VALOR_12'] = df2['Valor'].shift(12)
            # Columna con la Vt vs Vt-12 en %
            df2['VALOR_VAR12'] = round((df2['Valor']-df2['VALOR_12'])*100/df2['VALOR_12'],2)
            # Quita blancos, infinitos e indeterminaciones pasandolos a valor 0.
            df2.replace([np.inf, -np.inf, '',np.nan], 0, inplace = True)
            # Añadimos el PERIODO juntando dos columnas
            #df_cat['PERIODO'] = df_cat['Anyo'].astype(str) + df_cat['T3_Periodo'].astype(str)
            logger.info('Periodos de salida =  {}, rellenos = {}'.format(len(df2), len(df2)-len(df_cat)))
            # Concatena
            frames = [df0, df2]  # Para concatenar todas las categorias
            df3 = pd.concat(frames, join="outer").reset_index(drop=True)
            df0 = df3
            
        # Selecciono los campos que quiero del total del df0
        df0 = df0[['Fecha','PERIODO', 'Anyo','T3_Periodo','Valor', 'VALOR_12', 'VALOR_VAR12', 'COD', 'NOMBRE']]
        # Les cambio el nombre a los campos
        column_names = ['FECHA', 'PERIODO','ANYO', 'MES', 'VALOR','VALOR_12', 'VALOR_VAR12', 'COD', 'CATEGORIA']
        df0 = df0.set_axis(column_names, axis = 1) # Cambia el nombre de las columnas
        # Limpio de texto imnecesario
        df0['CATEGORIA'] = df0['CATEGORIA'].replace({' Total categorías.':''}, regex = True)
        df0['CATEGORIA'] = df0['CATEGORIA'].replace({' Dato Base.':''}, regex = True)
        df0 = df0.sort_values(by = ['COD','FECHA'], ascending = [True, True])
        
        logger.info('Return df_datos {} filas  y campos: \n{}'.format(len(df0), df0.dtypes))
        logger.info('Return df_categorias {} filas  y campos: \n{}'.format(len(df_categorias), df_categorias.dtypes))
        
        # Exporta el fichero
        path = 'd_turismo.xlsx'
        logger.info('Exportando: {}'.format(path))
        if export:
            df0.to_excel(path, index = False)
        
        #print(df0.describe())
        
        logger.info('<- Finita est')
        return df0, df_categorias
        
    except:  # catch *all* exceptions
        logger.exception('Error:\n [{}]'.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.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]
        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=1500)  
        ''' 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(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(lg): # 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('__' + sys._getframe().f_code.co_name)
    logger.disabled = lg
    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',
                       2008 : 'Burbuja Inmobiliaria',
                       2020 : 'Coronavirus'
                       }
    
    try:
        logger.info('Initium ->')
        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)
        
        logger.info('<- Finita est')
        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(df, lg): # Añade recesiones USA a un d
    '''
    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)
             
        # Regresion con UCARIMA con 12 retardos
        logger.info('UCARIMA-12')
        mod_ucarima = sm.tsa.UnobservedComponents(df, 'rwalk', autoregressive=12) 
        ''' 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[12:]+res_ucarima.resid[12:]
        R2_UCARIMA = 1 - np.sum(res_ucarima.resid[12:]**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 12 meses ************
        logger.info('FORECAST')
        fcast_ucarima = res_ucarima.get_forecast(steps=12, 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
        df_forecast['FECHA'] = df_forecast.index
        df_forecast['PERIODO'] = df_forecast['FECHA'].apply(lambda x:datetime.strftime(x,'%YM%m'))
        
        #x = df_forecast['PERIODO'].tolist()[-12:] # 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-12')
        df = np.e**(np.e**df) # df original en valores absolutos
        df = df.round(0)
        yt_12 = df['VALOR'][-12:].tolist() # los útlimos 12 valores
        y_var12 = [] # Variación en % de 12 meses anteriores del forecast 
        for i in range(len(df_forecast)):
            var = round((yt[i]- yt_12[i])*100/yt_12[i], 2)
            y_var12.append(var)
        df_forecast['Vt_12'] = yt_12
        df_forecast['V_var12'] = y_var12
        
        df_fc = df_forecast[['FECHA', 'PERIODO', 'mean', 'mean_ci_lower', 'mean_ci_upper', 'Vt_12', 'V_var12']]
        column_names = ['FECHA','PERIODO','FCMEAN', 'FCMEAN_CI_LOWER', 'FCMEAN_CI_UPPER','VT_12', 'V_VAR12']
        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_12', 'V_VAR12']]

        
        salida = [df_predict, df_forecastsalida]
        #salida = [x, yt, yt_12, y_var12, 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 predict_forecast_all (df, df_categorias, export = False, lg = False): # Predicción y forecast todas categorias 
     
    '''
    Calcula la predición, tendencia, recesion y forecast para todas las categorias
    Parameters
    ----------
    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_all = df_categorias['COD'].tolist()
        
        # Seleccionamos las categorias a procesar
        categorias = ['EOT1', 'EOT9', 'EOT17',  # Viajeros nacional
                      'EOT25', 'EOT33', 'EOT41',  # pernoctaciones nacional
                      'EOT1568', 'EOT1569', 'EOT1570',  # pernoctaciones cyl
                      'EOT1622', 'EOT1623']  # pernoctaciones zamora
        
        categorias = ['EOT1', 'EOT9', 'EOT17']  # Viajeros nacional
        categorias = ['EOT1', 'EOT1820']  # Daban error
        categorias = categorias_all # Todas las categorias
        
        # Añadimos las categorias al dataframe df0
        
        df0_predict= pd.DataFrame()
        df0_forecast= pd.DataFrame()
        
        i = 0
        for cat in categorias:
            i= i + 1
            try:
                logger.info('Categoría {}/{}: {}'.format(i, len(categorias), cat) )
                df0 = df.loc[df['COD'] == cat] # Filtro por categoria
                df0 = df0[['FECHA', 'VALOR']]
                dfst = transform_df_serie (df0, lg = True) # df a serie temporal
                trend_rc = hp_filter (dfst[0], lg= True) # Tendencia y recesiones
                regre = regression(dfst[0], lg = True) # Regresion
                
                # Predicción
                df_pre = regre[0].join(trend_rc) # Union prediccion-tendencia
                # Saco periodo
                df_pre['FECHA'] = df_pre.index
                #df_pre['PERIODO'] = df_pre['FECHA'].apply(lambda x:datetime.strftime(x,'%YM%m'))
                #Selecciono campos
                df_pre2 = df_pre[['FECHA','PREDICT', 'R2', 'TENDENCIA', 'RECESION']]
                df_pre2 = df_pre2.assign(COD = cat) # Añade columna COD
                frames = [df0_predict, df_pre2]  # 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(COD = cat) # Añade columna COD
                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())) 
                
            
            
        df0_predict = df0_predict.sort_values(by = ['COD','FECHA'], ascending = [True, True])
        df0_forecast = df0_forecast.sort_values(by = ['COD','FECHA'], ascending = [True, True])
        df = df.sort_values(by = ['COD','FECHA'], ascending = [True, True])
        
        columns_drop = ['FECHA', 'COD']
        df_pre = df0_predict.drop(columns_drop, axis=1) #Elimino columnas redundantes
        df = df.join(df_pre) # Union df con prediccion-tendencia
        
        logger.info('Return df_predict {} filas  y campos: \n{}'.format(len(df0_predict), df0_predict.dtypes))
        logger.info('Return df_forecast {} filas  y campos: \n{}'.format(len(df0_forecast), df0_forecast.dtypes))

        # Exporta el fichero predicción
        path = 'd_turismo_predict.xlsx'
        logger.info('Exportando: {}'.format(path))
        if export:
            df.to_excel(path, index = False)
            
        # Exporta el fichero forecast
        path = 'd_turismo_forecast.xlsx'
        logger.info('Exportando: {}'.format(path))
        if export:
            df0_forecast.to_excel(path, index = False)
               
        salida = df, 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()))   
        
        

def tendencia_recesiones_all (df, df_categorias, export = False, lg = False): # Predicción y forecast todas categorias 
     
    '''
    Calcula la predición, tendencia, recesion y forecast para todas las categorias
    Parameters
    ----------
    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_all = df_categorias['COD'].tolist()
        
        # Seleccionamos las categorias a procesar
        categorias = ['EOT1', 'EOT9', 'EOT17',  # Viajeros nacional
                      'EOT25', 'EOT33', 'EOT41',  # pernoctaciones nacional
                      'EOT1568', 'EOT1569', 'EOT1570',  # pernoctaciones cyl
                      'EOT1622', 'EOT1623']  # pernoctaciones zamora
        
        categorias = ['EOT1', 'EOT9', 'EOT17']  # Viajeros nacional
        categorias = categorias_all # Todas las categorias
        
        # Añadimos las categorias al dataframe df0
        
        df0_trend= pd.DataFrame()
        i = 0
        
        for cat in categorias:
            i = i+1
            try:
                logger.info('Categoría {}/{}: {}'.format(i, len(categorias), cat) )
                dforigen = df.loc[df['COD'] == cat] # Filtro por categoria
                dforigen = dforigen.reset_index(drop=True) # Elimino el indice
                df0 = dforigen[['FECHA', 'VALOR']]
                dfst = transform_df_serie (df0, lg = True) # df a serie temporal
                trend_rc = hp_filter (dfst[0], lg= True) # Tendencia y recesiones
                df1 = trend_rc[0] # Cojo el df
                df1 = df1.assign(COD = cat) # Añade columna COD
                df1['FECHA'] = df1.index # Añade columna FECHA
                df1 = df1.reset_index(drop=True) # Elimino indice para evitar redundancia FECHA
                df1 = df1.sort_values(by = ['COD','FECHA'], ascending = [True, True])
                dforigen = dforigen.sort_values(by = ['COD','FECHA'], ascending = [True, True])
                columns_drop = ['FECHA', 'COD']
                df2 = df1.drop(columns_drop, axis=1) #Elimino columnas redundantes
                df3 = dforigen.join(df2) # Union df con prediccion-tendencia   
                frames = [df0_trend, df3]  # Para concatenar todas las categorias
                df_cat = pd.concat(frames, join="outer").reset_index(drop=True) # Añade sin indice
                df0_trend = df_cat                
            except:
                  logger.info('Categoría {}/{}: {} *** ERROR ***'.format(i, len(categorias), cat) ) 
                  logging.exception('Error: [{}]'.format(sys.exc_info()))                                                                                         
            
        logger.info('df-tendencia:\nlen: {}, type: {}'.format(len(df0_trend), type(df0_trend)))     
        logger.info('df-tendencia - Campos: \n{}'.format(df0_trend.dtypes))

        # Exporta el fichero predicción
        path = 'd_turismo_tendencia.xlsx'
        logger.info('Exportando: {}'.format(path))
        if export:
            df0_trend.to_excel(path, index = False)
               
        salida = df0_trend
        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)
    logger.disabled = lg
    
    try:
        logger.info('Initium ->')
        # Bajamos el fichero json de la url
        iguales = download_url_json(lg)
        f = 'data.json' # Nombre del fichero bajado
        bd = '/var/www/html/lartica/db.sqlite3' # Path base de datos
        if iguales == False: # Solo transformamos si no son iguales
            # Transformamos el fichero json a  df y podemos exportarlo.
            df, df_categorias = transform_json_df(f, export = True, lg=lg)
            # Carga dataframes en base de datos de valores de turismo y categorias
            carga_df_bd (df, bd = bd, tabla = 'Turismo', lg=lg )
            carga_df_bd (df_categorias, bd = bd, tabla = 'TurismoCategoria', lg=lg)
            
            # Calculamos la tendencia y las recesiones. 
            #df = tendencia_recesiones_all (df, df_categorias, export = True, lg = lg)
            # Estas están incluidas en Predict por eso no las bajamos
            
            # Calculamos la predicción y el forecast y podemos exportarlo.
            df_predict, df_forecast = predict_forecast_all (df, df_categorias, export = True, lg=lg)
            # Carga dataframes en base de datos de predicción y forecast
            carga_df_bd (df_predict, bd = bd, tabla = 'TurismoPredict', lg=lg)
            carga_df_bd (df_forecast, bd = bd, tabla = 'TurismoForecast', lg=lg)
    
        logger.info('<- Finita est')
        return None

    except:  # catch *all* exceptions
        logging.exception('Error: [{}]'.format(sys.exc_info()))    

if __name__ == "__main__":
    start = time.perf_counter()
    assert sys.version_info >= (3, 7), "Script requires Python 3.7+."
    here = pathlib.Path(__file__).parent
    logger.info('Path:\n{}'.format(here))
    logger.info('Initium novum')  # Inicio
    # ************************************

    lgd = False # Disabled logging. False activa el logging
    main(lgd)
    
    # Test
    #df, df_categorias = transform_json_df('data.json', export = True, lg=lgd)
    
    # *************************************
    logger.info('Ave verum')  # Fin
    elapsed = time.perf_counter() - start
    logger.info(f"Program completed in {elapsed:0.5f} seconds.")
