Place removed rows of dash_table in a new table

Hi there,

I just discovered dash_table, and I am having some trouble figuring out the last step in my code.

I want to be able to load a csv or excel file and display it on the top.

The rows of the displayed table can then be selected and/or removed.

I would like the selected rows to populate a second table below, these are the “accepted rows”.

and the removed rows to populate a 3 table below that, these are the “rejected rows”.

I managed to do the loading and updating the accepted table, but still having trouble with the rejected one.

Basically I can only get one row back and each time its replaced by the new removed row.

How do make it work so that I keep the all rejected rows ?

input file: comma separated csv file

#=============================================================================#
#                                                                             #
#                                Dashboard                                    #
#                                                                             #
#=============================================================================#

# Perform imports here:
import pandas as pd
pd.options.mode.chained_assignment = None  # default  = 'warn'
import io
import base64
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table
import dash_bootstrap_components as dbc
import dash_daq as daq
from dash.dependencies import Input, Output, ALL, State

# Launch the application:
app = dash.Dash(external_stylesheets=[dbc.themes.CERULEAN])
app.config.suppress_callback_exceptions = True
server = app.server

#=============================================================================#
#                                                                             #
#                                 Layout                                      #
#                                                                             #
#=============================================================================#


content = dbc.Container(
    [
        dbc.Row(
            [
                dbc.Col(
                    [
                        dbc.Button("Select all",id='all-button',
                                   className="btn btn-outline-info")
                    ], md = 2, align='center',
                ),
                dbc.Col(
                    [
                        dcc.Upload(id='upload-data',
                                   children=html.Div(['Drag and Drop or ',
                                                      html.A('Select Files')]),
                                   style={
                                       'width': '100%',
                                       'height': '60px',
                                       'lineHeight': '60px',
                                       'borderWidth': '1px',
                                       'borderStyle': 'dashed',
                                       'borderRadius': '5px',
                                       'textAlign': 'center',
                                       'margin': '10px'
                                   },
                                  ),
                    ], md = 8, align='center',
                ),
                dbc.Col(
                    [
                        dbc.Button("Submit",id='submit-button', className="btn btn-warning"),
                    ], md = 2, align='center',
                ),
                
                
            ],justify='end',style={'background-color': '#fff7ed',}
        ),
        dbc.Row(
            [
                dbc.Col(id='output-data-upload',align='center',style={'height': '450px'}),
            ],justify='center',style={'background-color': '#fff7ed',}
        ),
        dbc.Row(
            [
                dbc.Col(
                    [
                        html.H4(['Accepted'],style={'color': '#000000'}),
                        html.Hr(style={'borderWidth': '0.3vh', 'borderColor': '#FEC700',}),
                    ],
                ),
            ],justify='left', style={'background-color': '#edffed',}, 
        ),
        dbc.Row(
            [
                dbc.Col(id='output-data-accepted',align='center',style={'height': '450px'}),
            ],justify='center',style={'background-color': '#edffed',}
        ),
        dbc.Row(
            [
                dbc.Col(
                    [
                        html.H4(['Rejected'],style={'color': '#000000'}),
                        html.Hr(style={'borderWidth': '0.3vh', 'borderColor': '#FEC700',}),
                    ]
                ),
            ],justify='left',style={'background-color': '#ffeded',}
        ),
        dbc.Row(
            [
                dbc.Col(id='output-data-rejected',align='center',style={'height': '450px'}),
                dcc.Store(id='rejected-memory'),
            ],justify='center',style={'background-color': '#ffeded',}
        ),

    ],
)

#=============================================================================#
#                               App Layout                                    #
#=============================================================================#

app.layout = html.Div([
    content,
])

#=============================================================================#
#                                                                             #
#                           Callbacks & Functions                             #
#                                                                             #
#=============================================================================#

#=============================================================================#
#                                Load Data                                    #
#=============================================================================#

