-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathapp.py
More file actions
203 lines (178 loc) · 8.64 KB
/
app.py
File metadata and controls
203 lines (178 loc) · 8.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import dash
from dash import dcc, html
from flask import session
import uuid
from callbacks import register_callbacks
from utils import get_disclaimer_with_hash
# Initialize Dash app
app = dash.Dash()
app.title = 'BMW CarData - Charging Session Dashboard'
app.css.config.serve_locally = True
app.scripts.config.serve_locally = True
# Set maximum file upload size (e.g., 5MB)
app.server.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024
app.server.secret_key = str(uuid.uuid4()) # Set a secret key for session management
# Generate a unique session ID for each user session
@app.server.before_request
def make_session_permanent():
session.permanent = True
if 'session_id' not in session:
session['session_id'] = str(uuid.uuid4())
# Layout
app.layout = html.Div([
html.H1('BMW CarData - Charging Session Dashboard', style={'textAlign': 'center', 'color': '#1f77b4'}),
# Disclaimer
html.Div([
dcc.Markdown(
get_disclaimer_with_hash(),
style={'textAlign': 'center', 'color': 'red', 'fontWeight': 'bold', "white-space": "pre"}
)
]),
# Warning message for estimated values
html.Div(id='energy-data-warning', style={'textAlign': 'center', 'color': 'orange', 'fontWeight': 'bold', 'margin': '10px', 'display': 'none'}),
# File upload component
html.Div([
dcc.Upload(
id='upload-json',
children=html.Div([
'Drag and Drop or ', html.A('Select your CarData JSON file (BMW-CarData-Ladehistorie_*.json)')
]),
style={
'width': '50%',
'height': '60px',
'lineHeight': '60px',
'borderWidth': '1px',
'borderStyle': 'dashed',
'borderRadius': '5px',
'textAlign': 'center',
'margin': 'auto',
'marginBottom': '10px', # Reduced margin
},
multiple=False
)
]),
# Button to load demo data
html.Div([
html.Button('Load Demo Data', id='load-demo-data', n_clicks=0, style={'marginBottom': '10px'}) # Reduced margin
], style={'textAlign': 'center'}),
# Store component to hold session data
dcc.Store(id='session-data'),
# Datepicker to select time range
html.Div([
html.Label('Select Date Range for analysis (optional):', style={'fontWeight': 'bold', 'color': '#1f77b4'}),
dcc.DatePickerRange(
id='date-picker-range',
start_date_placeholder_text='Start Date',
end_date_placeholder_text='End Date',
display_format='YYYY-MM-DD',
style={'border': '2px solid #1f77b4', 'borderRadius': '5px', 'marginBottom': '10px'} # Reduced margin
)
], style={'width': '50%', 'margin': 'auto', 'marginBottom': '10px'}), # Reduced margin
# Miles / kilometers toggle button
html.Div([
html.Button('Toggle Units (km/miles)', id='toggle-units', n_clicks=0, style={'marginBottom': '10px'}) # New button
], style={'textAlign': 'center'}),
# Total Energy Gauges Panel
html.Div([
dcc.Graph(
id='total-energy-gauge',
style={'height': '300px', 'width': '60%', 'display': 'inline-block', 'margin': '10px'}
),
dcc.Graph(
id='current-km-gauge',
style={'height': '300px', 'width': '30%', 'display': 'inline-block', 'margin': '10px'}
)
], style={'marginBottom': '10px', 'textAlign': 'center', 'clear': 'both'}), # Reduced margin
# Overall Efficiency and Power Consumption Gauges Panel
html.Div([
dcc.Graph(id='overall-efficiency-gauge', style={'height': '300px', 'width': '30%', 'display': 'inline-block'}),
dcc.Graph(id='power-consumption-gauge', style={'height': '300px', 'width': '30%', 'display': 'inline-block'}),
dcc.Graph(id='power-consumption-without-grid-losses-gauge', style={'height': '300px', 'width': '30%', 'display': 'inline-block'})
], style={'marginBottom': '10px', 'textAlign': 'center', 'clear': 'both'}), # Reduced margin
# Session Stats Gauges Panel
html.Div([
dcc.Graph(id='total-sessions-gauge', style={'height': '300px', 'width': '30%', 'display': 'inline-block'}),
dcc.Graph(id='successful-sessions-gauge', style={'height': '300px', 'width': '30%', 'display': 'inline-block'}),
dcc.Graph(id='failed-sessions-gauge', style={'height': '300px', 'width': '30%', 'display': 'inline-block'}),
], style={'marginBottom': '10px', 'textAlign': 'center', 'clear': 'both'}), # Reduced margin
# Top Providers
html.Div([
html.Div([
html.H4("Top 5 Successful Providers"),
html.Ul(id='top-successful-providers', style={'listStyleType': 'none', 'padding': '0'})
], style={'width': '45%', 'display': 'inline-block', 'verticalAlign': 'top'}),
html.Div([
html.H4("Top 5 Failed Providers"),
html.Ul(id='top-failed-providers', style={'listStyleType': 'none', 'padding': '0'})
], style={'width': '45%', 'display': 'inline-block', 'verticalAlign': 'top', 'marginRight': '5%'}),
], style={'textAlign': 'center', 'marginBottom': '10px', 'clear': 'both'}), # Reduced margin
# SOC Statistics
html.Div([
html.H4("SOC Statistics"),
html.Div(id='soc-stats', style={'textAlign': 'center', 'marginBottom': '10px', 'listStyleType': 'none'}) # New section for SOC statistics
], style={'textAlign': 'center', 'marginBottom': '10px', 'clear': 'both'}), # Reduced margin
# Overview Scatterplots
html.Div([
dcc.Graph(
id='overview-scatterplot',
style={'height': '400px', 'border': '2px solid #1f77b4', 'borderRadius': '10px', 'marginBottom': '10px'} # Reduced margin
),
dcc.Graph(
id='average-gridpower-scatterplot',
style={'height': '400px', 'border': '2px solid #1f77b4', 'borderRadius': '10px', 'marginBottom': '10px'} # Reduced margin
),
dcc.Graph(
id='estimated-battery-capacity-scatterplot',
style={'height': '400px', 'border': '2px solid #1f77b4', 'borderRadius': '10px', 'marginBottom': '10px'} # Reduced margin
)
], style={'marginBottom': '10px'}), # Reduced margin
# Second map for charging locations
html.Div([
html.Iframe(id='charging-locations-map', style={'width': '100%', 'height': '400px', 'border': '2px solid #1f77b4', 'borderRadius': '10px', 'marginBottom': '10px'}) # Reduced margin
], style={'marginBottom': '10px'}), # Reduced margin
# Dropdown to select session
html.Div([
html.Label('Select Charging Session by Time and Location:', style={'fontWeight': 'bold', 'color': '#1f77b4'}),
dcc.Dropdown(
id='session-dropdown',
options=[],
value=None,
style={'border': '2px solid #1f77b4', 'borderRadius': '5px'}
)
], style={'width': '50%', 'margin': 'auto', 'marginBottom': '10px'}), # Reduced margin
# Plaintext output
html.Div([
html.H3(id='session-info', style={'textAlign': 'center', 'color': '#1f77b4', 'marginBottom': '10px'}) # Reduced margin
]),
# Dashboard layout with compact and modern design
html.Div([
html.Div([
html.Div([
html.Iframe(id='range-map', style={'width': '100%', 'height': '300px', 'border': '2px solid #1f77b4', 'borderRadius': '10px', 'marginBottom': '10px'}) # Reduced margin
]),
html.Div([
html.Div([
dcc.Graph(
id='combined-gauges',
style={'height': '800px', 'border': '2px solid #1f77b4', 'borderRadius': '10px', 'marginBottom': '10px'}) # Reduced margin
])
])
], style={'flex': '1', 'paddingLeft': '10px'}),
html.Div([
dcc.Graph(id='charge-details-graph', style={'height': '300px', 'border': '2px solid #1f77b4', 'borderRadius': '10px', 'marginBottom': '10px'}), # Reduced margin
dcc.Graph(id='grid-power-graph', style={'height': '300px', 'border': '2px solid #1f77b4', 'borderRadius': '10px'}) # Reduced margin
], style={'flex': '1', 'paddingRight': '10px'})
], style={'display': 'flex', 'flexDirection': 'row', 'gap': '20px', 'alignItems': 'stretch'}),
# Imprint
html.Div([
html.P(
'Imprint: Freie Netze München e. V. / Parkstraße 28 / 82131 Gauting',
style={'textAlign': 'center', 'color': 'black', 'fontWeight': 'bold'}
)
]),
])
# Register callbacks
register_callbacks(app)
# Run the app
if __name__ == '__main__':
app.run_server(debug=False, port=8050, threaded=True, host='0.0.0.0')