rlgondong commited on
Commit
5545aac
Β·
verified Β·
1 Parent(s): cf0d483

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +252 -81
app.py CHANGED
@@ -1,81 +1,252 @@
1
- import gradio as gr
2
- import joblib
3
- import numpy as np
4
- import re
5
-
6
- # Load models
7
- model_flavor = joblib.load("dtc_model_flavor.pkl")
8
- model_topping = joblib.load("dtc_model_topping.pkl")
9
- model_drink = joblib.load("dtc_model_drink.pkl")
10
-
11
- # Load encoders
12
- encoder_flavor = joblib.load("encoder_flavor.pkl")
13
- encoder_topping = joblib.load("encoder_topping.pkl")
14
- encoder_drink = joblib.load("encoder_drink.pkl")
15
- input_encoders = joblib.load("input_encoders.pkl")
16
-
17
- # Function to clean emoji from text
18
- def clean_text(text):
19
- return re.sub(r'[^\w\s]', '', text).strip().lower()
20
-
21
- # Prediction function
22
- def predict_merienda(mood, weather, craving_level, last_meal, budget):
23
- features = [mood, weather, craving_level, last_meal, budget]
24
- cleaned_features = [clean_text(val) for val in features]
25
- encoded = [input_encoders[col].transform([val])[0] for col, val in zip(input_encoders.keys(), cleaned_features)]
26
- encoded_np = np.array(encoded).reshape(1, -1)
27
-
28
- pred_flavor = encoder_flavor.inverse_transform(model_flavor.predict(encoded_np))[0]
29
- pred_topping = encoder_topping.inverse_transform(model_topping.predict(encoded_np))[0]
30
- pred_drink = encoder_drink.inverse_transform(model_drink.predict(encoded_np))[0]
31
- return pred_flavor, pred_topping, pred_drink
32
-
33
- # Dropdown options with "Choose one" first
34
- mood_list = ["Choose one", "😌 Busog pa", "🧘 Chill lang", "⚑ G na G", "😴 Tinatamad"]
35
- weather_list = ["Choose one", "🌧️ Bed weather", "πŸ”₯ Impyerno", "πŸƒ Mahangin", "☁️ Makulimlim"]
36
- craving_list = ["Choose one", "πŸš€ High", "🐒 Low", "πŸ–οΈ Medium", "πŸŽ‰ OA"]
37
- last_meal_list = ["Choose one", "πŸͺ¨ Heavy", "🌿 Light", "🚫 No meal"]
38
- budget_list = ["Choose one", "πŸͺ™ Gipit", "πŸ’Έ Saks lang", "πŸ€‘ Kakadating lang ng allowance"]
39
-
40
- # Custom Theme 🎨
41
- custom_theme = gr.themes.Base(
42
- primary_hue="rose",
43
- secondary_hue="amber",
44
- neutral_hue="stone",
45
- font=[gr.themes.GoogleFont("Helvetica")]
46
- ).set(
47
- body_background_fill="#ffe5e5" # Light reddish background
48
- )
49
-
50
-
51
- # Gradio Interface
52
- with gr.Blocks(theme=custom_theme) as iface:
53
- gr.Markdown(
54
- """
55
- # 🍜 **Your Favorite Merienda Classifier**
56
- * Pancit-cantonize your vibe to match your mood with a perfect merienda soulmate! πŸŒΈπŸŽ‰πŸ΄πŸ§‘
57
- """
58
- )
59
-
60
- with gr.Row():
61
- with gr.Column():
62
- mood = gr.Dropdown(mood_list, label="🌈 Mood", value="Choose one")
63
- weather = gr.Dropdown(weather_list, label="🌦️ Weather", value="Choose one")
64
- craving = gr.Dropdown(craving_list, label="πŸ”₯ Craving Level", value="Choose one")
65
- last_meal = gr.Dropdown(last_meal_list, label="🍽️ Last Meal", value="Choose one")
66
- budget = gr.Dropdown(budget_list, label="πŸ’° Budget", value="Choose one")
67
-
68
- submit_btn = gr.Button("πŸš€ Classify Now!", variant="primary")
69
-
70
- with gr.Column():
71
- flavor = gr.Text(label="🍜 Recommended Flavor")
72
- topping = gr.Text(label="πŸ”‘ Recommended Topping")
73
- drink = gr.Text(label="πŸ₯€ Recommended Drink")
74
-
75
- submit_btn.click(
76
- predict_merienda,
77
- inputs=[mood, weather, craving, last_meal, budget],
78
- outputs=[flavor, topping, drink]
79
- )
80
-
81
- iface.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import torch
3
+ from torchvision import transforms
4
+ from PIL import Image
5
+ import joblib
6
+ import base64
7
+ import io
8
+ from contextlib import redirect_stdout
9
+
10
+ # Load model and metadata
11
+ model = torch.jit.load("resnet_grocery_model_scripted.pt", map_location="cpu")
12
+ model.eval()
13
+
14
+ assets = joblib.load("deployment_assets.joblib")
15
+ transform = assets['transform']
16
+ class_names = assets['class_names']
17
+
18
+ # Price list
19
+ items = [
20
+ 'Bisconni Chocolate Chip Cookies 46.8gm', 'Coca Cola Can 250ml', 'Colgate Maximum Cavity Protection 75gm',
21
+ 'Fanta 500ml', 'Fresher Guava Nectar 500ml', 'Fruita Vitals Red Grapes 200ml', 'Islamabad Tea 238gm',
22
+ 'Kolson Slanty Jalapeno 18gm', 'Kurkure Chutney Chaska 62gm', 'LU Candi Biscuit 60gm', 'LU Oreo Biscuit 19gm',
23
+ 'LU Prince Biscuit 55.2gm', 'Lays Masala 34gm', 'Lays Wavy Mexican Chili 34gm', 'Lifebuoy Total Protect Soap 96gm',
24
+ 'Lipton Yellow Label Tea 95gm', 'Meezan Ultra Rich Tea 190gm', 'Peek Freans Sooper Biscuit 13.2gm',
25
+ 'Safeguard Bar Soap Pure White 175gm', 'Shezan Apple 250ml', 'Sunsilk Shampoo Soft - Smooth 160ml',
26
+ 'Super Crisp BBQ 30gm', 'Supreme Tea 95gm', 'Tapal Danedar 95gm', 'Vaseline Healthy White Lotion 100ml'
27
+ ]
28
+
29
+ prices = [
30
+ 55.20, 31.75, 90.00, 63.50, 50.00, 35.00, 150.00, 15.00, 25.00, 30.00, 10.00, 30.00, 20.00, 20.00, 44.50,
31
+ 100.00, 200.00, 10.00, 70.00, 25.00, 120.00, 15.00, 100.00, 100.00, 120.00
32
+ ]
33
+
34
+ pricelist = dict(zip(items, prices))
35
+
36
+ def classify_and_track(files, budget):
37
+ results = []
38
+ total_cost = 0
39
+ receipt_lines = []
40
+
41
+ # Initialize LaTeX document for PDF
42
+ latex_lines = [
43
+ r"\documentclass[a4paper,12pt]{article}",
44
+ r"\usepackage[utf8]{inputenc}",
45
+ r"\usepackage[T1]{fontenc}",
46
+ r"\usepackage{lmodern}",
47
+ r"\usepackage{geometry}",
48
+ r"\geometry{margin=2cm}",
49
+ r"\usepackage{longtable}",
50
+ r"\usepackage{array}",
51
+ r"\usepackage{booktabs}",
52
+ r"\usepackage{fancyhdr}",
53
+ r"\pagestyle{fancy}",
54
+ r"\fancyhf{}",
55
+ r"\fancyhead[C]{SOBRANG TINDANG INA!}",
56
+ r"\fancyfoot[C]{\thepage}",
57
+ r"\usepackage{lastpage}",
58
+ r"\usepackage{noto}", # Using Noto font for compatibility
59
+ r"\begin{document}",
60
+ r"\begin{center}",
61
+ r"{\Large \textbf{OFFICIAL RECEIPT}} \\[0.2cm]",
62
+ r"{\normalsize SOBRANG TINDANG INA!} \\",
63
+ r"{\normalsize Online Smart Checkout} \\",
64
+ r"\vspace{0.5cm}",
65
+ r"\hrulefill \\",
66
+ r"\end{center}",
67
+ r"\vspace{0.5cm}",
68
+ r"\begin{longtable}{p{10cm} r}",
69
+ r"\hline",
70
+ r"\textbf{Item} & \textbf{Price (\textsf{β‚±})} \\",
71
+ r"\hline",
72
+ r"\endhead",
73
+ ]
74
+
75
+ receipt_lines.append("========================================")
76
+ receipt_lines.append(" 🧾 OFFICIAL RECEIPT")
77
+ receipt_lines.append(" πŸ›’ SOBRANG TINDANG INA!")
78
+ receipt_lines.append(" πŸ“ Online Smart Checkout")
79
+ receipt_lines.append("========================================")
80
+ receipt_lines.append(" ITEM PRICE")
81
+ receipt_lines.append("----------------------------------------")
82
+
83
+ for f in files:
84
+ try:
85
+ img = Image.open(f.name).convert("RGB")
86
+ except Exception:
87
+ results.append((None, "Invalid image"))
88
+ continue
89
+
90
+ input_tensor = transform(img).unsqueeze(0)
91
+ with torch.no_grad():
92
+ outputs = model(input_tensor)
93
+ predicted_index = torch.argmax(outputs, dim=1).item()
94
+ predicted_class = class_names[predicted_index]
95
+ price = pricelist.get(predicted_class, 0)
96
+ total_cost += price
97
+ results.append((img, f"{predicted_class} - β‚±{price:.2f}"))
98
+
99
+ name = predicted_class
100
+ if len(name) > 26:
101
+ name = name[:23] + "..."
102
+ receipt_lines.append(f" {name:<28} β‚±{price:>6.2f}")
103
+ # Sanitize name for LaTeX
104
+ latex_name = name.replace("_", r"\_").replace("&", r"\&").replace("%", r"\%")
105
+ latex_lines.append(f"{latex_name} & {price:.2f} \\\\")
106
+
107
+ remaining = budget - total_cost
108
+ receipt_lines.append("----------------------------------------")
109
+ receipt_lines.append(f" TOTAL{'':<24}β‚±{total_cost:>7.2f}")
110
+ receipt_lines.append(f" BUDGET{'':<23}β‚±{budget:>7.2f}")
111
+ receipt_lines.append(f" REMAINING{'':<20}β‚±{remaining:>7.2f}")
112
+ receipt_lines.append("========================================")
113
+
114
+ latex_lines.append(r"\hline")
115
+ latex_lines.append(f"\\textbf{{Total}} & {total_cost:.2f} \\\\")
116
+ latex_lines.append(f"\\textbf{{Budget}} & {budget:.2f} \\\\")
117
+ latex_lines.append(f"\\textbf{{Remaining}} & {remaining:.2f} \\\\")
118
+ latex_lines.append(r"\hline")
119
+ latex_lines.append(r"\end{longtable}")
120
+ latex_lines.append(r"\vspace{0.5cm}")
121
+
122
+ percent = min(100, int((total_cost / budget) * 100)) if budget else 0
123
+ status = f"**Total: β‚±{total_cost:.2f} / β‚±{budget:.2f}**"
124
+
125
+ if total_cost <= budget:
126
+ receipt_lines.append(" βœ… You're within budget! πŸŽ‰")
127
+ latex_lines.append(r"{\color{green} \textbf{You're within budget!}} \\")
128
+ status_msg = f"βœ… **Within Budget!** πŸŽ‰\n{status}"
129
+ sound_path = "success.mp3"
130
+ else:
131
+ receipt_lines.append(" ❌ Over budget! Remove some snacks!")
132
+ latex_lines.append(r"{\color{red} \textbf{Over budget! Remove some snacks!}} \\")
133
+ status_msg = f"🚨 **Over Budget!** πŸ’Έ\n{status}"
134
+ sound_path = "fail.mp3"
135
+
136
+ receipt_lines.append("========================================")
137
+ receipt_lines.append(" THANK YOU FOR SHOPPING WITH US!")
138
+ receipt_lines.append(" Come back for smarter buys! 🧾")
139
+ receipt_lines.append("========================================")
140
+
141
+ latex_lines.append(r"\vspace{0.5cm}")
142
+ latex_lines.append(r"\begin{center}")
143
+ latex_lines.append(r"THANK YOU FOR SHOPPING WITH US! \\")
144
+ latex_lines.append(r"Come back for smarter buys! \\")
145
+ latex_lines.append(r"\hrulefill")
146
+ latex_lines.append(r"\end{center}")
147
+ latex_lines.append(r"\end{document}")
148
+
149
+ receipt_text = "```\n" + "\n".join(receipt_lines) + "\n```"
150
+ latex_content = "\n".join(latex_lines)
151
+
152
+ # Create a file-like object for the LaTeX content
153
+ latex_file = io.StringIO()
154
+ with redirect_stdout(latex_file):
155
+ print(latex_content)
156
+
157
+ return results, status_msg, receipt_text, percent, sound_path, latex_file
158
+
159
+ def reset_ui():
160
+ return [], "", "", 0, None, None
161
+
162
+ # Load and encode local background image
163
+ with open("background.jpg", "rb") as img_file:
164
+ b64_string = base64.b64encode(img_file.read()).decode()
165
+
166
+ html_style = f"""
167
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;800&display=swap" rel="stylesheet">
168
+ <style>
169
+ body {{
170
+ background-image: url("data:image/jpg;base64,{b64_string}");
171
+ background-size: cover;
172
+ background-position: center;
173
+ background-repeat: no-repeat;
174
+ font-family: 'Poppins', sans-serif;
175
+ color: #222;
176
+ }}
177
+
178
+ .glass-card {{
179
+ background: rgba(255, 255, 255, 0.75);
180
+ border-radius: 20px;
181
+ box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
182
+ backdrop-filter: blur(10px);
183
+ -webkit-backdrop-filter: blur(10px);
184
+ border: 1px solid rgba(255, 255, 255, 0.2);
185
+ padding: 2rem;
186
+ margin-bottom: 1.5rem;
187
+ }}
188
+
189
+ h1 {{
190
+ text-align: center;
191
+ font-size: 2.6rem;
192
+ font-weight: 800;
193
+ color: #000 !important;
194
+ margin-bottom: 0.5rem;
195
+ font-family: 'Poppins', sans-serif;
196
+ }}
197
+
198
+ h2 {{
199
+ text-align: center;
200
+ font-size: 0.1rem;
201
+ font-weight: 0.5;
202
+ color: #000 !important;
203
+ font-family: 'Poppins', sans-serif;
204
+ }}
205
+
206
+ .gr-button {{
207
+ font-family: 'Poppins', sans-serif;
208
+ font-weight: 600;
209
+ }}
210
+
211
+ .gr-number input, .gr-file input {{
212
+ font-family: 'Poppins', sans-serif;
213
+ }}
214
+ </style>
215
+ """
216
+
217
+ with gr.Blocks(title="Grocery Classifier") as app:
218
+ gr.HTML(html_style)
219
+
220
+ with gr.Column(elem_classes="glass-card"):
221
+ gr.Markdown("<h1>🧾 Budget Check! Know Before You Chow πŸ₯—</h1>")
222
+ gr.Markdown("<h2>Snap a snack, upload the pack, and find out if you’re on track β€” or flat broke. Shopping has never been so dramatic.</h2>")
223
+
224
+ with gr.Column(elem_classes="glass-card"):
225
+ with gr.Row():
226
+ file_input = gr.File(file_types=["image"], file_count="multiple", label="πŸ“· Upload Grocery Items")
227
+ budget_input = gr.Number(label="πŸ’° Budget (β‚±)", value=500.0, precision=2)
228
+
229
+ with gr.Row():
230
+ classify_btn = gr.Button("πŸš€ Classify Items")
231
+ reset_btn = gr.Button("πŸ”„ Reset")
232
+
233
+ with gr.Column(elem_classes="glass-card"):
234
+ gallery = gr.Gallery(label="πŸ“Έ Classified Items", columns=3)
235
+ status_display = gr.Markdown()
236
+ progress_bar = gr.Slider(label="Budget Used (%)", minimum=0, maximum=100, interactive=False)
237
+ receipt_display = gr.Markdown(label="🧾 Grocery Receipt")
238
+ audio_output = gr.Audio(interactive=False, autoplay=True)
239
+ pdf_download = gr.File(label="πŸ“„ Download Receipt as PDF", interactive=False)
240
+
241
+ classify_btn.click(
242
+ fn=classify_and_track,
243
+ inputs=[file_input, budget_input],
244
+ outputs=[gallery, status_display, receipt_display, progress_bar, audio_output, pdf_download]
245
+ )
246
+
247
+ reset_btn.click(
248
+ fn=reset_ui,
249
+ outputs=[gallery, status_display, receipt_display, progress_bar, audio_output, pdf_download]
250
+ )
251
+
252
+ app.launch()