# AUTOGENERATED! DO NOT EDIT! File to edit: 00_core.ipynb (unless otherwise specified).

__all__ = ['get_added', 'get_deleted', 'get_modified', 'get_delta', 'log_delta', 'build_latest']

# Cell
import pandas as pd
import numpy as np

# Cell
def get_added(df_old, df_new, unique_id, trans_col="transaction", trans_vaL="added"):
    """Returns the added rows in df_new

    Parameters
    ----------
        df_old : pd.DataFrame
            dataframe with previous information
        df_new: pd.DataFrame
            dataframe with new information
        unique_id: str or list
            unique identifier(s)
        trans_col: str
            name of column to track transaction (default is "transaction")
        trans_val: str
            name of value to reflect transaction status (default is "added")

    Returns
    -------
    pd.DataFrame
        dataframe that contains added rows
    """

    cols = list(df_old.columns)
    new_rows = (pd
                .merge(df_old, df_new, how="outer", on=unique_id, indicator=True, suffixes=("_foo",""))
                .query('_merge == "right_only"')
                )
    new_rows = new_rows[cols]
    new_rows[trans_col] = trans_vaL
    return new_rows

# Cell
def get_deleted(df_old, df_new, unique_id, trans_col="transaction", trans_val="deleted"):
    """Returns the deleted rows that are not any longer in df_new

    Parameters
    ----------
        df_old : pd.DataFrame
            dataframe with previous information
        df_new: pd.DataFrame
            dataframe with new information
        unique_id: str or list
            unique identifier(s)
        trans_col: str
            name of column to track transaction (default is "transaction")
        trans_val: str
            name of value to reflect transaction status (default is "deleted")
    Returns
    -------
    pd.DataFrame
        dataframe that contains deleted rows
    """

    cols = list(df_old.columns)
    deleted_rows = (pd
                .merge(df_new, df_old, how="outer", on=unique_id, indicator=True, suffixes=("_foo",""))
                .query('_merge == "right_only"')
                )
    deleted_rows = deleted_rows[cols]
    deleted_rows[trans_col] = trans_val
    return deleted_rows

# Cell
def get_modified(df_old, df_new, unique_id, added_rows=None, trans_col="transaction", trans_val="modified"):
    """Returns the modified rows in df_new

    Parameters
    ----------
        df_old : pd.DataFrame
            dataframe with previous information
        df_new: pd.DataFrame
            dataframe with new information
        unique_id: str or list
            unique identifier(s)
        added_rows: pd.DataFrame
            added rows from calling get_added function
        trans_col: str
            name of column to track transaction (default is "transaction")
        trans_val: str
            name of value to reflect transaction status (default is "modified")
    Returns
    -------
    pd.DataFrame
        dataframe that contains modified rows
    """

    cols = list(df_new.columns)
    if added_rows is not None:
        df_new = df_new[~df_new.isin(list(added_rows[unique_id].values))].dropna()
    modified_rows = df_old.merge(df_new, indicator=True, how='outer')
    modified_rows = modified_rows[modified_rows['_merge'] == 'right_only']
    modified_rows = modified_rows[cols]
    modified_rows[trans_col] = trans_val
    return modified_rows

# Cell
def get_delta(df_old, df_new, unique_id, sort_by=None, trans_col="transaction", trans_val_added="added", trans_val_deleted="deleted", trans_val_modified="modified"):
    """Returns delta (added, deleted, modified) betwen df_old and df_new

    Parameters
    ----------
        df_old : pd.DataFrame
            dataframe with previous information
        df_new: pd.DataFrame
            dataframe with new information
        unique_id: str or list
            unique identifier(s)
        sort_by: str or list
            cols to sort result
        added_rows: pd.DataFrame
            added rows from calling get_added function
        trans_col: str
            name of column to track transaction (default is "transaction")
        trans_val_added: str
            name of value to reflect transaction status (default is "added")
        trans_val_deleted: str
            name of value to reflect transaction status (default is "deleted")
        trans_val_modified: str
            name of value to reflect transaction status (default is "modified")
    Returns
    -------
    pd.DataFrame
        dataframe that contains delta between df_old and df_new
    """

    added_rows = get_added(df_old=df_old, df_new=df_new, unique_id=unique_id,trans_col=trans_col, trans_vaL=trans_val_added)
    deleted_rows = get_deleted(df_old=df_old, df_new=df_new, unique_id=unique_id, trans_col=trans_col, trans_val=trans_val_deleted)
    modified_rows = get_modified(df_old=df_old, df_new=df_new, unique_id=unique_id, added_rows=added_rows, trans_col=trans_col, trans_val=trans_val_modified)
    df = added_rows.append([deleted_rows, modified_rows])
    if sort_by:
        df = df.sort_values(by=sort_by)
    return df

