Esvanth commited on
Commit
d7a2d2d
Β·
1 Parent(s): 0ed43fe

Add Task 6 Business Case tab

Browse files
Files changed (1) hide show
  1. app.py +181 -1
app.py CHANGED
@@ -373,12 +373,13 @@ def _met(y,yh):
373
  st.markdown("<h2 style='margin:0 0 12px;color:#1e293b'>πŸ›’ EcoCart AI System</h2>",
374
  unsafe_allow_html=True)
375
 
376
- T1,T2,T3,T4,T5=st.tabs([
377
  "πŸ€– Task 1 β€” AI Agents",
378
  "βš–οΈ Task 2 β€” Bias Check",
379
  "πŸ—ΊοΈ Task 3 β€” Route Finder",
380
  "πŸ“Š Task 4 β€” Speed Test",
381
  "πŸ“ˆ Task 5 β€” Sales Forecast",
 
382
  ])
383
 
384
  # ══════════════════════════════════════════════════════════════════════════════
@@ -858,3 +859,182 @@ with T5:
858
  "LR Prediction":lp.round(1),"RF Prediction":rp.round(1),
859
  "LR Error":(y-lp).round(1),"RF Error":(y-rp).round(1)}),
860
  use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
  st.markdown("<h2 style='margin:0 0 12px;color:#1e293b'>πŸ›’ EcoCart AI System</h2>",
374
  unsafe_allow_html=True)
375
 
376
+ T1,T2,T3,T4,T5,T6=st.tabs([
377
  "πŸ€– Task 1 β€” AI Agents",
378
  "βš–οΈ Task 2 β€” Bias Check",
379
  "πŸ—ΊοΈ Task 3 β€” Route Finder",
380
  "πŸ“Š Task 4 β€” Speed Test",
381
  "πŸ“ˆ Task 5 β€” Sales Forecast",
382
+ "πŸ’Ό Task 6 β€” Business Case",
383
  ])
384
 
385
  # ══════════════════════════════════════════════════════════════════════════════
 
859
  "LR Prediction":lp.round(1),"RF Prediction":rp.round(1),
860
  "LR Error":(y-lp).round(1),"RF Error":(y-rp).round(1)}),
861
  use_container_width=True)
862
+
863
+ # ══════════════════════════════════════════════════════════════════════════════
864
+ # TASK 6 β€” BUSINESS CASE (optional for AI students β€” covers 20% Business Viability)
865
+ # ══════════════════════════════════════════════════════════════════════════════
866
+ with T6:
867
+ st.markdown("### Business Case β€” ROI & Sustainability Impact")
868
+ st.caption("Adjust the assumptions below to model EcoCart's financial and environmental gains from the AI system.")
869
+
870
+ # ── assumption sliders ────────────────────────────────────────────────────
871
+ st.markdown("#### Your business assumptions")
872
+ c1,c2,c3=st.columns(3)
873
+ with c1:
874
+ fleet = st.slider("Fleet size (vehicles)", 5, 100, 30, 5)
875
+ deliveries = st.slider("Deliveries per vehicle/day", 10, 80, 40, 5)
876
+ avg_km = st.slider("Avg km per delivery", 2, 30, 12, 1)
877
+ with c2:
878
+ fuel_cost = st.slider("Fuel cost per km (€)", 0.10, 0.60, 0.32, 0.01, format="€%.2f")
879
+ driver_wage = st.slider("Driver hourly wage (€)", 10, 35, 18, 1, format="€%d")
880
+ working_days= st.slider("Working days per year", 200, 365, 300, 10)
881
+ with c3:
882
+ route_saving_pct = st.slider("Route saving from A* (%)", 5, 35, 18, 1, format="%d%%",
883
+ help="How much shorter routes become with A* vs manual planning")
884
+ forecast_waste_pct= st.slider("Waste cut from forecasting (%)", 5, 40, 22, 1, format="%d%%",
885
+ help="Reduction in overstock/understock from ML demand prediction")
886
+ segment_revenue = st.slider("Extra revenue from fair targeting (€k/yr)", 10, 200, 65, 5)
887
+
888
+ st.divider()
889
+
890
+ # ── calculations ──────────────────────────────────────────────────────────
891
+ total_deliveries_yr = fleet * deliveries * working_days
892
+ total_km_yr = total_deliveries_yr * avg_km
893
+
894
+ # route savings
895
+ km_saved = total_km_yr * route_saving_pct / 100
896
+ fuel_saved = km_saved * fuel_cost
897
+ time_saved_hrs = km_saved / 40 # assume 40 km/h avg
898
+ driver_time_saved = time_saved_hrs * driver_wage
899
+ route_total_saving = fuel_saved + driver_time_saved
900
+
901
+ # CO2 savings (diesel: ~0.27 kg CO2/km urban, ~0.21 rural β€” avg 0.24)
902
+ co2_saved_kg = km_saved * 0.24
903
+ co2_saved_tonnes = co2_saved_kg / 1000
904
+
905
+ # forecast savings (assume avg inventory cost €8 per unit, 500 SKUs)
906
+ inventory_cost_base = 500 * 8 * working_days * 0.05 # 5% daily holding cost approximation
907
+ forecast_saving = inventory_cost_base * forecast_waste_pct / 100
908
+
909
+ # segmentation revenue uplift
910
+ segment_saving = segment_revenue * 1000
911
+
912
+ # total benefit
913
+ total_benefit = route_total_saving + forecast_saving + segment_saving
914
+
915
+ # implementation cost (one-off dev + annual cloud)
916
+ dev_cost = 45000 # one-off
917
+ annual_ops = 8000 # cloud + maintenance per year
918
+ total_cost_yr1 = dev_cost + annual_ops
919
+ total_cost_yr3 = dev_cost + annual_ops * 3
920
+
921
+ roi_yr1 = round((total_benefit - total_cost_yr1) / total_cost_yr1 * 100, 1)
922
+ roi_yr3 = round((total_benefit * 3 - total_cost_yr3) / total_cost_yr3 * 100, 1)
923
+ payback = round(total_cost_yr1 / total_benefit * 12, 1) # months
924
+
925
+ # ── headline metrics ──────────────────────────────────────────────────────
926
+ st.markdown("#### Results")
927
+ m=st.columns(4)
928
+ m[0].metric("Annual cost saving", f"€{total_benefit/1000:.1f}k")
929
+ m[1].metric("Year-1 ROI", f"{roi_yr1}%",
930
+ delta="positive" if roi_yr1>0 else "negative")
931
+ m[2].metric("Payback period", f"{payback} months")
932
+ m[3].metric("COβ‚‚ saved per year", f"{co2_saved_tonnes:.1f} tonnes")
933
+
934
+ # ── savings breakdown bar chart ───────────────────────────────────────────
935
+ fig_roi=go.Figure()
936
+ categories=["Route\nOptimisation","Demand\nForecasting","Fairer\nSegmentation"]
937
+ values=[round(route_total_saving/1000,1),
938
+ round(forecast_saving/1000,1),
939
+ round(segment_saving/1000,1)]
940
+ colors=[BLUE,GREEN,AMBER]
941
+ fig_roi.add_trace(go.Bar(
942
+ x=categories, y=values,
943
+ marker_color=colors,
944
+ text=[f"€{v}k" for v in values],
945
+ textposition="outside", textfont_color=FG,
946
+ hovertemplate="%{x}<br>Saving: €%{y}k/year<extra></extra>",
947
+ width=0.5,
948
+ ))
949
+ fig_roi.update_layout(**_ch(300,"Annual savings breakdown by AI module (€ thousands)"))
950
+ fig_roi.update_xaxes(**_xax())
951
+ fig_roi.update_yaxes(**_yax(title="€ thousands"))
952
+ st.plotly_chart(fig_roi,use_container_width=True)
953
+
954
+ # ── 3-year cumulative ROI line ────────────────────────────────────────────
955
+ years=[0,1,2,3]
956
+ cumulative_benefit=[0, total_benefit, total_benefit*2, total_benefit*3]
957
+ cumulative_cost =[0, total_cost_yr1, total_cost_yr1+annual_ops, total_cost_yr1+annual_ops*2]
958
+ cumulative_net =[b-c for b,c in zip(cumulative_benefit,cumulative_cost)]
959
+
960
+ fig_cum=go.Figure()
961
+ fig_cum.add_trace(go.Scatter(x=years,y=[v/1000 for v in cumulative_benefit],
962
+ name="Cumulative benefit",line=dict(color=GREEN,width=2.5),
963
+ hovertemplate="Year %{x}<br>Benefit: €%{y:.1f}k<extra></extra>"))
964
+ fig_cum.add_trace(go.Scatter(x=years,y=[v/1000 for v in cumulative_cost],
965
+ name="Cumulative cost",line=dict(color=RED,width=2.5,dash="dash"),
966
+ hovertemplate="Year %{x}<br>Cost: €%{y:.1f}k<extra></extra>"))
967
+ fig_cum.add_trace(go.Scatter(x=years,y=[v/1000 for v in cumulative_net],
968
+ name="Net gain",line=dict(color=BLUE,width=2.5,dash="dot"),
969
+ fill="tozeroy",fillcolor=f"{BLUE}18",
970
+ hovertemplate="Year %{x}<br>Net: €%{y:.1f}k<extra></extra>"))
971
+ fig_cum.add_hline(y=0,line_color="#94a3b8",line_width=1.5,line_dash="dash")
972
+ fig_cum.update_layout(**_ch(300,"3-year cumulative ROI projection (€ thousands)"))
973
+ fig_cum.update_xaxes(**_xax(title="Year",tickvals=[0,1,2,3],ticktext=["Now","Year 1","Year 2","Year 3"]))
974
+ fig_cum.update_yaxes(**_yax(title="€ thousands"))
975
+ st.plotly_chart(fig_cum,use_container_width=True)
976
+
977
+ # ── CO2 & sustainability ──────────────────────────────────────────────────
978
+ st.markdown("#### Sustainability Impact")
979
+ sc=st.columns(3)
980
+ trees_equiv = round(co2_saved_tonnes * 45) # ~45 trees absorb 1 tonne CO2/year
981
+ cars_equiv = round(co2_saved_tonnes / 2.3) # avg car emits 2.3 tonnes CO2/year
982
+ sc[0].metric("COβ‚‚ saved per year", f"{co2_saved_tonnes:.1f} tonnes")
983
+ sc[1].metric("Equivalent trees planted", f"{trees_equiv:,}")
984
+ sc[2].metric("Cars taken off the road", f"{cars_equiv:,}")
985
+
986
+ fig_co2=go.Figure(go.Bar(
987
+ x=["Fuel savings\n(route opt.)","Green routing\n(COβ‚‚ mode)","Total COβ‚‚\nreduction"],
988
+ y=[round(co2_saved_tonnes*0.75,1), round(co2_saved_tonnes*0.25,1), round(co2_saved_tonnes,1)],
989
+ marker_color=[GREEN,BLUE,AMBER],
990
+ text=[f"{v:.1f}t" for v in [co2_saved_tonnes*0.75, co2_saved_tonnes*0.25, co2_saved_tonnes]],
991
+ textposition="outside",textfont_color=FG,width=0.45,
992
+ hovertemplate="%{x}<br>%{y:.1f} tonnes COβ‚‚/year<extra></extra>",
993
+ ))
994
+ fig_co2.update_layout(**_ch(280,"Annual COβ‚‚ reduction (tonnes)"))
995
+ fig_co2.update_xaxes(**_xax())
996
+ fig_co2.update_yaxes(**_yax(title="Tonnes COβ‚‚"))
997
+ st.plotly_chart(fig_co2,use_container_width=True)
998
+
999
+ # ── implementation roadmap ────────────────────────────────────────────────
1000
+ st.markdown("#### Implementation Roadmap")
1001
+ phases=[
1002
+ dict(Task="Phase 1: Route Optimisation (A*)", Start=0, Finish=2, Color=BLUE),
1003
+ dict(Task="Phase 2: Bias Fix (Segmentation)", Start=1, Finish=3, Color=AMBER),
1004
+ dict(Task="Phase 3: Demand Forecasting (ML)", Start=2, Finish=5, Color=GREEN),
1005
+ dict(Task="Phase 4: Integration & Testing", Start=4, Finish=6, Color=PURPLE),
1006
+ dict(Task="Phase 5: Full Deployment", Start=6, Finish=8, Color=RED),
1007
+ ]
1008
+ fig_rm=go.Figure()
1009
+ for i,p in enumerate(phases):
1010
+ fig_rm.add_trace(go.Bar(
1011
+ x=[p["Finish"]-p["Start"]], y=[p["Task"]],
1012
+ base=p["Start"], orientation="h",
1013
+ marker_color=p["Color"], marker_opacity=0.85,
1014
+ text=f"Month {p['Start']+1}–{p['Finish']}",
1015
+ textposition="inside", textfont=dict(color="#fff",size=11),
1016
+ showlegend=False,
1017
+ hovertemplate=f"<b>{p['Task']}</b><br>Month {p['Start']+1} β†’ {p['Finish']}<extra></extra>",
1018
+ ))
1019
+ fig_rm.update_layout(**_ch(280,"Deployment timeline (months)"))
1020
+ fig_rm.update_xaxes(**_xax(title="Month",tickvals=list(range(9)),
1021
+ ticktext=[f"M{i}" for i in range(9)]))
1022
+ fig_rm.update_yaxes(**_yax(autorange="reversed"))
1023
+ st.plotly_chart(fig_rm,use_container_width=True)
1024
+
1025
+ # ── summary box ───────────────────────────────────────────────────────────
1026
+ st.markdown(
1027
+ f"<div style='background:#f0fdf4;border:1px solid #bbf7d0;border-radius:12px;"
1028
+ f"padding:20px 24px;margin-top:8px'>"
1029
+ f"<b style='font-size:1rem;color:#065f46'>Business Summary</b><br><br>"
1030
+ f"EcoCart's AI system delivers an estimated <b>€{total_benefit/1000:.0f}k in annual savings</b> "
1031
+ f"across three areas: smarter routing (A* reduces km by {route_saving_pct}%), "
1032
+ f"better stock management (ML cuts waste by {forecast_waste_pct}%), "
1033
+ f"and fairer customer targeting (rural revenue uplift of €{segment_revenue}k). "
1034
+ f"The system pays for itself in <b>{payback} months</b> and generates a "
1035
+ f"<b>{roi_yr3}% ROI over 3 years</b>. "
1036
+ f"It also removes <b>{co2_saved_tonnes:.1f} tonnes of COβ‚‚</b> annually β€” "
1037
+ f"directly supporting EcoCart's sustainability commitments."
1038
+ f"</div>",
1039
+ unsafe_allow_html=True,
1040
+ )