""" Classes and functions to parse completed reconciliation reports and remember the resolutions of contracts. Also provides a way for the reconciler to check hold against previously resolved holds. *Last Updated: version 1.3 """ from . import drop_unnamed from ghlib.database.database_manager import SQLiteManager from pandas import DataFrame, Series, read_sql_query, read_excel, concat from logging import getLogger logger = getLogger(__name__) def normalize_cols(df: DataFrame) -> DataFrame: """ """ def process_resolutions(df: DataFrame) -> DataFrame: """ """ # Drop unnamed columns: drop_unnamed(df) # Works 'inplace' # Drop anything where resolution is blanks df: DataFrame = df[~df["Resolution"].isnull()] # Standardize the resolution df["Resolution"] = df["Resolution"].astype(str) df["Resolution"] = df["Resolution"].apply(lambda res: res.strip().lower()) # Check for multiple 'onhold_amount' columns cols: list[str] = list(df.keys()) mult_amounts: bool = True if "onhold_amount_ob" in cols else False if mult_amounts: # Create duplicates with the other amounts gp_amts: DataFrame = df[ ["contract_number", "onhold_amount_gp", "Resolution", "Notes" ]] df = df[ ["contract_number", "onhold_amount_ob", "Resolution", "Notes" ]] # Rename the amount columns and add the source gp_amts.rename(columns={"onhold_amount_gp":"onhold_amount"}, inplace=True) gp_amts["Source"] = "GP" df.rename(columns={"onhold_amount_ob":"onhold_amount"}, inplace=True) df["Source"] = "OB" # Combine them back together df: DataFrame = concat([df, gp_amts]) df["Type"] = "AmountMismatch" else: # Filter columns df = df[ ["Source", "contract_number", "onhold_amount", "Resolution", "Notes" ]] df["Type"] = "NoMatch" return df def save_recs(resolved_dataframes: list[DataFrame]): """ """ sqlManager: SQLiteManager = SQLiteManager("OnHold.db") with sqlManager.get_session() as session: conn = session.connection() df: DataFrame for df in resolved_dataframes: try: # Drop uneeded columns and filter only to resolved data df = process_resolutions(df) # Save to the database df.to_sql("Resolutions", conn, if_exists="append") except Exception as e: logger.exception(f"Could not save resolution dataframe: {e}") continue def get_prev_reconciled(contracts: list[str]) -> DataFrame: """ Get a DataFrame of previously reconciled contracts from an SQLite database. Args: contracts (list[str]): A list of contract numbers to check for previously reconciled contracts. Returns: DataFrame: A DataFrame of previously reconciled contracts, or an empty DataFrame if none are found. """ # Create a DB manager sqlManager: SQLiteManager = SQLiteManager("OnHold.db") # Create a temp table to hold this batches contract numbers # this table will be cleared when sqlManager goes out of scope temp_table_statement = """ CREATE TEMPORARY TABLE CUR_CONTRACTS (contract_numbers VARCHAR(11)); """ sqlManager.execute(temp_table_statement) # Insert the current contracts into the temp table insert_contracts = f""" INSERT INTO CUR_CONTRACTS (contract_numbers) VALUES {', '.join([f"('{cn}')" for cn in contracts])}; """ sqlManager.execute(insert_contracts) # Select previously resolved contracts res_query = """ SELECT r.* FROM Resolutions r JOIN CUR_CONTRACTS t ON r.contract_number = t.contract_number; """ resolved: DataFrame = sqlManager.execute(res_query, as_dataframe=True) return resolved if __name__ == "__main__": import argparse parser = argparse.ArgumentParser( prog="HoldReconcilerRecord", ) parser.add_argument("-i", "--input") args = parser.parse_args() # No Match no_match: DataFrame = read_excel(args.input, sheet_name="No Match") # Amount Mismatch amt_mm: DataFrame = read_excel(args.input, sheet_name="Amount Mismatch") save_recs(resolved_dataframes=[no_match, amt_mm])