#! /usr/bin/env python3 from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from contextlib import closing from sql import sql_queries import snowflake.connector import psycopg2 import smtplib import argparse import sys import json import datetime with open('config.json', 'r') as f: config = json.load(f) sf_user = config['snowflake']["user"] sf_password = config['snowflake']["password"] sf_account = config['snowflake']["account"] sf_warehouse = config['snowflake']['warehouse'] db_name = config['postgres']["database"] db_user = config['postgres']["user"] db_password = config['postgres']["password"] db_host = config['postgres']["host"] db_port = config['postgres']["port"] local_db_name = config['failure_counter']["database"] local_db_user = config['failure_counter']["user"] local_db_password = config['failure_counter']["password"] local_db_host = config['failure_counter']["host"] local_db_port = config['failure_counter']["port"] failure_count_increase = "UPDATE failed_count SET failed_count = failed_count + 1, last = 'FAILED', last_updated = NOW() WHERE count_id = 'apen';" failure_count_to_zero = "UPDATE failed_count SET failed_count = 0, last = 'OK', last_updated = NOW() WHERE count_id = 'apen';" def create_arg_parser(): parser = argparse.ArgumentParser() parser.add_argument('-m', '--mail', nargs='+', action='store', dest='recipients', type=str) parser.add_argument('-p', '--print', action="store_const", const=True) return parser def send_mail(recipients: str, message: str, status: str) -> None: # Create a multipart message msg = MIMEMultipart() body_part = MIMEText(message, 'plain') msg['Subject'] = f"[{status}][PROD] Appreciation Engine" msg['From'] = "support.sme@dataart.com" msg['To'] = recipients # Add body to email msg.attach(body_part) # Create SMTP object server = smtplib.SMTP('relay1.dataart.com', 25) server.sendmail(msg['From'], msg['To'].split(','), msg.as_string()) server.quit() def snowflake_fetch_data(sql_query) -> dict: ctx = snowflake.connector.connect( user=sf_user, password=sf_password, account=sf_account, warehouse=sf_warehouse ) cs = ctx.cursor(snowflake.connector.DictCursor) try: cs.execute(sql_query) result_dict = cs.fetchall() finally: cs.close() ctx.close() return result_dict def postgres_fetch_data(sql_query) -> list: result_list = [] with closing(psycopg2.connect(database=db_name, user=db_user, password=db_password, host=db_host, port=db_port)) as connection: with connection.cursor() as cursor: cursor.execute(sql_query) columns = cursor.description rows = cursor.fetchall() for row in rows: tmp = {} for i in range(len(columns)): tmp[columns[i][0]] = row[i] result_list.append(tmp) return result_list def postgres_update_failure_count(sql_query): with closing(psycopg2.connect(database=local_db_name, user=local_db_user, password=local_db_password, host=local_db_host, port=local_db_port)) as connection: with connection.cursor() as cursor: cursor.execute(sql_query) connection.commit() def step_1() -> dict: result_dict = {} full_log = {} ### Check 2 RAW ################################################ tmp_pg_query_step_1 = postgres_fetch_data(sql_queries.pg_query) print(f"PG: - {tmp_pg_query_step_1}") #Check if tmp_pg_query_step_1: full_log["PG"] = {} result_dict["PG"] = {} for item in tmp_pg_query_step_1: full_log["PG"].update({item['REPORT_DATE'].strftime("%Y-%m-%d"): item['MISSING_REPORTS'].lower()}) print(f"{item['REPORT_DATE']} - {item['MISSING_REPORTS']}") result_dict["PG"].update({item['REPORT_DATE'].strftime("%Y-%m-%d"): item['MISSING_REPORTS'].lower()}) else: full_log["PG"] = {'OK': 'Query returned 0 records'} return result_dict, full_log def step_1_1() -> dict: result_dict = {} full_log = {} ### Check 2 RAW ################################################ tmp_pg_query_step_1_1 = postgres_fetch_data(sql_queries.pg_query_1_1) print(f"PG_1_1: - {tmp_pg_query_step_1_1}") #Check if tmp_pg_query_step_1_1: full_log["PG_1_1"] = {} result_dict["PG_1_1"] = {} for item in tmp_pg_query_step_1_1: full_log["PG_1_1"].update({item['REPORT_DATE'].strftime("%Y-%m-%d"): item['NOT_FULLY_DISASSEMBLED_REPORTS'].lower()}) print(f"{item['REPORT_DATE']} - {item['NOT_FULLY_DISASSEMBLED_REPORTS']}") result_dict["PG_1_1"].update({item['REPORT_DATE'].strftime("%Y-%m-%d"): item['NOT_FULLY_DISASSEMBLED_REPORTS'].lower()}) else: full_log["PG_1_1"] = {'OK': 'Query returned 0 records'} return result_dict, full_log def step_1_2() -> dict: result_dict = {} full_log = {} ### Check 2 RAW ################################################ tmp_pg_query_step_1_2 = postgres_fetch_data(sql_queries.pg_query_1_2) print(f"PG_1_2: - {tmp_pg_query_step_1_2}") #Check if tmp_pg_query_step_1_2: full_log["PG_1_2"] = {} result_dict["PG_1_2"] = {} for item in tmp_pg_query_step_1_2: full_log["PG_1_2"].update({item['REPORT_DATE'].strftime("%Y-%m-%d"): item['MISSING_AE_CONTEXTS'].lower()}) print(f"{item['REPORT_DATE']} - {item['MISSING_AE_CONTEXTS']}") result_dict["PG_1_2"].update({item['REPORT_DATE'].strftime("%Y-%m-%d"): item['MISSING_AE_CONTEXTS'].lower()}) else: full_log["PG_1_2"] = {'OK': 'Query returned 0 records'} return result_dict, full_log def step_2() -> dict: result_dict = {} full_log = {} ### Check 4 RAW vs. EXP ######################################## #Data tmp_sf_query_step_2 = snowflake_fetch_data(sql_queries.sf_query) print(f"SF - {tmp_sf_query_step_2}") #Check if tmp_sf_query_step_2: full_log["SF"] = {} result_dict["SF"] = {} for item in tmp_sf_query_step_2: full_log["SF"].update({item['REPORT_DATE'].strftime("%Y-%m-%d"): item['MISSING_REPORTS'].lower()}) print(f"{item['REPORT_DATE']} - {item['MISSING_REPORTS']}") result_dict["SF"].update({item['REPORT_DATE'].strftime("%Y-%m-%d"): item['MISSING_REPORTS'].lower()}) else: full_log["SF"] = {'OK': 'Query returned 0 records'} return result_dict, full_log ################# run the all steps ###################### def run_steps() -> dict: message_dict = {'name':"[PROD] Appreciation Engine",'status':"",'date':"", 'info':"", 'details':"", 'full_log':""} # set date according to sql searching date, currently it's `today` message_dict["date"] = (datetime.datetime.now() - datetime.timedelta(days=1)).strftime("%Y-%m-%d") result_dict_1, monitoring_1_log = step_1() result_dict_1_1, monitoring_1_1_log = step_1_1() result_dict_1_2, monitoring_1_2_log = step_1_2() result_dict_2, monitoring_2_log = step_2() message_dict['full_log'] = {**monitoring_1_log, **monitoring_1_1_log, **monitoring_1_2_log, **monitoring_2_log} # if result_dict_7 or result_dict_2 or result_dict_1: if result_dict_2 or result_dict_1 or result_dict_1_1 or result_dict_1_2: postgres_update_failure_count(failure_count_increase) if result_dict_1: # Data is partially synced with Fivetran message_dict['status'] = "FAILED" message_dict['info'] = "Some UOWs are not completed." message_dict['details'] = result_dict_1 message_dict['step'] = "1" elif result_dict_1_1: # Data is synced with Fivetran and partially available in API message_dict['status'] = "FAILED" message_dict['info'] = "Some UOWs are not completed." message_dict['details'] = result_dict_1_1 message_dict['step'] = "1_1" elif result_dict_1_2: # Data is synced with Fivetran and partially available in API message_dict['status'] = "FAILED" message_dict['info'] = "Some AE Contexts are missing." message_dict['details'] = result_dict_1_2 message_dict['step'] = "1_2" elif result_dict_2: # Data is synced with Fivetran and partially available in API message_dict['status'] = "FAILED" message_dict['info'] = "Partially ingested to Snowflake." message_dict['details'] = result_dict_2 message_dict['step'] = "2" # if result_dict_7: # message_dict['details'].update(result_dict_7) else: message_dict['status'] = "OK" message_dict['info'] = "Ingested to Snowflake" postgres_update_failure_count(failure_count_to_zero) return message_dict if __name__ == '__main__': # # check command line parameters and print or email result parser = create_arg_parser() arg_list = parser.parse_args() if len(sys.argv[1:]) > 0: result = run_steps() with open('/tmp/Appreciation_Engine_daily_log.json', 'w') as json_file: json.dump(result, json_file, indent=4) if arg_list.recipients: recipients = arg_list.recipients[0] send_mail(recipients=recipients, message=json.dumps(result, indent=4), status=result['status']) if arg_list.print: print(json.dumps(result, indent=4)) else: parser.print_help()