# -*- coding: utf-8 -*-
"""
Created on Thu Feb 20 15:00:14 2025
@author: win11
"""
import dash as dash
from dash import dcc, html, Input, Output, callback
#import plotly.express as px
import plotly.graph_objects as go
#from data.dataprep import data_prep,create_ym_revenue,data_prep_ai_data
import pandas as pd
import numpy as np
import dash_bootstrap_components as dbc
#import plotly.io as pio
dbc_css = "https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates/dbc.min.css"
#df_money_order, df_money_orderdetail = data_prep()
#df_revenue_mom = pd.read_csv('orderdetails_money.csv')
#df_revenue_mom = data_prep_ai_data().reset_index()
df_revenue_mom = pd.read_csv('ym_revenue_ai.csv', index_col=False)
df_revenue_mom['Last_dayofmonth_Date'] = df_revenue_mom['Last_dayofmonth_Date'].apply(pd.to_datetime, format='%Y-%m-%d')
def create_bar_ac_py(df_revenue_mom):
colors = {'AC': 'rgba(64,64,64,1)','PY':'rgba(166,166,166,1)' }
# Define a small shift using Timedelta
#shift = pd.Timedelta(days=3) # Adjust as needed
max_y = df_revenue_mom['revenue'].max()
current_year = df_revenue_mom['Last_dayofmonth_Date'].dt.year.max()
src_background = f"assets/{current_year}_figPY.svg"
figPY = go.Figure()
# First bar trace (shift left)
figPY.add_trace(go.Bar(
x=df_revenue_mom['Last_dayofmonth_Date'],
y=df_revenue_mom['revenue_py'],
#width=0.5,
marker_color=colors.get('PY'),
name='PY'
))
#background needs to be transparent
figPY.update_layout({
'plot_bgcolor': 'rgba(0, 0, 0, 0)',
'paper_bgcolor': 'rgba(0, 0, 0, 0)',
})
# Change the bar mode
figPY.update_layout(bargap=0.2)
figPY.update_layout(yaxis_title=None)
figPY.update_yaxes(showticklabels=False)
figPY.update_layout(xaxis_title=None)
figPY.update_xaxes(showticklabels=False)
figPY.update_layout(yaxis_range=[0,max_y])
figPY.update_layout(
margin=dict(l=0, r=0, t=0, b=0),
)
#solution stuck kaleido 2 on win11 : https://github.com/plotly/Kaleido/issues/126
#writing an image dynamically triggers the callback function for a second time
#pio.write_image(figPY, "assets/figPY.svg")
figAC = go.Figure()
# Second bar trace (shift right)
figAC.add_trace(go.Bar(
x=df_revenue_mom['Last_dayofmonth_Date'],
y=df_revenue_mom['revenue'],
#width=0.5,
marker_color=colors.get('AC'),
name='AC',
text=df_revenue_mom['revenue'],
texttemplate = "%{y:,.3s}"
))
# Change the bar mode
#figAC.update_layout(bargap=0.2)
#background needs to be transparent fo a see through to PY
figAC.update_layout({
'plot_bgcolor': 'rgba(0, 0, 0, 0)',
'paper_bgcolor': 'rgba(0, 0, 0, 0)',
})
figAC.update_xaxes(showgrid=False, zeroline=False)
figAC.update_yaxes(showgrid=False, zeroline=False)
# Add images
figAC.add_layout_image(
dict(
source=src_background,
xref="paper", yref="paper",
#x=1, y=1,
x=-0.01, y=0,
sizex=1,
sizey=1,
xanchor="left",
yanchor="bottom",
opacity=1,
layer="below",
sizing="stretch")
)
figAC.update_layout(yaxis_title=None)
figAC.update_yaxes(showticklabels=False)
figAC.update_xaxes(
dtick="M1",
tickformat="%b")
#ticklabelmode="period")
figAC.update_layout(
margin=dict(l=0, r=0, t=0, b=0),
autosize=False,
width=800,
height=300,
hovermode=False
)
figAC.update_traces(textfont_size=12, textangle=0, textposition="outside", cliponaxis=False)
return dcc.Graph(figure = figAC)
def create_delta_py(df_revenue_mom):
colors = {'AC': 'rgba(64,64,64,1)','PY':'rgba(166,166,166,1)', 'red': 'rgba(255,0,0,1)', 'green':'rgba(0,142,150,1)' }
figPY = go.Figure()
# First bar trace (shift left)
figPY.add_trace(go.Bar(
x=df_revenue_mom['Last_dayofmonth_Date'],
y=df_revenue_mom['delta_py'],
#width=0.5,
#marker_color=colors.get('PY'),
marker_color = df_revenue_mom['delta_py'].apply(lambda x: colors.get('red') if x < 0 else colors.get('green')) ,
name='PY',
text=df_revenue_mom['delta_py'],
texttemplate = df_revenue_mom['delta_py'].apply(lambda x: "%{y:,.3s}" if x < 0 else "+%{y:,.3s}")
))
#background needs to be transparent
figPY.update_layout({
'plot_bgcolor': 'rgba(0, 0, 0, 0)',
'paper_bgcolor': 'rgba(0, 0, 0, 0)',
})
# Change the bar mode
figPY.update_layout(bargap=0.2)
#figPY.update_layout(yaxis_title="<span style='rotate: 90deg;'>ΔPY</span>")
figPY.update_layout(yaxis_title=None)
figPY.update_yaxes(showticklabels=False)
figPY.update_layout(xaxis_title=None)
figPY.update_xaxes(showticklabels=False)
figPY.update_layout(
margin=dict(l=0, r=0, t=0, b=0),
autosize=False,
width=800,
height=350,
hovermode=False
)
figPY.update_traces(textfont_size=12, textangle=0, textposition="outside", cliponaxis=False)
return dcc.Graph(figure = figPY)
def create_delta_py_perc(df_revenue_mom):
colors = {'AC': 'rgba(64,64,64,1)','PY':'rgba(166,166,166,1)', 'red': 'rgba(255,0,0,1)', 'green':'rgba(0,142,150,1)' }
figPY = go.Figure()
#adding y=0
#opacity + line_color make the lines appear nice on the white template as they are meant to be
figPY.add_hline(y=0, line_dash="solid", line_width=2, opacity=1, line_color="Black")
for x_val, y_val in zip(df_revenue_mom['Last_dayofmonth_Date'], df_revenue_mom['delta_perc_py']):
figPY.add_shape(
type="line",
x0=x_val, x1=x_val,
y0=0, y1=y_val, # From y=0 to marker y-value
line=dict(color=colors["red"] if y_val < 0 else colors["green"], # Red for negative, green for positive
width=2) # Adjust color and width as needed
)
figPY.add_trace(go.Scatter(
x=df_revenue_mom['Last_dayofmonth_Date'],
y=df_revenue_mom['delta_perc_py'],
marker = dict(color=colors['AC'],size=12),
mode="markers + text",
name="Percentage change with same month last year",
text=df_revenue_mom['delta_py'],
texttemplate = df_revenue_mom['delta_perc_py'].apply(lambda x: "%{y:.1%}" if x < 0 else "+%{y:.1%}") ,
textposition= df_revenue_mom['delta_perc_py'].apply(lambda x: "bottom center" if x < 0 else "top center")
))
#background needs to be transparent
figPY.update_layout({
'plot_bgcolor': 'rgba(0, 0, 0, 0)',
'paper_bgcolor': 'rgba(0, 0, 0, 0)',
})
# Change the bar mode
figPY.update_layout(bargap=0.2)
#figPY.update_layout(yaxis_title="<span style='rotate: 90deg;'>ΔPY%</span>" )
figPY.update_layout(yaxis_title=None )
figPY.update_yaxes(showticklabels=False)
figPY.update_layout(xaxis_title=None)
figPY.update_xaxes(showticklabels=False)
figPY.update_layout(
margin=dict(l=0, r=0, t=0, b=0),
autosize=False,
width=800,
height=300,
hovermode=False
)
figPY.update_traces(textfont_size=12, textfont_style='italic')
return dcc.Graph(figure = figPY)
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.SANDSTONE,dbc_css])
app.layout = dbc.Container([
dbc.Row([
dbc.Col([
html.P('Imaginary Traders'),
html.P('Revenue by year in K$'),
html.P( id='title'),
], className='header col-md-4'),
dbc.Col([
dbc.Select(
id="select_year",
options=[
{"label": "2024", "value": 2024},
{"label": "2023", "value": 2023}
], value = 2024
)
], className='col-md-2'),
dbc.Col([ html.H2('IBCS Experiment no.2', style={'textAlign':'right'})])
], className='col-md-12'),
dbc.Row([
dbc.Col([
html.Div([
html.Div("ΔPY%", className='title-border'),
html.Div(id='revenue_percentage')
], className='minigrid'),
html.Div([
html.Div("ΔPY ", className='title-border'),
html.Div(id='revenue_money')
], className='minigrid'),
html.Div([
html.Div([html.P('AC ', style={'fontWeight':'bold'}),
html.P('PY ')], className='title-border'),
html.Div(id='total_revenue')
], className='minigrid'),
]),
]),
dbc.Row([
dbc.Col(html.P('dash(2.18.2), dash-bootstrap-components(1.7.1), dash-bootstrap-templates 2.1.0', style={'fontSize':'9px','marginTop':'2rem'})
),
]),
],style={'marginTop': '3rem', 'marginBottom': '2rem'}, fluid=False)
@app.callback( Output('revenue_percentage', 'children'),
Output('revenue_money', 'children'),
Output('total_revenue', 'children'),
Output('title', 'children'),
Input(component_id='select_year', component_property='value')
)
def update_all(value):
#the incoming dataframe has all total, delta pm and delta pm% for each month,
#filter it on the selected value (=year-month), drop=True to remove the extra
#created useless index column
if not value:
data = df_revenue_mom[df_revenue_mom['Last_dayofmonth_Date'].dt.year == 2024]
else:
data = df_revenue_mom[df_revenue_mom['Last_dayofmonth_Date'].dt.year == int(value)]
title = f"{value}, AC and PY by month"
#update all visuals with the updated data
return create_delta_py_perc(data),\
create_delta_py(data),\
create_bar_ac_py(data),\
title
app.run_server(debug=True)