# module level doc-string
__doc__ = """
General logging utility module for KDS

@author: Manoj Bonam
Created on Thu Mar 11 14:57:55 2021

DEBUG: Detailed information, typically of interest only when diagnosing problems.
INFO: Confirmation that things are working as expected.
WARNING: An indication that something unexpected happened, or indicative of some problem in the near future (e.g. ‘disk space low’). The software is still working as expected.
ERROR: Due to a more serious problem, the software has not been able to perform some function.
CRITICAL: A serious error, indicating that the program itself may be unable to continue running.

################################################# INSTRUCTIONS #########################################################
# In your program always import the entire kdsutil module first, to properly set the local variables.                  #
# from kdslib import kdsutil                                                                                           #
# The main class can be instantiated like this: loggingutil = kdsutil.Generate_Logger(**kwargs)                        #
# The class variables can be accessed in this format: loggingutil.logger.info('Hello World')                           #
# The class methods can be accessed in this format: loggingutil.log_info('Hello World')                                #
########################################################################################################################

Library: https://docs.python.org/3/library/logging.html

"""

import os
import logging
import yaml
import sys
import datetime
import traceback
import teradata
import urllib.parse as urllib
import pyodbc
from sqlalchemy import create_engine

# Import smtplib for the actual sending function
import smtplib
# Import the email modules we'll need
from email.message import EmailMessage


#================================================================
# Check if environment variable is set
#================================================================
try:
    env = os.environ['kds-python-config']
    print("User's Environment variable:" + str(env))
except:
    print("Configuration file env variable is not set.")
    print("set kds-python-config variable")
    sys.exit(1)
    
#===============================================================
# If environment variable is set then read the config yaml file
#===============================================================
with open(env,"r") as yamlConfig:
    cfg = yaml.safe_load(yamlConfig)

#################################################################
# Generate a logger 
#################################################################
class Generate_Logger:  
    """
    When instantiated, creates a logging object.
    
    """    
    #Main constructor of the Generate_Logger class.
    def __init__(self,*args, **kwargs):
        self.logger = logging.getLogger(__name__)
        if kwargs.get('jobFrequency') is None:
            self.timeStampObj = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
        elif kwargs.get('jobFrequency').lower().strip() == 'high':
            self.timeStampObj = datetime.datetime.now().strftime('%Y%m%d')
        else:
            self.timeStampObj = datetime.datetime.now().strftime('%Y%m%d%H%M%S') 
        self.programName = kwargs.get('programName')
        self.fileName = cfg.get('KDS_LOG').get('log_loc') + self.timeStampObj + "_" + self.programName + ".log"
        self.appname = kwargs.get('appname')
        self.loglevel= cfg.get(self.appname).get('log_level')
        
        if self.loglevel.lower() == "info":
            self.logger.setLevel(logging.INFO)
        elif self.loglevel.lower() == "warning":
            self.logger.setLevel(logging.WARNING)
        elif self.loglevel.lower() == "debug":
            self.logger.setLevel(logging.DEBUG)
        elif self.loglevel.lower() == "error":
            self.logger.setLevel(logging.ERROR)

        if self.logger.handlers:
            self.logger.handlers = []
   
        self.file_handler = logging.FileHandler(self.fileName)
        self.formatter = logging.Formatter('%(asctime)s: %(levelname)s: %(name)s: %(message)s')

        self.file_handler.setFormatter(self.formatter)

        self.stream_handler = logging.StreamHandler()
        self.stream_handler.setFormatter(self.formatter)

        self.logger.addHandler(self.file_handler)
        self.logger.addHandler(self.stream_handler)
        
    # Class methods that can be accessed using the class instance.
    def log_info(self, msg):
       self.logger.info(msg)
    
    def log_error(self, msg):
        self.logger.error(msg)
    
    def log_debug(self, msg):
        self.logger.debug(msg)  
     
    # This method cleans up the logging object/handlers and exits the log file.
    def delete(self):
        handlers = self.logger.handlers[:]
        for handler in handlers:
            handler.flush()
            handler.close()
            self.logger.removeHandler(handler)   


#################################################################
# Templates for database connections.
# Depending on the type of connection, proper key word arguments 
# must be passed to get the connection variable.
#################################################################

def get_kds_teradata_conn(**kwargs):
    udaExec = teradata.UdaExec (appName='kds', version="2", configureLogging=False, logConsole = False)
    conn = udaExec.connect(DSN=kwargs.get('ODBC_name'),method="odbc")

    return conn 

def get_pyodbc_windowsauth_conn(**kwargs):
   
    params_str = urllib.quote("""Driver={driver};Server={server};Database={database};Trusted_Connection=yes;""".format(driver=kwargs.get('driver'),
                                                   server=kwargs.get('server'),
                                                   database=kwargs.get('database')))
    conn_str = 'mssql+pyodbc:///?odbc_connect={params_str}'.format(params_str=params_str)    
    
    return conn_str