# Cell
def log_delta(df_log, df_old, df_new, unique_id, sort_by=None, trans_col="transaction", trans_val_added="added", trans_val_deleted="deleted", trans_val_modified="modified"):
    """Returns logged delta (added, deleted, modified) betwen df_old and df_new

    Parameters
    ----------
        df_log : pd.DataFrame
            dataframe with logged delta - if no log exists yet set df_log=None
        df_old : pd.DataFrame
            dataframe with previous information
        df_new: pd.DataFrame
            dataframe with new information
        unique_id: str or list
            unique identifier(s)
        sort_by: str or list
            cols to sort result
        added_rows: pd.DataFrame
            added rows from calling get_added function
        trans_col: str
            name of column to track transaction (default is "transaction")
        trans_val_added: str
            name of value to reflect transaction status (default is "added")
        trans_val_deleted: str
            name of value to reflect transaction status (default is "deleted")
        trans_val_modified: str
            name of value to reflect transaction status (default is "modified")
    Returns
    -------
    pd.DataFrame
        dataframe that contains logged delta
    """

    if df_log is None:
        df_log = df_old
        df_log[trans_col] = trans_val_added
    else:
        subset = list(df_log.columns)
        subset.remove(trans_col)
        added_rows = get_added(df_old=df_old, df_new=df_new, unique_id=unique_id, trans_col=trans_col, trans_vaL=trans_val_added)
        deleted_rows = get_deleted(df_old=df_old, df_new=df_new, unique_id=unique_id, trans_col=trans_col, trans_val=trans_val_deleted)
        modified_rows = get_modified(df_new=df_new, df_old=df_old, unique_id=unique_id, added_rows=added_rows, trans_col=trans_col, trans_val=trans_val_modified)
        df_log = df_log.append(modified_rows, ignore_index=True)
        df_log = df_log.drop_duplicates(subset=subset, keep="first")
        df_log = df_log.append(added_rows, ignore_index=True)
        df_log = df_log.append(deleted_rows, ignore_index=True)
    if sort_by:
        df_log = df_log.sort_values(by=sort_by)
    return df_log

# Cell
def build_latest(df_old, df_new, unique_id, sort_by=None, trans_col="transaction"):
    """Returns logged the latest dataframe based on changes from new dataframe (added, removed, modified)

    Parameters
    ----------
        df_old : pd.DataFrame
            dataframe with previous information
        df_new: pd.DataFrame
            dataframe with new information
        unique_id: str or list
            unique identifier(s)
        sort_by: str or list
            cols to sort result
        trans_col: str
            name of column to track transaction (default is "transaction")
    Returns
    -------
    pd.DataFrame
        dataframe that contains latest state
    """
    added_rows = get_added(df_old=df_old, df_new=df_new, unique_id=unique_id)
    deleted_rows = get_deleted(df_old=df_old, df_new=df_new, unique_id=unique_id)
    modified_rows = get_modified(df_old=df_old, df_new=df_new, unique_id=unique_id, added_rows=added_rows)
    df_old = df_old[df_old[unique_id].isin(list(deleted_rows[unique_id].values)) == False]
    df_old = df_old[df_old[unique_id].isin(list(modified_rows[unique_id].values)) == False]
    df_latest = df_old.append(added_rows)
    df_latest = df_latest.append(modified_rows)
    df_latest.drop(columns=[trans_col], inplace=True)
    if sort_by: df_latest = df_latest.sort_values(by=sort_by)
    return df_latest