dash-space / app.py
mic3333's picture
Create app.py
51fd446 verified
import base64
import io
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from dash import Dash, html, dcc, Input, Output, State, callback_context
import dash_bootstrap_components as dbc
# Initialize Dash app
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
server = app.server
# App layout
app.layout = dbc.Container([
dbc.Row([
dbc.Col([
html.H1("📊 Dynamic Dashboard Creator", className="text-center mb-4"),
html.Hr(),
], width=12)
]),
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H4("Upload Dataset", className="card-title"),
dcc.Upload(
id='upload-data',
children=html.Div([
'Drag and Drop or ',
html.A('Select Files')
]),
style={
'width': '100%',
'height': '60px',
'lineHeight': '60px',
'borderWidth': '1px',
'borderStyle': 'dashed',
'borderRadius': '5px',
'textAlign': 'center',
'margin': '10px'
},
multiple=False,
accept='.csv,.xlsx'
),
html.Div(id='upload-status', className="mt-2"),
html.Hr(),
html.H5("Chart Configuration"),
dbc.Row([
dbc.Col([
dbc.Label("Chart Type"),
dcc.Dropdown(
id='chart-type',
options=[
{'label': 'Bar Chart', 'value': 'bar'},
{'label': 'Line Chart', 'value': 'line'},
{'label': 'Scatter Plot', 'value': 'scatter'},
{'label': 'Histogram', 'value': 'histogram'},
{'label': 'Box Plot', 'value': 'box'},
{'label': 'Heatmap', 'value': 'heatmap'},
],
value='bar'
)
], width=12, className="mb-3"),
dbc.Col([
dbc.Label("X-axis Column"),
dcc.Dropdown(id='x-column')
], width=6, className="mb-3"),
dbc.Col([
dbc.Label("Y-axis Column"),
dcc.Dropdown(id='y-column')
], width=6, className="mb-3"),
dbc.Col([
dbc.Label("Color Column (Optional)"),
dcc.Dropdown(id='color-column')
], width=6, className="mb-3"),
dbc.Col([
dbc.Label("Size Column (Optional)"),
dcc.Dropdown(id='size-column')
], width=6, className="mb-3"),
]),
dbc.Button(
"Generate Dashboard",
id="generate-btn",
color="primary",
className="w-100 mt-3"
)
])
])
], width=4),
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H4("Dashboard", className="card-title"),
dcc.Graph(id='main-graph', style={'height': '500px'}),
])
]),
dbc.Card([
dbc.CardBody([
html.H4("Dataset Preview", className="card-title"),
html.Div(id='data-table')
])
], className="mt-3")
], width=8)
], className="mt-4"),
# Store component to hold the dataframe
dcc.Store(id='stored-data')
], fluid=True)
def parse_contents(contents, filename):
"""Parse uploaded file contents"""
content_type, content_string = contents.split(',')
decoded = base64.b64decode(content_string)
try:
if 'csv' in filename:
df = pd.read_csv(io.StringIO(decoded.decode('utf-8')))
elif 'xls' in filename:
df = pd.read_excel(io.BytesIO(decoded))
else:
return None, "Unsupported file type"
return df, None
except Exception as e:
return None, f"Error processing file: {str(e)}"
@app.callback(
[Output('stored-data', 'data'),
Output('upload-status', 'children'),
Output('x-column', 'options'),
Output('y-column', 'options'),
Output('color-column', 'options'),
Output('size-column', 'options'),
Output('data-table', 'children')],
[Input('upload-data', 'contents')],
[State('upload-data', 'filename')]
)
def update_data(contents, filename):
"""Update data when file is uploaded"""
if contents is None:
return None, "", [], [], [], [], ""
df, error = parse_contents(contents, filename)
if error:
return None, dbc.Alert(error, color="danger"), [], [], [], [], ""
# Create column options
columns = [{'label': col, 'value': col} for col in df.columns]
columns_with_none = [{'label': 'None', 'value': None}] + columns
# Create data table preview
table = dbc.Table.from_dataframe(
df.head(10),
striped=True,
bordered=True,
hover=True,
size='sm'
)
success_msg = dbc.Alert([
html.H6("File uploaded successfully!"),
html.P(f"Shape: {df.shape[0]} rows × {df.shape[1]} columns"),
html.P(f"Columns: {', '.join(df.columns.tolist())}")
], color="success")
return df.to_dict('records'), success_msg, columns, columns, columns_with_none, columns_with_none, table
@app.callback(
Output('main-graph', 'figure'),
[Input('generate-btn', 'n_clicks')],
[State('stored-data', 'data'),
State('chart-type', 'value'),
State('x-column', 'value'),
State('y-column', 'value'),
State('color-column', 'value'),
State('size-column', 'value')]
)
def update_graph(n_clicks, data, chart_type, x_col, y_col, color_col, size_col):
"""Generate graph based on selections"""
if not n_clicks or not data:
return {}
df = pd.DataFrame(data)
try:
# Generate different chart types
if chart_type == 'bar':
fig = px.bar(df, x=x_col, y=y_col, color=color_col,
title=f"Bar Chart: {y_col} by {x_col}")
elif chart_type == 'line':
fig = px.line(df, x=x_col, y=y_col, color=color_col,
title=f"Line Chart: {y_col} vs {x_col}")
elif chart_type == 'scatter':
fig = px.scatter(df, x=x_col, y=y_col, color=color_col, size=size_col,
title=f"Scatter Plot: {y_col} vs {x_col}")
elif chart_type == 'histogram':
fig = px.histogram(df, x=x_col, color=color_col,
title=f"Histogram: Distribution of {x_col}")
elif chart_type == 'box':
fig = px.box(df, x=x_col, y=y_col, color=color_col,
title=f"Box Plot: {y_col} by {x_col}")
elif chart_type == 'heatmap':
# Create correlation heatmap for numeric columns
numeric_df = df.select_dtypes(include=['number'])
if len(numeric_df.columns) > 1:
corr = numeric_df.corr()
fig = px.imshow(corr, text_auto=True, aspect="auto",
title="Correlation Heatmap")
else:
fig = go.Figure()
fig.add_annotation(text="Not enough numeric columns for heatmap",
showarrow=False, x=0.5, y=0.5)
else:
fig = {}
fig.update_layout(template="plotly_white")
return fig
except Exception as e:
fig = go.Figure()
fig.add_annotation(text=f"Error: {str(e)}", showarrow=False, x=0.5, y=0.5)
return fig
if __name__ == '__main__':
app.run_server(host='0.0.0.0', port=7860, debug=False)