Plotly Dash : Sharing dataframe between pages - python

I can't find how I can share a dataframe between my different page to avoid to recreate it every time i change of page. Is their a way to create it in the app.py and access it from all pages ? I want to share df_role and df_best_pos
my app.py :
df_role, df_best_pos = build_data_frame()
app = dash.Dash(
__name__,
use_pages=True,
external_stylesheets=[dbc.icons.FONT_AWESOME, "https://rsms.me/inter/inter.css"]
# Loads icons css and Inter font
)
navbar = create_nav_bar()
content = dash.html.Div([dash.page_container], id="pages-content")
app.layout = dash.html.Div([dash.dcc.Location(id="url"), navbar, content])
app.run_server(debug=False, port=3005)

Related

Consistent update dash page across connections

I'm making a multi-page dash application that I plan to host on a server using Gunicorn and Nginx. It will access a PostgreSQL database on an external server over the network.
The data on one of the pages is obtained by a query from the database and should be updated every 30 seconds. I use to update the #callback through the dcc.Interval.
My code (simplified version):
from dash import Dash, html, dash_table, dcc, Input, Output, callback
import dash_bootstrap_components as dbc
from flask import Flask
import pandas as pd
from random import random
server = Flask(__name__)
app = Dash(__name__, server=server, suppress_callback_exceptions=True, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = html.Div([
dcc.Interval(
id='interval-component-time',
interval=1000,
n_intervals=0
),
html.Br(),
html.H6(id='time_update'),
dcc.Interval(
id='interval-component-table',
interval=1/2*60000,
n_intervals=0
),
html.Br(),
html.H6(id='table_update')
])
#callback(
Output('time_update', 'children'),
Input('interval-component-time', 'n_intervals')
)
def time_update(n_intervals):
time_show = 30
text = "Next update in {} sec".format(time_show - (n_intervals % 30))
return text
#callback(
Output('table_update', 'children'),
Input('interval-component-table', 'n_intervals')
)
def data_update(n_intervals):
# here in a separate file a query is made to the database and a dataframe is returned
# now here is a simplified receipt df
col = ["Col1", "Col2", "Col3"]
data = [[random(), random(), random()]]
df = pd.DataFrame(data, columns=col)
return dash_table.DataTable(df.to_dict('records'),
style_cell={'text-align': 'center', 'margin-bottom': '0'},
style_table={'width':'500px'})
if __name__ == '__main__':
server.run(port=5000, debug=True)
Locally, everything works fine for me, the load on the database is small, one such request loads 1 out of 8 processors by 30% for 3 seconds.
But, if you open my application in several browser windows, then the same data is displayed on two pages by two queries to the database at different times, that is, the load doubles. I am worried that when connecting more than 10 people, my server with the database will not withstand / will freeze heavily, and the database on it should work without delay and not fall.
Question:
Is it possible to make page refresh the same for different connections? That is, so that the data is updated at the same time for different users and only with the help of one query to the database.
I studied everything about the callback in the documentation and did not find an answer.
Solution
Thanks for the advice, #Epsi95! I studied page Dash Performance and added this to my code:
cache = Cache(app.server, config={
'CACHE_TYPE': 'filesystem',
'CACHE_DIR': 'cache-directory',
'CACHE_THRESHOLD': 50
})
#cache.memoize(timeout=30)
def query_data():
# here I make a query to the database and save the result in a dataframe
return df
def dataframe():
df = query_data()
return df
And in the #callback function I make a call to the dataframe() function.
Everything works the way I needed it. Thank you!

dash load up with an empty table

I am making my first dash application. I am creating a layout which will contain a dash_table however at load up time the table will be empty as the table will populate once the user selects an option.
I have tried setting the dash table to {} & none but when I do this the page will not load. How can I have an empty table as part of my layout when loading the page?
You need to provide a dictionary of column names at least to create an empty datatable. You can leave the data attribute as empty, here is a minimally working example:
from dash import Dash, dash_table, html
app = Dash(__name__)
app.layout = html.Div([
dash_table.DataTable(id="table_infos",
columns=[
{'id': "Intitulé", 'name': "Intitulé"},
{'id': "Donnée", 'name': "Donnée"}
]
)
])
if __name__ == '__main__':
app.run_server(debug=True)

Dash testing dcc.upload with dash.testing

