kpranav22 commited on
Commit
206963c
·
verified ·
1 Parent(s): 3a99310

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +310 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,312 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ import re
3
+ import random
4
+ import string
5
+ from datetime import datetime, timedelta
6
 
7
+ # ---------------------------------------------------------
8
+ # Simple Pizza Support Chatbot (Streamlit)
9
+ # - Rule-based NLU (keyword & pattern matching)
10
+ # - Menu lookup, offers, order placement, order tracking, FAQs
11
+ # - No external APIs, perfect for a live workshop demo
12
+ # ---------------------------------------------------------
13
+
14
+ st.set_page_config(page_title="Pizza Order Chatbot Demo", page_icon="🍕", layout="centered")
15
+
16
+ # ----------------------------
17
+ # Demo Data
18
+ # ----------------------------
19
+ MENU = {
20
+ "Classic": [
21
+ {"name": "Margherita", "desc": "Cheese, tomato, basil", "prices": {"small": 199, "medium": 299, "large": 399}},
22
+ {"name": "Farmhouse", "desc": "Onion, capsicum, corn, olives", "prices": {"small": 249, "medium": 349, "large": 449}},
23
+ ],
24
+ "Meat": [
25
+ {"name": "Pepperoni", "desc": "Pepperoni & extra cheese", "prices": {"small": 279, "medium": 379, "large": 479}},
26
+ {"name": "BBQ Chicken", "desc": "Smoky chicken, onion, BBQ sauce", "prices": {"small": 299, "medium": 399, "large": 499}},
27
+ ],
28
+ "Special": [
29
+ {"name": "Paneer Tikka", "desc": "Spiced paneer, onion, capsicum", "prices": {"small": 269, "medium": 369, "large": 469}},
30
+ {"name": "Veggie Overload", "desc": "Mushroom, corn, olives, jalapeño", "prices": {"small": 289, "medium": 389, "large": 489}},
31
+ ],
32
+ }
33
+
34
+ OFFERS = [
35
+ "Use code RIDHI to get 5% off on all orders!",
36
+ "Buy 2 Large pizzas and get 1 Garlic Bread free.",
37
+ "Free delivery for orders above ₹599.",
38
+ ]
39
+
40
+ FAQ = {
41
+ "timings": "We deliver daily from 10 AM to 11 PM.",
42
+ "payment": "We accept UPI, cards, netbanking, and cash on delivery.",
43
+ "delivery time": "Average delivery time is 30-40 minutes depending on your location.",
44
+ "refund": "If there is an issue, please contact support and refunds will be processed within 3-5 working days.",
45
+ }
46
+
47
+ SIZES = ["small", "medium", "large"]
48
+
49
+ # ----------------------------
50
+ # Utilities
51
+ # ----------------------------
52
+
53
+ def _rand_order_id(prefix="OD"):
54
+ return prefix + "-" + ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))
55
+
56
+
57
+ def format_menu() -> str:
58
+ lines = ["Here is our pizza menu:\n"]
59
+ for category, items in MENU.items():
60
+ lines.append(f"**{category}**")
61
+ for it in items:
62
+ price_line = ", ".join([f"{sz.title()}: ₹{amt}" for sz, amt in it["prices"].items()])
63
+ lines.append(f"- **{it['name']}** – {it['desc']} ({price_line})")
64
+ lines.append("")
65
+ return "\n".join(lines)
66
+
67
+
68
+ def list_offers() -> str:
69
+ return "\n".join([f"- {o}" for o in OFFERS])
70
+
71
+
72
+ def menu_lookup(pizza_name: str):
73
+ pizza_name_l = pizza_name.lower()
74
+ for _, items in MENU.items():
75
+ for it in items:
76
+ if it["name"].lower() == pizza_name_l:
77
+ return it
78
+ # fuzzy contains
79
+ for _, items in MENU.items():
80
+ for it in items:
81
+ if pizza_name_l in it["name"].lower():
82
+ return it
83
+ return None
84
+
85
+
86
+ def extract_size(text: str):
87
+ for s in SIZES:
88
+ if re.search(rf"\b{s}\b", text, flags=re.I):
89
+ return s
90
+ return None
91
+
92
+
93
+ def extract_pizza(text: str):
94
+ # search exact names first
95
+ for _, items in MENU.items():
96
+ for it in items:
97
+ if re.search(rf"\b{re.escape(it['name'])}\b", text, flags=re.I):
98
+ return it['name']
99
+ # fallback: partial tokens
100
+ tokens = re.findall(r"[A-Za-z]+", text)
101
+ for t in tokens:
102
+ p = menu_lookup(t)
103
+ if p:
104
+ return p['name']
105
+ return None
106
+
107
+
108
+ def estimate_eta(minutes=35):
109
+ return (datetime.now() + timedelta(minutes=minutes)).strftime("%I:%M %p")
110
+
111
+ # ----------------------------
112
+ # Intent Detection (very simple & explainable for workshop)
113
+ # ----------------------------
114
+
115
+ INTENT_PATTERNS = {
116
+ "greet": [r"\bhi\b", r"\bhello\b", r"\bhey\b"],
117
+ "menu": [r"menu", r"what.*pizzas", r"list.*pizza", r"show.*pizza"],
118
+ "offers": [r"offer", r"discount", r"coupon", r"promo"],
119
+ "order": [r"order", r"i want", r"i'd like", r"buy", r"add to cart"],
120
+ "track": [r"track", r"where.*order", r"status", r"deliver"],
121
+ "help": [r"help", r"support", r"issue"],
122
+ "thanks": [r"thanks", r"thank you"],
123
+ }
124
+
125
+
126
+ def detect_intent(text: str) -> str:
127
+ t = text.lower().strip()
128
+ for intent, patterns in INTENT_PATTERNS.items():
129
+ for pat in patterns:
130
+ if re.search(pat, t):
131
+ return intent
132
+ # FAQs
133
+ for key in FAQ.keys():
134
+ if key in t:
135
+ return "faq"
136
+ return "unknown"
137
+
138
+ # ----------------------------
139
+ # Order & State Management
140
+ # ----------------------------
141
+
142
+ if "chat" not in st.session_state:
143
+ st.session_state.chat = [] # list of dicts: {role, content}
144
+ if "orders" not in st.session_state:
145
+ st.session_state.orders = {} # order_id -> {pizza, size, price, status, eta}
146
+ if "last_order_id" not in st.session_state:
147
+ st.session_state.last_order_id = None
148
+
149
+
150
+ def add_bot(msg: str):
151
+ st.session_state.chat.append({"role": "assistant", "content": msg})
152
+
153
+
154
+ def add_user(msg: str):
155
+ st.session_state.chat.append({"role": "user", "content": msg})
156
+
157
+
158
+ def place_order(pizza_name: str, size: str):
159
+ item = menu_lookup(pizza_name)
160
+ if not item:
161
+ return None, "Sorry, I couldn't find that pizza. Use 'menu' to see options."
162
+ if size not in item["prices"]:
163
+ return None, f"Please choose a valid size for {item['name']}: small / medium / large."
164
+
165
+ order_id = _rand_order_id()
166
+ price = item["prices"][size]
167
+ eta = estimate_eta(random.choice([25, 30, 35, 40]))
168
+ st.session_state.orders[order_id] = {
169
+ "pizza": item["name"],
170
+ "size": size,
171
+ "price": price,
172
+ "status": "Confirmed",
173
+ "eta": eta,
174
+ }
175
+ st.session_state.last_order_id = order_id
176
+ return order_id, (
177
+ f"Order placed!\n\n"
178
+ f"**Order ID:** {order_id}\n"
179
+ f"**Item:** {item['name']} ({size.title()})\n"
180
+ f"**Amount:** ₹{price}\n"
181
+ f"**Estimated Delivery:** {eta}\n\n"
182
+ f"Use `track {order_id}` anytime to check status."
183
+ )
184
+
185
+
186
+ def track_order(order_id: str):
187
+ data = st.session_state.orders.get(order_id)
188
+ if not data:
189
+ return "I couldn't find that order ID. Please check and try again."
190
+
191
+ # Simulate simple status transitions for demo
192
+ transitions = ["Confirmed", "Being Prepared", "Out for Delivery", "Delivered"]
193
+ cur_status = data["status"]
194
+ try:
195
+ idx = transitions.index(cur_status)
196
+ if idx < len(transitions) - 1 and random.random() < 0.5:
197
+ data["status"] = transitions[idx + 1]
198
+ st.session_state.orders[order_id] = data
199
+ except ValueError:
200
+ pass
201
+
202
+ return (
203
+ f"**Order ID:** {order_id}\n"
204
+ f"**Item:** {data['pizza']} ({data['size'].title()})\n"
205
+ f"**Status:** {data['status']}\n"
206
+ f"**ETA:** {data['eta']}"
207
+ )
208
+
209
+ # ----------------------------
210
+ # Bot Brain
211
+ # ----------------------------
212
+
213
+ def bot_reply(user_text: str) -> str:
214
+ intent = detect_intent(user_text)
215
+
216
+ if intent == "greet":
217
+ return (
218
+ "Hello! I am your Pizza Order Assistant. You can say things like:\n"
219
+ "- 'menu' to see pizzas\n- 'any offers?'\n- 'order a medium Margherita'\n- 'track OD-ABC123'"
220
+ )
221
+
222
+ if intent == "menu":
223
+ return format_menu()
224
+
225
+ if intent == "offers":
226
+ return "Here are the current offers:\n\n" + list_offers()
227
+
228
+ if intent == "faq":
229
+ # find which FAQ
230
+ t = user_text.lower()
231
+ for k, v in FAQ.items():
232
+ if k in t:
233
+ return f"**{k.title()}**: {v}"
234
+ return "You can ask about timings, payment, delivery time, or refund."
235
+
236
+ if intent == "order":
237
+ pizza = extract_pizza(user_text) or "Margherita"
238
+ size = extract_size(user_text) or "medium"
239
+ order_id, msg = place_order(pizza, size)
240
+ return msg
241
+
242
+ if intent == "track":
243
+ # allow 'track <order_id>' or fallback to last order
244
+ m = re.search(r"(od-[a-z0-9]{6})", user_text.lower())
245
+ if m:
246
+ oid = m.group(1).upper()
247
+ else:
248
+ oid = st.session_state.last_order_id
249
+ if not oid:
250
+ return "Please provide an order ID, e.g., 'track OD-ABC123'. If you just ordered, try again in a moment."
251
+ return track_order(oid)
252
+
253
+ if intent == "help":
254
+ return (
255
+ "I can help you with:\n"
256
+ "- Viewing the menu (type 'menu')\n"
257
+ "- Placing an order (e.g., 'order a large Pepperoni')\n"
258
+ "- Tracking orders (e.g., 'track OD-XXXXXX')\n"
259
+ "- Checking offers (type 'offers')\n"
260
+ "- FAQs: timings, payment, delivery time, refund"
261
+ )
262
+
263
+ if intent == "thanks":
264
+ return "You're welcome! Anything else I can do for you?"
265
+
266
+ # Unknown → assistively guide
267
+ return (
268
+ "I didn't quite get that. Try:\n"
269
+ "- 'menu' to see pizzas\n- 'offers' to see discounts\n- 'order a medium Margherita'\n- 'track OD-XXXXXX' to track your order"
270
+ )
271
+
272
+ # ----------------------------
273
+ # UI
274
+ # ----------------------------
275
+ with st.sidebar:
276
+ st.title("🍕 Pizza Bot – Demo")
277
+ st.caption("A simple, explainable chatbot for workshops")
278
+
279
+ if st.button("Reset Conversation"):
280
+ st.session_state.chat = []
281
+ st.session_state.last_order_id = None
282
+ st.experimental_rerun()
283
+
284
+ st.subheader("Quick Actions")
285
+ if st.button("Show Menu"):
286
+ add_user("menu")
287
+ add_bot(format_menu())
288
+ if st.button("View Offers"):
289
+ add_user("offers")
290
+ add_bot("Here are the current offers:\n\n" + list_offers())
291
+
292
+ st.markdown("""
293
+ # 🍕 Pizza Order Chatbot (Workshop Demo)
294
+ Welcome! Type a message below. Try: **menu**, **offers**, **order a medium Margherita**, **track OD-XXXXXX**
295
+ """)
296
+
297
+ for msg in st.session_state.chat:
298
+ with st.chat_message(msg["role"]):
299
+ st.markdown(msg["content"])
300
+
301
+ user_text = st.chat_input("Type here…")
302
+ if user_text:
303
+ add_user(user_text)
304
+ reply = bot_reply(user_text)
305
+ add_bot(reply)
306
+ with st.chat_message("assistant"):
307
+ st.markdown(reply)
308
+
309
+ # Footer note for presenter
310
+ st.caption(
311
+ "Presenter tip: Explain how intents are detected via regex and how state (orders, last order) is kept in session_state."
312
+ )