def get_pyodbc_windowsauth_sa_engine(**kwargs):
   
    params_str = urllib.quote("""Driver={driver};Server={server};Database={database};Trusted_Connection=yes;""".format(driver=kwargs.get('driver'),
                                                   server=kwargs.get('server'),
                                                   database=kwargs.get('database')))
    conn_str = 'mssql+pyodbc:///?odbc_connect={params_str}'.format(params_str=params_str)
    
    sqlsaengine = create_engine(conn_str, fast_executemany=True)
    
    return sqlsaengine

def get_pyodbc_windowsauth_cnxn(**kwargs):

    params_str = ("Driver={driver};Server={server};Database={database};Trusted_Connection={trusted_connection};").format(
        driver=kwargs.get('driver'),
        server=kwargs.get('server'),
        database=kwargs.get('database'),
        trusted_connection="yes")
        
    conn = pyodbc.connect(params_str)   
    
    return conn    

def get_pyodbc_credentials_conn(**kwargs):
    
    if  kwargs.get('username') == None:
        user_ = kwargs.get('user')
    else:
        user_ = kwargs.get('username')
    
    params_str = ("Driver={driver};Server={server};Database={database};Uid={user};Pwd={password};").format(
        driver=kwargs.get('driver'),
        server=kwargs.get('server'),
        database=kwargs.get('database'),
        user=user_,
        password=kwargs.get('password'))
    
    conn = pyodbc.connect(params_str)
    
    return conn

def get_pyodbc_credentials_sa_engine(**kwargs):
    
    if  kwargs.get('username') == None:
        user_ = kwargs.get('user')
    else:
        user_ = kwargs.get('username')
    
    params_str = urllib.quote("""Driver={driver};Server={server};Database={database};Uid={user};Pwd={password};""".format(
                                                   driver=kwargs.get('driver'),
                                                   server=kwargs.get('server'),
                                                   database=kwargs.get('database'),
                                                   user=user_,
                                                   password=kwargs.get('password')
                                                   ))
    conn_str = 'mssql+pyodbc:///?odbc_connect={params_str}'.format(params_str=params_str)
   
  
    
    sqlACEngine = create_engine(conn_str, fast_executemany=True)
    
    return sqlACEngine

def get_pyodbc_azure_ad_auth_conn(**kwargs):
    params_str = ("Driver={driver};Server={server};Database={database};Authentication={auth};").format(
        driver=kwargs.get('driver'),
        server=kwargs.get('server'),
        database=kwargs.get('database'),
        auth=kwargs.get('auth'))
    
    conn = pyodbc.connect(params_str)
    
    return conn
    
def get_pyodbc_azure_credentials_conn(**kwargs):
    
    if  kwargs.get('username') == None:
        user_ = kwargs.get('user')
    else:
        user_ = kwargs.get('username')
        
    params_str = ("Driver={driver};Server={server};Database={database};Uid={username};Pwd={password};").format(
        driver=kwargs.get('driver'),
        server=kwargs.get('server'),
        database=kwargs.get('database'),
        username=user_,
        password=kwargs.get('password'))
    
    conn = pyodbc.connect(params_str)
    
    return conn   

def senderroremail(**kwargs):
    
    programName_ = kwargs.get('programName')

    msg = EmailMessage()
    msg.set_content("There was an error running " + programName_ + os.linesep + kwargs.get('errStackTrace'))
    msg['Subject'] = 'Error executing ' + programName_
    msg['From'] = cfg.get('EMAIL').get('reply')
    msg['To'] = cfg.get(kwargs.get('appName')).get('support_email')
    # Send the message via our own SMTP server.
    s = smtplib.SMTP(cfg.get('EMAIL').get('SMTP'))
    s.send_message(msg)
    s.quit()
 
#################################################################
# Main function equivalent. 
# Only gets executed when this file is run independently  
#################################################################
if __name__ == '__main__':          
    
   _filename = sys.argv[0].split(os.path.sep)[-1].split('.')[0]
   print(_filename)
   
   loggerdetails= {'appname':'KDS_UTIL'}
   #Create a logging instance of the Generate_Logger class.
   logutilobj = Generate_Logger(**loggerdetails)
   
   try:
       #Invoke the log_info class method for the logutilobj.
       logutilobj.log_info('Main function test begin.')
       result = 10/0
    
   except:
       err = sys.exc_info()[0]
       #Invoke the log_error class method for the logutilobj.
       logutilobj.log_error('Tried to divide by zero')
       errStackTrace = traceback.format_exc(limit=None, chain=True)
       #Invoke the log_error class method for the logutilobj.
       logutilobj.log_error(errStackTrace)
       logutilobj.delete() 
       print('completed the except section')