When writing production ready code we want to be able to automatically test our webapp everytime we update the code. Dash for python allows this through dash.testing. However in my app I upload an excel file utilizing the dcc.Upload() component.
How do I write a test that can send the upload link to this component?
The dcc.Upload component does not allow you to put an id on the that stores the upload link.
It is easy to work around this by inspecting the upload button/field that you have created with web developer tools. look for the line that contains "<input type=file ... >". in the elements tab.
Right click it and press copy xpath and it should give you a relative path like //*[#id="upload-data"]/div/input
The test case would look like this
from dash.testing.application_runners import import_app
def test_xxxx001_upload(dash_duo):
# get app from app.py
app = import_app("src.app")
dash_duo.start_server(app)
# find element that contains input link. Utilize the web driver to get the element
element = dash_duo.driver.find_element_by_xpath('//*[#id="upload-data"]/div/input')
element.send_keys("C:\\path\\to\\testData.xlsx")
folder structure
myapp
--src
--app.py
--server.py
--run.py
--tests
--test_app
the use of the dcc.Upload component to create an upload button
import dash_core_components as dcc
import dash_html_components as html
html.Div(
id="file-drop",
children=[
dcc.Upload(
id="upload-data",
children=html.Div(
["Drag and Drop or ", html.A("Select File"),],
id="select-file",
),
multiple=False,
),
html.Div(id="output-data-upload"),
],
)

How to receive a form input in a python script in django?

what i nee is: On a html form when user click the submit button the value of that button to be received as a python variable so that i can use it in my script.
My button value is a path to the csv file "CSV/sample_submission.csv" uploaded by user in my app and stored in a folder in my django app.
What i want to do is plot some plotly plots by using the file uploaded by the user.
I've tried to send the data through the post request and receive it in my view function and render it on html page but what i want is that data to be saved as a global or any type of variable that can be used in any python script in my app.
URL.py
path("event/",views.event,name="event"),
Views.py:
def event(request):
context={}
file= request.POST.get('file',None)
context['file']=file
return render(request,'main/event.html',context)
Following is my code of plot.py
from plotly.offline import plot
import plotly.graph_objs as go
import pandas as pd
def get_simple_candlestick():
df = pd.read_csv("FILE PATH RECEIVED FROM FORM")
df.head()
data = [
go.Bar(
x=df['x'], # assign x as the dataframe column 'x'
y=df['y']
)
]
layout = go.Layout(
title='Plot Title',
xaxis=dict(
autorange=True
),
yaxis=dict(
autorange=True
)
)
plot_data = data
figure = go.Figure(data=plot_data, layout=layout)
plot_div = plot(figure, output_type='div', include_plotlyjs=False)
return plot_div
what i expect is that the data to be read by .read_csv() function should be the the one that user uploaded and selected from many of the files he uploaded.
I did it by simply making global variable.

Call Local CSS files in Dash App

I am attempting to run the Dash Vanguard demo app while hosting the 4 css files locally. I have successfully been able to use a workaround and locally host a single css file in Dash, but have not been able to simultaneously call all 4.
This is the current Vanguard dash app with the css files externally hosted:
external_css =
["https://cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.min.css",
"https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css",
"//fonts.googleapis.com/css?family=Raleway:400,300,600",
"https://codepen.io/bcd/pen/KQrXdb.css",
"https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"]
for css in external_css:
app.css.append_css({"external_url": css})
My attempt at hosting css files locally:
app.scripts.config.serve_locally = True
app.css.config.serve_locally = True
....
app.layout = html.Div([
html.Link(href='/assets/skeleton.min.css', rel='stylesheet'),
html.Link(href='/assets/skelly.css', rel='stylesheet'),
html.Link(href='/assets/normalize.min.css', rel='stylesheet'),
html.Link(href='/assets/font.css', rel='stylesheet'),
dcc.Location(id='url', refresh=False),
html.Div(id='page-content')
])
....
#app.server.route('/assets/<path:path>')
def static_file(path):
static_folder = os.path.join(os.getcwd(), 'assets')
return send_from_directory(static_folder, path)
The app currently loads without any styling. Not sure why it won't load even one of the css files.
I had the same issue loading local files. The problem was in the #app.server.route. I changed it to:
#app.server.route('/static/<path>')
and it worked.
Edit: Starting with Dash 0.22 you now just need to put the css file in an assets folder. See the docs
I'm currently having the same issue so if you find an answer please add it here!... I don't have a solution but here is the research I've done in case you haven't seen any of these:
https://github.com/plotly/dash/pull/171
https://dash.plot.ly/external-resources
https://github.com/plotly/dash-recipes/blob/master/dash-local-css-link.py

Categories