I am trying to solve this ‘Dangerous Link created issue’ As per suggestion of @AnnMarieW , I am posting my code here.
import dash
import numpy as np
from dash import dcc, html
from dash.dash_table import DataTable
import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
from dash.dependencies import Input, Output
from base64 import b64encode
# Demo DataFrame
data = {
'Plastic': ['A', 'A', 'B', 'B', 'A', 'B', 'A', 'A', 'B', 'B', 'A', 'B'],
'Make': [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2],
'Block': [10, 20, 30, 10, 20, 30, 10, 20, 30, 10, 20, 30],
'Measurement': [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2],
'Actual_Yield': [85, 90, 88, 92, 87, 91, 85, 90, 88, 92, 87, 91],
'Min': [80, 85, 85, 88, 82, 90, 80, 85, 85, 88, 82, 90],
'Max': [88, 92, 90, 94, 89, 94, 88, 92, 90, 94, 89, 94]
}
df = pd.DataFrame(data)
# Calculate average yield for each grade and additional filters
average_yield_per_grade = df.groupby(['Plastic', 'Make', 'Block', 'Measurement'])['Actual_Yield'].mean().reset_index()
# Calculate overall average yield
overall_avg_yield = df['Actual_Yield'].mean()
app = dash.Dash(__name__)
app.layout = html.Div(className='row', children=[
# Left column for Production Line Chart
html.Div(className='six columns', children=[
html.H1(children='KPI Dashboard', style={'textAlign': 'center'}),
# Dropdown for selecting plastic grades
dcc.Dropdown(
id='plastic-dropdown',
options=[
{'label': plastic, 'value': plastic} for plastic in df['Plastic'].unique()
],
value=df['Plastic'].iloc[0], # Set default value to the first grade in the dataframe
style={'width': '50%', 'margin': '20px auto'},
placeholder='Select Plastic Grade'
),
# Dropdown for selecting Make
dcc.Dropdown(
id='make-dropdown',
options=[], # Will be populated dynamically
value='', # Set default value to empty
style={'width': '50%', 'margin': '20px auto'},
placeholder='Select Make'
),
# Dropdown for selecting Block
dcc.Dropdown(
id='block-dropdown',
options=[], # Will be populated dynamically
value='', # Set default value to empty
style={'width': '50%', 'margin': '20px auto'},
placeholder='Select Block'
),
# Dropdown for selecting Measurement
dcc.Dropdown(
id='measurement-dropdown',
options=[], # Will be populated dynamically
value='', # Set default value to empty
style={'width': '50%', 'margin': '20px auto'},
placeholder='Select Measurement'
),
# Line Chart
dcc.Graph(
id='production-line-chart',
figure=px.line(title='Default Actual_Yield Trend') # Set default figure
),
# Download Button
# html.Button('Download Data as Excel', id='download-link'),
# dcc.Download(id="download-dataframe-csv"),
dcc.Link('Download Data as Excel', id='download-link', href='/'),
dcc.Download(id="download-dataframe-csv"),
]),
# Center column for DataTable
html.Div(className='four columns', children=[
html.H3(children='Selected Combinations Data Table', style={'textAlign': 'center'}),
DataTable(
id='selected-data-table',
columns=[
{'name': 'Plastic', 'id': 'Plastic'},
{'name': 'Make', 'id': 'Make'},
{'name': 'Block', 'id': 'Block'},
{'name': 'Measurement', 'id': 'Measurement'},
{'name': 'Actual_Yield', 'id': 'Actual_Yield'},
{'name': 'Min', 'id': 'Min'},
{'name': 'Max', 'id': 'Max'},
# Add more columns as needed
],
style_table={'height': '300px', 'overflowY': 'auto'},
),
]),
# Right column for Yield Gauge Charts
html.Div(className='six columns', children=[
# Yield Gauge Chart for Selected Grade
html.Div(className='graph-container', children=[
dcc.Graph(id='yield-gauge-chart-selected-grade',
style={'width': '700px', 'height': '500px', 'marginLeft': '280px'}
),
]),
# Yield Gauge Chart for Overall Average
html.Div(className='graph-container', children=[
dcc.Graph(id='yield-gauge-chart-overall-average',
style={'width': '700px', 'height': '500px', 'marginLeft': '-10px'}
),
]),
], style={'display': 'flex'}),
# Interval component for updating gauge value
dcc.Interval(
id='interval-component',
interval=1 * 1000, # in milliseconds (1 second)
n_intervals=0
),
])
# Callback to populate Make dropdown based on selected plastic grade
@app.callback(
Output('make-dropdown', 'options'),
[Input('plastic-dropdown', 'value')]
)
def update_make_dropdown(selected_plastic):
make_options = [{'label': str(make), 'value': make} for make in
df[df['Plastic'] == selected_plastic]['Make'].unique()]
return make_options
# Callback to populate Block dropdown based on selected plastic grade and Make
@app.callback(
Output('block-dropdown', 'options'),
[Input('plastic-dropdown', 'value'),
Input('make-dropdown', 'value')]
)
def update_block_dropdown(selected_plastic, selected_make):
# Check if selected_make is not empty
if not selected_make:
return []
block_options = [{'label': str(block), 'value': block} for block in
df[(df['Plastic'] == selected_plastic) & (df['Make'] == int(selected_make))]['Block'].unique()]
return block_options
# Callback to populate Measurement dropdown based on selected plastic grade, Make, and Block
@app.callback(
Output('measurement-dropdown', 'options'),
[Input('plastic-dropdown', 'value'),
Input('make-dropdown', 'value'),
Input('block-dropdown', 'value')]
)
def update_measurement_dropdown(selected_plastic, selected_make, selected_block):
# Check if selected_block is not empty
if not selected_block:
return []
measurement_options = [{'label': str(measurement), 'value': measurement} for measurement in
df[(df['Plastic'] == selected_plastic) & (df['Make'] == int(selected_make)) &
(df['Block'] == float(selected_block))]['Measurement'].unique()]
return measurement_options
# Callback to update line chart based on selected plastic grade and filters
@app.callback(
Output('production-line-chart', 'figure'),
[Input('plastic-dropdown', 'value'),
Input('make-dropdown', 'value'),
Input('block-dropdown', 'value'),
Input('measurement-dropdown', 'value'),
Input('interval-component', 'n_intervals')]
)
def update_line_chart(selected_plastic, selected_make, selected_block, selected_measurement, n_intervals):
# Check if any selected value is empty
if any(value is None or value == '' for value in
[selected_plastic, selected_make, selected_block, selected_measurement]):
return px.line(title='No data available') # Return a default line chart with a title
selected_df = df[(df['Plastic'] == selected_plastic) & (df['Make'] == int(selected_make)) &
(df['Block'] == float(selected_block)) & (df['Measurement'] == float(selected_measurement))].copy()
if selected_df.empty:
return px.line(title='No data available') # Return a default line chart with a title
selected_df['Count'] = range(1, len(selected_df) + 1)
# Check if 'Min' and 'Max' columns are present in the DataFrame
if 'Min' not in selected_df.columns or 'Max' not in selected_df.columns:
return px.line(title='Missing data columns') # Return a default line chart with a title
line_chart = px.line(selected_df, x='Count', y=['Min', 'Max'],
title=f'Plastic-{selected_plastic}, Make-{selected_make}, Block-{selected_block}, Measurement-{selected_measurement} Min'
f' and Max Trend')
# Update marker properties for Min and Max
line_chart.update_traces(
line=dict(width=2), # Adjust the width of the line
mode='markers+lines', # Show markers and lines
marker=dict(size=10) # Adjust the size of the marker
)
line_chart.update_layout(xaxis_title='Count')
return line_chart
@app.callback(
[Output('yield-gauge-chart-selected-grade', 'figure'),
Output('yield-gauge-chart-overall-average', 'figure')],
[Input('plastic-dropdown', 'value'),
Input('make-dropdown', 'value'),
Input('block-dropdown', 'value'),
Input('measurement-dropdown', 'value'),
Input('interval-component', 'n_intervals')]
)
def update_gauge_charts(selected_plastic, selected_make, selected_block, selected_measurement, n_intervals):
# Check if any selected value is None or an empty string
if any(value is None or value == '' for value in
[selected_plastic, selected_make, selected_block, selected_measurement]):
return go.Figure(), go.Figure() # Return default figures
# Check if selected_block and selected_measurement are not None or empty string before converting to float
selected_block_float = float(selected_block) if selected_block and selected_block != '' else None
selected_measurement_float = float(
selected_measurement) if selected_measurement and selected_measurement != '' else None
selected_df = df[(df['Plastic'] == selected_plastic) & (df['Make'] == int(selected_make)) &
(df['Block'] == selected_block_float) & (df['Measurement'] == selected_measurement_float)].copy()
if selected_df.empty:
return go.Figure(), go.Figure()
selected_df['Count'] = range(1, len(selected_df) + 1)
current_yield = average_yield_per_grade.loc[
(average_yield_per_grade['Plastic'] == selected_plastic) &
(average_yield_per_grade['Make'] == int(selected_make)) &
(average_yield_per_grade['Block'] == selected_block_float) &
(average_yield_per_grade['Measurement'] == selected_measurement_float), 'Actual_Yield'].values
if len(current_yield) > 0:
current_yield = current_yield[0]
else:
current_yield = 0
steps_selected_grade = [
dict(range=[0, current_yield], color="green"),
dict(range=[current_yield, 100], color="red")
]
yield_gauge_chart_selected_grade = go.Figure(go.Indicator(
mode='gauge+number',
value=current_yield,
title=f'Average Yield for Plastic {selected_plastic}',
gauge=dict(
axis=dict(range=[None, 100]),
bar=dict(color="green"),
steps=steps_selected_grade,
threshold=dict(line=dict(color="red", width=2), thickness=0.75)
)
))
steps_overall_avg = [
dict(range=[0, overall_avg_yield], color="green"),
dict(range=[overall_avg_yield, 100], color="red")
]
yield_gauge_chart_overall_avg = go.Figure(go.Indicator(
mode='gauge+number',
value=overall_avg_yield,
title='Overall Average Yield',
gauge=dict(
axis=dict(range=[None, 100]),
bar=dict(color="green"),
steps=steps_overall_avg,
threshold=dict(line=dict(color="red", width=2), thickness=0.75)
)
))
return yield_gauge_chart_selected_grade, yield_gauge_chart_overall_avg
@app.callback(
[Output('selected-data-table', 'data'),
Output('download-link', 'href')],
[Input('plastic-dropdown', 'value'),
Input('make-dropdown', 'value'),
Input('block-dropdown', 'value'),
Input('measurement-dropdown', 'value')]
)
def update_data_table(selected_plastic, selected_make, selected_block, selected_measurement):
# Check if all selected values are not None or empty string
if all(value is not None and value != '' for value in
[selected_plastic, selected_make, selected_block, selected_measurement]):
selected_df = df[(df['Plastic'] == selected_plastic) & (df['Make'] == int(selected_make)) &
(df['Block'] == float(selected_block)) & (df['Measurement'] == float(selected_measurement))]
# Convert DataFrame to CSV and encode as base64 for download link
csv_string = selected_df.to_csv(index=False, encoding='utf-8')
csv_base64 = 'data:text/csv;base64,' + b64encode(csv_string.encode()).decode()
return selected_df.to_dict('records'), csv_base64
else:
return [], ''
# Run the app
if __name__ == '__main__':
app.run_server(host='127.0.0.1', port=8058, debug=True)