import pandas as pd import datetime import config from scripts import del_dup_analytics as del_model from scripts import load_missing_analytics as load_model cnx = config.credentials.cursor() REPROCESS_QUERY_PATH = 'queries/' STORES_MANIFEST = 'change_log/{delete_load}_log/' \ '{delete_load}_dates_stores_manifest_{datetime}.txt' DAYS_BACK = 100 def get_dates_stores_to_reprocess(days_back): """Get full dataframe of date/store combinations to reprocess Args: days_back (int): Number of dates to look back Returns: DataFrame with date/store combinations to reprocess """ sql = config.load_query('dates_stores_to_reprocess.sql', REPROCESS_QUERY_PATH) rows = cnx.execute(sql.format(days_back=days_back)).fetchall() return pd.DataFrame(rows, columns=[x[0] for x in cnx.description]) def trim_dates_stores_to_delete_load(delete_load, full_dates_stores_to_reprocess_df): """Trim full dataframe of dates/stores to reprocess to either just the dates/stores to delete or just dates/stores to load. Also remove duplicates date store combinations Args: delete_load (string): String of either delete or load full_dates_stores_to_reprocess_df (dataframe): Full Dataframe of all dates/stores to delete and or load Returns: Trimmed DataFrame with only date/store combinations to either delete or load """ if delete_load == 'load': dates_stores_to_reprocess_df = full_dates_stores_to_reprocess_df.loc[:, ('FA_STOREID', 'FA_PROCESSEDDAYTIME', 'TABLE_SOURCE')]\ .dropna().drop_duplicates() else: dates_stores_to_reprocess_df = full_dates_stores_to_reprocess_df.loc[:, ('AGG_STOREID', 'AGG_PROCESSEDDAYTIME')] \ .dropna().drop_duplicates() return dates_stores_to_reprocess_df def check_available_data(delete_load, dates_stores_to_reprocess_df): """"Check that data is available to delete/load Args: delete_load (string): String of either delete or load dates_stores_to_reprocess_df (dataframe): Trimmed Dataframe of dates/stores to either delete and or load Returns: The string 'empty' if the Dataframe provided is empty """ if dates_stores_to_reprocess_df.empty: print("\nThere are no dates/stores to {delete_load} \n" .format(delete_load=delete_load)) return 'empty' def create_loaded_stores_dates_manifest(delete_load, dates_stores_to_reprocess_df): """Creates csv file of dates/stores to be deleted or loaded Args: delete_load (string): String of either delete or load dates_stores_to_reprocess_df (dataframe): Trimmed Dataframe of dates/stores to either delete and or load Returns: CSV file """ return dates_stores_to_reprocess_df.to_csv(STORES_MANIFEST.format( delete_load=delete_load, datetime=datetime.datetime.now())) def confirm_delete_load(delete_load, dates_stores_to_reprocess_df): """"Force user to confirm delete or load Args: delete_load (string): String of either delete or load dates_stores_to_reprocess_df (dataframe): Trimmed Dataframe of dates/stores to either delete and or load Returns: Continues process only if user inputs 'y' """ print("We will {delete_load} the following dates/store combinations " "(Some rows may have been cut off. To see the full list, please " "check the `change_log` directory): \n\n" .format(delete_load=delete_load) + str(dates_stores_to_reprocess_df)) user_input = input("\nDo you wish to continue?(Press y for yes): ") if user_input == 'y': return else: exit() def execute_delete_load(delete_load, dates_stores_to_reprocess_df): """Create SQL and executes Delete or Load queries Args: delete_load (string): String of either delete or load dates_stores_to_reprocess_df (dataframe): Trimmed Dataframe of dates/stores to either delete and or load """ if delete_load == 'delete': constructed_delete = \ del_model.construct_delete_statement(dates_stores_to_reprocess_df) del_model.delete_dates_stores(constructed_delete) else: constructed_load = \ load_model.construct_load_statement(dates_stores_to_reprocess_df) load_model.load_dates_stores(constructed_load) def main(): """Main function that does the following steps: 1. identify dates/stores that are either incorrectly duplicated or missing from the Looker Analytics tables 2. creates log of dates/stores that are to be deleted or loaded 3. generate sql to delete and or load incorrect data 4. execute delete and or load """ delete_load_ls = ['delete', 'load'] full_dates_stores_to_reprocess_df = get_dates_stores_to_reprocess(DAYS_BACK) for delete_load in delete_load_ls: dates_stores_to_reprocess_df = trim_dates_stores_to_delete_load( delete_load, full_dates_stores_to_reprocess_df) check_available = check_available_data(delete_load, dates_stores_to_reprocess_df) if check_available != 'empty': create_loaded_stores_dates_manifest(delete_load, dates_stores_to_reprocess_df) confirm_delete_load(delete_load, dates_stores_to_reprocess_df) execute_delete_load(delete_load, dates_stores_to_reprocess_df) if __name__ == '__main__': main() exit()