Filter selection only stays selected for 1 second, something then re-selects all filters

Hey there,

I’ve got to be doing something wrong with my callback, but whenever I specify data to filter for in any column, that selection is only there for ~1 second. Right after making the selection, all options are then automatically selected again.

@app.callback([Output("plant-processing-grid", "rowData"),
               Output("plant-processing-grid", "pinnedBottomRowData")],
              [Input('date-range-picker', 'start_date'),
               Input('date-range-picker', 'end_date'),
               Input('plant-processing-grid', 'filterModel')])

def update_grid(start_date, end_date, filter_model):
    productionStartDate = start_date.replace('-', '')
    productionEndDate = end_date.replace('-', '')

    data = get_data(productionStartDate, productionEndDate)

    if filter_model:
        for column, filter_options in filter_model.items():
            values = filter_options.get('values', [])
            if values:
                data = data[data[column].isin(values)]

    total_cases = data['totalCases'].sum()
    total_pounds = data['totalPounds'].sum()

    pinned_bottom_data = [{"cvProductCategory": "Total", "totalCases": total_cases, "totalPounds": total_pounds}]

    grid_data = data.to_dict("records")

    return grid_data, pinned_bottom_data

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

Hi @jimbob and welcome to the Dash community :slightly_smiling_face:

It would be helpful if you could create a complete minimal example that replicates the issues. More info on how to do that here:

Hi @AnnMarieW, thank you for the welcome! Hopefully this helps clarify the issue:

On the dashboard all options are default selected for all filters, and the pinned bottom row performs a calculation for total pounds as well as total cases.

I can click (Select All) to de-select all entries, in that column, then just choose what I’d like to filter for, and the pinned bottom row will recalculate the total pounds/cases only for the data that’s been filtered on the table.

The goal is for this to be the extent of what filtering + total row calculation does. However, within a second of that last total row calculation being performed (took me a couple tries to get the screenshot), my filtered selection “resets” for a reason I can’t identify, and all options become selected again. The total row does recalculate properly, but seeing the grand total again isn’t what we need.

Changing the start date, end date, applying any filter or refreshing the page are the only four things that should update the table. With only the three inputs listed in the callback, I thought that’s how this would work- but something is obviously resetting the filter applied.

Really appreciate you taking a look at this with me!

hi @jimbob
can you share your full code and data with us so we can run it and replicate the issue on our computer?

Hi @adamschroeder, below is the current code.

import dash
from dash import dcc, html, Input, Output, State
import dash_ag_grid as ag
import pandas as pd
import pyodbc
import os
from datetime import datetime, timedelta
from django_plotly_dash import DjangoDash

app = DjangoDash('plant_processing_date_range', add_bootstrap_links=True)

def get_data(productionStartDate, productionEndDate):
    conn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};'
                          f'SERVER={os.getenv("INNOVA_SERVER")};'
                          'DATABASE=ClearView;'
                          f'UID={os.getenv("CLEARVIEW_SQL_USER")};'
                          f'PWD={os.getenv("CLEARVIEW_SQL_PASSWORD")}')

    query = f"EXEC sp_cv_SelectPlantProcessingReportDateRange @productionStartDate='{productionStartDate}', @productionEndDate='{productionEndDate}'"

    df = pd.read_sql(query, conn)

    custom_sort_order_category = {
        "Fresh Branded": 1,
        "Fresh Private Label": 2,
        "Fresh Prvt Label Red/Golden": 3,
        "Fresh Outsourced": 4,
        "Frozen Branded": 5,
        "Frozen Prvt Label Red/Golden": 6,
        "Fresh Grade 2": 7,
        "Frozen Grade 2": 8,
        "Bulk": 9,
    }

    df['sorting_key_category'] = df['cvProductCategory'].map(custom_sort_order_category)
    df = df.sort_values(by=['sorting_key_category', 'cvProductStyle'])

    df['sorting_key_style'] = df.groupby('cvProductCategory').cumcount()

    return df

def calculate_totals(data):
    sum_values = data[["totalPounds", "totalCases"]] if "totalPounds" in data.columns and "totalCases" in data.columns else data
    return {
        "cvProductCategory": "Total",
        "inMaterialCode": "",
        "cvProductDescription": "",
        "cvPackStyle": "",
        "totalPounds": sum_values["totalPounds"].sum() if "totalPounds" in sum_values.columns else 0,
        "totalCases": sum_values["totalCases"].sum() if "totalCases" in sum_values.columns else 0,
        "cvProductStyle": "",
    }


