JibexBanks commited on
Commit
fc8eee0
Β·
verified Β·
1 Parent(s): 4d31c47

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +520 -0
app.py ADDED
@@ -0,0 +1,520 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException, UploadFile, File
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from pydantic import BaseModel
4
+ from datetime import datetime, timedelta
5
+ from typing import Optional, List
6
+ import sqlite3
7
+ import csv
8
+ import io
9
+ from ai_handler import MetaAIHandler
10
+ from database import Database
11
+
12
+ app = FastAPI(title="Ejide Pharmacy API")
13
+
14
+ # CORS
15
+ app.add_middleware(
16
+ CORSMiddleware,
17
+ allow_origins=["*"],
18
+ allow_credentials=True,
19
+ allow_methods=["*"],
20
+ allow_headers=["*"],
21
+ )
22
+
23
+ # Initialize
24
+ db = Database()
25
+ ai_handler = MetaAIHandler()
26
+
27
+ class ChatMessage(BaseModel):
28
+ phone_number: str
29
+ message: str
30
+ is_admin: bool
31
+ timestamp: str
32
+
33
+ @app.on_event("startup")
34
+ async def startup():
35
+ db.initialize()
36
+ print("βœ… Database initialized with medication tracking")
37
+
38
+ @app.post("/chat")
39
+ async def chat(msg: ChatMessage):
40
+ """Main chat endpoint - handles all incoming messages"""
41
+
42
+ # Log conversation
43
+ db.log_conversation(msg.phone_number, msg.message, msg.is_admin)
44
+
45
+ message_lower = msg.message.lower().strip()
46
+
47
+ # Admin commands
48
+ if msg.is_admin:
49
+ # Inventory management
50
+ if message_lower.startswith(("add drug", "update drug", "add inventory")):
51
+ return handle_admin_inventory(msg.message, msg.phone_number)
52
+
53
+ # Analytics commands
54
+ elif message_lower in ["analytics", "show analytics", "predictive insights", "insights"]:
55
+ return generate_analytics_report()
56
+
57
+ # Inventory analysis
58
+ elif message_lower in ["inventory report", "show inventory", "stock report", "inventory analysis"]:
59
+ return generate_inventory_report()
60
+
61
+ # Weekly report
62
+ elif message_lower in ["weekly report", "week report", "weekly summary"]:
63
+ return generate_weekly_report()
64
+
65
+ # Help command
66
+ elif message_lower == "help":
67
+ return {"reply": get_admin_help()}
68
+
69
+ # Check for cart/checkout commands
70
+ if message_lower.startswith(("checkout", "check out")):
71
+ return handle_checkout(msg.phone_number, msg.message)
72
+
73
+ # Get context
74
+ customer_history = db.get_customer_history(msg.phone_number)
75
+ inventory = db.get_inventory()
76
+ cart = db.get_cart(msg.phone_number)
77
+
78
+ # Generate AI response
79
+ ai_response = ai_handler.generate_response(
80
+ message=msg.message,
81
+ customer_history=customer_history,
82
+ inventory=inventory,
83
+ cart=cart,
84
+ is_admin=msg.is_admin
85
+ )
86
+
87
+ # Check if customer wants to add to cart
88
+ cart_action = parse_cart_action(msg.message, inventory)
89
+ if cart_action:
90
+ db.add_to_cart(msg.phone_number, cart_action['drug_name'], cart_action['quantity'])
91
+ cart = db.get_cart(msg.phone_number)
92
+ cart_summary = format_cart_summary(cart)
93
+ ai_response += f"\n\n{cart_summary}"
94
+
95
+ return {"reply": ai_response}
96
+
97
+ def parse_cart_action(message: str, inventory: List[dict]) -> Optional[dict]:
98
+ """Parse if customer wants to add items to cart"""
99
+ message_lower = message.lower()
100
+
101
+ # Patterns: "add 2 paracetamol", "3 ibuprofen", "I want 5 chloroquine"
102
+ words = message_lower.split()
103
+
104
+ for i, word in enumerate(words):
105
+ if word.isdigit() and i + 1 < len(words):
106
+ quantity = int(word)
107
+ potential_drug = words[i + 1]
108
+
109
+ for item in inventory:
110
+ if potential_drug in item['drug_name'].lower():
111
+ return {
112
+ 'drug_name': item['drug_name'],
113
+ 'quantity': quantity
114
+ }
115
+
116
+ return None
117
+
118
+ def format_cart_summary(cart: List[dict]) -> str:
119
+ """Format cart items with total"""
120
+ if not cart:
121
+ return "πŸ›’ Your cart is empty."
122
+
123
+ summary = "πŸ›’ *YOUR CART:*\n"
124
+ total = 0
125
+
126
+ for item in cart:
127
+ item_total = item['quantity'] * item['price']
128
+ total += item_total
129
+ summary += f"β€’ {item['drug_name'].title()} x{item['quantity']} = ₦{item_total:,.2f}\n"
130
+
131
+ summary += f"\nπŸ’° *TOTAL: ₦{total:,.2f}*\n"
132
+ summary += "\nReady to checkout? Reply 'checkout'"
133
+
134
+ return summary
135
+
136
+ def handle_checkout(phone_number: str, message: str) -> dict:
137
+ """Handle customer checkout"""
138
+ cart = db.get_cart(phone_number)
139
+
140
+ if not cart:
141
+ return {"reply": "Your cart is empty. Add items first!\n\nExample: 'I want 2 paracetamol'"}
142
+
143
+ # Calculate total
144
+ total = sum(item['quantity'] * item['price'] for item in cart)
145
+
146
+ # Generate order summary
147
+ order_id = f"EJD{datetime.now().strftime('%Y%m%d%H%M%S')}"
148
+
149
+ receipt = "🧾 *ORDER SUMMARY*\n"
150
+ receipt += f"Order ID: {order_id}\n"
151
+ receipt += f"Date: {datetime.now().strftime('%B %d, %Y %I:%M %p')}\n"
152
+ receipt += "="*35 + "\n\n"
153
+
154
+ receipt += "πŸ“¦ *ITEMS:*\n"
155
+ for item in cart:
156
+ item_total = item['quantity'] * item['price']
157
+ receipt += f"β€’ {item['drug_name'].title()}\n"
158
+ receipt += f" Qty: {item['quantity']} x ₦{item['price']:,.2f} = ₦{item_total:,.2f}\n"
159
+
160
+ # Add dosage info if available
161
+ if item.get('dosage_days') and item['dosage_days'] > 0:
162
+ receipt += f" πŸ“… Treatment: {item['dosage_days']} days ({item.get('dosage_frequency', 'as prescribed')})\n"
163
+ receipt += "\n"
164
+
165
+ receipt += "="*35 + "\n"
166
+ receipt += f"πŸ’° *TOTAL: ₦{total:,.2f}*\n\n"
167
+
168
+ receipt += "πŸ’³ *PAYMENT DETAILS:*\n"
169
+ receipt += get_account_details() + "\n\n"
170
+
171
+ receipt += "πŸ“ *NEXT STEPS:*\n"
172
+ receipt += "1. Transfer amount to account above\n"
173
+ receipt += "2. Send screenshot of payment\n"
174
+ receipt += "3. We'll confirm and prepare order\n"
175
+ receipt += "4. Visit pharmacy or request delivery\n\n"
176
+
177
+ # Add medication reminder notice
178
+ has_medication = any(item.get('dosage_days', 0) > 0 for item in cart)
179
+ if has_medication:
180
+ receipt += "πŸ’Š *MEDICATION REMINDERS:*\n"
181
+ receipt += "You'll receive daily reminders to take your medication and health checkups after treatment. Stay healthy! 😊\n\n"
182
+
183
+ receipt += "⏰ Order valid for 24 hours\n"
184
+ receipt += "πŸ“ž Reply 'help' for assistance\n\n"
185
+ receipt += "Thank you for choosing Ejide Pharmacy! πŸ₯"
186
+
187
+ # Record purchases
188
+ for item in cart:
189
+ db.record_purchase(
190
+ phone_number,
191
+ item['drug_name'],
192
+ item['quantity'],
193
+ item['price'] * item['quantity']
194
+ )
195
+
196
+ # Clear cart
197
+ db.clear_cart(phone_number)
198
+
199
+ return {"reply": receipt}
200
+
201
+ def get_account_details() -> str:
202
+ """Get pharmacy account details"""
203
+ return """Bank: GTBank
204
+ Account Name: Ejide Pharmacy Ltd
205
+ Account Number: 0123456789
206
+
207
+ OR
208
+
209
+ Bank: Access Bank
210
+ Account Name: Ejide Pharmacy
211
+ Account Number: 9876543210"""
212
+
213
+ def handle_admin_inventory(message: str, phone_number: str) -> dict:
214
+ """Handle admin inventory commands"""
215
+ try:
216
+ parts = message.lower().split()
217
+
218
+ if len(parts) >= 5:
219
+ drug_name = parts[2]
220
+ quantity = int(parts[3])
221
+ price = float(parts[4])
222
+ category = " ".join(parts[5:]) if len(parts) > 5 else "general"
223
+
224
+ db.update_inventory(drug_name, quantity, price, category)
225
+
226
+ return {
227
+ "reply": f"βœ… *Inventory Updated!*\n\n"
228
+ f"Drug: {drug_name.title()}\n"
229
+ f"Qty: {quantity}\n"
230
+ f"Price: ₦{price:,.2f}\n"
231
+ f"Category: {category.title()}"
232
+ }
233
+ else:
234
+ return {
235
+ "reply": "❌ Invalid format.\n\n"
236
+ "*Use:* add drug [name] [qty] [price] [category]\n"
237
+ "*Example:* add drug paracetamol 100 500 fever"
238
+ }
239
+ except Exception as e:
240
+ return {"reply": f"❌ Error: {str(e)}"}
241
+
242
+ def generate_analytics_report() -> dict:
243
+ """Generate predictive analytics report"""
244
+ analytics = db.get_predictive_analytics()
245
+
246
+ report = "πŸ“Š *PREDICTIVE ANALYTICS & INSIGHTS*\n"
247
+ report += f"πŸ“… Generated: {datetime.now().strftime('%B %d, %Y %I:%M %p')}\n"
248
+ report += "="*40 + "\n\n"
249
+
250
+ # Revenue Trends
251
+ report += "πŸ’° *REVENUE TRENDS:*\n"
252
+ rev = analytics['revenue_trend']
253
+ report += f"This Week: ₦{rev['this_week']:,.2f}\n"
254
+ report += f"Last Week: ₦{rev['last_week']:,.2f}\n"
255
+ growth = rev.get('growth_percent', 0)
256
+ emoji = "πŸ“ˆ" if growth > 0 else "πŸ“‰" if growth < 0 else "➑️"
257
+ report += f"Growth: {growth:+.1f}% {emoji}\n\n"
258
+
259
+ # Top Selling Drugs
260
+ report += "πŸ”₯ *TOP SELLING (Last 30 Days):*\n"
261
+ for i, drug in enumerate(analytics['top_drugs_30days'][:3], 1):
262
+ report += f"{i}. {drug['drug_name'].title()}: {drug['total_qty']} units ({drug['purchase_count']} orders)\n"
263
+ report += "\n"
264
+
265
+ # Stock-Out Risk
266
+ if analytics['stockout_risk']:
267
+ report += "⚠️ *STOCK-OUT RISK ALERT:*\n"
268
+ for item in analytics['stockout_risk'][:3]:
269
+ report += f"β€’ {item['drug_name'].title()}: {item['current_stock']} left "
270
+ report += f"({item.get('days_until_stockout', 'N/A')} days until out)\n"
271
+ report += "πŸ’‘ *Action:* Restock these items soon!\n\n"
272
+
273
+ # Customer Retention
274
+ report += "πŸ‘₯ *CUSTOMER METRICS:*\n"
275
+ ret = analytics['retention_metrics']
276
+ report += f"Total Customers: {ret['total_customers']}\n"
277
+ report += f"Returning: {ret['returning_customers']}\n"
278
+ report += f"Retention Rate: {ret['retention_rate']}%\n\n"
279
+
280
+ # Medication Adherence
281
+ report += "πŸ’Š *MEDICATION ADHERENCE:*\n"
282
+ adh = analytics['adherence_metrics']
283
+ report += f"Active Treatments: {adh['total_prescriptions']}\n"
284
+ report += f"Completed: {adh['completed_treatments']}\n"
285
+ report += f"Adherence Rate: {adh['adherence_rate']}%\n\n"
286
+
287
+ # Peak Hours
288
+ if analytics['peak_hours']:
289
+ report += "⏰ *BUSIEST HOURS:*\n"
290
+ for peak in analytics['peak_hours']:
291
+ hour = peak['hour']
292
+ period = "AM" if hour < 12 else "PM"
293
+ display_hour = hour if hour <= 12 else hour - 12
294
+ if display_hour == 0:
295
+ display_hour = 12
296
+ report += f"β€’ {display_hour} {period}: {peak['message_count']} messages\n"
297
+ report += "\n"
298
+
299
+ report += "πŸ’‘ *AI RECOMMENDATIONS:*\n"
300
+
301
+ # Generate smart recommendations
302
+ if analytics['stockout_risk']:
303
+ report += "β€’ Urgent: Restock low inventory items\n"
304
+
305
+ if rev.get('growth_percent', 0) < 0:
306
+ report += "β€’ Consider promotional campaigns\n"
307
+
308
+ if ret['retention_rate'] < 50:
309
+ report += "β€’ Improve customer retention programs\n"
310
+
311
+ if adh['adherence_rate'] < 70:
312
+ report += "β€’ Enhance medication reminder system\n"
313
+
314
+ report += "\nπŸ“ˆ Powered by Meta AI Analytics"
315
+
316
+ return {"reply": report}
317
+
318
+ def generate_inventory_report() -> dict:
319
+ """Generate comprehensive inventory analysis"""
320
+ analysis = db.get_inventory_analysis()
321
+
322
+ report = "πŸ“¦ *INVENTORY ANALYSIS*\n"
323
+ report += "="*35 + "\n\n"
324
+
325
+ # Overview
326
+ overview = analysis['overview']
327
+ report += f"πŸ“Š *OVERVIEW:*\n"
328
+ report += f"Total Items: {overview['total_items']}\n"
329
+ report += f"Total Value: ₦{overview['total_value']:,.2f}\n"
330
+ report += f"Avg Stock: {int(overview['avg_stock_level'])} units\n\n"
331
+
332
+ # Low Stock
333
+ if analysis['low_stock']:
334
+ report += f"⚠️ *LOW STOCK ({len(analysis['low_stock'])} items):*\n"
335
+ for item in analysis['low_stock'][:5]:
336
+ report += f"β€’ {item['drug_name'].title()}: {item['quantity']} left (₦{item['price']})\n"
337
+ report += "\n"
338
+
339
+ # High Value Items
340
+ report += "πŸ’Ž *TOP VALUE ITEMS:*\n"
341
+ for item in analysis['high_value_items'][:3]:
342
+ report += f"β€’ {item['drug_name'].title()}: ₦{item['total_value']:,.2f}\n"
343
+ report += "\n"
344
+
345
+ # By Category
346
+ report += "πŸ“ *BY CATEGORY:*\n"
347
+ for cat in analysis['by_category'][:5]:
348
+ report += f"β€’ {cat['category'].title()}: {cat['item_count']} items (₦{cat['category_value']:,.2f})\n"
349
+
350
+ return {"reply": report}
351
+
352
+ def generate_weekly_report() -> dict:
353
+ """Generate weekly summary report"""
354
+ stats = db.get_weekly_stats()
355
+
356
+ report = "πŸ“Š *WEEKLY SUMMARY REPORT*\n"
357
+ report += f"πŸ“… {datetime.now().strftime('%B %d, %Y')}\n"
358
+ report += "="*35 + "\n\n"
359
+
360
+ report += f"πŸ’° *SALES:*\n"
361
+ report += f"Total Revenue: ₦{stats['total_revenue']:,.2f}\n"
362
+ report += f"Orders: {stats['total_purchases']}\n"
363
+ report += f"Customers: {stats['unique_customers']}\n"
364
+ report += f"Top Drug: {stats['top_drug']}\n\n"
365
+
366
+ report += f"πŸ“¨ *ENGAGEMENT:*\n"
367
+ report += f"Messages: {stats['total_messages']}\n\n"
368
+
369
+ # Get low stock items
370
+ inventory = db.get_inventory()
371
+ low_stock = [i for i in inventory if i['quantity'] < 20]
372
+
373
+ if low_stock:
374
+ report += f"⚠️ *LOW STOCK:* {len(low_stock)} items\n"
375
+ for item in low_stock[:3]:
376
+ report += f" β€’ {item['drug_name'].title()}: {item['quantity']}\n"
377
+
378
+ return {"reply": report}
379
+
380
+ def get_admin_help() -> str:
381
+ """Admin help message"""
382
+ return """πŸ”§ *ADMIN COMMANDS:*
383
+
384
+ πŸ“¦ *Inventory:*
385
+ β€’ add drug [name] [qty] [price] [category]
386
+ β€’ inventory report / inventory analysis
387
+ β€’ Upload CSV via WhatsApp
388
+
389
+ πŸ“Š *Analytics:*
390
+ β€’ analytics / predictive insights
391
+ β€’ weekly report / weekly summary
392
+
393
+ πŸ’‘ *Examples:*
394
+ β€’ "analytics" - Get AI-powered insights
395
+ β€’ "inventory report" - Full stock analysis
396
+ β€’ "weekly report" - Week summary
397
+ β€’ "add drug paracetamol 100 500 fever"
398
+
399
+ πŸ“€ *CSV Upload:*
400
+ Send CSV file with columns:
401
+ drug_name,quantity,price,category,description,dosage_days,dosage_frequency"""
402
+
403
+ @app.post("/upload-inventory")
404
+ async def upload_inventory_csv(file: UploadFile = File(...)):
405
+ """Upload inventory via CSV"""
406
+ try:
407
+ contents = await file.read()
408
+ decoded = contents.decode('utf-8')
409
+ csv_reader = csv.DictReader(io.StringIO(decoded))
410
+
411
+ added_count = 0
412
+ errors = []
413
+
414
+ for row in csv_reader:
415
+ try:
416
+ drug_name = row['drug_name'].strip().lower()
417
+ quantity = int(row['quantity'])
418
+ price = float(row['price'])
419
+ category = row.get('category', 'general').strip()
420
+ description = row.get('description', '').strip()
421
+ dosage_days = int(row.get('dosage_days', 0))
422
+ dosage_frequency = row.get('dosage_frequency', 'as prescribed').strip()
423
+
424
+ db.update_inventory(drug_name, quantity, price, category, description, dosage_days, dosage_frequency)
425
+ added_count += 1
426
+
427
+ except Exception as e:
428
+ errors.append(f"Row {added_count + 1}: {str(e)}")
429
+
430
+ response = f"βœ… *CSV Upload Complete!*\n\n"
431
+ response += f"βœ“ Added/Updated: {added_count} items\n"
432
+
433
+ if errors:
434
+ response += f"\n⚠️ Errors ({len(errors)}):\n"
435
+ for error in errors[:5]:
436
+ response += f"β€’ {error}\n"
437
+
438
+ return {"reply": response}
439
+
440
+ except Exception as e:
441
+ return {"reply": f"❌ CSV Upload Failed: {str(e)}"}
442
+
443
+ @app.get("/medication-reminders")
444
+ async def get_medication_reminders():
445
+ """Get medication reminders for automated system"""
446
+ reminders_list = db.get_medication_reminders()
447
+
448
+ reminders = []
449
+ for reminder in reminders_list:
450
+ phone = reminder['phone_number']
451
+ drug = reminder['drug_name']
452
+ reminder_type = reminder['reminder_type']
453
+ dosage = reminder.get('dosage_frequency', 'as prescribed')
454
+
455
+ if reminder_type == 'daily':
456
+ message = (
457
+ f"πŸ’Š *MEDICATION REMINDER*\n\n"
458
+ f"Time to take your {drug.title()}!\n"
459
+ f"Dosage: {dosage}\n\n"
460
+ f"βœ… Reply 'took it' to confirm\n"
461
+ f"❌ Reply 'missed' if you missed a dose\n\n"
462
+ f"Stay consistent for best results! πŸ’ͺ"
463
+ )
464
+ elif reminder_type == 'completion':
465
+ message = (
466
+ f"πŸŽ‰ *TREATMENT MILESTONE*\n\n"
467
+ f"You've completed your {drug.title()} treatment course!\n\n"
468
+ f"How are you feeling?\n"
469
+ f"β€’ Much better 😊\n"
470
+ f"β€’ Some improvement πŸ€”\n"
471
+ f"β€’ No change 😟\n\n"
472
+ f"Your feedback helps us serve you better!"
473
+ )
474
+ else: # checkup
475
+ message = (
476
+ f"πŸ₯ *HEALTH CHECK-IN*\n\n"
477
+ f"It's been 3 days since you completed {drug.title()}.\n\n"
478
+ f"Quick checkup:\n"
479
+ f"β€’ Are your symptoms gone?\n"
480
+ f"β€’ Any side effects?\n"
481
+ f"β€’ Need any other medication?\n\n"
482
+ f"We're here to help! 😊"
483
+ )
484
+
485
+ reminders.append({
486
+ "phone_number": phone,
487
+ "message": message,
488
+ "purchase_id": reminder['purchase_id'],
489
+ "reminder_type": reminder_type
490
+ })
491
+
492
+ # Mark reminder as sent
493
+ db.mark_reminder_sent(reminder['purchase_id'])
494
+
495
+ # Mark as completed if checkup
496
+ if reminder_type == 'checkup':
497
+ db.mark_treatment_completed(reminder['purchase_id'])
498
+
499
+ return {"reminders": reminders}
500
+
501
+ @app.get("/generate-weekly-report")
502
+ async def api_generate_weekly_report():
503
+ """API endpoint for weekly report generation"""
504
+ result = generate_weekly_report()
505
+ return {"report": result["reply"]}
506
+
507
+ @app.get("/health")
508
+ async def health_check():
509
+ return {
510
+ "status": "healthy",
511
+ "service": "Ejide Pharmacy API",
512
+ "features": [
513
+ "24/7 WhatsApp engagement",
514
+ "AI-powered inventory",
515
+ "Medication reminders",
516
+ "Smart shopping cart",
517
+ "Predictive analytics",
518
+ "On-demand admin reports"
519
+ ]
520
+ }