Update app.py
Browse files
app.py
CHANGED
|
@@ -49,6 +49,7 @@ class Order(Base):
|
|
| 49 |
price = Column(String, default="0")
|
| 50 |
status = Column(String, default="Pending Payment")
|
| 51 |
payment_reference = Column(String, nullable=True)
|
|
|
|
| 52 |
timestamp = Column(DateTime, default=datetime.utcnow)
|
| 53 |
|
| 54 |
class UserProfile(Base):
|
|
@@ -212,9 +213,10 @@ def stream_image_completion(image_b64: str):
|
|
| 212 |
def process_order_flow(user_id: str, message: str) -> str:
|
| 213 |
"""
|
| 214 |
Implements an FSM-based order flow that:
|
| 215 |
-
-
|
| 216 |
-
-
|
| 217 |
-
-
|
|
|
|
| 218 |
"""
|
| 219 |
# Retrieve or initialize conversation state
|
| 220 |
state = user_state.get(user_id)
|
|
@@ -253,7 +255,6 @@ def process_order_flow(user_id: str, message: str) -> str:
|
|
| 253 |
if dish.lower() in message.lower():
|
| 254 |
found_dish = dish
|
| 255 |
break
|
| 256 |
-
|
| 257 |
numbers = re.findall(r'\d+', message)
|
| 258 |
if found_dish:
|
| 259 |
state.data["dish"] = found_dish
|
|
@@ -262,9 +263,11 @@ def process_order_flow(user_id: str, message: str) -> str:
|
|
| 262 |
if quantity <= 0:
|
| 263 |
return "Please enter a valid quantity (e.g., 1, 2, 3)."
|
| 264 |
state.data["quantity"] = quantity
|
| 265 |
-
state.step = 3 #
|
|
|
|
| 266 |
else:
|
| 267 |
state.step = 2 # ask for quantity
|
|
|
|
| 268 |
else:
|
| 269 |
return "I couldn't identify the dish. Please type the dish name from our menu."
|
| 270 |
|
|
@@ -278,44 +281,59 @@ def process_order_flow(user_id: str, message: str) -> str:
|
|
| 278 |
return "Please enter a valid quantity (e.g., 1, 2, 3)."
|
| 279 |
state.data["quantity"] = quantity
|
| 280 |
state.step = 3
|
|
|
|
| 281 |
|
| 282 |
-
# --- Step 3:
|
| 283 |
if state.step == 3:
|
| 284 |
-
|
| 285 |
-
state.data["
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
)
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
else:
|
| 318 |
-
|
|
|
|
|
|
|
|
|
|
| 319 |
|
| 320 |
return ""
|
| 321 |
|
|
@@ -508,8 +526,6 @@ async def chatbot_response(request: Request, background_tasks: BackgroundTasks):
|
|
| 508 |
return StreamingResponse(stream_response(), media_type="text/plain")
|
| 509 |
|
| 510 |
# --- Other Endpoints (Chat History, Order Details, User Profile, Analytics, Voice, Payment Callback) ---
|
| 511 |
-
# ... (Implement other endpoints as needed) ...
|
| 512 |
-
|
| 513 |
@app.get("/chat_history/{user_id}")
|
| 514 |
async def get_chat_history(user_id: str):
|
| 515 |
async with async_session() as session:
|
|
|
|
| 49 |
price = Column(String, default="0")
|
| 50 |
status = Column(String, default="Pending Payment")
|
| 51 |
payment_reference = Column(String, nullable=True)
|
| 52 |
+
delivery_address = Column(String, default="") # New field for address
|
| 53 |
timestamp = Column(DateTime, default=datetime.utcnow)
|
| 54 |
|
| 55 |
class UserProfile(Base):
|
|
|
|
| 213 |
def process_order_flow(user_id: str, message: str) -> str:
|
| 214 |
"""
|
| 215 |
Implements an FSM-based order flow that:
|
| 216 |
+
- Step 1: Expects the user to mention a dish name (optionally with quantity)
|
| 217 |
+
- Step 2: Asks for quantity if not provided
|
| 218 |
+
- Step 3: Requests the delivery address
|
| 219 |
+
- Step 4: Asks for order confirmation (yes/no) and finalizes the order
|
| 220 |
"""
|
| 221 |
# Retrieve or initialize conversation state
|
| 222 |
state = user_state.get(user_id)
|
|
|
|
| 255 |
if dish.lower() in message.lower():
|
| 256 |
found_dish = dish
|
| 257 |
break
|
|
|
|
| 258 |
numbers = re.findall(r'\d+', message)
|
| 259 |
if found_dish:
|
| 260 |
state.data["dish"] = found_dish
|
|
|
|
| 263 |
if quantity <= 0:
|
| 264 |
return "Please enter a valid quantity (e.g., 1, 2, 3)."
|
| 265 |
state.data["quantity"] = quantity
|
| 266 |
+
state.step = 3 # move directly to address step
|
| 267 |
+
return f"You selected {found_dish} with {quantity} serving(s). Please provide your delivery address."
|
| 268 |
else:
|
| 269 |
state.step = 2 # ask for quantity
|
| 270 |
+
return f"You selected {found_dish}. How many servings would you like?"
|
| 271 |
else:
|
| 272 |
return "I couldn't identify the dish. Please type the dish name from our menu."
|
| 273 |
|
|
|
|
| 281 |
return "Please enter a valid quantity (e.g., 1, 2, 3)."
|
| 282 |
state.data["quantity"] = quantity
|
| 283 |
state.step = 3
|
| 284 |
+
return f"Got it. {quantity} serving(s) of {state.data.get('dish')}. Now, please provide your delivery address."
|
| 285 |
|
| 286 |
+
# --- Step 3: Requesting Delivery Address ---
|
| 287 |
if state.step == 3:
|
| 288 |
+
# Assume any text is a valid address
|
| 289 |
+
state.data["address"] = message
|
| 290 |
+
state.step = 4
|
| 291 |
+
return f"Thanks. Your delivery address is recorded as: {message}. Confirm order? (yes/no)"
|
| 292 |
+
|
| 293 |
+
# --- Step 4: Order Confirmation & Finalization ---
|
| 294 |
+
if state.step == 4:
|
| 295 |
+
if message.lower() in ["yes", "y"]:
|
| 296 |
+
order_id = f"ORD-{int(time.time())}"
|
| 297 |
+
state.data["order_id"] = order_id
|
| 298 |
+
price_per_serving = 1500 # fixed price per serving for demonstration
|
| 299 |
+
quantity = state.data.get("quantity", 1)
|
| 300 |
+
total_price = quantity * price_per_serving
|
| 301 |
+
state.data["price"] = str(total_price)
|
| 302 |
+
|
| 303 |
+
# Save the order asynchronously (including delivery_address)
|
| 304 |
+
async def save_order():
|
| 305 |
+
async with async_session() as session:
|
| 306 |
+
order = Order(
|
| 307 |
+
order_id=order_id,
|
| 308 |
+
user_id=user_id,
|
| 309 |
+
dish=state.data["dish"],
|
| 310 |
+
quantity=str(quantity),
|
| 311 |
+
price=str(total_price),
|
| 312 |
+
status="Pending Payment",
|
| 313 |
+
delivery_address=state.data.get("address", "")
|
| 314 |
+
)
|
| 315 |
+
session.add(order)
|
| 316 |
+
await session.commit()
|
| 317 |
+
asyncio.create_task(save_order())
|
| 318 |
+
|
| 319 |
+
email = "customer@example.com" # Placeholder; retrieve from profile if available
|
| 320 |
+
payment_data = create_paystack_payment_link(email, total_price * 100, order_id)
|
| 321 |
+
dish_name = state.data.get("dish", "")
|
| 322 |
+
state.reset()
|
| 323 |
+
if user_id in user_state:
|
| 324 |
+
del user_state[user_id]
|
| 325 |
+
if payment_data.get("status"):
|
| 326 |
+
payment_link = payment_data["data"]["authorization_url"]
|
| 327 |
+
return (f"Thank you for your order of {quantity} serving(s) of {dish_name}! "
|
| 328 |
+
f"Your Order ID is {order_id}.\nPlease complete payment here: {payment_link}\n"
|
| 329 |
+
"You can track your order status using your Order ID.")
|
| 330 |
+
else:
|
| 331 |
+
return f"Your order has been placed with Order ID {order_id}, but we could not initialize payment. Please try again later."
|
| 332 |
else:
|
| 333 |
+
state.reset()
|
| 334 |
+
if user_id in user_state:
|
| 335 |
+
del user_state[user_id]
|
| 336 |
+
return "Order canceled. Let me know if you'd like to try again."
|
| 337 |
|
| 338 |
return ""
|
| 339 |
|
|
|
|
| 526 |
return StreamingResponse(stream_response(), media_type="text/plain")
|
| 527 |
|
| 528 |
# --- Other Endpoints (Chat History, Order Details, User Profile, Analytics, Voice, Payment Callback) ---
|
|
|
|
|
|
|
| 529 |
@app.get("/chat_history/{user_id}")
|
| 530 |
async def get_chat_history(user_id: str):
|
| 531 |
async with async_session() as session:
|