wayne-chi commited on
Commit
d0c7c87
Β·
verified Β·
1 Parent(s): 0246907

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +809 -10
src/streamlit_app.py CHANGED
@@ -1,15 +1,814 @@
1
- # app.py
2
  import streamlit as st
3
- import pkg_resources
 
 
 
 
 
 
4
 
5
- st.title("πŸ“¦ Installed Python Modules")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
- # Get all installed packages
8
- packages = sorted(
9
- [(d.project_name, d.version) for d in pkg_resources.working_set],
10
- key=lambda x: x[0].lower()
 
 
 
 
 
 
 
 
11
  )
12
 
13
- # Display them
14
- for name, version in packages:
15
- st.write(f"{name} β€” {version}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ import pandas as pd
3
+ import matplotlib
4
+ import matplotlib.pyplot as plt
5
+ import plotly.express as px
6
+ import numpy as np
7
+ import plotly.graph_objects as go
8
+ # from blend_logic import run_dummy_prediction
9
 
10
+ ##---- fucntions ------
11
+ import pandas as pd
12
+ import streamlit as st
13
+
14
+ # Load fuel data from CSV (create this file if it doesn't exist)
15
+ FUEL_CSV_PATH = "fuel_properties.csv"
16
+
17
+ def load_fuel_data():
18
+ """Load fuel data from CSV or create default if not exists"""
19
+ try:
20
+ df = pd.read_csv(FUEL_CSV_PATH, index_col=0)
21
+ return df.to_dict('index')
22
+ except FileNotFoundError:
23
+ # Create default fuel properties if file doesn't exist
24
+ default_fuels = {
25
+ "Gasoline": {f"Property{i+1}": round(0.7 + (i*0.02), 1) for i in range(10)},
26
+ "Diesel": {f"Property{i+1}": round(0.8 + (i*0.02), 1) for i in range(10)},
27
+ "Ethanol": {f"Property{i+1}": round(0.75 + (i*0.02), 1) for i in range(10)},
28
+ "Biodiesel": {f"Property{i+1}": round(0.85 + (i*0.02), 1) for i in range(10)},
29
+ "Jet Fuel": {f"Property{i+1}": round(0.78 + (i*0.02), 1) for i in range(10)}
30
+ }
31
+ pd.DataFrame(default_fuels).T.to_csv(FUEL_CSV_PATH)
32
+ return default_fuels
33
+
34
+ # Initialize or load fuel data
35
+ if 'FUEL_PROPERTIES' not in st.session_state:
36
+ st.session_state.FUEL_PROPERTIES = load_fuel_data()
37
 
38
+ def save_fuel_data():
39
+ """Save current fuel data to CSV"""
40
+ pd.DataFrame(st.session_state.FUEL_PROPERTIES).T.to_csv(FUEL_CSV_PATH)
41
+
42
+ # FUEL_PROPERTIES = st.session_state.FUEL_PROPERTIES
43
+
44
+ # ---------------------- Page Config ----------------------
45
+ st.set_page_config(
46
+ layout="wide",
47
+ page_title="Eagle Blend Optimizer",
48
+ page_icon="πŸ¦…",
49
+ initial_sidebar_state="expanded"
50
  )
51
 
52
+ # ---------------------- Custom Styling ---------------------- ##e0e0e0;
53
+
54
+ st.markdown("""
55
+ <style>
56
+
57
+ .block-container {
58
+ padding-top: 1rem;
59
+ }
60
+ /* Main app background */
61
+ .stApp {
62
+ background-color: #f8f5f0;
63
+ overflow: visible;
64
+ padding-top: 0
65
+
66
+ }
67
+ /* Remove unnecessary space at the top */
68
+ /* Remove any fixed headers */
69
+ .stApp > header {
70
+ position: static !important;
71
+ }
72
+
73
+ /* Header styling */
74
+ .header {
75
+ background: linear-gradient(135deg, #654321 0%, #8B4513 100%);
76
+ color: white;
77
+ padding: 2rem 1rem;
78
+ margin-bottom: 2rem;
79
+ border-radius: 0 0 15px 15px;
80
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
81
+ }
82
+
83
+ /* Metric card styling */
84
+ .metric-card {
85
+ background: #ffffff; /* Pure white cards for contrast */
86
+ border-radius: 10px;
87
+ padding: 1.5rem;
88
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
89
+ height: 100%;
90
+ transition: all 0.3s ease;
91
+ border: 1px solid #CFB53B;
92
+ }
93
+
94
+ .metric-card:hover {
95
+ transform: translateY(-3px);
96
+ background: #FFF8E1; /* Very light blue tint on hover */
97
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
98
+ border-color: #8B4513;
99
+ }
100
+
101
+ /* Metric value styling */
102
+ .metric-value {
103
+ color: #8B4513 !important; /* Deep, vibrant blue */
104
+ font-weight: 700;
105
+ font-size: 1.8rem;
106
+ text-shadow: 0 1px 2px rgba(0, 82, 204, 0.1);
107
+ }
108
+
109
+ /* Metric label styling */
110
+ .metric-label {
111
+ color: #654321; /* Navy blue-gray */
112
+ font-weight: 600;
113
+ letter-spacing: 0.5px;
114
+ }
115
+
116
+
117
+ /* Metric delta styling */
118
+ .metric-delta {
119
+ color: #A67C52; /* Medium blue-gray */
120
+ font-size: 0.9rem;
121
+ font-weight: 500;
122
+ }
123
+
124
+ /* Tab styling */
125
+ /* Main tab container */
126
+ .stTabs [data-baseweb="tab-list"] {
127
+ display: flex;
128
+ justify-content: center;
129
+ gap: 6px;
130
+ padding: 8px;
131
+ margin: 0 auto;
132
+ width: 95% !important;
133
+ }
134
+
135
+ /* Individual tabs */
136
+ .stTabs [data-baseweb="tab"] {
137
+ flex: 1; /* Equal width distribution */
138
+ min-width: 0; /* Allows flex to work */
139
+ height: 60px; /* Fixed height or use aspect ratio */
140
+ padding: 0 12px;
141
+ margin: 0;
142
+ font-weight: 600;
143
+ font-size: 1rem;
144
+ color: #654321;
145
+ background: #FFF8E1;
146
+ border: 2px solid #CFB53B;
147
+ border-radius: 12px;
148
+ transition: all 0.3s ease;
149
+ display: flex;
150
+ align-items: center;
151
+ justify-content: center;
152
+ text-align: center;
153
+ }
154
+
155
+ /* Hover state */
156
+ .stTabs [data-baseweb="tab"]:hover {
157
+ background: #FFE8A1;
158
+ transform: translateY(-2px);
159
+ }
160
+
161
+
162
+ /* Active tab */
163
+ .stTabs [aria-selected="true"] {
164
+ background: #654321;
165
+ color: #FFD700 !important;
166
+ border-color: #8B4513;
167
+ font-size: 1.05rem;
168
+ }
169
+
170
+ /* Icon sizing */
171
+ .stTabs [data-baseweb="tab"] svg {
172
+ width: 24px !important;
173
+ height: 24px !important;
174
+ margin-right: 8px !important;
175
+ }
176
+
177
+ /* Button styling */
178
+ .stButton>button {
179
+ background-color: #654321;
180
+ color: #FFD700 !important;
181
+ border-radius: 8px;
182
+ padding: 0.5rem 1rem;
183
+ transition: all 0.3s ease;
184
+ }
185
+
186
+ .stButton>button:hover {
187
+ background-color: #8B4513;
188
+ color: white;
189
+ }
190
+
191
+ /* Dataframe styling */
192
+ .table-container {
193
+ display: flex;
194
+ justify-content: center;
195
+ margin-top: 30px;
196
+ }
197
+ .table-inner {
198
+ width: 50%;
199
+ }
200
+
201
+
202
+ @media only screen and (max-width: 768px) {
203
+ .table-inner {
204
+ width: 90%; /* For mobile */
205
+ }
206
+ }
207
+
208
+ .stDataFrame {
209
+ border-radius: 10px;
210
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
211
+ background-color:white !important;
212
+ border: #CFB53B !important;
213
+ }
214
+
215
+
216
+
217
+ /* Section headers */
218
+ .st-emotion-cache-16txtl3 {
219
+ padding-top: 1rem;
220
+ }
221
+
222
+ /* Custom hr style */
223
+ .custom-divider {
224
+ border: 0;
225
+ height: 1px;
226
+ background: linear-gradient(90deg, transparent, #dee2e6, transparent);
227
+ margin: 2rem 0;
228
+ }
229
+
230
+
231
+ /* Consistent chart styling */
232
+ .stPlotlyChart {
233
+ border-radius: 10px;
234
+ background: white;
235
+ padding: 15px;
236
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
237
+ margin-bottom: 25px;
238
+ }
239
+
240
+
241
+
242
+ /* Match number inputs */
243
+ # .stNumberInput > div {
244
+ # padding: 0.25rem 0.5rem !important;
245
+ # }
246
+
247
+ #/* Better select widget alignment */
248
+ # .stSelectbox > div {
249
+ # margin-bottom: -15px;
250
+ # }
251
+
252
+
253
+ .custom-uploader > label div[data-testid="stFileUploadDropzone"] {
254
+ border: 2px solid #4CAF50;
255
+ background-color: #4CAF50;
256
+ color: white;
257
+ padding: 0.6em 1em;
258
+ border-radius: 0.5em;
259
+ text-align: center;
260
+ cursor: pointer;
261
+ }
262
+ .custom-uploader > label div[data-testid="stFileUploadDropzone"]:hover {
263
+ background-color: #45a049;
264
+ }
265
+
266
+
267
+
268
+
269
+
270
+ /* Color scale adjustments */
271
+ .plotly .colorbar {
272
+ padding: 10px !important;
273
+ color: #654321 !important;
274
+ }
275
+
276
+ </style>
277
+ """, unsafe_allow_html=True)
278
+
279
+ # ---------------------- App Header ----------------------
280
+ st.markdown("""
281
+ <div class="header">
282
+ <h1 style='text-align: center; margin-bottom: 0.5rem;'>πŸ¦… Eagle Blend Optimizer</h1>
283
+ <h4 style='text-align: center; font-weight: 400; margin-top: 0;'>
284
+ AI-Powered Fuel Blend Property Prediction & Optimization
285
+ </h4>
286
+ </div>
287
+ """, unsafe_allow_html=True)
288
+ #------ universal variables
289
+
290
+
291
+ # ---------------------- Tabs ----------------------
292
+ tabs = st.tabs([
293
+ "πŸ“Š Dashboard",
294
+ "πŸŽ›οΈ Blend Designer",
295
+ "πŸ“€ Nothing For Now",
296
+ "βš™οΈ Optimization Engine",
297
+ "πŸ“š Fuel Registry",
298
+ "🧠 Model Insights"
299
+ ])
300
+
301
+ # ---------------------- Dashboard Tab ----------------------
302
+
303
+ with tabs[0]:
304
+ st.subheader("Performance Metrics")
305
+ col1, col2, col3, col4 = st.columns(4)
306
+
307
+ with col1:
308
+ st.markdown("""
309
+ <div class="metric-card">
310
+ <div class="metric-label">Model Accuracy</div>
311
+ <div class="metric-value">94.7%</div>
312
+ <div class="metric-delta">RΒ² Score</div>
313
+ </div>
314
+ """, unsafe_allow_html=True)
315
+
316
+ with col2:
317
+ st.markdown("""
318
+ <div class="metric-card">
319
+ <div class="metric-label">Predictions Made</div>
320
+ <div class="metric-value">12,847</div>
321
+ <div class="metric-delta">Today</div>
322
+ </div>
323
+ """, unsafe_allow_html=True)
324
+
325
+ with col3:
326
+ st.markdown("""
327
+ <div class="metric-card">
328
+ <div class="metric-label">Optimizations</div>
329
+ <div class="metric-value">156</div>
330
+ <div class="metric-delta">This Week</div>
331
+ </div>
332
+ """, unsafe_allow_html=True)
333
+
334
+ with col4:
335
+ st.markdown("""
336
+ <div class="metric-card">
337
+ <div class="metric-label">Cost Savings</div>
338
+ <div class="metric-value">$2.4M</div>
339
+ <div class="metric-delta">Estimated Annual</div>
340
+ </div>
341
+ """, unsafe_allow_html=True)
342
+
343
+
344
+
345
+ st.markdown('<hr class="custom-divider">', unsafe_allow_html=True)
346
+
347
+
348
+
349
+ st.subheader("Current Blend Properties")
350
+ blend_props = {
351
+ "Property 1": 0.847,
352
+ "Property 2": 0.623,
353
+ "Property 3": 0.734,
354
+ "Property 4": 0.912,
355
+ "Property 5": 0.456,
356
+ "Property 6": -1.234,
357
+ }
358
+
359
+ # Enhanced dataframe display
360
+ df = pd.DataFrame(blend_props.items(), columns=["Property", "Value"])
361
+ # st.dataframe(
362
+ # df.style
363
+ # .background_gradient(cmap="YlOrBr", subset=["Value"])
364
+ # .format({"Value": "{:.3f}"}),
365
+ # use_container_width=True
366
+ # )
367
+
368
+ st.markdown('<div class="table-container"><div class="table-inner">', unsafe_allow_html=True)
369
+ st.dataframe(df, use_container_width=True)
370
+ st.markdown('</div></div>', unsafe_allow_html=True)
371
+
372
+
373
+
374
+
375
+ with tabs[1]:
376
+ col_header = st.columns([0.8, 0.2])
377
+ with col_header[0]:
378
+ st.subheader("πŸŽ›οΈ Blend Designer")
379
+ with col_header[1]:
380
+ batch_blend = st.checkbox("Batch Blend Mode", value=False,
381
+ help="Switch between manual input and predefined fuel selection",
382
+ key="batch_blend_mode")
383
+
384
+ # Initialize session state
385
+ if 'show_visualization' not in st.session_state:
386
+ st.session_state.show_visualization = False
387
+ if 'blended_value' not in st.session_state:
388
+ st.session_state.blended_value = None
389
+ if 'selected_property' not in st.session_state:
390
+ st.session_state.selected_property = "Property1"
391
+
392
+ # Batch mode file upload
393
+ if batch_blend:
394
+ st.subheader("πŸ“€ Batch Processing")
395
+ uploaded_file = st.file_uploader("Upload CSV File", type=["csv"], key="Batch_upload")
396
+ weights = [0.1, 0.2, 0.25, 0.15, 0.3] # Default weights for batch mode
397
+
398
+ if not uploaded_file:
399
+ st.warning("Please upload a CSV file for batch processing")
400
+ data_input = None
401
+ else:
402
+ try:
403
+ data_input = pd.read_csv(uploaded_file)
404
+ st.success("File uploaded successfully")
405
+ st.dataframe(data_input.head())
406
+ except Exception as e:
407
+ st.error(f"Error reading file: {str(e)}")
408
+ data_input = None
409
+ else:
410
+ # Regular mode
411
+ data_input = None
412
+ weights, props = [], []
413
+ col1, col2 = st.columns(2)
414
+
415
+ with col1:
416
+ st.markdown("##### βš–οΈ Component Weights")
417
+ for i in range(5):
418
+ weight = st.number_input(
419
+ f"Weight for Component {i+1}",
420
+ min_value=0.0,
421
+ max_value=1.0,
422
+ value=0.2,
423
+ step=0.01,
424
+ key=f"w_{i}"
425
+ )
426
+ weights.append(weight)
427
+
428
+ with col2:
429
+ st.markdown("##### Fuel Selection")
430
+ for i in range(5):
431
+ fuel = st.selectbox(
432
+ f"Component {i+1} Fuel Type",
433
+ options=list(st.session_state.FUEL_PROPERTIES.keys()),
434
+ key=f"fuel_{i}"
435
+ )
436
+ props.append(st.session_state.FUEL_PROPERTIES[fuel])
437
+
438
+ if st.button("βš™οΈ Predict Blended Property", key="predict_btn"):
439
+ if batch_blend:
440
+ if data_input is None:
441
+ st.error("⚠️ Please upload a valid CSV file first!")
442
+ st.session_state.show_visualization = False
443
+ else:
444
+ st.session_state.show_visualization = True
445
+ else:
446
+ if abs(sum(weights) - 1.0) > 0.01:
447
+ st.warning("⚠️ The total of weights must be **1.0**.")
448
+ st.session_state.show_visualization = False
449
+ else:
450
+ st.session_state.show_visualization = True
451
+
452
+ if st.session_state.show_visualization:
453
+ # Show calculation details
454
+ st.subheader("Blend Components Data")
455
+
456
+ if not batch_blend:
457
+ weights_data = {f"Component{i+1}_fraction": weights[i] for i in range(len(weights))}
458
+ props_data = {f"Component{i+1}_{j}": props[i][j] for j in props[i].keys() for i in range(len(props))}
459
+ combined = {**weights_data, **props_data}
460
+ data_input = pd.DataFrame([combined])
461
+
462
+ st.write("Properties:", data_input)
463
+
464
+ # Show visualization only if prediction was made
465
+ if st.session_state.show_visualization:
466
+ if not batch_blend:
467
+ st.markdown('<hr class="custom-divider">', unsafe_allow_html=True)
468
+ st.subheader("Blend Visualization")
469
+
470
+ components = [f"Component {i+1}" for i in range(5)]
471
+
472
+ # 1. Weight Distribution Pie Chart
473
+ col1, col2 = st.columns(2)
474
+ with col1:
475
+ fig1 = px.pie(
476
+ names=components,
477
+ values=weights,
478
+ title="Weight Distribution",
479
+ color_discrete_sequence=['#8B4513', '#CFB53B', '#654321'],
480
+ hole=0.4
481
+ )
482
+ fig1.update_layout(
483
+ margin=dict(t=50, b=10),
484
+ showlegend=False
485
+ )
486
+ fig1.update_traces(
487
+ textposition='inside',
488
+ textinfo='percent+label',
489
+ marker=dict(line=dict(color='#ffffff', width=1))
490
+ )
491
+ st.plotly_chart(fig1, use_container_width=True)
492
+
493
+ # 2. Property Comparison Bar Chart
494
+ with col2:
495
+ # Property selection for fuel mode
496
+ viz_property = st.selectbox(
497
+ "Select Property to View",
498
+ [f"Property{i+1}" for i in range(10)],
499
+ key="viz_property"
500
+ )
501
+ bar_values = [p[viz_property] for p in props]
502
+ blended_value = 123 #Modify
503
+
504
+ fig2 = px.bar(
505
+ x=components,
506
+ y=bar_values,
507
+ title=f"{viz_property} Values",
508
+ color=bar_values,
509
+ color_continuous_scale='YlOrBr'
510
+ )
511
+ fig2.update_layout(
512
+ yaxis_title=viz_property,
513
+ xaxis_title="Component",
514
+ margin=dict(t=50, b=10),
515
+ coloraxis_showscale=False
516
+ )
517
+
518
+ fig2.add_hline(
519
+ y=blended_value,
520
+ line_dash="dot",
521
+ line_color="#ff6600",
522
+ annotation_text="Blended Value",
523
+ annotation_position="top right"
524
+ )
525
+ st.plotly_chart(fig2, use_container_width=True)
526
+
527
+ # Display the calculated value prominently
528
+ st.markdown(f"""
529
+ <div style="
530
+ background-color: #FAF3E6;
531
+ border-left: 4px solid #8B4513;
532
+ border-radius: 4px;
533
+ padding: 12px;
534
+ margin: 12px 0;
535
+ ">
536
+ <p style="margin: 0; color: #654321;
537
+ font-size: 2.2rem;
538
+ font-weight: 800;
539
+ color: #000;
540
+ text-align:center;">
541
+ Calculated <strong>{viz_property}</strong> =
542
+ <strong style="color: #000">{blended_value:.4f}</strong>
543
+ </p>
544
+ </div>
545
+ """, unsafe_allow_html=True)
546
+ else:
547
+ # Batch mode visualization placeholder
548
+ st.markdown('<hr class="custom-divider">', unsafe_allow_html=True)
549
+ st.subheader("Batch Processing Results")
550
+ st.dataframe(data_input, use_container_width=True)
551
+ # st.info("Batch processing complete. Add custom visualizations here.")
552
+
553
+
554
+ with tabs[2]:
555
+ st.subheader("πŸ“€ Nothing FOr NOw")
556
+ # uploaded_file = st.file_uploader("Upload CSV File", type=["csv"])
557
+
558
+ # if uploaded_file:
559
+ # df = pd.read_csv(uploaded_file)
560
+ # st.success("File uploaded successfully")
561
+ # st.dataframe(df.head())
562
+
563
+ # if st.button("βš™οΈ Run Batch Prediction"):
564
+ # result_df = df.copy()
565
+ # # result_df["Predicted_Property"] = df.apply(
566
+ # # lambda row: run_dummy_prediction(row.values[:5], row.values[5:10]), axis=1
567
+ # # )
568
+ # st.success("Batch prediction completed")
569
+ # st.dataframe(result_df.head())
570
+ # csv = result_df.to_csv(index=False).encode("utf-8")
571
+ # st.download_button("Download Results", csv, "prediction_results.csv", "text/csv")
572
+
573
+
574
+
575
+ with tabs[3]:
576
+ st.subheader("βš™οΈ Optimization Engine")
577
+
578
+ # Pareto frontier demo
579
+ st.markdown("#### Cost vs Performance Trade-off")
580
+ np.random.seed(42)
581
+ optimization_data = pd.DataFrame({
582
+ 'Cost ($/ton)': np.random.uniform(100, 300, 50),
583
+ 'Performance Score': np.random.uniform(70, 95, 50)
584
+ })
585
+
586
+ fig3 = px.scatter(
587
+ optimization_data,
588
+ x='Cost ($/ton)',
589
+ y='Performance Score',
590
+ title="Potential Blend Formulations",
591
+ color='Performance Score',
592
+ color_continuous_scale='YlOrBr'
593
+ )
594
+
595
+ # Add dummy pareto frontier
596
+ x_pareto = np.linspace(100, 300, 10)
597
+ y_pareto = 95 - 0.1*(x_pareto-100)
598
+ fig3.add_trace(px.line(
599
+ x=x_pareto,
600
+ y=y_pareto,
601
+ color_discrete_sequence= ['#8B4513', '#CFB53B', '#654321']
602
+ ).data[0])
603
+
604
+ fig3.update_layout(
605
+ showlegend=False,
606
+ annotations=[
607
+ dict(
608
+ x=200,
609
+ y=88,
610
+ text="Pareto Frontier",
611
+ showarrow=True,
612
+ arrowhead=1,
613
+ ax=-50,
614
+ ay=-30
615
+ )
616
+ ]
617
+ )
618
+ st.plotly_chart(fig3, use_container_width=True)
619
+
620
+ # Blend optimization history
621
+ st.markdown("#### Optimization Progress")
622
+ iterations = np.arange(20)
623
+ performance = np.concatenate([np.linspace(70, 85, 10), np.linspace(85, 89, 10)])
624
+
625
+ fig4 = px.line(
626
+ x=iterations,
627
+ y=performance,
628
+ title="Best Performance by Iteration",
629
+ markers=True
630
+ )
631
+ fig4.update_traces(
632
+ line_color='#1d3b58',
633
+ marker_color='#2c5282',
634
+ line_width=2.5
635
+ )
636
+ fig4.update_layout(
637
+ yaxis_title="Performance Score",
638
+ xaxis_title="Iteration"
639
+ )
640
+ st.plotly_chart(fig4, use_container_width=True)
641
+
642
+
643
+
644
+ with tabs[4]:
645
+ st.subheader("πŸ“š Fuel Registry") # Changed to book emoji for registry
646
+
647
+ # Button to add new fuel
648
+ st.markdown("#### βž• Add a New Fuel Type")
649
+ with st.expander("Click to Add New Fuel", expanded=False):
650
+ with st.form("new_fuel_form", clear_on_submit=False):
651
+ fuel_name = st.text_input("Fuel Name", placeholder="e.g. Bioethanol")
652
+
653
+ cols = st.columns(5)
654
+ properties = {}
655
+ for i in range(10):
656
+ with cols[i % 5]:
657
+ prop_val = st.number_input(
658
+ f"Property {i+1}",
659
+ min_value=0.0,
660
+ step=0.1,
661
+ key=f"prop_{i}",
662
+ format="%.2f"
663
+ )
664
+ properties[f"Property{i+1}"] = round(prop_val, 2)
665
+
666
+ col1, col2 = st.columns(2)
667
+ with col1:
668
+ submitted = st.form_submit_button("πŸ’Ύ Save Fuel", use_container_width=True)
669
+ with col2:
670
+ cancelled = st.form_submit_button("❌ Cancel", use_container_width=True)
671
+
672
+ if submitted:
673
+ if not fuel_name.strip():
674
+ st.warning("Fuel name cannot be empty.")
675
+ elif fuel_name in st.session_state.FUEL_PROPERTIES:
676
+ st.error(f"{fuel_name} already exists in registry.")
677
+ else:
678
+ # Update both session state and CSV
679
+ st.session_state.FUEL_PROPERTIES[fuel_name] = properties
680
+ save_fuel_data()
681
+ st.success(f"{fuel_name} successfully added!")
682
+ st.rerun() # Refresh to show new fuel
683
+
684
+ if cancelled:
685
+ st.rerun()
686
+
687
+ with st.expander("Batch Add New Fuel", expanded=False):
688
+ uploaded_file = st.file_uploader(
689
+ "πŸ“€ Upload Fuel Batch (CSV)",
690
+ type=['csv'],
691
+ accept_multiple_files=False,
692
+ key="fuel_uploader",
693
+ help="Upload a CSV file with the same format as the exported registry"
694
+ )
695
+ if uploaded_file is not None:
696
+ try:
697
+ new_fuels = pd.read_csv(uploaded_file, index_col=0).to_dict('index')
698
+
699
+ # Check for duplicates
700
+ duplicates = [name for name in new_fuels if name in st.session_state.FUEL_PROPERTIES]
701
+
702
+ if duplicates:
703
+ st.warning(f"These fuels already exist and won't be updated: {', '.join(duplicates)}")
704
+ # Only add new fuels
705
+ new_fuels = {name: props for name, props in new_fuels.items()
706
+ if name not in st.session_state.FUEL_PROPERTIES}
707
+
708
+ if new_fuels:
709
+ st.session_state.FUEL_PROPERTIES.update(new_fuels)
710
+ save_fuel_data()
711
+ st.success(f"Added {len(new_fuels)} new fuel(s) to registry!")
712
+ st.rerun()
713
+ else:
714
+ st.info("No new fuels to add from the uploaded file.")
715
+
716
+ except Exception as e:
717
+ st.error(f"Error processing file: {str(e)}")
718
+ st.error("Please ensure the file matches the expected format")
719
+
720
+ # Display current fuel properties
721
+ st.markdown("#### πŸ” Current Fuel Properties")
722
+ st.dataframe(
723
+ pd.DataFrame(st.session_state.FUEL_PROPERTIES).T.style
724
+ .background_gradient(cmap="YlOrBr", axis=None)
725
+ .format(precision=2),
726
+ use_container_width=True,
727
+ height=(len(st.session_state.FUEL_PROPERTIES) + 1) * 35 + 3,
728
+ hide_index=False
729
+ )
730
+
731
+ # File operations section
732
+
733
+
734
+ st.download_button(
735
+ label="πŸ“₯ Download Registry (CSV)",
736
+ data=pd.DataFrame(st.session_state.FUEL_PROPERTIES).T.to_csv().encode('utf-8'),
737
+ file_name='fuel_properties.csv',
738
+ mime='text/csv',
739
+ # use_container_width=True
740
+ )
741
+
742
+
743
+
744
+
745
+
746
+
747
+
748
+
749
+
750
+
751
+
752
+ with tabs[5]:
753
+ st.subheader("🧠 Model Insights")
754
+
755
+ # Feature importance
756
+ st.markdown("#### Property Importance")
757
+ features = ['Property 1', 'Property 2', 'Property 3', 'Property 4', 'Property 5']
758
+ importance = np.array([0.35, 0.25, 0.2, 0.15, 0.05])
759
+
760
+ fig5 = px.bar(
761
+ x=importance,
762
+ y=features,
763
+ orientation='h',
764
+ title="Feature Importance for Blend Prediction",
765
+ color=importance,
766
+ color_continuous_scale='YlOrBr'
767
+ )
768
+ fig5.update_layout(
769
+ xaxis_title="Importance Score",
770
+ yaxis_title="Property",
771
+ coloraxis_showscale=False
772
+ )
773
+ st.plotly_chart(fig5, use_container_width=True)
774
+
775
+ # SHAP values demo
776
+ st.markdown("#### Property Impact Direction")
777
+ fig6 = px.scatter(
778
+ x=np.random.randn(100),
779
+ y=np.random.randn(100),
780
+ color=np.random.choice(features, 100),
781
+ title="SHAP Values (Simulated)",
782
+ labels={'x': 'Impact on Prediction', 'y': 'Property Value'}
783
+ )
784
+ fig6.update_traces(
785
+ marker=dict(size=10, opacity=0.7),
786
+ selector=dict(mode='markers')
787
+ )
788
+ fig6.add_vline(x=0, line_width=1, line_dash="dash")
789
+ st.plotly_chart(fig6, use_container_width=True)
790
+
791
+
792
+
793
+ # st.markdown("""
794
+ # <style>
795
+ # /* Consistent chart styling */
796
+ # .stPlotlyChart {
797
+ # border-radius: 10px;
798
+ # background: white;
799
+ # padding: 15px;
800
+ # box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
801
+ # margin-bottom: 25px;
802
+ # }
803
+
804
+ # /* Better select widget alignment */
805
+ # .stSelectbox > div {
806
+ # margin-bottom: -15px;
807
+ # }
808
+
809
+ # /* Color scale adjustments */
810
+ # .plotly .colorbar {
811
+ # padding: 10px !important;
812
+ # }
813
+ # </style>
814
+ # """, unsafe_allow_html=True)