I am new to python and plotly-dash.
I am trying to use "Hidden Div" to store a data frame as suggested in dash tutorial 5.
But I am not able to process the uploaded file.
import base64
import io
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_table
import pandas as pd
#global_df = pd.read_csv('...')
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Graph(id='graph'),
html.Table(id='table'),
dcc.Upload(
id='datatable-upload',
children=html.Div(['Drag and Drop or ',html.A('Select Files')]),
),
# Hidden div inside the app that stores the intermediate value
html.Div(id='intermediate-value', style={'display': 'none'})
])
def parse_contents(contents, filename):
content_type, content_string = contents.split(',') #line 28
decoded = base64.b64decode(content_string)
if 'csv' in filename:
# Assume that the user uploaded a CSV file
return pd.read_csv(
io.StringIO(decoded.decode('utf-8')))
elif 'xls' in filename:
# Assume that the user uploaded an excel file
return pd.read_excel(io.BytesIO(decoded))
elif 'xlsx' in filename:
# Assume that the user uploaded an excel file
return pd.read_excel(io.BytesIO(decoded))
#app.callback(Output('intermediate-value', 'children'),
[Input('datatable-upload', 'contents')],
[State('datatable-upload', 'filename')])
def update_output(contents, filename):
# some expensive clean data step
cleaned_df = parse_contents(contents, filename)
# more generally, this line would be
# json.dumps(cleaned_df)
return cleaned_df.to_json(date_format='iso', orient='split')
#app.callback(Output('graph', 'figure'), [Input('intermediate-value', 'children')])
def update_graph(jsonified_cleaned_data):
# more generally, this line would be
# json.loads(jsonified_cleaned_data)
dff = pd.read_json(jsonified_cleaned_data, orient='split')
figure = create_figure(dff)
return figure
#app.callback(Output('table', 'children'), [Input('intermediate-value', 'children')])
def update_table(jsonified_cleaned_data):
dff = pd.read_json(jsonified_cleaned_data, orient='split')
table = create_table(dff)
return table
if __name__ == '__main__':
app.run_server(port=8050, host='0.0.0.0')
I am getting the following error while running the code:
File "ipython-input-12-4bd6fe1b7399", line 28, in parse_contents
content_type, content_string = contents.split(',')
AttributeError: 'NoneType' object has no attribute 'split'
The callback is likely running on initialization with empty values. You can prevent this by adding something like this at the top of your callback:
if contents is None:
raise dash.exceptions.PreventUpdate
Related
So I am trying to run this pymongo app through jupyter notebook and all it does is give me a "Loading..." output and then the kernel goes idle... when I just print the "df", it gives me the data so I know that it is working data-wise. I have tried restarting the kernel; deleting the output, restarting the kernel, and reinputting the input; Restarting and clearing output - and all do not seem to have any effect. I am thinking it may have something to do with my inputted data but I am not sure what exactly...
This is the .ipynb:
from jupyter_plotly_dash import JupyterDash
import dash
import dash_leaflet as dl
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
import dash_table
from dash.dependencies import Input, Output
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pymongo import MongoClient
#### FIX ME #####
# change animal_shelter and AnimalShelter to match your CRUD Python module file name and class name
from AAC import AnimalShelter
###########################
# Data Manipulation / Model
###########################
# FIX ME update with your username and password and CRUD Python module name
username = "aacuser"
password = "monogoadmin"
shelter = AnimalShelter(username, password)
# class read method must support return of cursor object and accept projection json input
df = pd.DataFrame.from_records(shelter.read({}))
#########################
# Dashboard Layout / View
#########################
app = JupyterDash('Dash DataTable Only')
app.layout = html.Div([
html.Div(id='hidden-div', style={'display':'none'}),
html.Center(html.B(html.H1('SNHU CS-340 Dashboard'))),
html.Hr(),
dash_table.DataTable(
id='datatable-interactivity',
columns=[
{"name": i, "id": i, "deletable": False, "selectable": True} for i in df.columns
],
data=df.to_dict('records'),
#FIXME: Set up the features for your interactive data table to make it user-friendly for your client
editable=False,
filter_action="native",
sort_action="native",
sort_mode="multi",
column_selectable=False,
row_selectable=False,
row_deletable=False,
selected_columns=[],
selected_rows=[],
page_action="native",
page_current= 0,
page_size= 10,
),
html.Br(),
html.Hr(),
])
app
This is the .py file:
import pymongo
from pymongo import MongoClient
from bson.objectid import ObjectId
class AnimalShelter(object):
"""CRUD operations for Animal collection in Mongodatabase"""
#Initializes MongoClient
def __init__(self, username, password):
self.client = MongoClient('mongodb://127.0.0.1:38574', username='aacuser', password='mongoadmin', authSource='AAC', authMechanism='SCRAM-SHA-1')
self.database = self.client['AAC']
#Implement create method
def create(self, data):
if data is not None:
return self.database.animals.insert_one(data)
else:
raise Exception("Nothing to save, because data parameter is empty")
#Implement read method
def read(self, data):
if data is not None:
return self.database.animals.find(data)
else:
raise Exception("Nothing to read, because data parameter is empty")
#Implement update method
def update(self, data):
if find is not None:
return self.database.animals.update_one(data)
else:
raise Exception("Nothing to update, because data parameter is empty")
#Implement delete method
def delete(self, data):
if data is not None:
return self.database.animals.delete_one(data)
else:
raise Exception("Nothing to delete, because data parameter is empty")
Any help is greatly appreciated!
EDIT:
Here is the requested screen shot
I also wanted to note that I cleared cache and cookies and this had no effect.
i changed the following code from using ".png" to ".pkl". but this doesn't work
import plotly.express as px
import joblib
#generate example pkl
fig = px.scatter(x=range(10), y=range(10))
joblib.dump(fig, "img_dash/figure.pkl")
#%%
import dash
import dash_core_components as dcc
import dash_html_components as html
import flask
import glob
import os
image_directory = 'img_dash/'
list_of_images = [os.path.basename(x) for x in glob.glob('{}*.pkl'.format(image_directory))]
static_image_route = '/static/'
app = dash.Dash()
app.layout = html.Div([
dcc.Dropdown(
id='image-dropdown',
options=[{'label': i, 'value': i} for i in list_of_images],
value=list_of_images[0]
),
html.Img(id='image')
])
#app.callback(
dash.dependencies.Output('image', 'src'),
[dash.dependencies.Input('image-dropdown', 'value')])
def update_image_src(value):
return static_image_route + value
#app.server.route('{}<image_path>.pkl'.format(static_image_route))
def serve_image(image_path):
image_name = '{}.pkl'.format(image_path)
if image_name not in list_of_images:
raise Exception('"{}" is excluded from the allowed static files'.format(image_path))
return flask.send_from_directory(image_directory, image_name)
if __name__ == '__main__':
app.run_server(debug=False)
i expect to show the .pkl files in the dashboard with hover-function. may other formats also possible! i was not able to check this.
I recommend you to use a Graph component here instead of an Img component since you want to display these figures dynamically.
Since each pkl file already stores a Figure you can just dynamically load these files using joblib in your callback based on the dropdown value.
Example based on your code:
# Example figures for demo purposes
fig1 = px.scatter(x=range(10), y=range(10))
fig2 = px.scatter(x=range(15), y=range(15))
fig3 = px.scatter(x=range(20), y=range(20))
joblib.dump(fig1, "img_dash/figure1.pkl")
joblib.dump(fig2, "img_dash/figure2.pkl")
joblib.dump(fig3, "img_dash/figure3.pkl")
# ...
image_directory = "img_dash/"
list_of_images = [
os.path.basename(x) for x in glob.glob("{}*.pkl".format(image_directory))
]
app = dash.Dash()
app.layout = html.Div(
[
dcc.Dropdown(
id="image-dropdown",
options=[{"label": i, "value": i} for i in list_of_images],
value=list_of_images[0],
),
dcc.Graph(id="graph"),
]
)
#app.callback(
dash.dependencies.Output("graph", "figure"),
[dash.dependencies.Input("image-dropdown", "value")],
)
def update_graph(file):
return joblib.load(os.path.join(image_directory, file))
if __name__ == "__main__":
app.run_server(debug=False)
I have a Dash application where the user interacts with the app and a PDF will be generated using FPDF. I am trying to use the Dash-extensions package and the download option to forward the PDF to be downloaded but am getting an error.
I have this function to generate the PDF:
def makeReport(info):
return create_pdf(info)
def create_pdf(info):
pdf = CustomPDF(format='letter')
for i, beam in enumerate(info):
pdf.addPage(info[i],i)
data = pdf.output(dest='S').encode('latin-1')
return data
Then this is the download button related code:
#app.callback(Output("download", "data"), [Input("download_btn", "n_clicks")], prevent_initial_call=True)
def sendPDF(n_clicks):
firstName, lastName, mrn = nameParser(patient)
fileName = lastName + ', ' + firstName + ' Cutout Factor Calc.pdf'
return send_bytes(makeReport(info), fileName)
def downloadButton():
return html.Div([html.Hr(),html.Button("Download Report", id="download_btn"), Download(id="download")])
The error I get when I click the button is:
The send_bytes utility function expects as first argument a function that writes bytes to BytesIO object, but you are passing it a byte string, so you need to write a small wrapper function. Here is a small example demonstrating how it can be done,
import dash
import dash_html_components as html
from dash.dependencies import Output, Input
from dash_extensions import Download
from dash_extensions.snippets import send_bytes
from fpdf import FPDF
# Create example app.
app = dash.Dash(prevent_initial_callbacks=True)
app.layout = html.Div([html.Button("Download PDF", id="btn"), Download(id="download")])
def create_pdf(n_nlicks):
pdf = FPDF()
pdf.add_page()
pdf.set_font('Arial', 'B', 16)
pdf.cell(40, 10, f'Hello World! (n_clicks is {n_nlicks})')
return pdf
#app.callback(Output("download", "data"), [Input("btn", "n_clicks")])
def generate_xlsx(n_nlicks):
def write_pdf(bytes_io):
pdf = create_pdf(n_nlicks) # pass argument to PDF creation here
bytes_io.write(pdf.output(dest='S').encode('latin-1'))
return send_bytes(write_pdf, "some_name.pdf")
if __name__ == '__main__':
app.run_server()
I’m learning python and dash and I’m building an application for monitoring my trading.
I’ve adapted the code from : “Live Updating Components” example (Orbital Satellite). It works well on jupyter notebook and repl.it but I have an error when I try it on my computer :
"Traceback (most recent call last):
File "orbital.py", line 62, in
Input('interval-component', 'n_intervals'))"
“The input argument interval-component.n_intervals must be a list or tuple of
dash.dependencies.Inputs.”
I don’t understand why
Here is my code :
import ccxt
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly
from dash.dependencies import Input, Output
import pandas as pd
app = dash.Dash(__name__)
ftx = ccxt.ftx({'verbose': True})
ftx = ccxt.ftx({
'apiKey': '',
'secret': '',
})
app.layout = html.Div(
html.Div([
html.H4('FTX Live Feed'),
html.Div(id='live-update-text'),
dcc.Interval(
id='interval-component',
interval=1*1000, # in milliseconds
n_intervals=0
)
])
)
class Client:
"""A sample client class"""
def __init__(self):
self.pnl = []
#classmethod
def get_balances(client):
try:
balance = ftx.fetch_balance()
except ccxt.BaseError as e:
print(f"Could not get account with error: {e}")
raise e
else:
result = balance["info"]["result"]
total = []
for x in result:
if float(x["free"]) > 0.0:
total.append(x["usdValue"])
a = list(map(float, total))
df = pd.Series(a)
# global totCap
totCap = df.sum()
return totCap
#app.callback(Output('live-update-text', 'children'),
Input('interval-component', 'n_intervals'))
def update_metrics(n):
arc = Client().get_balances()
style = {'padding': '5px', 'fontSize': '16px'}
return [
html.Span('Balance: {0:.2f}'.format(arc), style=style)
]
if __name__ == '__main__':
app.run_server(host="0.0.0.0", port="8050")
Here is a screenshot from the replit app :
replit dash
As the error is trying to say, you need to wrap your Input in a list, like this:
#app.callback(Output('live-update-text', 'children'),
[Input('interval-component', 'n_intervals')])
I'd like to display the count of certain criteria inside a div in my dash layout based off callback selection from dropdown.
I'm able to get the dropdown for the values in a pandas dataframe column, but I'm having trouble figuring out how to display the total count of the a selected element of the column.
For example, I've written a function in Jupyter notebook to get a count
def getCount(df, selected_org):
totCount = df[df['ORGANIZATIONS'] == selected_org].AGENCY.count()
return count
selected_org = 'REGION 1'
getCount(df, selected_org)
Which displays a value of: 3187
This value is the result of my selected_org variable.
Now, I'd like to do the same thing in my dash layout based off the selection from dropdown. I used much of the code here to get started from Udemy course: Interactive Python Dashboards with Plotly and Dash
I start with my layout:
org_options = []
for org in df['ORG_CODE_LEVEL_2_SHORT'].unique():
org_options.append({'label': str(org), 'value': org})
app.layout = html.Div(children=[
html.Label('Select Org:'),
dcc.Dropdown(id='org-dropdown', options=org_options,
value=df['ORG_CODE_LEVEL_2_SHORT'].min()),
html.P(html.Div(id='container'))
Then my call back:
#app.callback(
Output(component_id='container', component_property='children'),
[Input(component_id='org-dropdown', component_property='value')])
def update_table(selected_org):
filtered_org = df[df['ORG_CODE_LEVEL_2_SHORT'] == selected_org]
return getCount(df, filtered_org)
And a function to generate the count:
def getCount(df, selected_org):
totCount = df[df['ORG_CODE_LEVEL_2_SHORT'] ==
selected_org].AGENCY_INFO_5.count()
return html.H2(totCount)
Which give me the following:
But it isn't giving me the count. Any help is appreciated.
Final complete program:
import os
import glob
import pandas as pd
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
app = dash.Dash()
# DATA FROM EEPROFILE
path = r'mydata'
extension = 'xlsx'
os.chdir(path)
files = [i for i in glob.glob('*.{}'.format(extension))]
latest_file = max(files, key=os.path.getctime)
df = pd.DataFrame()
eeprofi = pd.read_excel(latest_file, converters={'ORG_CODE_LEVEL_3': str})
df = df.append(eeprofi)
def getCount(df, selected_org):
totCount = df[df['ORG_CODE_LEVEL_2_SHORT'] == selected_org].AGENCY_INFO_5.count()
return html.H2(totCount)
org_options = []
for org in df['ORG_CODE_LEVEL_2_SHORT'].unique():
org_options.append({'label': str(org), 'value': org})
app.layout = html.Div(children=[
html.Label('Select Org:'),
dcc.Dropdown(id='org-dropdown', options=org_options, value=df['ORG_CODE_LEVEL_2_SHORT'].min()),
html.P(html.Div(id='container'))
])
#app.callback(
Output(component_id='container', component_property='children'), [Input(component_id='org-dropdown', component_property='value')])
def update_table(selected_org):
filtered_org = df[df['ORG_CODE_LEVEL_2_SHORT'] == selected_org]
return getCount(df, filtered_org)
if __name__ == '__main__':
app.run_server()
Your input is a children so you should return a list and not html.H2(your_count) like you did ;) so the solution is to replace this function
def getCount(df, selected_org):
totCount = df[df['ORG_CODE_LEVEL_2_SHORT'] == selected_org].AGENCY_INFO_5.count()
return html.H2(totCount)
by this one :
def getCount(df, selected_org):
totCount = df[df['ORG_CODE_LEVEL_2_SHORT'] == selected_org].AGENCY_INFO_5.count()
return [html.H2(totCount)]