def parse_contents(contents, filename):
    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            # Assume that the user uploaded a CSV file
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            # Assume that the user uploaded an excel file
            df = pd.read_excel(io.BytesIO(decoded))
    except Exception as e:
        print(e)
        return html.Div([
            'There was an error processing this file.'
        ])

    return html.Div([
        dash_table.DataTable(id='input-datatable',
            data=df.to_dict('records'),
            columns=[{'name': i, 'id': i} for i in df.columns],
            row_deletable=True,
            row_selectable='multi',
            selected_rows=[],
            fixed_rows={'headers': True},
            style_table={'overflowX': 'auto','height': 400},
            style_cell={'overflow': 'hidden',
                        'textOverflow': 'ellipsis',
                        'minWidth': '180px', 'width': '180px', 'maxWidth': '180px',
                       },
            tooltip_data=[
                {
                    column: {'value': str(value), 'type': 'markdown'}
                    for column, value in row.items()
                } for row in df.to_dict('records')
            ],
            tooltip_duration=None
        ),
    ])


@app.callback(Output('output-data-upload', 'children'),
              Input('upload-data', 'contents'),
              State('upload-data', 'filename'))
def update_output(contents, filename):
    if contents is not None:
        children = [parse_contents(contents, filename)]
        return children


    
#=============================================================================#
#                               Accept/Reject Rows                            #
#=============================================================================#

@app.callback(
    [Output('input-datatable', "selected_rows"),],
    [Input('all-button', 'n_clicks'),],
    [State('input-datatable', "derived_virtual_data"),]
)
def select_all(n_clicks, selected_rows):
    if selected_rows is None:
        return [[]]
    else:
        return [[i for i in range(len(selected_rows))]]

@app.callback(
        Output('output-data-accepted', 'children'),
        Input('input-datatable', 'selected_rows'),
        State('input-datatable', 'data'),
)
def update_accepted_table(selected_rows, rows):
    if selected_rows is None:
        selected_rows = []

    if rows is None:
        dff = pd.DataFrame()
    else:
        dff = pd.DataFrame(rows)

    dff = dff.iloc[selected_rows]
    
    table = html.Div([
        dash_table.DataTable(
            columns=[
                {"name": i, "id": i} for i in dff.columns
            ],
            data=dff.to_dict("rows"),
            fixed_rows={'headers': True},
            style_table={'overflowX': 'auto','height': 400},
            style_cell={'overflow': 'hidden',
                        'textOverflow': 'ellipsis',
                        'minWidth': '180px', 'width': '180px', 'maxWidth': '180px',
                       },
            tooltip_data=[
                {
                    column: {'value': str(value), 'type': 'markdown'}
                    for column, value in row.items()
                } for row in dff.to_dict('records')
            ],
            tooltip_duration=None
        ),
    ])

    return table

@app.callback(
        Output('output-data-rejected', 'children'),
        Input('input-datatable', 'data_previous'),
        State('input-datatable', 'data'),
)
def update_rejected_table(previous_rows, current_rows):
    if previous_rows is None:
        dff = pd.DataFrame()
    else:
        df_1row = pd.DataFrame([row for row in previous_rows if row not in current_rows])
        
        dff = df_1row
        
    table = html.Div([
        dash_table.DataTable(
            columns=[
                {"name": i, "id": i} for i in dff.columns
            ],
            data=dff.to_dict("rows"),
            fixed_rows={'headers': True},
            style_table={'overflowX': 'auto','height': 400},
            style_cell={'overflow': 'hidden',
                        'textOverflow': 'ellipsis',
                        'minWidth': '180px', 'width': '180px', 'maxWidth': '180px',
                       },
            tooltip_data=[
                {
                    column: {'value': str(value), 'type': 'markdown'}
                    for column, value in row.items()
                } for row in dff.to_dict('records')
            ],
            tooltip_duration=None
        ),
    ])

    return table

#=============================================================================#
#=============================================================================#

if __name__ == '__main__':
    app.run_server()