Is it possible to store data in a user session? I didn’t find any example, and from the following scenario, I think I need it. Thanks in advance for any help!
I’m developing a dashboard where people can upload a csv file, and based on that data, charts are displayed. And users can then also apply filters to update the charts.
I have all charting and filtering working fine when I work on a dataframe in stored in a global variable, and I am now struggling to move that to non-local variables. I have the upload component triggering a callback that parses the csv and creates the dataframe as needed. But then I’m stuck because the function that updates the charts is a callback on the filtering fields and these need to access data generated by the callback on the upload component.
To make it short, I want to get the example here working when the function update_output is not a callback of the upload component, but a callback on a form element giving a value for a filtering criteria.
My problem is that I don’t see how I can get the dataframe built in handle_upload in the first chain. filter_data is accessing the dataframe via a global_store function, but it currenly uses a global variable to access the dataframe. It is that access to the global variable that I need to avoid. But I don’t see how.
I explored several suggestions I found in the doc:
I could store the dataframe serialised in a div as explained on https://dash.plot.ly/sharing-data-between-callbacks , but all example are about callback on the div accessing the data from the div. However in my case, I don’t want callback on this div to access the data, I want callbacks on form fields to access this data.
There’s a suggestion to Uses Redis via Flask-Cache for storing “global variables”. This data is accessed through a function, the output of which is cached and keyed by its input arguments., however, I don’t see how to get it to work as the return of the function is the dataframe constructed in the callback of the upload component.
Finally I found a way to get it working by storing data in a div as suggested in the doc. To spare some time to others, here is a skeleton illustrating how I did it:
# -*- coding: utf-8 -*-
import dash
import base64
import io
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import numpy as np
import csv
import json
from flask_caching import Cache
app = dash.Dash(__name__)
from datetime import datetime as dt
app.layout = html.Div(children=[
# upload component
dcc.Upload(
id='upload-data',
children=html.Div([
'Drag and Drop or ',
html.A('Select Files')
]),
multiple=False
),
dcc.Input( id = "maximum-amount", placeholder = "Maximum amount", type = 'numeric',),
html.Div(id='scatter_of_expenses'),
# signaling divs, meant to be hidden
html.Div(id='signal'),
html.Div(id='uploaded'),
])
@app.callback(dash.dependencies.Output('uploaded', 'children'),
[dash.dependencies.Input('upload-data', 'contents')],
)
# this function is handling the uploaded file and storing the result in the div #uploaded
def handle_upload(type_and_content):
# return empty dataframe if no file uploaded yet
if type_and_content == None:
return pd.DataFrame({}).to_json()
content_type, content_string = type_and_content.split(',')
content_csv = base64.b64decode(content_string)
dateparse = lambda x: pd.datetime.strptime(x, '%d/%m/%Y')
e = pd.read_csv(io.StringIO(content_csv.decode('utf-8')))
return e.to_json()
@app.callback(
dash.dependencies.Output('signal', 'children'),
[dash.dependencies.Input('maximum-amount', 'value'),
],
# FIXME
# next line makes that the handle_upload callback is not called
state = [dash.dependencies.State('uploaded', 'children'), ]
)
# this function is triggered by the search form, and reads the data from the div #uploaded as
# it gets it via a state variable
def filter_expenses(maximum_amount, df):
data = df.read_json(df)
res = data[data.amount<maximum_amount]
return res.to_json
#--------------------------------------------------------------------------------
@app.callback(dash.dependencies.Output('scatter_of_expenses', 'children'),
[dash.dependencies.Input('signal', 'children')],
state = [dash.dependencies.State('uploaded', 'children')]
)
# This function displays a chart of the filtered data. It gets triggered by the filter_expenses function which
# puts the filtered data in the div #signal
def scatter_of_expenses(filter, df):
return dcc.Graph(...)
if __name__ == '__main__':
app.run_server(debug=True)