app.layout = html.Div(
    [
        html.H1("Plant Processing Report by Date Range"),
        html.Hr(),
        dcc.Store(id="plant-processing-data"),  

        html.Div([
            dcc.DatePickerRange(
                id='date-range-picker',
                display_format='MM/DD/YYYY',
                start_date=(datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d'),
                end_date=datetime.now().strftime('%Y-%m-%d'),
            ),
        ], style={'margin-bottom': '20px'}),  # Apply margin to this div

        html.Div([ 
            ag.AgGrid(
                id="plant-processing-grid",
                style={"height": '80vh'},
                enableEnterpriseModules=True,
                rowData=None,
                defaultColDef={"flex": 1}, 
                columnDefs=[
                    {"headerName": "Product Category", "field": "cvProductCategory", "sortable": True, "filter": True},
                    {"headerName": "Item #", "field": "inMaterialCode", "sortable": True, "filter": True},
                    {"headerName": "Description", "field": "cvProductDescription", "sortable": True, "filter": True},
                    {"headerName": "Pack Size", "field": "cvPackStyle", "sortable": True, "filter": True},
                    {"headerName": "Total Pounds", "field": "totalPounds", "sortable": True, "filter": True},
                    {"headerName": "Total Cases", "field": "totalCases", "sortable": True, "filter": True},
                    {"headerName": "Product Style", "field": "cvProductStyle", "sortable": True, "filter": True},
                ],
                # Add the pinnedBottomRowData attribute
                dashGridOptions={'pinnedBottomRowData': [calculate_totals(pd.DataFrame())]},
            ),
        ], style={'margin-top': '20px'}),  # Apply margin to this div
    ]
)

@app.callback([Output("plant-processing-grid", "rowData"),
               Output("plant-processing-grid", "pinnedBottomRowData")],
              [Input('date-range-picker', 'start_date'),
               Input('date-range-picker', 'end_date'),
               Input('plant-processing-grid', 'filterModel')])

def update_grid(start_date, end_date, filter_model):
    productionStartDate = start_date.replace('-', '')
    productionEndDate = end_date.replace('-', '')

    data = get_data(productionStartDate, productionEndDate)

    if filter_model:
        for column, filter_options in filter_model.items():
            values = filter_options.get('values', [])
            if values:
                data = data[data[column].isin(values)]

    total_cases = data['totalCases'].sum()
    total_pounds = data['totalPounds'].sum()

    pinned_bottom_data = [{"cvProductCategory": "Total", "totalCases": total_cases, "totalPounds": total_pounds}]

    grid_data = data.to_dict("records")

    return grid_data, pinned_bottom_data

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

And here’s some sample output that it pulls from our server:

inMaterialCode cvProductDescription cvProductCategory cvProductStyle cvPackStyle totalCases totalPounds
46241 FZN BONED 11-13 OZ USA Frozen Branded Boned 1/10# FZN IQF BOX 4 40
14248 FRSH BONED 8 OZ (7-9 OZ) Fresh Branded Boned 2/10# FRESH DYNO 5 100

Appreciate you looking at this!

Hi @jimbob

I wouldn’t expect this callback to work because "pinnedBottomRowData" is not a property of Dash AG Grid. Try changing it to "dashGridOptions" and returning

return grid_data, {'pinnedBottomRowData': pinned_bottom_data}

But I’m just guessing because I can’t run your code. It’s best to write a minimal example and include some fake data in a df rather than using a database connection.

1 Like

@AnnMarieW

Noted! Made that change to the callback and will keep this in mind for future reports.

However- the filter refreshing each time I clicked it was a result of the data being fetched every time the callback was triggered. That next set of data would only have the one category.

After loading the initial data into a dataframe though, then making one callback just to take date inputs, and a second to calculate the totals row, everything worked as intended.

I appreciate both of your input! Real new to this and feel bad for wasting your time on small stuff. I was able to glean some great info from here, Virtual Row Data | Dash for Python Documentation | Plotly and if you’ve got any reccomendations on educational videos on Dash I’d love to hear em!

Thank you,

jimbob

I’m glad you got it working :confetti_ball: