Esvanth commited on
Commit
1a2c8bc
Β·
1 Parent(s): 30b6a05

Add output folder tracking, report generator, sidebar download button

Browse files
Files changed (2) hide show
  1. app.py +190 -8
  2. requirements.txt +1 -0
app.py CHANGED
@@ -18,9 +18,167 @@ os.makedirs("output", exist_ok=True)
18
  import numpy as np
19
  import pandas as pd
20
  import streamlit as st
 
 
 
21
  import plotly.graph_objects as go
22
  from plotly.subplots import make_subplots
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  # ── page config ───────────────────────────────────────────────────────────────
25
  st.set_page_config(page_title="EcoCart AI", layout="wide",
26
  initial_sidebar_state="expanded")
@@ -94,15 +252,36 @@ with st.sidebar:
94
  5. Read the *What does this tell us?* section
95
  """)
96
  st.divider()
97
- st.markdown("**Tasks covered:**")
98
- st.markdown("πŸ€– Task 1 β€” AI Agent Types")
99
- st.markdown("βš–οΈ Task 2 β€” Bias in Segmentation")
100
- st.markdown("πŸ—ΊοΈ Task 3 β€” Search Algorithms")
101
- st.markdown("πŸ“Š Task 4 β€” A* vs IDA* Benchmark")
102
- st.markdown("πŸ“ˆ Task 5 β€” Demand Forecasting")
103
- st.markdown("πŸ’Ό Task 6 β€” Business Case")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  st.divider()
105
- st.caption("All outputs in Tasks 2–5 are generated by running the actual task Python scripts.")
106
 
107
  # ── header ────────────────────────────────────────────────────────────────────
108
  st.markdown("<h2 style='margin:0 0 16px;color:#1e293b'>EcoCart AI System</h2>",
@@ -306,6 +485,7 @@ exists and applies a fix to make the results fair.
306
 
307
  with st.spinner("Running task2_segmentation.py..."):
308
  t2_output = _run_task2()
 
309
 
310
  st.markdown("<div class='step-box'><span class='step-num'>2</span>"
311
  "<b>Terminal output from task2_segmentation.py</b></div>",
@@ -376,6 +556,7 @@ how each algorithm explores the network step by step.
376
 
377
  with st.spinner("Running task3_4_routing.py..."):
378
  t3_output = _run_task3()
 
379
 
380
  st.markdown("<div class='step-box'><span class='step-num'>2</span>"
381
  "<b>Terminal output from task3_4_routing.py</b></div>",
@@ -664,6 +845,7 @@ on 140 unseen test days.
664
 
665
  with st.spinner("Running task5_forecasting.py..."):
666
  t5_output = _run_task5()
 
667
 
668
  st.markdown("<div class='step-box'><span class='step-num'>2</span>"
669
  "<b>Terminal output from task5_forecasting.py</b></div>",
 
18
  import numpy as np
19
  import pandas as pd
20
  import streamlit as st
21
+ from docx import Document
22
+ from docx.shared import Inches, Pt, RGBColor
23
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
24
  import plotly.graph_objects as go
25
  from plotly.subplots import make_subplots
26
 
27
+ # ══════════════════════════════════════════════════════════════════════════════
28
+ # REPORT GENERATOR
29
+ # ══════════════════════════════════════════════════════════════════════════════
30
+
31
+ def _build_report(t2_text, t3_text, t5_text):
32
+ """Build a Word report from the task outputs stored in session state."""
33
+ TNR = "Times New Roman"
34
+ doc = Document()
35
+
36
+ # default style
37
+ doc.styles["Normal"].font.name = TNR
38
+ doc.styles["Normal"].font.size = Pt(12)
39
+ doc.styles["Normal"].paragraph_format.space_after = Pt(8)
40
+ for lvl, sz in [(1,14),(2,13)]:
41
+ s = doc.styles[f"Heading {lvl}"]
42
+ s.font.name = TNR; s.font.bold = True; s.font.size = Pt(sz)
43
+ s.font.color.rgb = RGBColor(0,0,0)
44
+ s.paragraph_format.space_before = Pt(12)
45
+ s.paragraph_format.space_after = Pt(4)
46
+
47
+ def H(txt, lv=1):
48
+ p = doc.add_heading(txt, level=lv)
49
+ p.alignment = WD_ALIGN_PARAGRAPH.LEFT
50
+ for r in p.runs: r.font.name=TNR; r.font.color.rgb=RGBColor(0,0,0)
51
+
52
+ def P(txt):
53
+ p = doc.add_paragraph()
54
+ p.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
55
+ p.paragraph_format.space_after = Pt(8)
56
+ r = p.add_run(txt); r.font.name=TNR; r.font.size=Pt(12)
57
+
58
+ def CODE(txt):
59
+ p = doc.add_paragraph()
60
+ p.paragraph_format.space_after = Pt(6)
61
+ r = p.add_run(txt)
62
+ r.font.name = "Courier New"; r.font.size = Pt(9)
63
+
64
+ def IMG(path, caption="", width=5.5):
65
+ if os.path.exists(path):
66
+ doc.add_picture(path, width=Inches(width))
67
+ doc.paragraphs[-1].alignment = WD_ALIGN_PARAGRAPH.CENTER
68
+ if caption:
69
+ cp = doc.add_paragraph(caption)
70
+ cp.alignment = WD_ALIGN_PARAGRAPH.CENTER
71
+ cp.paragraph_format.space_after = Pt(8)
72
+ for r in cp.runs: r.font.name=TNR; r.font.size=Pt(10); r.font.italic=True
73
+
74
+ def SP(): doc.add_paragraph("").paragraph_format.space_after = Pt(4)
75
+
76
+ # ── cover ─────────────────────────────────────────────────────────────────
77
+ SP(); SP()
78
+ p = doc.add_paragraph()
79
+ p.alignment = WD_ALIGN_PARAGRAPH.CENTER
80
+ r = p.add_run("EcoCart AI System")
81
+ r.font.name=TNR; r.font.size=Pt(24); r.font.bold=True
82
+ p.paragraph_format.space_after = Pt(8)
83
+
84
+ p2 = doc.add_paragraph()
85
+ p2.alignment = WD_ALIGN_PARAGRAPH.CENTER
86
+ r2 = p2.add_run("Technical Report β€” TABA Section II")
87
+ r2.font.name=TNR; r2.font.size=Pt(14)
88
+ p2.paragraph_format.space_after = Pt(20)
89
+
90
+ for line in ["National College of Ireland",
91
+ "MSc Artificial Intelligence",
92
+ "Fundamentals of Artificial Intelligence", "May 2026"]:
93
+ lp = doc.add_paragraph(); lp.alignment = WD_ALIGN_PARAGRAPH.CENTER
94
+ lr = lp.add_run(line); lr.font.name=TNR; lr.font.size=Pt(12)
95
+ lp.paragraph_format.space_after = Pt(4)
96
+
97
+ SP()
98
+ lnk = doc.add_paragraph(); lnk.alignment = WD_ALIGN_PARAGRAPH.CENTER
99
+ lr2 = lnk.add_run("Live Demo: https://esvanth-ecocart-ai.streamlit.app")
100
+ lr2.font.name=TNR; lr2.font.size=Pt(11); lr2.font.bold=True
101
+ lr2.font.color.rgb = RGBColor(37,99,235)
102
+ doc.add_page_break()
103
+
104
+ # ── task 2 ────────────────────────────────────────────────────────────────
105
+ H("Task 2 β€” Customer Segmentation & Bias Mitigation")
106
+ P("Running task2_segmentation.py produced the following output:")
107
+ if t2_text:
108
+ CODE(t2_text)
109
+ SP()
110
+ IMG("output/bias_before_after.png",
111
+ "Figure 1: Customer clusters before and after bias mitigation")
112
+ SP()
113
+ IMG("output/disparate_impact.png",
114
+ "Figure 2: Disparate Impact and High Value rates before and after fix")
115
+ SP()
116
+ P("Before the fix: 0.0% of rural customers were in High Value (DI = 0.0 β€” biased). "
117
+ "After the fix: 57.3% of rural customers are in High Value (DI = 0.847 β€” fair, above 0.80 threshold).")
118
+ doc.add_page_break()
119
+
120
+ # ── task 3 ────────────────────────────────────────────────────────────────
121
+ H("Tasks 3 & 4 β€” Route Optimisation and Algorithm Comparison")
122
+ P("Running task3_4_routing.py produced the following output:")
123
+ if t3_text:
124
+ CODE(t3_text)
125
+ SP()
126
+ IMG("output/network_map.png",
127
+ "Figure 3: EcoCart 20-node delivery network")
128
+ SP()
129
+ IMG("output/algo_comparison.png",
130
+ "Figure 4: A* vs IDA* comparison across urban and rural routes")
131
+ SP()
132
+ IMG("output/green_vs_fast.png",
133
+ "Figure 5: Fastest route vs lowest COβ‚‚ route comparison")
134
+ SP()
135
+ P("A* found the optimal path on every route with the fewest nodes expanded. "
136
+ "DFS was the only algorithm that did not find the shortest path. "
137
+ "Both A* and IDA* found identical optimal paths β€” A* is faster on small graphs, "
138
+ "IDA* uses less memory and is better suited for large-scale networks.")
139
+ doc.add_page_break()
140
+
141
+ # ── task 5 ────────────────────────────────────────────────────────────────
142
+ H("Task 5 β€” Demand Forecasting with Machine Learning")
143
+ P("Running task5_forecasting.py produced the following output:")
144
+ if t5_text:
145
+ CODE(t5_text)
146
+ SP()
147
+ IMG("output/forecast.png",
148
+ "Figure 6: Actual vs predicted daily sales on the 140-day test set")
149
+ SP()
150
+ IMG("output/residuals.png",
151
+ "Figure 7: Residuals for Linear Regression and Random Forest")
152
+ SP()
153
+ IMG("output/feature_importance.png",
154
+ "Figure 8: Random Forest feature importance β€” lag_7 is the strongest predictor")
155
+ SP()
156
+ P("Linear Regression: MAE=9.62, RMSE=12.38, RΒ²=0.762, MAPE=9.41%. "
157
+ "Random Forest: MAE=9.75, RMSE=13.50, RΒ²=0.716, MAPE=9.43%. "
158
+ "Linear Regression performed slightly better on this dataset. "
159
+ "The top predictors were lag_7 (same weekday last week), lag_14, and is_promo.")
160
+ doc.add_page_break()
161
+
162
+ # ── references ─────────────────────────────────────────────────────────────
163
+ H("References")
164
+ refs = [
165
+ "[1] S. Russell and P. Norvig, Artificial Intelligence: A Modern Approach, 4th ed. Hoboken, NJ: Pearson, 2020.",
166
+ "[2] F. Pedregosa et al., \"Scikit-learn: Machine Learning in Python,\" JMLR, vol. 12, pp. 2825-2830, 2011.",
167
+ "[3] M. Feldman et al., \"Certifying and Removing Disparate Impact,\" in Proc. ACM SIGKDD, 2015.",
168
+ "[4] P. E. Hart, N. J. Nilsson, and B. Raphael, \"A Formal Basis for the Heuristic Determination of Minimum Cost Paths,\" IEEE Trans. Syst. Sci. Cybern., vol. 4, no. 2, pp. 100-107, 1968.",
169
+ ]
170
+ for ref in refs:
171
+ p = doc.add_paragraph(ref)
172
+ p.paragraph_format.space_after = Pt(5)
173
+ p.paragraph_format.left_indent = Inches(0.3)
174
+ p.paragraph_format.first_line_indent = Inches(-0.3)
175
+ for r in p.runs: r.font.name=TNR; r.font.size=Pt(11)
176
+
177
+ buf = io.BytesIO()
178
+ doc.save(buf)
179
+ buf.seek(0)
180
+ return buf
181
+
182
  # ── page config ───────────────────────────────────────────────────────────────
183
  st.set_page_config(page_title="EcoCart AI", layout="wide",
184
  initial_sidebar_state="expanded")
 
252
  5. Read the *What does this tell us?* section
253
  """)
254
  st.divider()
255
+
256
+ t2_done = st.session_state.get("t2_done", False)
257
+ t3_done = st.session_state.get("t3_done", False)
258
+ t5_done = st.session_state.get("t5_done", False)
259
+
260
+ st.markdown("**Task status:**")
261
+ st.markdown(f"{'βœ…' if t2_done else '⬜'} Task 2 β€” Bias & Segmentation")
262
+ st.markdown(f"{'βœ…' if t3_done else '⬜'} Task 3 β€” Route Algorithms")
263
+ st.markdown(f"{'βœ…' if t5_done else '⬜'} Task 5 β€” Demand Forecasting")
264
+ st.divider()
265
+
266
+ st.markdown("**Download Report**")
267
+ if t2_done or t3_done or t5_done:
268
+ report_buf = _build_report(
269
+ st.session_state.get("t2_text",""),
270
+ st.session_state.get("t3_text",""),
271
+ st.session_state.get("t5_text",""),
272
+ )
273
+ st.download_button(
274
+ label="⬇ Download .docx report",
275
+ data=report_buf,
276
+ file_name="EcoCart_Report.docx",
277
+ mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
278
+ use_container_width=True,
279
+ )
280
+ st.caption("Report includes all outputs and charts from completed tasks.")
281
+ else:
282
+ st.caption("Run Tasks 2, 3 and 5 first β€” download button will appear here.")
283
  st.divider()
284
+ st.caption("All outputs are from the actual task Python scripts.")
285
 
286
  # ── header ────────────────────────────────────────────────────────────────────
287
  st.markdown("<h2 style='margin:0 0 16px;color:#1e293b'>EcoCart AI System</h2>",
 
485
 
486
  with st.spinner("Running task2_segmentation.py..."):
487
  t2_output = _run_task2()
488
+ st.session_state["t2_text"] = t2_output
489
 
490
  st.markdown("<div class='step-box'><span class='step-num'>2</span>"
491
  "<b>Terminal output from task2_segmentation.py</b></div>",
 
556
 
557
  with st.spinner("Running task3_4_routing.py..."):
558
  t3_output = _run_task3()
559
+ st.session_state["t3_text"] = t3_output
560
 
561
  st.markdown("<div class='step-box'><span class='step-num'>2</span>"
562
  "<b>Terminal output from task3_4_routing.py</b></div>",
 
845
 
846
  with st.spinner("Running task5_forecasting.py..."):
847
  t5_output = _run_task5()
848
+ st.session_state["t5_text"] = t5_output
849
 
850
  st.markdown("<div class='step-box'><span class='step-num'>2</span>"
851
  "<b>Terminal output from task5_forecasting.py</b></div>",
requirements.txt CHANGED
@@ -5,3 +5,4 @@ plotly>=5.0.0
5
  scikit-learn>=1.3.0
6
  matplotlib>=3.7.0
7
  networkx>=3.0
 
 
5
  scikit-learn>=1.3.0
6
  matplotlib>=3.7.0
7
  networkx>=3.0
8
+ python-docx>=1.1.0