skeleton (#3)
Browse files- first commit (9b406094e66833776ff788397b51f7ba733fcdc1)
- .gitignore +7 -0
- README.md +5 -1
- client/main.py +183 -0
- mcp/client/host.py +0 -110
- mcp/server/db_schema.py +0 -124
- mcp/server/eval.txt +0 -37
- mcp/server/main.py +0 -24
- mcp/server/routes.py +0 -167
- mcp/server/swiggy_cache.json +0 -1379
- mcp/server/swiggy_scraper.py +0 -179
- requirements.txt +9 -1
- server/email_db.json +119 -0
- server/email_scraper.py +267 -0
- server/main.py +29 -0
- server/query_parser.py +189 -0
- server/routes.py +206 -0
.gitignore
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.env
|
2 |
+
myenv/
|
3 |
+
|
4 |
+
__pycache__/
|
5 |
+
*.py[cod]
|
6 |
+
*.pyo
|
7 |
+
*.pyd
|
README.md
CHANGED
@@ -10,4 +10,8 @@ pinned: false
|
|
10 |
short_description: Answer any questions you have about the content of your mail
|
11 |
---
|
12 |
|
13 |
-
An example chatbot using [Gradio](https://gradio.app), [`huggingface_hub`](https://huggingface.co/docs/huggingface_hub/v0.22.2/en/index), and the [Hugging Face Inference API](https://huggingface.co/docs/api-inference/index).
|
|
|
|
|
|
|
|
|
|
10 |
short_description: Answer any questions you have about the content of your mail
|
11 |
---
|
12 |
|
13 |
+
An example chatbot using [Gradio](https://gradio.app), [`huggingface_hub`](https://huggingface.co/docs/huggingface_hub/v0.22.2/en/index), and the [Hugging Face Inference API](https://huggingface.co/docs/api-inference/index).
|
14 |
+
|
15 |
+
|
16 |
+
|
17 |
+
uvicorn main:app --reload
|
client/main.py
ADDED
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
|
3 |
+
import requests
|
4 |
+
import sys
|
5 |
+
from typing import Dict, Any
|
6 |
+
|
7 |
+
API_BASE = "http://127.0.0.1:8000/api/v1"
|
8 |
+
|
9 |
+
class EmailQueryCLI:
|
10 |
+
def __init__(self):
|
11 |
+
self.session = requests.Session()
|
12 |
+
|
13 |
+
def check_connection(self) -> bool:
|
14 |
+
"""Check if API server is running"""
|
15 |
+
try:
|
16 |
+
response = self.session.get(f"{API_BASE}/health")
|
17 |
+
response.raise_for_status()
|
18 |
+
return True
|
19 |
+
except:
|
20 |
+
return False
|
21 |
+
|
22 |
+
def pretty_print_email(self, email: Dict) -> str:
|
23 |
+
"""Format email for display"""
|
24 |
+
return f"""
|
25 |
+
📧 {email['subject']}
|
26 |
+
📅 {email['date']} {email['time']}
|
27 |
+
💬 {email['content'][:200]}...
|
28 |
+
🆔 {email['message_id'][:20]}...
|
29 |
+
{"─" * 60}"""
|
30 |
+
|
31 |
+
def handle_query(self, query: str):
|
32 |
+
"""Handle a natural language query"""
|
33 |
+
print(f"\n🔍 Processing: '{query}'")
|
34 |
+
|
35 |
+
try:
|
36 |
+
# Try to get emails directly
|
37 |
+
response = self.session.post(
|
38 |
+
f"{API_BASE}/get_emails",
|
39 |
+
json={"query": query}
|
40 |
+
)
|
41 |
+
|
42 |
+
if response.status_code == 200:
|
43 |
+
data = response.json()
|
44 |
+
self.display_email_results(data)
|
45 |
+
return True
|
46 |
+
|
47 |
+
elif response.status_code == 400:
|
48 |
+
error_detail = response.json()["detail"]
|
49 |
+
|
50 |
+
# Check if we need email mapping
|
51 |
+
if isinstance(error_detail, dict) and error_detail.get("type") == "need_email_input":
|
52 |
+
mapping_success = self.handle_missing_mapping(error_detail)
|
53 |
+
if mapping_success and hasattr(self, '_retry_query'):
|
54 |
+
# Retry the query after successful mapping
|
55 |
+
print(f"🔄 Retrying query...")
|
56 |
+
delattr(self, '_retry_query')
|
57 |
+
return self.handle_query(query) # Recursive retry
|
58 |
+
return mapping_success
|
59 |
+
else:
|
60 |
+
print(f"❌ Error: {error_detail}")
|
61 |
+
return False
|
62 |
+
else:
|
63 |
+
print(f"❌ API Error: {response.status_code}")
|
64 |
+
return False
|
65 |
+
|
66 |
+
except Exception as e:
|
67 |
+
print(f"❌ Connection Error: {e}")
|
68 |
+
return False
|
69 |
+
|
70 |
+
def handle_missing_mapping(self, error_detail: Dict) -> bool:
|
71 |
+
"""Handle case where email mapping is needed"""
|
72 |
+
sender_intent = error_detail["sender_intent"]
|
73 |
+
print(f"\n❓ {error_detail['message']}")
|
74 |
+
|
75 |
+
try:
|
76 |
+
email = input(f"📧 Enter email for '{sender_intent}': ").strip()
|
77 |
+
if not email or "@" not in email:
|
78 |
+
print("❌ Invalid email address")
|
79 |
+
return False
|
80 |
+
|
81 |
+
# Add the mapping
|
82 |
+
mapping_response = self.session.post(
|
83 |
+
f"{API_BASE}/add_email_mapping",
|
84 |
+
json={"name": sender_intent, "email": email}
|
85 |
+
)
|
86 |
+
|
87 |
+
if mapping_response.status_code == 200:
|
88 |
+
print(f"✅ Mapping saved: '{sender_intent}' → '{email}'")
|
89 |
+
self._retry_query = True # Flag to retry the original query
|
90 |
+
return True
|
91 |
+
else:
|
92 |
+
print(f"❌ Failed to save mapping: {mapping_response.text}")
|
93 |
+
return False
|
94 |
+
|
95 |
+
except KeyboardInterrupt:
|
96 |
+
print("\n❌ Cancelled")
|
97 |
+
return False
|
98 |
+
|
99 |
+
def display_email_results(self, data: Dict):
|
100 |
+
"""Display email search results"""
|
101 |
+
print(f"\n✅ Found {data['total_emails']} emails")
|
102 |
+
print(f"📤 From: {data['resolved_email']}")
|
103 |
+
print(f"📅 Period: {data['start_date']} to {data['end_date']}")
|
104 |
+
|
105 |
+
if data['emails']:
|
106 |
+
print(f"\n📧 Emails:")
|
107 |
+
for email in data['emails'][:10]: # Show first 10
|
108 |
+
print(self.pretty_print_email(email))
|
109 |
+
|
110 |
+
if len(data['emails']) > 10:
|
111 |
+
print(f"\n... and {len(data['emails']) - 10} more emails")
|
112 |
+
else:
|
113 |
+
print("\n📭 No emails found in this date range")
|
114 |
+
|
115 |
+
def show_mappings(self):
|
116 |
+
"""Display all stored name-to-email mappings"""
|
117 |
+
try:
|
118 |
+
response = self.session.get(f"{API_BASE}/view_mappings")
|
119 |
+
if response.status_code == 200:
|
120 |
+
data = response.json()
|
121 |
+
mappings = data["mappings"]
|
122 |
+
|
123 |
+
print(f"\n📇 Stored Mappings ({len(mappings)}):")
|
124 |
+
if mappings:
|
125 |
+
for name, email in mappings.items():
|
126 |
+
print(f" 👤 {name} → 📧 {email}")
|
127 |
+
else:
|
128 |
+
print(" (No mappings stored)")
|
129 |
+
else:
|
130 |
+
print(f"❌ Failed to load mappings: {response.text}")
|
131 |
+
except Exception as e:
|
132 |
+
print(f"❌ Error: {e}")
|
133 |
+
|
134 |
+
def run(self):
|
135 |
+
"""Main CLI loop"""
|
136 |
+
if not self.check_connection():
|
137 |
+
print("❌ Cannot connect to API server at http://127.0.0.1:8000")
|
138 |
+
print(" Make sure to run: uvicorn main:app --reload")
|
139 |
+
sys.exit(1)
|
140 |
+
|
141 |
+
print("✅ Connected to Email Query System")
|
142 |
+
print("💡 Try queries like:")
|
143 |
+
print(" • 'emails from john last week'")
|
144 |
+
print(" • 'show amazon emails from last month'")
|
145 |
+
print(" • 'get [email protected] emails yesterday'")
|
146 |
+
print("\n📋 Commands:")
|
147 |
+
print(" • 'mappings' - View stored name-to-email mappings")
|
148 |
+
print(" • 'quit' or Ctrl+C - Exit")
|
149 |
+
print("=" * 60)
|
150 |
+
|
151 |
+
while True:
|
152 |
+
try:
|
153 |
+
query = input("\n🗨️ You: ").strip()
|
154 |
+
|
155 |
+
if not query:
|
156 |
+
continue
|
157 |
+
|
158 |
+
if query.lower() in ['quit', 'exit', 'q']:
|
159 |
+
break
|
160 |
+
elif query.lower() in ['mappings', 'map', 'm']:
|
161 |
+
self.show_mappings()
|
162 |
+
elif query.lower() in ['help', 'h']:
|
163 |
+
print("\n💡 Examples:")
|
164 |
+
print(" • emails from amazon last 5 days")
|
165 |
+
print(" • show john smith emails this week")
|
166 |
+
print(" • get notifications from google yesterday")
|
167 |
+
else:
|
168 |
+
self.handle_query(query)
|
169 |
+
|
170 |
+
except KeyboardInterrupt:
|
171 |
+
break
|
172 |
+
except Exception as e:
|
173 |
+
print(f"❌ Unexpected error: {e}")
|
174 |
+
|
175 |
+
print("\n👋 Goodbye!")
|
176 |
+
|
177 |
+
def main():
|
178 |
+
"""Entry point for CLI"""
|
179 |
+
cli = EmailQueryCLI()
|
180 |
+
cli.run()
|
181 |
+
|
182 |
+
if __name__ == "__main__":
|
183 |
+
main()
|
mcp/client/host.py
DELETED
@@ -1,110 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""
|
3 |
-
CLI host for the FastAPI-powered Swiggy server.
|
4 |
-
|
5 |
-
Flow
|
6 |
-
1. Send natural-language query → /parse_query
|
7 |
-
2. If is_swiggy_query == True and dates are present → /get_orders
|
8 |
-
3. Pretty-print the result
|
9 |
-
"""
|
10 |
-
|
11 |
-
import requests, sys
|
12 |
-
from dateutil import parser as dtparse
|
13 |
-
|
14 |
-
API_BASE = "http://127.0.0.1:8000"
|
15 |
-
|
16 |
-
# ----------------------------------------------------------------------
|
17 |
-
# Helpers
|
18 |
-
# ----------------------------------------------------------------------
|
19 |
-
def nice_date(dt_str: str | None) -> str:
|
20 |
-
if not dt_str:
|
21 |
-
return "??"
|
22 |
-
return dtparse.parse(dt_str).strftime("%d %b %Y")
|
23 |
-
|
24 |
-
def pretty_order(order: dict) -> str:
|
25 |
-
if "error" in order:
|
26 |
-
return f" - Email #{order['email_number']}: ❌ {order['error']}"
|
27 |
-
head = (
|
28 |
-
f"Restaurant : {order['restaurant_name']}\n"
|
29 |
-
f"Date : {nice_date(order['order_date'])} {order['order_time']}\n"
|
30 |
-
f"Total : ₹{order['total_price']:.0f}\n"
|
31 |
-
"Items:"
|
32 |
-
)
|
33 |
-
items = "\n".join(
|
34 |
-
f" • {it['quantity']} × {it['name']} – ₹{it['price']:.0f}"
|
35 |
-
for it in order["items"]
|
36 |
-
)
|
37 |
-
return head + "\n" + items
|
38 |
-
|
39 |
-
# ----------------------------------------------------------------------
|
40 |
-
# Main REPL
|
41 |
-
# ----------------------------------------------------------------------
|
42 |
-
def main() -> None:
|
43 |
-
# Quick health-check
|
44 |
-
try:
|
45 |
-
requests.get(f"{API_BASE}/docs").raise_for_status()
|
46 |
-
except Exception as e:
|
47 |
-
print("❌ Cannot reach FastAPI server:", e)
|
48 |
-
sys.exit(1)
|
49 |
-
print("✅ Connected. Type a Swiggy question (or Ctrl-C to quit).")
|
50 |
-
|
51 |
-
while True:
|
52 |
-
try:
|
53 |
-
query = input("\n🗨️ You: ").strip()
|
54 |
-
except (EOFError, KeyboardInterrupt):
|
55 |
-
break
|
56 |
-
if not query:
|
57 |
-
continue
|
58 |
-
|
59 |
-
# ── Stage 1: parse_query ────────────────────────────────────
|
60 |
-
try:
|
61 |
-
r = requests.post(f"{API_BASE}/parse_query", json={"query": query})
|
62 |
-
r.raise_for_status()
|
63 |
-
meta = r.json() # {is_swiggy_query, start_date, end_date, intent}
|
64 |
-
except Exception as e:
|
65 |
-
print("⚠️ Parse error:", e)
|
66 |
-
print("🔎 Server:", r.text if 'r' in locals() else "no response")
|
67 |
-
continue
|
68 |
-
|
69 |
-
# Handle non-Swiggy or missing dates
|
70 |
-
if not meta.get("is_swiggy_query"):
|
71 |
-
print("🤷 That doesn’t look like a Swiggy order query.")
|
72 |
-
continue
|
73 |
-
if not meta.get("start_date") or not meta.get("end_date"):
|
74 |
-
print("⚠️ Couldn’t find a full date range in your question.")
|
75 |
-
continue
|
76 |
-
|
77 |
-
print(f" ↳ Date range: {meta['start_date']} → {meta['end_date']}")
|
78 |
-
print(f" ↳ Intent : {meta['intent']}")
|
79 |
-
|
80 |
-
# ── Stage 2: get_orders ────────────────────────────────────
|
81 |
-
try:
|
82 |
-
r2 = requests.post(
|
83 |
-
f"{API_BASE}/get_orders",
|
84 |
-
json={
|
85 |
-
"start_date": meta["start_date"],
|
86 |
-
"end_date": meta["end_date"],
|
87 |
-
},
|
88 |
-
)
|
89 |
-
r2.raise_for_status()
|
90 |
-
orders = r2.json()
|
91 |
-
except Exception as e:
|
92 |
-
print("⚠️ Failed to fetch orders:", e)
|
93 |
-
print("🔎 Server:", r2.text if 'r2' in locals() else "no response")
|
94 |
-
continue
|
95 |
-
|
96 |
-
# ── Output ────────────────────────────────────────────────
|
97 |
-
if not orders:
|
98 |
-
print("😕 No orders found for that range.")
|
99 |
-
continue
|
100 |
-
|
101 |
-
print("\n📦 Orders:")
|
102 |
-
for o in orders:
|
103 |
-
print(pretty_order(o))
|
104 |
-
print("-" * 40)
|
105 |
-
|
106 |
-
print("\n👋 Bye!")
|
107 |
-
|
108 |
-
# ----------------------------------------------------------------------
|
109 |
-
if __name__ == "__main__":
|
110 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp/server/db_schema.py
DELETED
@@ -1,124 +0,0 @@
|
|
1 |
-
# File: db_schema.py
|
2 |
-
import sqlite3
|
3 |
-
|
4 |
-
DB_NAME = "swiggy_orders.db"
|
5 |
-
|
6 |
-
|
7 |
-
def init_db(db_path: str = DB_NAME) -> None:
|
8 |
-
"""
|
9 |
-
Initialize the SQLite database with the necessary tables.
|
10 |
-
"""
|
11 |
-
conn = sqlite3.connect(db_path)
|
12 |
-
c = conn.cursor()
|
13 |
-
# Orders metadata
|
14 |
-
c.execute(
|
15 |
-
"""
|
16 |
-
CREATE TABLE IF NOT EXISTS orders (
|
17 |
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
18 |
-
email_number INTEGER,
|
19 |
-
order_date TEXT,
|
20 |
-
order_time TEXT,
|
21 |
-
restaurant_name TEXT,
|
22 |
-
delivery_address TEXT,
|
23 |
-
total_price REAL
|
24 |
-
)
|
25 |
-
"""
|
26 |
-
)
|
27 |
-
# Individual items per order
|
28 |
-
c.execute(
|
29 |
-
"""
|
30 |
-
CREATE TABLE IF NOT EXISTS order_items (
|
31 |
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
32 |
-
order_id INTEGER,
|
33 |
-
item_name TEXT,
|
34 |
-
quantity INTEGER,
|
35 |
-
price REAL,
|
36 |
-
FOREIGN KEY(order_id) REFERENCES orders(id)
|
37 |
-
)
|
38 |
-
"""
|
39 |
-
)
|
40 |
-
conn.commit()
|
41 |
-
conn.close()
|
42 |
-
|
43 |
-
|
44 |
-
def get_db_connection(db_path: str = DB_NAME) -> sqlite3.Connection:
|
45 |
-
"""Return a new connection to the database."""
|
46 |
-
return sqlite3.connect(db_path)
|
47 |
-
|
48 |
-
|
49 |
-
def get_orders_by_date_from_db(date_str: str) -> list[dict]:
|
50 |
-
"""
|
51 |
-
Fetch all orders and their items for a given date from the database.
|
52 |
-
"""
|
53 |
-
conn = get_db_connection()
|
54 |
-
c = conn.cursor()
|
55 |
-
c.execute(
|
56 |
-
"SELECT id, email_number, order_time, restaurant_name, delivery_address, total_price"
|
57 |
-
" FROM orders WHERE order_date = ?",
|
58 |
-
(date_str,)
|
59 |
-
)
|
60 |
-
orders = []
|
61 |
-
for order_id, email_number, order_time, restaurant_name, delivery_address, total_price in c.fetchall():
|
62 |
-
# fetch items for this order
|
63 |
-
c.execute(
|
64 |
-
"SELECT item_name, quantity, price FROM order_items WHERE order_id = ?",
|
65 |
-
(order_id,)
|
66 |
-
)
|
67 |
-
items = [
|
68 |
-
{"name": name, "quantity": qty, "price": price}
|
69 |
-
for name, qty, price in c.fetchall()
|
70 |
-
]
|
71 |
-
orders.append({
|
72 |
-
"email_number": email_number,
|
73 |
-
"order_date": date_str,
|
74 |
-
"order_time": order_time,
|
75 |
-
"restaurant_name": restaurant_name,
|
76 |
-
"delivery_address": delivery_address,
|
77 |
-
"items": items,
|
78 |
-
"total_price": total_price
|
79 |
-
})
|
80 |
-
conn.close()
|
81 |
-
return orders
|
82 |
-
|
83 |
-
|
84 |
-
def save_orders_to_db(date_str: str, orders: list[dict]) -> None:
|
85 |
-
"""
|
86 |
-
Insert scraped orders and their items for a given date into the database.
|
87 |
-
"""
|
88 |
-
conn = get_db_connection()
|
89 |
-
c = conn.cursor()
|
90 |
-
for order in orders:
|
91 |
-
c.execute(
|
92 |
-
"""
|
93 |
-
INSERT INTO orders
|
94 |
-
(email_number, order_date, order_time, restaurant_name, delivery_address, total_price)
|
95 |
-
VALUES (?, ?, ?, ?, ?, ?)
|
96 |
-
""",
|
97 |
-
(
|
98 |
-
order["email_number"],
|
99 |
-
date_str,
|
100 |
-
order["order_time"],
|
101 |
-
order["restaurant_name"],
|
102 |
-
order["delivery_address"],
|
103 |
-
order["total_price"]
|
104 |
-
)
|
105 |
-
)
|
106 |
-
order_id = c.lastrowid
|
107 |
-
for item in order.get("items", []):
|
108 |
-
c.execute(
|
109 |
-
"""
|
110 |
-
INSERT INTO order_items
|
111 |
-
(order_id, item_name, quantity, price)
|
112 |
-
VALUES (?, ?, ?, ?)
|
113 |
-
""",
|
114 |
-
(
|
115 |
-
order_id,
|
116 |
-
item["name"],
|
117 |
-
item["quantity"],
|
118 |
-
item["price"]
|
119 |
-
)
|
120 |
-
)
|
121 |
-
conn.commit()
|
122 |
-
conn.close()
|
123 |
-
|
124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp/server/eval.txt
DELETED
@@ -1,37 +0,0 @@
|
|
1 |
-
EVALUATIONS
|
2 |
-
|
3 |
-
able to differentiate between swiggy based queries or not
|
4 |
-
able to break prompts into 2 instruction. First instruction will be extracting dates.
|
5 |
-
second instructions will be analysing the data scrapped.
|
6 |
-
able to extract dates
|
7 |
-
able to extract the analysis:
|
8 |
-
|
9 |
-
-How much non-veg did I order last week / between date X and date Y?
|
10 |
-
|
11 |
-
-Counts and/or total rupees spent on non-veg dishes.
|
12 |
-
|
13 |
-
-What was my total expense between date X and date Y?
|
14 |
-
|
15 |
-
-What is my average daily spend on Swiggy (overall or for a given period)?
|
16 |
-
|
17 |
-
-Which restaurant got the most orders (or the most money) in that period?
|
18 |
-
|
19 |
-
-What single order cost me the most during that window?
|
20 |
-
|
21 |
-
-Show a day-by-day spend chart between date X and date Y.
|
22 |
-
|
23 |
-
-List every item I’ve ordered only once—my “one-hit wonders.”
|
24 |
-
|
25 |
-
-How many unique cuisines did I try in the last month?
|
26 |
-
|
27 |
-
-Compare veg vs. non-veg spend for the current calendar year.
|
28 |
-
|
29 |
-
-When was my longest streak of days without ordering anything?
|
30 |
-
|
31 |
-
-Identify any “late-night” orders placed after 11 p.m. this week.
|
32 |
-
|
33 |
-
-Tell me my top three most-ordered dishes in the past six months.
|
34 |
-
|
35 |
-
-Which new restaurant did I try most recently, and what did I order?
|
36 |
-
|
37 |
-
-Forecast next month’s spend based on my last three-month trend.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp/server/main.py
DELETED
@@ -1,24 +0,0 @@
|
|
1 |
-
# File: main.py
|
2 |
-
from fastapi import FastAPI
|
3 |
-
from contextlib import asynccontextmanager
|
4 |
-
from routes import router
|
5 |
-
from db_schema import init_db
|
6 |
-
|
7 |
-
@asynccontextmanager
|
8 |
-
async def lifespan(app: FastAPI):
|
9 |
-
print("🚀 Server is starting up...")
|
10 |
-
# Ensure SQLite tables exist before handling requests
|
11 |
-
init_db()
|
12 |
-
yield
|
13 |
-
print("🧹 Server is shutting down... Cleaned up!")
|
14 |
-
|
15 |
-
app = FastAPI(
|
16 |
-
title="Swiggy Email API",
|
17 |
-
description="Extract Swiggy orders from Gmail",
|
18 |
-
version="1.0.0",
|
19 |
-
lifespan=lifespan
|
20 |
-
)
|
21 |
-
|
22 |
-
app.include_router(router)
|
23 |
-
|
24 |
-
# Run with: uvicorn main:app --reload
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp/server/routes.py
DELETED
@@ -1,167 +0,0 @@
|
|
1 |
-
from fastapi import APIRouter, HTTPException
|
2 |
-
from pydantic import BaseModel
|
3 |
-
from swiggy_scraper import fetch_swiggy_orders
|
4 |
-
from datetime import datetime
|
5 |
-
from openai import OpenAI
|
6 |
-
import os, json
|
7 |
-
|
8 |
-
router = APIRouter()
|
9 |
-
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
10 |
-
|
11 |
-
class QueryInput(BaseModel):
|
12 |
-
query: str
|
13 |
-
|
14 |
-
class DateRange(BaseModel):
|
15 |
-
start_date: str
|
16 |
-
end_date: str
|
17 |
-
|
18 |
-
# @router.post("/parse_query")
|
19 |
-
# def parse_query_llm(input: QueryInput):
|
20 |
-
|
21 |
-
# print("\ninput query:")
|
22 |
-
# print(input)
|
23 |
-
|
24 |
-
# today_str = datetime.today().strftime("%d-%b-%Y")
|
25 |
-
# system = (
|
26 |
-
# f"You are a date range extractor.\n"
|
27 |
-
# f"Today is {today_str}.\n"
|
28 |
-
# "Extract start_date and end_date in 'DD-MMM-YYYY' format.\n"
|
29 |
-
# "Respond with:\n"
|
30 |
-
# "Output ONLY a valid JSON object like:\n"
|
31 |
-
# '{ "start_date": "17-May-2025", "end_date": "18-May-2025" }\n'
|
32 |
-
# 'no extra commentry needed.'
|
33 |
-
# )
|
34 |
-
# try:
|
35 |
-
# rsp = client.chat.completions.create(
|
36 |
-
# model="gpt-4o-mini",
|
37 |
-
# temperature=0,
|
38 |
-
# messages=[
|
39 |
-
# {"role": "system", "content": system},
|
40 |
-
# {"role": "user", "content": input.query},
|
41 |
-
# ]
|
42 |
-
# )
|
43 |
-
# result = json.loads(rsp.choices[0].message.content.strip())
|
44 |
-
# if "start_date" not in result or "end_date" not in result:
|
45 |
-
# raise ValueError("Invalid response format")
|
46 |
-
# print("results:", result)
|
47 |
-
# return result
|
48 |
-
# except Exception as e:
|
49 |
-
# raise HTTPException(status_code=400, detail=str(e))
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
def _llm(messages, model="gpt-4o-mini", temperature=0):
|
58 |
-
rsp = client.chat.completions.create(
|
59 |
-
model=model,
|
60 |
-
temperature=temperature,
|
61 |
-
messages=messages,
|
62 |
-
)
|
63 |
-
return rsp.choices[0].message.content.strip()
|
64 |
-
|
65 |
-
# ---------- Stage 1: classify + extract dates --------------------------
|
66 |
-
|
67 |
-
def _extract_scope(user_query: str):
|
68 |
-
today_str = datetime.today().strftime("%d-%b-%Y")
|
69 |
-
|
70 |
-
sys_prompt = f"""
|
71 |
-
Today is {today_str}.
|
72 |
-
You are a SCOPING assistant: decide if the user's text is about Swiggy food orders,
|
73 |
-
extract ONE date range, and keep the leftover words.
|
74 |
-
|
75 |
-
Return ONLY valid JSON like:
|
76 |
-
{{
|
77 |
-
"is_swiggy_query": true,
|
78 |
-
"start_date": "15-May-2025",
|
79 |
-
"end_date": "20-May-2025",
|
80 |
-
"remainder": "non veg expense"
|
81 |
-
}}
|
82 |
-
|
83 |
-
Rules:
|
84 |
-
• Accept natural phrases (“last week”, “since 1 May”).
|
85 |
-
• If no dates → start_date & end_date = null.
|
86 |
-
• If not Swiggy related → is_swiggy_query=false and remainder is full original text.
|
87 |
-
• Do NOT invent a remainder; it is literally whatever words follow the date phrase(s).
|
88 |
-
"""
|
89 |
-
raw = _llm(
|
90 |
-
[
|
91 |
-
{"role": "system", "content": sys_prompt},
|
92 |
-
{"role": "user", "content": user_query}
|
93 |
-
]
|
94 |
-
)
|
95 |
-
return json.loads(raw)
|
96 |
-
|
97 |
-
# ---------- Stage 2: shrink “remainder” into an intent -----------------
|
98 |
-
|
99 |
-
def _extract_intent(remainder: str):
|
100 |
-
sys_prompt = """
|
101 |
-
You are an INTENT classifier for Swiggy-order analytics.
|
102 |
-
Map the sentence into one concise snake_case intent.
|
103 |
-
Allowed intents (extendable):
|
104 |
-
|
105 |
-
• calculate_expense
|
106 |
-
• list_orders
|
107 |
-
• list_items
|
108 |
-
• list_nonveg_items
|
109 |
-
• list_veg_items
|
110 |
-
• count_orders
|
111 |
-
• unknown
|
112 |
-
|
113 |
-
Return JSON: { "intent": "calculate_expense" }
|
114 |
-
If unsure choose "unknown".
|
115 |
-
"""
|
116 |
-
raw = _llm(
|
117 |
-
[
|
118 |
-
{"role": "system", "content": sys_prompt},
|
119 |
-
{"role": "user", "content": remainder.strip()}
|
120 |
-
]
|
121 |
-
)
|
122 |
-
return json.loads(raw)["intent"]
|
123 |
-
|
124 |
-
# ---------- FastAPI route ----------------------------------------------
|
125 |
-
|
126 |
-
@router.post("/parse_query")
|
127 |
-
def parse_query_llm(input: QueryInput):
|
128 |
-
try:
|
129 |
-
scope = _extract_scope(input.query)
|
130 |
-
print("scope")
|
131 |
-
print(scope)
|
132 |
-
# If it is a Swiggy query, classify intent; else, intent = "unrelated"
|
133 |
-
if scope.get("is_swiggy_query", False):
|
134 |
-
intent = _extract_intent(scope.get("remainder", ""))
|
135 |
-
else:
|
136 |
-
intent = "unrelated"
|
137 |
-
|
138 |
-
result = {
|
139 |
-
"is_swiggy_query": scope["is_swiggy_query"],
|
140 |
-
"start_date": scope["start_date"],
|
141 |
-
"end_date": scope["end_date"],
|
142 |
-
"intent": intent
|
143 |
-
}
|
144 |
-
|
145 |
-
|
146 |
-
print("result")
|
147 |
-
print(result)
|
148 |
-
|
149 |
-
return result
|
150 |
-
|
151 |
-
except Exception as e:
|
152 |
-
raise HTTPException(status_code=400, detail=str(e))
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
@router.post("/get_orders")
|
162 |
-
def get_orders(range: DateRange):
|
163 |
-
try:
|
164 |
-
orders = fetch_swiggy_orders(range.start_date, range.end_date)
|
165 |
-
return orders
|
166 |
-
except Exception as e:
|
167 |
-
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp/server/swiggy_cache.json
DELETED
@@ -1,1379 +0,0 @@
|
|
1 |
-
{
|
2 |
-
"2025-05-17": [
|
3 |
-
{
|
4 |
-
"email_number": 1,
|
5 |
-
"order_date": "17-May-2025",
|
6 |
-
"order_time": "11:00:08",
|
7 |
-
"restaurant_name": "IDC Kitchen",
|
8 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
9 |
-
"items": [
|
10 |
-
{
|
11 |
-
"name": "Thatte Idli",
|
12 |
-
"quantity": 1,
|
13 |
-
"price": 59
|
14 |
-
},
|
15 |
-
{
|
16 |
-
"name": "Idly 2 Pc",
|
17 |
-
"quantity": 1,
|
18 |
-
"price": 59
|
19 |
-
},
|
20 |
-
{
|
21 |
-
"name": "Vada (1 Pc)",
|
22 |
-
"quantity": 3,
|
23 |
-
"price": 174
|
24 |
-
}
|
25 |
-
],
|
26 |
-
"total_price": 330
|
27 |
-
},
|
28 |
-
{
|
29 |
-
"email_number": 2,
|
30 |
-
"order_date": "17-May-2025",
|
31 |
-
"order_time": "17:02:59",
|
32 |
-
"restaurant_name": "Suryawanshi",
|
33 |
-
"delivery_address": "Shobhit\n2C, Orchard Green Apartment\n2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
34 |
-
"items": [
|
35 |
-
{
|
36 |
-
"name": "Vada Pav (twin)",
|
37 |
-
"quantity": 1,
|
38 |
-
"price": 95
|
39 |
-
},
|
40 |
-
{
|
41 |
-
"name": "Kande Bhaji",
|
42 |
-
"quantity": 1,
|
43 |
-
"price": 160
|
44 |
-
}
|
45 |
-
],
|
46 |
-
"total_price": 300
|
47 |
-
},
|
48 |
-
{
|
49 |
-
"email_number": 3,
|
50 |
-
"order_date": "17-May-2025",
|
51 |
-
"order_time": "20:25:39",
|
52 |
-
"restaurant_name": "Meghana Foods",
|
53 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
54 |
-
"items": [
|
55 |
-
{
|
56 |
-
"name": "Extra Aloo 4 Pcs",
|
57 |
-
"quantity": 1,
|
58 |
-
"price": 45
|
59 |
-
},
|
60 |
-
{
|
61 |
-
"name": "Chicken Boneless Biryani",
|
62 |
-
"quantity": 1,
|
63 |
-
"price": 360
|
64 |
-
},
|
65 |
-
{
|
66 |
-
"name": "Lemon Chicken",
|
67 |
-
"quantity": 1,
|
68 |
-
"price": 360
|
69 |
-
},
|
70 |
-
{
|
71 |
-
"name": "Veg Manchurian Biryani",
|
72 |
-
"quantity": 1,
|
73 |
-
"price": 360
|
74 |
-
}
|
75 |
-
],
|
76 |
-
"total_price": 1240
|
77 |
-
}
|
78 |
-
],
|
79 |
-
"2025-05-19": [
|
80 |
-
{
|
81 |
-
"email_number": 1,
|
82 |
-
"order_date": "19-May-2025",
|
83 |
-
"order_time": "22:28:08",
|
84 |
-
"restaurant_name": "Swiggy Instamart",
|
85 |
-
"delivery_address": "2C, Orchard Green Apartment 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
86 |
-
"items": [
|
87 |
-
{
|
88 |
-
"name": "Safal Frozen Green Peas (Matar)",
|
89 |
-
"quantity": 1,
|
90 |
-
"price": 110
|
91 |
-
},
|
92 |
-
{
|
93 |
-
"name": "NOTO Strawberry Raspberry Sugar Free Popsicle Ice Cream",
|
94 |
-
"quantity": 4,
|
95 |
-
"price": 500
|
96 |
-
},
|
97 |
-
{
|
98 |
-
"name": "Baby Lady's Finger (Bendekaayi)",
|
99 |
-
"quantity": 2,
|
100 |
-
"price": 32
|
101 |
-
},
|
102 |
-
{
|
103 |
-
"name": "Whisper Ultra Skin Love Soft 30 Xl+ Sanitary Pads, Cottony Soft",
|
104 |
-
"quantity": 1,
|
105 |
-
"price": 323
|
106 |
-
},
|
107 |
-
{
|
108 |
-
"name": "Akshayakalpa Artisanal Organic Set Curd",
|
109 |
-
"quantity": 1,
|
110 |
-
"price": 145
|
111 |
-
},
|
112 |
-
{
|
113 |
-
"name": "Akshayakalpa Organic Malai Paneer",
|
114 |
-
"quantity": 1,
|
115 |
-
"price": 119
|
116 |
-
},
|
117 |
-
{
|
118 |
-
"name": "Aashirvaad Superior MP Atta",
|
119 |
-
"quantity": 2,
|
120 |
-
"price": 146
|
121 |
-
},
|
122 |
-
{
|
123 |
-
"name": "NOTO Kala Jamun Sugar Free Popsicle Ice Cream",
|
124 |
-
"quantity": 2,
|
125 |
-
"price": 250
|
126 |
-
},
|
127 |
-
{
|
128 |
-
"name": "Royal Gala Apple (Sebu)",
|
129 |
-
"quantity": 2,
|
130 |
-
"price": 356
|
131 |
-
}
|
132 |
-
],
|
133 |
-
"total_price": 1564
|
134 |
-
}
|
135 |
-
],
|
136 |
-
"2025-05-01": [
|
137 |
-
{
|
138 |
-
"email_number": 1,
|
139 |
-
"order_date": "01-May-2025",
|
140 |
-
"order_time": "11:36:15",
|
141 |
-
"restaurant_name": "Starbucks Coffee",
|
142 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
143 |
-
"items": [
|
144 |
-
{
|
145 |
-
"name": "Caffe Americano",
|
146 |
-
"quantity": 2,
|
147 |
-
"price": 710
|
148 |
-
}
|
149 |
-
],
|
150 |
-
"total_price": 647
|
151 |
-
},
|
152 |
-
{
|
153 |
-
"email_number": 2,
|
154 |
-
"order_date": "01-May-2025",
|
155 |
-
"order_time": "18:34:08",
|
156 |
-
"restaurant_name": "Chaayos Chai+Snacks=Relax",
|
157 |
-
"delivery_address": "Shobhit\n2C, Orchard Green Apartment\n2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
158 |
-
"items": [
|
159 |
-
{
|
160 |
-
"name": "Vada Pav",
|
161 |
-
"quantity": 2,
|
162 |
-
"price": 238
|
163 |
-
},
|
164 |
-
{
|
165 |
-
"name": "Desi Chai",
|
166 |
-
"quantity": 1,
|
167 |
-
"price": 219
|
168 |
-
},
|
169 |
-
{
|
170 |
-
"name": "Samosa(2 pc)",
|
171 |
-
"quantity": 1,
|
172 |
-
"price": 90
|
173 |
-
}
|
174 |
-
],
|
175 |
-
"total_price": 528
|
176 |
-
},
|
177 |
-
{
|
178 |
-
"email_number": 3,
|
179 |
-
"order_date": "01-May-2025",
|
180 |
-
"order_time": "21:28:12",
|
181 |
-
"restaurant_name": "Lavonne Cafe",
|
182 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
183 |
-
"items": [
|
184 |
-
{
|
185 |
-
"name": "Baguette",
|
186 |
-
"quantity": 1,
|
187 |
-
"price": 190.48
|
188 |
-
}
|
189 |
-
],
|
190 |
-
"total_price": 278
|
191 |
-
}
|
192 |
-
],
|
193 |
-
"2025-05-02": [
|
194 |
-
{
|
195 |
-
"email_number": 1,
|
196 |
-
"order_date": "02-May-2025",
|
197 |
-
"order_time": "11:13:38",
|
198 |
-
"restaurant_name": "Starbucks Coffee",
|
199 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
200 |
-
"items": [
|
201 |
-
{
|
202 |
-
"name": "Caffe Americano",
|
203 |
-
"quantity": 1,
|
204 |
-
"price": 315
|
205 |
-
},
|
206 |
-
{
|
207 |
-
"name": "Caffe Americano",
|
208 |
-
"quantity": 1,
|
209 |
-
"price": 355
|
210 |
-
}
|
211 |
-
],
|
212 |
-
"total_price": 605
|
213 |
-
},
|
214 |
-
{
|
215 |
-
"email_number": 2,
|
216 |
-
"order_date": "02-May-2025",
|
217 |
-
"order_time": "14:43:19",
|
218 |
-
"restaurant_name": "Harvest Salad Co",
|
219 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
220 |
-
"items": [
|
221 |
-
{
|
222 |
-
"name": "Hummus And Chipotle Chicken Wrap",
|
223 |
-
"quantity": 2,
|
224 |
-
"price": 498
|
225 |
-
},
|
226 |
-
{
|
227 |
-
"name": "Mediterranean Bowl (Veg)",
|
228 |
-
"quantity": 1,
|
229 |
-
"price": 299
|
230 |
-
}
|
231 |
-
],
|
232 |
-
"total_price": 899
|
233 |
-
},
|
234 |
-
{
|
235 |
-
"email_number": 3,
|
236 |
-
"order_date": "02-May-2025",
|
237 |
-
"order_time": "21:10:20",
|
238 |
-
"restaurant_name": "Swiggy Instamart",
|
239 |
-
"delivery_address": "2C, Orchard Green Apartment 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
240 |
-
"items": [
|
241 |
-
{
|
242 |
-
"name": "Classic Ice Burst",
|
243 |
-
"quantity": 1,
|
244 |
-
"price": 170
|
245 |
-
}
|
246 |
-
],
|
247 |
-
"total_price": 222
|
248 |
-
},
|
249 |
-
{
|
250 |
-
"email_number": 4,
|
251 |
-
"order_date": "02-May-2025",
|
252 |
-
"order_time": "22:01:35",
|
253 |
-
"restaurant_name": "Dhaba Estd 1986 Delhi",
|
254 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
255 |
-
"items": [
|
256 |
-
{
|
257 |
-
"name": "Non Vegetarian Platter ( 12 Pcs)",
|
258 |
-
"quantity": 1,
|
259 |
-
"price": 1249
|
260 |
-
},
|
261 |
-
{
|
262 |
-
"name": "Butter Naan",
|
263 |
-
"quantity": 2,
|
264 |
-
"price": 198
|
265 |
-
},
|
266 |
-
{
|
267 |
-
"name": "Dhabe Di Roti",
|
268 |
-
"quantity": 2,
|
269 |
-
"price": 238
|
270 |
-
},
|
271 |
-
{
|
272 |
-
"name": "Laal Mirchi Parantha",
|
273 |
-
"quantity": 1,
|
274 |
-
"price": 109
|
275 |
-
},
|
276 |
-
{
|
277 |
-
"name": "Handi Murgh",
|
278 |
-
"quantity": 1,
|
279 |
-
"price": 629
|
280 |
-
}
|
281 |
-
],
|
282 |
-
"total_price": 2006
|
283 |
-
}
|
284 |
-
],
|
285 |
-
"2025-05-03": [
|
286 |
-
{
|
287 |
-
"email_number": 1,
|
288 |
-
"order_date": "03-May-2025",
|
289 |
-
"order_time": "09:50:51",
|
290 |
-
"restaurant_name": "Starbucks Coffee",
|
291 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
292 |
-
"items": [
|
293 |
-
{
|
294 |
-
"name": "Cold Brew Black",
|
295 |
-
"quantity": 1,
|
296 |
-
"price": 410
|
297 |
-
},
|
298 |
-
{
|
299 |
-
"name": "Caffe Americano.",
|
300 |
-
"quantity": 1,
|
301 |
-
"price": 315
|
302 |
-
}
|
303 |
-
],
|
304 |
-
"total_price": 691
|
305 |
-
},
|
306 |
-
{
|
307 |
-
"email_number": 2,
|
308 |
-
"order_date": "03-May-2025",
|
309 |
-
"order_time": "15:06:18",
|
310 |
-
"restaurant_name": "Copper + Cloves",
|
311 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
312 |
-
"items": [
|
313 |
-
{
|
314 |
-
"name": "Asian Peanut Salad",
|
315 |
-
"quantity": 1,
|
316 |
-
"price": 500
|
317 |
-
},
|
318 |
-
{
|
319 |
-
"name": "Tofu",
|
320 |
-
"quantity": 1,
|
321 |
-
"price": 100
|
322 |
-
}
|
323 |
-
],
|
324 |
-
"total_price": 432
|
325 |
-
},
|
326 |
-
{
|
327 |
-
"email_number": 3,
|
328 |
-
"order_date": "03-May-2025",
|
329 |
-
"order_time": "21:32:47",
|
330 |
-
"restaurant_name": "Shiro",
|
331 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
332 |
-
"items": [
|
333 |
-
{
|
334 |
-
"name": "Super Crunch (8 pcs)",
|
335 |
-
"quantity": 1,
|
336 |
-
"price": 695
|
337 |
-
},
|
338 |
-
{
|
339 |
-
"name": "Bulgogi Chicken Spring Rolls (6 pcs)",
|
340 |
-
"quantity": 1,
|
341 |
-
"price": 595
|
342 |
-
},
|
343 |
-
{
|
344 |
-
"name": "Cantonese Chicken Wonton (6 pcs)",
|
345 |
-
"quantity": 1,
|
346 |
-
"price": 410
|
347 |
-
},
|
348 |
-
{
|
349 |
-
"name": "Smoky Pork Gyoza (4 pcs)",
|
350 |
-
"quantity": 1,
|
351 |
-
"price": 495
|
352 |
-
},
|
353 |
-
{
|
354 |
-
"name": "Spicy Salmon Negi (8 pcs)",
|
355 |
-
"quantity": 1,
|
356 |
-
"price": 670
|
357 |
-
}
|
358 |
-
],
|
359 |
-
"total_price": 2600
|
360 |
-
}
|
361 |
-
],
|
362 |
-
"2025-05-04": [
|
363 |
-
{
|
364 |
-
"email_number": 1,
|
365 |
-
"order_date": "04-May-2025",
|
366 |
-
"order_time": "09:54:22",
|
367 |
-
"restaurant_name": "IDC Kitchen",
|
368 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
369 |
-
"items": [
|
370 |
-
{
|
371 |
-
"name": "Masala Dosa",
|
372 |
-
"quantity": 1,
|
373 |
-
"price": 140
|
374 |
-
},
|
375 |
-
{
|
376 |
-
"name": "Idly 2 Pc",
|
377 |
-
"quantity": 1,
|
378 |
-
"price": 59
|
379 |
-
},
|
380 |
-
{
|
381 |
-
"name": "Thatte Idli",
|
382 |
-
"quantity": 1,
|
383 |
-
"price": 59
|
384 |
-
},
|
385 |
-
{
|
386 |
-
"name": "Vada (1 Pc)",
|
387 |
-
"quantity": 1,
|
388 |
-
"price": 58
|
389 |
-
}
|
390 |
-
],
|
391 |
-
"total_price": 356
|
392 |
-
},
|
393 |
-
{
|
394 |
-
"email_number": 2,
|
395 |
-
"order_date": "04-May-2025",
|
396 |
-
"order_time": "10:21:33",
|
397 |
-
"restaurant_name": "Starbucks Coffee",
|
398 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
399 |
-
"items": [
|
400 |
-
{
|
401 |
-
"name": "Caffe Americano",
|
402 |
-
"quantity": 1,
|
403 |
-
"price": 315
|
404 |
-
},
|
405 |
-
{
|
406 |
-
"name": "Caffe Americano",
|
407 |
-
"quantity": 1,
|
408 |
-
"price": 355
|
409 |
-
}
|
410 |
-
],
|
411 |
-
"total_price": 694
|
412 |
-
}
|
413 |
-
],
|
414 |
-
"2025-05-05": [
|
415 |
-
{
|
416 |
-
"email_number": 1,
|
417 |
-
"order_date": "05-May-2025",
|
418 |
-
"order_time": "09:23:24",
|
419 |
-
"restaurant_name": "Starbucks Coffee",
|
420 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
421 |
-
"items": [
|
422 |
-
{
|
423 |
-
"name": "Caffe Americano",
|
424 |
-
"quantity": 1,
|
425 |
-
"price": 315
|
426 |
-
},
|
427 |
-
{
|
428 |
-
"name": "Caffe Americano",
|
429 |
-
"quantity": 1,
|
430 |
-
"price": 355
|
431 |
-
}
|
432 |
-
],
|
433 |
-
"total_price": 694
|
434 |
-
},
|
435 |
-
{
|
436 |
-
"email_number": 2,
|
437 |
-
"order_date": "05-May-2025",
|
438 |
-
"order_time": "13:58:29",
|
439 |
-
"restaurant_name": "Maverick & Farmer Coffee",
|
440 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
441 |
-
"items": [
|
442 |
-
{
|
443 |
-
"name": "Cold Brew Latte",
|
444 |
-
"quantity": 1,
|
445 |
-
"price": 300
|
446 |
-
},
|
447 |
-
{
|
448 |
-
"name": "Smoked Tandoori Chicken",
|
449 |
-
"quantity": 1,
|
450 |
-
"price": 450
|
451 |
-
}
|
452 |
-
],
|
453 |
-
"total_price": 730
|
454 |
-
},
|
455 |
-
{
|
456 |
-
"email_number": 3,
|
457 |
-
"order_date": "05-May-2025",
|
458 |
-
"order_time": "14:42:09",
|
459 |
-
"restaurant_name": "Magnolia Bakery",
|
460 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
461 |
-
"items": [
|
462 |
-
{
|
463 |
-
"name": "CLASSIC TRES LECHES",
|
464 |
-
"quantity": 1,
|
465 |
-
"price": 410
|
466 |
-
}
|
467 |
-
],
|
468 |
-
"total_price": 538
|
469 |
-
},
|
470 |
-
{
|
471 |
-
"email_number": 4,
|
472 |
-
"order_date": "05-May-2025",
|
473 |
-
"order_time": "21:09:39",
|
474 |
-
"restaurant_name": "Hyderabad Biryaani House",
|
475 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
476 |
-
"items": [
|
477 |
-
{
|
478 |
-
"name": "Mutton Biryani Family Pack",
|
479 |
-
"quantity": 1,
|
480 |
-
"price": 749
|
481 |
-
}
|
482 |
-
],
|
483 |
-
"total_price": 806
|
484 |
-
}
|
485 |
-
],
|
486 |
-
"2025-05-06": [
|
487 |
-
{
|
488 |
-
"email_number": 1,
|
489 |
-
"order_date": "06-May-2025",
|
490 |
-
"order_time": "09:58:07",
|
491 |
-
"restaurant_name": "Swiggy Instamart",
|
492 |
-
"delivery_address": "2C, Orchard Green Apartment 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
493 |
-
"items": [
|
494 |
-
{
|
495 |
-
"name": "Yellaki Banana (Baalehannu)",
|
496 |
-
"quantity": 1,
|
497 |
-
"price": 59
|
498 |
-
},
|
499 |
-
{
|
500 |
-
"name": "Robusta Banana (Pachha Baalehannu)",
|
501 |
-
"quantity": 1,
|
502 |
-
"price": 30
|
503 |
-
},
|
504 |
-
{
|
505 |
-
"name": "Eggoz Farm Fresh High Protein White Eggs Box",
|
506 |
-
"quantity": 1,
|
507 |
-
"price": 325
|
508 |
-
},
|
509 |
-
{
|
510 |
-
"name": "Amul Processed Cheese Slices",
|
511 |
-
"quantity": 1,
|
512 |
-
"price": 145
|
513 |
-
},
|
514 |
-
{
|
515 |
-
"name": "NOTO Orange Sugar Free Popsicle Ice Cream",
|
516 |
-
"quantity": 2,
|
517 |
-
"price": 157.5
|
518 |
-
},
|
519 |
-
{
|
520 |
-
"name": "Rocket Leaves (Arugula)",
|
521 |
-
"quantity": 1,
|
522 |
-
"price": 54
|
523 |
-
},
|
524 |
-
{
|
525 |
-
"name": "Royal Gala Apple (Sebu)",
|
526 |
-
"quantity": 2,
|
527 |
-
"price": 254.6
|
528 |
-
},
|
529 |
-
{
|
530 |
-
"name": "NOTO Kala Jamun Sugar Free Popsicle Ice Cream",
|
531 |
-
"quantity": 2,
|
532 |
-
"price": 132.5
|
533 |
-
},
|
534 |
-
{
|
535 |
-
"name": "Imported Avocado - Tanzania",
|
536 |
-
"quantity": 1,
|
537 |
-
"price": 75
|
538 |
-
},
|
539 |
-
{
|
540 |
-
"name": "Ratnagiri Alphonso Mango (Hapus)",
|
541 |
-
"quantity": 1,
|
542 |
-
"price": 415
|
543 |
-
},
|
544 |
-
{
|
545 |
-
"name": "Indian Blueberries",
|
546 |
-
"quantity": 1,
|
547 |
-
"price": 199
|
548 |
-
},
|
549 |
-
{
|
550 |
-
"name": "Flyer Dil Foods",
|
551 |
-
"quantity": 1,
|
552 |
-
"price": 0
|
553 |
-
},
|
554 |
-
{
|
555 |
-
"name": "Premium Guava (Thai)",
|
556 |
-
"quantity": 1,
|
557 |
-
"price": 71
|
558 |
-
},
|
559 |
-
{
|
560 |
-
"name": "Lettuce Mix Salad",
|
561 |
-
"quantity": 2,
|
562 |
-
"price": 120
|
563 |
-
}
|
564 |
-
],
|
565 |
-
"total_price": 2049
|
566 |
-
},
|
567 |
-
{
|
568 |
-
"email_number": 2,
|
569 |
-
"order_date": "06-May-2025",
|
570 |
-
"order_time": "10:51:28",
|
571 |
-
"restaurant_name": "Starbucks Coffee",
|
572 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
573 |
-
"items": [
|
574 |
-
{
|
575 |
-
"name": "Caffe Americano",
|
576 |
-
"quantity": 1,
|
577 |
-
"price": 355
|
578 |
-
},
|
579 |
-
{
|
580 |
-
"name": "Caffe Americano",
|
581 |
-
"quantity": 1,
|
582 |
-
"price": 315
|
583 |
-
}
|
584 |
-
],
|
585 |
-
"total_price": 694
|
586 |
-
},
|
587 |
-
{
|
588 |
-
"email_number": 3,
|
589 |
-
"order_date": "06-May-2025",
|
590 |
-
"order_time": "11:06:59",
|
591 |
-
"restaurant_name": "Yogisthaan",
|
592 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
593 |
-
"items": [
|
594 |
-
{
|
595 |
-
"name": "Tapioca (sabu Dana) Khichadi",
|
596 |
-
"quantity": 1,
|
597 |
-
"price": 252
|
598 |
-
},
|
599 |
-
{
|
600 |
-
"name": "Sattu Paratha",
|
601 |
-
"quantity": 1,
|
602 |
-
"price": 288
|
603 |
-
}
|
604 |
-
],
|
605 |
-
"total_price": 537
|
606 |
-
},
|
607 |
-
{
|
608 |
-
"email_number": 4,
|
609 |
-
"order_date": "06-May-2025",
|
610 |
-
"order_time": "15:05:22",
|
611 |
-
"restaurant_name": "Sweet Karam Coffee",
|
612 |
-
"delivery_address": "2C, Orchard Green Apartment 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
613 |
-
"items": [
|
614 |
-
{
|
615 |
-
"name": "Sweet Karam Coffee Tapioca Chips (Kappa) - No Palm Oil",
|
616 |
-
"quantity": 1,
|
617 |
-
"price": 65
|
618 |
-
},
|
619 |
-
{
|
620 |
-
"name": "Sweet Karam Coffee Special Madras Mixture (No Palm Oil)",
|
621 |
-
"quantity": 1,
|
622 |
-
"price": 90
|
623 |
-
},
|
624 |
-
{
|
625 |
-
"name": "Sweet Karam Coffee Peanut Chikki Bites (No White Sugar, No Liquid Glucose)",
|
626 |
-
"quantity": 1,
|
627 |
-
"price": 87
|
628 |
-
},
|
629 |
-
{
|
630 |
-
"name": "Mother's Recipe Potato Masala Papad",
|
631 |
-
"quantity": 4,
|
632 |
-
"price": 126.64
|
633 |
-
},
|
634 |
-
{
|
635 |
-
"name": "Sweet Karam Coffee Kerala Nendran Banana Chips",
|
636 |
-
"quantity": 1,
|
637 |
-
"price": 87.12
|
638 |
-
},
|
639 |
-
{
|
640 |
-
"name": "Iyer's Rice Papad Buy 1 Get 1 (inside one pack)",
|
641 |
-
"quantity": 1,
|
642 |
-
"price": 64
|
643 |
-
},
|
644 |
-
{
|
645 |
-
"name": "Ginger kombucha (Low cal, Low sugar)",
|
646 |
-
"quantity": 4,
|
647 |
-
"price": 368.28
|
648 |
-
}
|
649 |
-
],
|
650 |
-
"total_price": 899
|
651 |
-
},
|
652 |
-
{
|
653 |
-
"email_number": 5,
|
654 |
-
"order_date": "06-May-2025",
|
655 |
-
"order_time": "22:36:46",
|
656 |
-
"restaurant_name": "Misu",
|
657 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India.",
|
658 |
-
"items": [
|
659 |
-
{
|
660 |
-
"name": "Spicy Chilli Basil Noodles Non-veg",
|
661 |
-
"quantity": 1,
|
662 |
-
"price": 465
|
663 |
-
},
|
664 |
-
{
|
665 |
-
"name": "Vietnamese Pho Chicken",
|
666 |
-
"quantity": 1,
|
667 |
-
"price": 305
|
668 |
-
},
|
669 |
-
{
|
670 |
-
"name": "Sour & Spicy Dumplings Chicken (6 Pcs)",
|
671 |
-
"quantity": 1,
|
672 |
-
"price": 405
|
673 |
-
}
|
674 |
-
],
|
675 |
-
"total_price": 1117
|
676 |
-
}
|
677 |
-
],
|
678 |
-
"2025-05-07": [
|
679 |
-
{
|
680 |
-
"email_number": 1,
|
681 |
-
"order_date": "07-May-2025",
|
682 |
-
"order_time": "13:26:32",
|
683 |
-
"restaurant_name": "Kale - A Salad Symphony",
|
684 |
-
"delivery_address": "Shobhit\n2nd Floor\nKariyammana Agrahara Road, Marathahalli, Bengaluru, Karnataka 560037, India. (Divyasree Technopolis)",
|
685 |
-
"items": [
|
686 |
-
{
|
687 |
-
"name": "Thai Chicken Bowl (nutrients & Vitamin B12)",
|
688 |
-
"quantity": 1,
|
689 |
-
"price": 425
|
690 |
-
},
|
691 |
-
{
|
692 |
-
"name": "Green Protein Smoothie",
|
693 |
-
"quantity": 1,
|
694 |
-
"price": 250
|
695 |
-
}
|
696 |
-
],
|
697 |
-
"total_price": 693
|
698 |
-
},
|
699 |
-
{
|
700 |
-
"email_number": 2,
|
701 |
-
"order_date": "07-May-2025",
|
702 |
-
"order_time": "13:53:43",
|
703 |
-
"restaurant_name": "Chakum Chukum",
|
704 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
705 |
-
"items": [
|
706 |
-
{
|
707 |
-
"name": "Chicken Tikka Roll",
|
708 |
-
"quantity": 1,
|
709 |
-
"price": 237.15
|
710 |
-
},
|
711 |
-
{
|
712 |
-
"name": "Kasundi Paneer Roll",
|
713 |
-
"quantity": 1,
|
714 |
-
"price": 275.24
|
715 |
-
}
|
716 |
-
],
|
717 |
-
"total_price": 518
|
718 |
-
},
|
719 |
-
{
|
720 |
-
"email_number": 3,
|
721 |
-
"order_date": "07-May-2025",
|
722 |
-
"order_time": "20:50:27",
|
723 |
-
"restaurant_name": "Lavonne Cafe",
|
724 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
725 |
-
"items": [
|
726 |
-
{
|
727 |
-
"name": "Sourdough Bread Plain",
|
728 |
-
"quantity": 1,
|
729 |
-
"price": 219.05
|
730 |
-
}
|
731 |
-
],
|
732 |
-
"total_price": 41
|
733 |
-
},
|
734 |
-
{
|
735 |
-
"email_number": 4,
|
736 |
-
"order_date": "07-May-2025",
|
737 |
-
"order_time": "21:51:42",
|
738 |
-
"restaurant_name": "Basco And Fry",
|
739 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
740 |
-
"items": [
|
741 |
-
{
|
742 |
-
"name": "Truffle Shroom Burger",
|
743 |
-
"quantity": 1,
|
744 |
-
"price": 365
|
745 |
-
},
|
746 |
-
{
|
747 |
-
"name": "Peri Peri Wings",
|
748 |
-
"quantity": 1,
|
749 |
-
"price": 425
|
750 |
-
},
|
751 |
-
{
|
752 |
-
"name": "Chicken Katsu Burger",
|
753 |
-
"quantity": 1,
|
754 |
-
"price": 365
|
755 |
-
}
|
756 |
-
],
|
757 |
-
"total_price": 1100
|
758 |
-
}
|
759 |
-
],
|
760 |
-
"2025-05-08": [
|
761 |
-
{
|
762 |
-
"email_number": 1,
|
763 |
-
"order_date": "08-May-2025",
|
764 |
-
"order_time": "13:04:12",
|
765 |
-
"restaurant_name": "NATRAJ Chole bhature",
|
766 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
767 |
-
"items": [
|
768 |
-
{
|
769 |
-
"name": "Chole Bhature(full Plate)",
|
770 |
-
"quantity": 1,
|
771 |
-
"price": 225
|
772 |
-
},
|
773 |
-
{
|
774 |
-
"name": "Samosa(1pc)",
|
775 |
-
"quantity": 1,
|
776 |
-
"price": 45
|
777 |
-
}
|
778 |
-
],
|
779 |
-
"total_price": 310
|
780 |
-
},
|
781 |
-
{
|
782 |
-
"email_number": 2,
|
783 |
-
"order_date": "08-May-2025",
|
784 |
-
"order_time": "14:27:04",
|
785 |
-
"restaurant_name": "Salad Days",
|
786 |
-
"delivery_address": "2nd Floor, Kariyammana Agrahara Road, Marathahalli, Bengaluru, Karnataka 560037, India. (Divyasree Technopolis)",
|
787 |
-
"items": [
|
788 |
-
{
|
789 |
-
"name": "Asian Chicken, Egg & Soba Noodle Salad",
|
790 |
-
"quantity": 1,
|
791 |
-
"price": 379
|
792 |
-
},
|
793 |
-
{
|
794 |
-
"name": "Green Pressed Juice",
|
795 |
-
"quantity": 1,
|
796 |
-
"price": 199
|
797 |
-
}
|
798 |
-
],
|
799 |
-
"total_price": 655
|
800 |
-
},
|
801 |
-
{
|
802 |
-
"email_number": 3,
|
803 |
-
"order_date": "08-May-2025",
|
804 |
-
"order_time": "22:47:20",
|
805 |
-
"restaurant_name": "Mahesh Lunch Home",
|
806 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
807 |
-
"items": [
|
808 |
-
{
|
809 |
-
"name": "Appam",
|
810 |
-
"quantity": 2,
|
811 |
-
"price": 60
|
812 |
-
},
|
813 |
-
{
|
814 |
-
"name": "Chicken Ghee Roast",
|
815 |
-
"quantity": 1,
|
816 |
-
"price": 460
|
817 |
-
},
|
818 |
-
{
|
819 |
-
"name": "Butter Roti",
|
820 |
-
"quantity": 2,
|
821 |
-
"price": 80
|
822 |
-
},
|
823 |
-
{
|
824 |
-
"name": "Chicken Manglorean",
|
825 |
-
"quantity": 1,
|
826 |
-
"price": 405
|
827 |
-
},
|
828 |
-
{
|
829 |
-
"name": "Neer Dosa (4 Pcs)",
|
830 |
-
"quantity": 1,
|
831 |
-
"price": 80
|
832 |
-
},
|
833 |
-
{
|
834 |
-
"name": "Dal Fry",
|
835 |
-
"quantity": 1,
|
836 |
-
"price": 210
|
837 |
-
}
|
838 |
-
],
|
839 |
-
"total_price": 1267
|
840 |
-
},
|
841 |
-
{
|
842 |
-
"email_number": 4,
|
843 |
-
"order_date": "08-May-2025",
|
844 |
-
"order_time": "23:41:54",
|
845 |
-
"restaurant_name": "Swiggy Instamart",
|
846 |
-
"delivery_address": "2C, Orchard Green Apartment 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
847 |
-
"items": [
|
848 |
-
{
|
849 |
-
"name": "NOTO Strawberry Raspberry Sugar Free Popsicle Ice Cream",
|
850 |
-
"quantity": 4,
|
851 |
-
"price": 325
|
852 |
-
},
|
853 |
-
{
|
854 |
-
"name": "NOTO Orange Sugar Free Popsicle Ice Cream",
|
855 |
-
"quantity": 2,
|
856 |
-
"price": 162.5
|
857 |
-
}
|
858 |
-
],
|
859 |
-
"total_price": 498
|
860 |
-
}
|
861 |
-
],
|
862 |
-
"2025-05-09": [
|
863 |
-
{
|
864 |
-
"email_number": 1,
|
865 |
-
"order_date": "09-May-2025",
|
866 |
-
"order_time": "15:22:57",
|
867 |
-
"restaurant_name": "Kale - A Salad Symphony",
|
868 |
-
"delivery_address": "Shobhit\n2nd Floor\nKariyammana Agrahara Road, Marathahalli, Bengaluru, Karnataka 560037, India. (Divyasree Technopolis)",
|
869 |
-
"items": [
|
870 |
-
{
|
871 |
-
"name": "Thai Chicken Bowl (nutrients & Vitamin B12)",
|
872 |
-
"quantity": 1,
|
873 |
-
"price": 425
|
874 |
-
},
|
875 |
-
{
|
876 |
-
"name": "Green Protein Smoothie",
|
877 |
-
"quantity": 1,
|
878 |
-
"price": 250
|
879 |
-
}
|
880 |
-
],
|
881 |
-
"total_price": 693
|
882 |
-
},
|
883 |
-
{
|
884 |
-
"email_number": 2,
|
885 |
-
"order_date": "09-May-2025",
|
886 |
-
"order_time": "16:25:50",
|
887 |
-
"restaurant_name": "Swiggy Instamart",
|
888 |
-
"delivery_address": "2C, Orchard Green Apartment 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
889 |
-
"items": [
|
890 |
-
{
|
891 |
-
"name": "Kodai Parmesan Cheese",
|
892 |
-
"quantity": 1,
|
893 |
-
"price": 475
|
894 |
-
}
|
895 |
-
],
|
896 |
-
"total_price": 487
|
897 |
-
}
|
898 |
-
],
|
899 |
-
"2025-05-10": [
|
900 |
-
{
|
901 |
-
"email_number": 1,
|
902 |
-
"order_date": "10-May-2025",
|
903 |
-
"order_time": "13:34:39",
|
904 |
-
"restaurant_name": "Swiggy Instamart",
|
905 |
-
"delivery_address": "2C, Orchard Green Apartment 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
906 |
-
"items": [
|
907 |
-
{
|
908 |
-
"name": "Gooseberry (Nellikaayi)",
|
909 |
-
"quantity": 1,
|
910 |
-
"price": 39
|
911 |
-
},
|
912 |
-
{
|
913 |
-
"name": "Fresh Turmeric",
|
914 |
-
"quantity": 2,
|
915 |
-
"price": 42
|
916 |
-
},
|
917 |
-
{
|
918 |
-
"name": "Licious Chicken Curry Cut (Large Pieces) - Skinless",
|
919 |
-
"quantity": 1,
|
920 |
-
"price": 165
|
921 |
-
},
|
922 |
-
{
|
923 |
-
"name": "White Radish (Moolangi)",
|
924 |
-
"quantity": 2,
|
925 |
-
"price": 30.6
|
926 |
-
},
|
927 |
-
{
|
928 |
-
"name": "English Cucumber (Sowthekaayi)",
|
929 |
-
"quantity": 2,
|
930 |
-
"price": 65.8
|
931 |
-
},
|
932 |
-
{
|
933 |
-
"name": "Robusta Banana (Pachha Baalehannu)",
|
934 |
-
"quantity": 1,
|
935 |
-
"price": 44
|
936 |
-
},
|
937 |
-
{
|
938 |
-
"name": "Curry Leaves (Karibevu)",
|
939 |
-
"quantity": 1,
|
940 |
-
"price": 11
|
941 |
-
},
|
942 |
-
{
|
943 |
-
"name": "Green Chilli (Hasiru Menasinakaayi)",
|
944 |
-
"quantity": 1,
|
945 |
-
"price": 12
|
946 |
-
},
|
947 |
-
{
|
948 |
-
"name": "Coriander - Without Roots (Kotthambari)",
|
949 |
-
"quantity": 1,
|
950 |
-
"price": 18
|
951 |
-
},
|
952 |
-
{
|
953 |
-
"name": "Ginger (Shunti)",
|
954 |
-
"quantity": 1,
|
955 |
-
"price": 16
|
956 |
-
},
|
957 |
-
{
|
958 |
-
"name": "Premium Guava (Thai)",
|
959 |
-
"quantity": 2,
|
960 |
-
"price": 131.4
|
961 |
-
},
|
962 |
-
{
|
963 |
-
"name": "Royal Gala Apple (Sebu)",
|
964 |
-
"quantity": 2,
|
965 |
-
"price": 275
|
966 |
-
}
|
967 |
-
],
|
968 |
-
"total_price": 862
|
969 |
-
},
|
970 |
-
{
|
971 |
-
"email_number": 2,
|
972 |
-
"order_date": "10-May-2025",
|
973 |
-
"order_time": "17:23:45",
|
974 |
-
"restaurant_name": "Irani Std. Tea",
|
975 |
-
"delivery_address": "Swiggy,Tower D, 9th Floor, IBC Knowledge Park, Bannerghatta Road, Bangalore - 560029",
|
976 |
-
"items": [],
|
977 |
-
"total_price": 291
|
978 |
-
},
|
979 |
-
{
|
980 |
-
"email_number": 3,
|
981 |
-
"order_date": "10-May-2025",
|
982 |
-
"order_time": "18:05:00",
|
983 |
-
"restaurant_name": "Irani Std. Tea",
|
984 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India",
|
985 |
-
"items": [
|
986 |
-
{
|
987 |
-
"name": "Irani Ginger Chai",
|
988 |
-
"quantity": 1,
|
989 |
-
"price": 219
|
990 |
-
},
|
991 |
-
{
|
992 |
-
"name": "Osmania Biscuits 100 Grams",
|
993 |
-
"quantity": 1,
|
994 |
-
"price": 100
|
995 |
-
},
|
996 |
-
{
|
997 |
-
"name": "Bun Maska",
|
998 |
-
"quantity": 2,
|
999 |
-
"price": 158
|
1000 |
-
}
|
1001 |
-
],
|
1002 |
-
"total_price": 399
|
1003 |
-
}
|
1004 |
-
],
|
1005 |
-
"2025-05-11": [
|
1006 |
-
{
|
1007 |
-
"email_number": 1,
|
1008 |
-
"order_date": "11-May-2025",
|
1009 |
-
"order_time": "12:36:17",
|
1010 |
-
"restaurant_name": "Anand Sweets & Savouries",
|
1011 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India.",
|
1012 |
-
"items": [
|
1013 |
-
{
|
1014 |
-
"name": "Shahi Tohfa Dry Fruits 750 Gms",
|
1015 |
-
"quantity": 1,
|
1016 |
-
"price": 1022.32
|
1017 |
-
}
|
1018 |
-
],
|
1019 |
-
"total_price": 1167
|
1020 |
-
},
|
1021 |
-
{
|
1022 |
-
"email_number": 2,
|
1023 |
-
"order_date": "11-May-2025",
|
1024 |
-
"order_time": "21:17:18",
|
1025 |
-
"restaurant_name": "Hotel Empire",
|
1026 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
1027 |
-
"items": [
|
1028 |
-
{
|
1029 |
-
"name": "Paneer Butter Masala",
|
1030 |
-
"quantity": 1,
|
1031 |
-
"price": 252
|
1032 |
-
},
|
1033 |
-
{
|
1034 |
-
"name": "Kerala Parotta",
|
1035 |
-
"quantity": 1,
|
1036 |
-
"price": 39
|
1037 |
-
},
|
1038 |
-
{
|
1039 |
-
"name": "Coin Parotta",
|
1040 |
-
"quantity": 2,
|
1041 |
-
"price": 64
|
1042 |
-
},
|
1043 |
-
{
|
1044 |
-
"name": "Dal Fry",
|
1045 |
-
"quantity": 1,
|
1046 |
-
"price": 137
|
1047 |
-
},
|
1048 |
-
{
|
1049 |
-
"name": "Ghee Rice",
|
1050 |
-
"quantity": 1,
|
1051 |
-
"price": 110
|
1052 |
-
},
|
1053 |
-
{
|
1054 |
-
"name": "Malabar Parotta",
|
1055 |
-
"quantity": 2,
|
1056 |
-
"price": 70
|
1057 |
-
}
|
1058 |
-
],
|
1059 |
-
"total_price": 749
|
1060 |
-
}
|
1061 |
-
],
|
1062 |
-
"2025-05-12": [
|
1063 |
-
{
|
1064 |
-
"email_number": 1,
|
1065 |
-
"order_date": "12-May-2025",
|
1066 |
-
"order_time": "22:12:42",
|
1067 |
-
"restaurant_name": "Swiggy Instamart",
|
1068 |
-
"delivery_address": "2C, Orchard Green Apartment 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
1069 |
-
"items": [
|
1070 |
-
{
|
1071 |
-
"name": "NOTO Strawberry Raspberry Sugar Free Popsicle Ice Cream",
|
1072 |
-
"quantity": 4,
|
1073 |
-
"price": 275
|
1074 |
-
},
|
1075 |
-
{
|
1076 |
-
"name": "NOTO Orange Sugar Free Popsicle Ice Cream",
|
1077 |
-
"quantity": 2,
|
1078 |
-
"price": 137.5
|
1079 |
-
}
|
1080 |
-
],
|
1081 |
-
"total_price": 423
|
1082 |
-
}
|
1083 |
-
],
|
1084 |
-
"2025-05-13": [],
|
1085 |
-
"2025-05-14": [
|
1086 |
-
{
|
1087 |
-
"email_number": 1,
|
1088 |
-
"order_date": "14-May-2025",
|
1089 |
-
"order_time": "14:14:14",
|
1090 |
-
"restaurant_name": "Swiggy Instamart",
|
1091 |
-
"delivery_address": "2C, Orchard Green Apartment 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
1092 |
-
"items": [
|
1093 |
-
{
|
1094 |
-
"name": "Robusta Banana (Pachha Baalehannu)",
|
1095 |
-
"quantity": 1,
|
1096 |
-
"price": 42
|
1097 |
-
},
|
1098 |
-
{
|
1099 |
-
"name": "Coriander - Without Roots (Kotthambari)",
|
1100 |
-
"quantity": 1,
|
1101 |
-
"price": 18
|
1102 |
-
},
|
1103 |
-
{
|
1104 |
-
"name": "Tata Sampann Turmeric Powder Masala",
|
1105 |
-
"quantity": 2,
|
1106 |
-
"price": 85.04
|
1107 |
-
},
|
1108 |
-
{
|
1109 |
-
"name": "Organic Certified Onion (Eerulli)",
|
1110 |
-
"quantity": 1,
|
1111 |
-
"price": 66
|
1112 |
-
},
|
1113 |
-
{
|
1114 |
-
"name": "NOTO Strawberry Raspberry Sugar Free Popsicle Ice Cream",
|
1115 |
-
"quantity": 4,
|
1116 |
-
"price": 275
|
1117 |
-
},
|
1118 |
-
{
|
1119 |
-
"name": "Premium Guava (Thai)",
|
1120 |
-
"quantity": 2,
|
1121 |
-
"price": 154
|
1122 |
-
},
|
1123 |
-
{
|
1124 |
-
"name": "Garlic (Bellulli)",
|
1125 |
-
"quantity": 1,
|
1126 |
-
"price": 47
|
1127 |
-
},
|
1128 |
-
{
|
1129 |
-
"name": "Royal Gala Apple (Sebu)",
|
1130 |
-
"quantity": 1,
|
1131 |
-
"price": 142
|
1132 |
-
}
|
1133 |
-
],
|
1134 |
-
"total_price": 841
|
1135 |
-
}
|
1136 |
-
],
|
1137 |
-
"2025-05-15": [],
|
1138 |
-
"2025-05-16": [
|
1139 |
-
{
|
1140 |
-
"email_number": 1,
|
1141 |
-
"order_date": "16-May-2025",
|
1142 |
-
"order_time": "15:43:43",
|
1143 |
-
"restaurant_name": "Glen's Bakehouse",
|
1144 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
1145 |
-
"items": [
|
1146 |
-
{
|
1147 |
-
"name": "Mushroom Puff",
|
1148 |
-
"quantity": 1,
|
1149 |
-
"price": 62.86
|
1150 |
-
},
|
1151 |
-
{
|
1152 |
-
"name": "Chicken Puff",
|
1153 |
-
"quantity": 1,
|
1154 |
-
"price": 84.23
|
1155 |
-
}
|
1156 |
-
],
|
1157 |
-
"total_price": 235
|
1158 |
-
},
|
1159 |
-
{
|
1160 |
-
"email_number": 2,
|
1161 |
-
"order_date": "16-May-2025",
|
1162 |
-
"order_time": "15:48:57",
|
1163 |
-
"restaurant_name": "The Himalayan Momo Company",
|
1164 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
1165 |
-
"items": [
|
1166 |
-
{
|
1167 |
-
"name": "Woked Maggi",
|
1168 |
-
"quantity": 1,
|
1169 |
-
"price": 209
|
1170 |
-
},
|
1171 |
-
{
|
1172 |
-
"name": "Himalayan Steamed Chicken Momos",
|
1173 |
-
"quantity": 2,
|
1174 |
-
"price": 318
|
1175 |
-
}
|
1176 |
-
],
|
1177 |
-
"total_price": 547
|
1178 |
-
}
|
1179 |
-
],
|
1180 |
-
"2025-05-18": [
|
1181 |
-
{
|
1182 |
-
"email_number": 1,
|
1183 |
-
"order_date": "18-May-2025",
|
1184 |
-
"order_time": "12:57:21",
|
1185 |
-
"restaurant_name": "Starbucks Coffee",
|
1186 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
1187 |
-
"items": [
|
1188 |
-
{
|
1189 |
-
"name": "Caffe Americano",
|
1190 |
-
"quantity": 1,
|
1191 |
-
"price": 315
|
1192 |
-
}
|
1193 |
-
],
|
1194 |
-
"total_price": 363
|
1195 |
-
},
|
1196 |
-
{
|
1197 |
-
"email_number": 2,
|
1198 |
-
"order_date": "18-May-2025",
|
1199 |
-
"order_time": "13:02:51",
|
1200 |
-
"restaurant_name": "Maiz Mexican Kitchen",
|
1201 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
1202 |
-
"items": [
|
1203 |
-
{
|
1204 |
-
"name": "Chips With Guacamole",
|
1205 |
-
"quantity": 1,
|
1206 |
-
"price": 249
|
1207 |
-
},
|
1208 |
-
{
|
1209 |
-
"name": "Fresh Tomato Salsa (50ml)",
|
1210 |
-
"quantity": 1,
|
1211 |
-
"price": 69
|
1212 |
-
},
|
1213 |
-
{
|
1214 |
-
"name": "Chipotle Chicken Burrito",
|
1215 |
-
"quantity": 1,
|
1216 |
-
"price": 299
|
1217 |
-
}
|
1218 |
-
],
|
1219 |
-
"total_price": 491
|
1220 |
-
}
|
1221 |
-
],
|
1222 |
-
"2025-05-20": [
|
1223 |
-
{
|
1224 |
-
"email_number": 1,
|
1225 |
-
"order_date": "20-May-2025",
|
1226 |
-
"order_time": "11:24:05",
|
1227 |
-
"restaurant_name": "IDC Kitchen",
|
1228 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
1229 |
-
"items": [
|
1230 |
-
{
|
1231 |
-
"name": "Vada (1 Pc)",
|
1232 |
-
"quantity": 1,
|
1233 |
-
"price": 58
|
1234 |
-
},
|
1235 |
-
{
|
1236 |
-
"name": "Masala Dosa",
|
1237 |
-
"quantity": 1,
|
1238 |
-
"price": 140
|
1239 |
-
}
|
1240 |
-
],
|
1241 |
-
"total_price": 167
|
1242 |
-
},
|
1243 |
-
{
|
1244 |
-
"email_number": 2,
|
1245 |
-
"order_date": "20-May-2025",
|
1246 |
-
"order_time": "16:15:50",
|
1247 |
-
"restaurant_name": "Irani Std. Tea",
|
1248 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
1249 |
-
"items": [
|
1250 |
-
{
|
1251 |
-
"name": "Bun Omelette",
|
1252 |
-
"quantity": 1,
|
1253 |
-
"price": 139
|
1254 |
-
},
|
1255 |
-
{
|
1256 |
-
"name": "Osmania Biscuits 100 Grams",
|
1257 |
-
"quantity": 1,
|
1258 |
-
"price": 100
|
1259 |
-
},
|
1260 |
-
{
|
1261 |
-
"name": "Plain Maggie",
|
1262 |
-
"quantity": 1,
|
1263 |
-
"price": 119
|
1264 |
-
},
|
1265 |
-
{
|
1266 |
-
"name": "Bun Maska",
|
1267 |
-
"quantity": 1,
|
1268 |
-
"price": 79
|
1269 |
-
},
|
1270 |
-
{
|
1271 |
-
"name": "Irani Ginger Chai",
|
1272 |
-
"quantity": 1,
|
1273 |
-
"price": 219
|
1274 |
-
},
|
1275 |
-
{
|
1276 |
-
"name": "Aloo Samosa",
|
1277 |
-
"quantity": 1,
|
1278 |
-
"price": 49
|
1279 |
-
}
|
1280 |
-
],
|
1281 |
-
"total_price": 627
|
1282 |
-
},
|
1283 |
-
{
|
1284 |
-
"email_number": 3,
|
1285 |
-
"order_date": "20-May-2025",
|
1286 |
-
"order_time": "20:13:33",
|
1287 |
-
"restaurant_name": "Gayatri Sandwich(Mithibai College)",
|
1288 |
-
"delivery_address": "Shobhit, ground floor, Vile Parle East, Vile Parle, Mumbai, Maharashtra, India. (T1)",
|
1289 |
-
"items": [
|
1290 |
-
{
|
1291 |
-
"name": "Sada Sandwich",
|
1292 |
-
"quantity": 1,
|
1293 |
-
"price": 50
|
1294 |
-
},
|
1295 |
-
{
|
1296 |
-
"name": "Vada Pav",
|
1297 |
-
"quantity": 1,
|
1298 |
-
"price": 25
|
1299 |
-
}
|
1300 |
-
],
|
1301 |
-
"total_price": 178
|
1302 |
-
}
|
1303 |
-
],
|
1304 |
-
"2025-05-21": [
|
1305 |
-
{
|
1306 |
-
"email_number": 1,
|
1307 |
-
"order_date": "21-May-2025",
|
1308 |
-
"order_time": "12:56:56",
|
1309 |
-
"restaurant_name": "Starbucks Coffee",
|
1310 |
-
"delivery_address": "Shobhit\n2C, Orchard Green Apartment\n2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
1311 |
-
"items": [
|
1312 |
-
{
|
1313 |
-
"name": "Cold Brew Black (Cold Brew)",
|
1314 |
-
"quantity": 1,
|
1315 |
-
"price": 390
|
1316 |
-
}
|
1317 |
-
],
|
1318 |
-
"total_price": 401
|
1319 |
-
},
|
1320 |
-
{
|
1321 |
-
"email_number": 2,
|
1322 |
-
"order_date": "21-May-2025",
|
1323 |
-
"order_time": "12:58:49",
|
1324 |
-
"restaurant_name": "Starbucks Coffee",
|
1325 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
1326 |
-
"items": [
|
1327 |
-
{
|
1328 |
-
"name": "Caffe Americano",
|
1329 |
-
"quantity": 1,
|
1330 |
-
"price": 315
|
1331 |
-
}
|
1332 |
-
],
|
1333 |
-
"total_price": 363
|
1334 |
-
},
|
1335 |
-
{
|
1336 |
-
"email_number": 3,
|
1337 |
-
"order_date": "21-May-2025",
|
1338 |
-
"order_time": "22:07:56",
|
1339 |
-
"restaurant_name": "Magnolia Bakery",
|
1340 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
1341 |
-
"items": [
|
1342 |
-
{
|
1343 |
-
"name": "Chocolate Cake with Chocolate Buttercream Cake Slice",
|
1344 |
-
"quantity": 1,
|
1345 |
-
"price": 260
|
1346 |
-
},
|
1347 |
-
{
|
1348 |
-
"name": "CLASSIC TRES LECHES",
|
1349 |
-
"quantity": 1,
|
1350 |
-
"price": 410
|
1351 |
-
}
|
1352 |
-
],
|
1353 |
-
"total_price": 850
|
1354 |
-
}
|
1355 |
-
],
|
1356 |
-
"2025-05-22": [
|
1357 |
-
{
|
1358 |
-
"email_number": 1,
|
1359 |
-
"order_date": "22-May-2025",
|
1360 |
-
"order_time": "12:00:34",
|
1361 |
-
"restaurant_name": "Yogisthaan",
|
1362 |
-
"delivery_address": "2C, Orchard Green Apartment, 2nd Main Rd, Domlur, Bangalore, Karnataka 560071, India. (Orchard Green)",
|
1363 |
-
"items": [
|
1364 |
-
{
|
1365 |
-
"name": "Poha (quick Of Breakfast)",
|
1366 |
-
"quantity": 1,
|
1367 |
-
"price": 288
|
1368 |
-
},
|
1369 |
-
{
|
1370 |
-
"name": "Tapioca (sabu Dana) Khichadi",
|
1371 |
-
"quantity": 1,
|
1372 |
-
"price": 252
|
1373 |
-
}
|
1374 |
-
],
|
1375 |
-
"total_price": 547
|
1376 |
-
}
|
1377 |
-
],
|
1378 |
-
"2025-05-23": []
|
1379 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp/server/swiggy_scraper.py
DELETED
@@ -1,179 +0,0 @@
|
|
1 |
-
"""
|
2 |
-
Reusable helper to fetch Swiggy order e-mails and return a list[dict].
|
3 |
-
|
4 |
-
Usage:
|
5 |
-
from swiggy_scraper import fetch_swiggy_orders
|
6 |
-
orders = fetch_swiggy_orders("17-May-2025", "20-May-2025")
|
7 |
-
"""
|
8 |
-
|
9 |
-
import os, imaplib, json
|
10 |
-
from email import message_from_bytes
|
11 |
-
from bs4 import BeautifulSoup
|
12 |
-
from openai import OpenAI
|
13 |
-
from dotenv import load_dotenv
|
14 |
-
from datetime import datetime, timedelta
|
15 |
-
from email.utils import parsedate_to_datetime
|
16 |
-
from zoneinfo import ZoneInfo
|
17 |
-
|
18 |
-
from db_schema import init_db, get_orders_by_date_from_db, save_orders_to_db
|
19 |
-
|
20 |
-
|
21 |
-
load_dotenv()
|
22 |
-
|
23 |
-
APP_PASSWORD = os.getenv("APP_PASSWORD")
|
24 |
-
EMAIL_ID = os.getenv("EMAIL_ID")
|
25 |
-
OPENAI_KEY = os.getenv("OPENAI_API_KEY")
|
26 |
-
|
27 |
-
client = OpenAI(api_key=OPENAI_KEY)
|
28 |
-
|
29 |
-
def _imap_connect():
|
30 |
-
m = imaplib.IMAP4_SSL("imap.gmail.com")
|
31 |
-
m.login(EMAIL_ID, APP_PASSWORD)
|
32 |
-
m.select('"[Gmail]/All Mail"')
|
33 |
-
return m
|
34 |
-
|
35 |
-
def _email_to_clean_text(msg):
|
36 |
-
html = next(
|
37 |
-
(part.get_payload(decode=True).decode(errors="ignore")
|
38 |
-
for part in msg.walk()
|
39 |
-
if part.get_content_type() == "text/html"),
|
40 |
-
None,
|
41 |
-
)
|
42 |
-
if not html:
|
43 |
-
return ""
|
44 |
-
soup = BeautifulSoup(html, "html.parser")
|
45 |
-
for t in soup(["script", "style", "head", "meta", "link"]):
|
46 |
-
t.decompose()
|
47 |
-
return "\n".join(
|
48 |
-
line.strip() for line in soup.get_text("\n").splitlines() if line.strip()
|
49 |
-
)
|
50 |
-
|
51 |
-
def _get_all_dates(start_date: str, end_date: str):
|
52 |
-
start = datetime.strptime(start_date, "%d-%b-%Y")
|
53 |
-
end = datetime.strptime(end_date, "%d-%b-%Y")
|
54 |
-
delta = (end - start).days + 1
|
55 |
-
return [(start + timedelta(days=i)).strftime("%Y-%m-%d") for i in range(delta)]
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
def _extract_with_llm(email_number, subject, body, email_date, email_time):
|
61 |
-
current_email = {
|
62 |
-
"subject": subject,
|
63 |
-
"body": body
|
64 |
-
}
|
65 |
-
|
66 |
-
prompt = f"""
|
67 |
-
You are given a Swiggy order confirmation email with a subject and body.
|
68 |
-
|
69 |
-
Extract and return only the following:
|
70 |
-
- "restaurant_name": name of the restaurant
|
71 |
-
- "delivery_address": the delivery address
|
72 |
-
- "items": a list of ordered items, each with "name", "quantity", and "price" (number)
|
73 |
-
- "total_price": the total bill paid including taxes, charges, etc.
|
74 |
-
|
75 |
-
Example output format:
|
76 |
-
{{
|
77 |
-
"restaurant_name": "Dominos Pizza",
|
78 |
-
"delivery_address": "123 Main St, City",
|
79 |
-
"total_price": 567,
|
80 |
-
"items": [
|
81 |
-
{{ "name": "Veg Pizza", "quantity": 2, "price": 199 }},
|
82 |
-
{{ "name": "Coke", "quantity": 1, "price": 45 }}
|
83 |
-
]
|
84 |
-
}}
|
85 |
-
|
86 |
-
Return only valid JSON. No extra text or comments.
|
87 |
-
|
88 |
-
{json.dumps(current_email, indent=2)}
|
89 |
-
"""
|
90 |
-
|
91 |
-
|
92 |
-
try:
|
93 |
-
rsp = client.chat.completions.create(
|
94 |
-
model="gpt-4o-mini",
|
95 |
-
temperature=0,
|
96 |
-
messages=[
|
97 |
-
{"role": "system", "content": "You are a precise JSON extractor."},
|
98 |
-
{"role": "user", "content": prompt},
|
99 |
-
],
|
100 |
-
)
|
101 |
-
|
102 |
-
# Attempt to parse the returned content
|
103 |
-
parsed_data = json.loads(rsp.choices[0].message.content)
|
104 |
-
|
105 |
-
# Wrap into final structure
|
106 |
-
final_output = {
|
107 |
-
"email_number": email_number,
|
108 |
-
"order_date": email_date,
|
109 |
-
"order_time": email_time,
|
110 |
-
"restaurant_name": parsed_data.get("restaurant_name", ""),
|
111 |
-
"delivery_address": parsed_data.get("delivery_address", ""),
|
112 |
-
"items": parsed_data.get("items", []),
|
113 |
-
"total_price": parsed_data.get("total_price", 0)
|
114 |
-
}
|
115 |
-
|
116 |
-
|
117 |
-
return final_output
|
118 |
-
|
119 |
-
except json.JSONDecodeError as json_err:
|
120 |
-
return {
|
121 |
-
"email_number": email_number,
|
122 |
-
"error": f"JSON decoding failed: {str(json_err)}",
|
123 |
-
"raw_response": rsp.choices[0].message.content if 'rsp' in locals() else None
|
124 |
-
}
|
125 |
-
|
126 |
-
except Exception as e:
|
127 |
-
return {
|
128 |
-
"email_number": email_number,
|
129 |
-
"error": f"Unexpected error: {str(e)}"
|
130 |
-
}
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
def fetch_swiggy_orders(start_date: str, end_date: str) -> list[dict]:
|
135 |
-
mail = _imap_connect()
|
136 |
-
all_dates = _get_all_dates(start_date, end_date)
|
137 |
-
orders = []
|
138 |
-
|
139 |
-
for date_str in all_dates:
|
140 |
-
# 1) Try loading from DB
|
141 |
-
day_orders = get_orders_by_date_from_db(date_str)
|
142 |
-
if day_orders:
|
143 |
-
print(f"{date_str} loaded from DB")
|
144 |
-
orders.extend(day_orders)
|
145 |
-
continue
|
146 |
-
|
147 |
-
# 2) Otherwise scrape emails for that date
|
148 |
-
print(f"Fetching Swiggy emails for {date_str}")
|
149 |
-
dt_obj = datetime.strptime(date_str, "%Y-%m-%d")
|
150 |
-
next_day = (dt_obj + timedelta(days=1)).strftime("%d-%b-%Y")
|
151 |
-
this_day = dt_obj.strftime("%d-%b-%Y")
|
152 |
-
|
153 |
-
crit = f'(FROM "[email protected]") SINCE "{this_day}" BEFORE "{next_day}"'
|
154 |
-
_, data = mail.search(None, crit)
|
155 |
-
ids = data[0].split()
|
156 |
-
|
157 |
-
scraped_orders = []
|
158 |
-
for idx, eid in enumerate(ids, 1):
|
159 |
-
_, msg_data = mail.fetch(eid, "(RFC822)")
|
160 |
-
msg = message_from_bytes(msg_data[0][1])
|
161 |
-
subject = msg.get("Subject", "")
|
162 |
-
body_text = _email_to_clean_text(msg)
|
163 |
-
|
164 |
-
try:
|
165 |
-
dt_obj = parsedate_to_datetime(msg["Date"]).astimezone(ZoneInfo("Asia/Kolkata"))
|
166 |
-
email_date = dt_obj.strftime("%d-%b-%Y")
|
167 |
-
email_time = dt_obj.strftime("%H:%M:%S")
|
168 |
-
|
169 |
-
order = _extract_with_llm(idx, subject, body_text, email_date, email_time)
|
170 |
-
scraped_orders.append(order)
|
171 |
-
except Exception as exc:
|
172 |
-
scraped_orders.append({"email_number": idx, "error": str(exc)})
|
173 |
-
|
174 |
-
# 3) Save newly scraped data to DB
|
175 |
-
save_orders_to_db(date_str, scraped_orders)
|
176 |
-
orders.extend(scraped_orders)
|
177 |
-
|
178 |
-
mail.logout()
|
179 |
-
return orders
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
CHANGED
@@ -1 +1,9 @@
|
|
1 |
-
huggingface_hub==0.25.2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
huggingface_hub==0.25.2
|
2 |
+
uvicorn
|
3 |
+
fastapi
|
4 |
+
openai
|
5 |
+
requests
|
6 |
+
python-dateutil
|
7 |
+
beautifulsoup4
|
8 |
+
python-dotenv
|
9 |
+
pydantic[email]
|
server/email_db.json
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"[email protected]": {
|
3 |
+
"emails": [
|
4 |
+
{
|
5 |
+
"date": "01-Jan-2025",
|
6 |
+
"time": "11:00:26",
|
7 |
+
"subject": "2025: A Year to Unite, Innovate, and Lead with Integrity: Let's make\r\n IIT Jodhpur a beacon of excellence and sustainability!",
|
8 |
+
"content": "Dear Members of the IIT Jodhpur Fraternity, Students, Staff Members, and Faculty Colleagues, As we stand on the brink of a new year, I take this opportunity to reflect on\u00a0the remarkable journey of IIT Jodhpur so far and the collective efforts that\u00a0have brought us to where we are today over the last\u00a016\u00a0years or so. Each one of you - our faculty,\u00a0students, staff, alumni, and partners, has played a vital role in shaping\u00a0this institute and its evolution as a hub of learning, innovation, and excellence. It is\u00a0your commitment, resilience, and unwavering dedication that inspires all of us to aim higher and envision a future of much greater achievements,\u00a0breaking the\u00a0moulds of conventional thinking and defining excellence at a new\u00a0level. The year 2025 holds immense promise. It is a year when our shared vision of growth, innovation, and sustainability will take centre stage. Our institution stands at the confluence of tradition and modernity, of\u00a0ambition and responsibility. Let this be the year where we align our\u00a0efforts to not only push the boundaries of research and education but\u00a0also embrace a balanced approach toward sustainability in every aspect. Together, we can make IIT Jodhpur a beacon of innovation,\u00a0collaboration, and societal impact.\u00a0Our strength lies in our unity. As a diverse and dynamic community, we\u00a0can bring together varied perspectives, talents, and\u00a0disciplines to create something extraordinary. It is through this synergy that we will achieve the breakthroughs needed to address global\u00a0challenges and contribute meaningfully to the world. Let us continue to\u00a0foster an environment of mutual respect, inclusivity, and shared\u00a0purpose, ensuring that every member of our community feels\u00a0empowered to contribute their best. The journey ahead requires us to focus on strengthening collaborations\u00a0that amplify our impact. By working closely with industry, academia, and\u00a0governmental organizations, we can create pathways to meaningful\u00a0innovation and transformative research. O",
|
9 |
+
"message_id": "<CADCv5Wjqd09OCsR0fT2YXv5zzyJT=+xsuvsu4rhLTKbDBznYNw@mail.gmail.com>"
|
10 |
+
},
|
11 |
+
{
|
12 |
+
"date": "14-Jan-2025",
|
13 |
+
"time": "09:52:47",
|
14 |
+
"subject": "Makar Sankranti, Lohri, Bihu, Pongal: Festivals of Progress and New Aspirations",
|
15 |
+
"content": "Dear Members of the IIT Jodhpur Fraternity, \u0906\u092a\u0915\u094b \u0932\u094b\u0939\u093f\u095c\u0940, \u092c\u093f\u0939\u0942, \u092a\u094b\u0902\u0917\u0932 \u0914\u0930 \u092e\u0915\u0930 \u0938\u0902\u0915\u094d\u0930\u093e\u0902\u0924\u093f \u0915\u0940 \u0939\u093e\u0930\u094d\u0926\u093f\u0915 \u0936\u0941\u092d\u0915\u093e\u092e\u0928\u093e\u090f\u0901! \u092d\u093e\u0938\u094d\u0915\u0930\u0938\u094d\u092f \u092f\u0925\u093e \u0924\u0947\u091c\u094b \u092e\u0915\u0930\u0938\u094d\u0925\u0938\u094d\u092f \u0935\u0930\u094d\u0927\u0924\u0947\u0964 \u0924\u0925\u0948\u0935 \u092d\u0935\u0924\u093e\u0902 \u0924\u0947\u091c\u094b \u0935\u0930\u094d\u0927\u0924\u093e\u092e\u093f\u0924\u093f \u0915\u093e\u092e\u092f\u0947\u0964\u0964 \u092d\u0917\u0935\u093e\u0928 \u0938\u0942\u0930\u094d\u092f \u0915\u0947 \u092e\u0915\u0930 \u0930\u093e\u0936\u093f \u092e\u0947\u0902 \u092a\u094d\u0930\u0935\u0947\u0936 \u0924\u0925\u093e \u0909\u0924\u094d\u0924\u0930\u093e\u092f\u0923 \u0939\u094b\u0928\u0947 \u092a\u0930 \u0938\u0902\u092a\u0942\u0930\u094d\u0923 \u092d\u093e\u0930\u0924\u0935\u0930\u094d\u0937 \u092e\u0947\u0902 \u090a\u0930\u094d\u091c\u093e \u0935 \u0909\u0937\u094d\u092e\u093e \u092e\u0947\u0902 \u0935\u0943\u0926\u094d\u0927\u093f \u0915\u0947 \u092a\u094d\u0930\u0924\u0940\u0915-\u092a\u0930\u094d\u0935 \u00a0\"\u092e\u0915\u0930 \u0938\u0902\u0915\u094d\u0930\u093e\u0902\u0924\u093f\" \u092a\u0930 \u0906\u092a\u0915\u093e \u090f\u0935\u0902 \u0906\u092a\u0915\u0947 \u092a\u0930\u093f\u0935\u093e\u0930 \u092e\u0947\u0902 \u0938\u092d\u0940 \u0915\u093e \u091c\u0940\u0935\u0928 \u0905\u0924\u094d\u092f\u0902\u0924 \u092a\u094d\u0930\u0915\u093e\u0936\u092e\u093e\u0928 \u0939\u094b! \u0906\u092a \u0938\u092d\u0940 \u0938\u094d\u0935\u0938\u094d\u0925 \u0930\u0939\u0947\u0902, \u092a\u094d\u0930\u0938\u0928\u094d\u0928 \u0930\u0939\u0947\u0902 \u0914\u0930 \u0938\u0942\u0930\u094d\u092f \u0915\u0940 \u092d\u093e\u0901\u0924\u093f \u0905\u092a\u0928\u0947 \u092a\u094d\u0930\u0915\u093e\u0936 \u0938\u0947 \u0935\u093f\u0936\u094d\u0935 \u0915\u094b \u0906\u0932\u094b\u0915\u093f\u0924 \u0915\u0930\u0947\u0902! \u0906\u0907\u090f, \u0905\u092a\u0928\u0940 \u0906\u0932\u094b\u0915\u0927\u0930\u094d\u092e\u0940 \u0938\u0902\u0938\u094d\u0915\u0943\u0924\u093f \u0915\u0940 \u0935\u093f\u0930\u093e\u0938\u0924 \u0915\u0947 \u0935\u093e\u0939\u0915 \u092c\u0928\u0947\u0902\u0964 \u0909\u0938\u0915\u0947 \u092e\u0939\u0924\u094d\u0924\u094d\u0935 \u090f\u0935\u0902 \u0935\u0948\u091c\u094d\u091e\u093e\u0928\u093f\u0915\u0924\u093e \u0915\u094b \u092a\u0939\u0932\u0947 \u0938\u094d\u0935\u092f\u0902 \u0938\u092e\u091d\u0947\u0902, \u092b\u093f\u0930 \u0905\u092a\u0928\u0940 \u0938\u0902\u0924\u0924\u093f\u092f\u094b\u0902 \u0915\u094b \u092d\u0940 \u0938\u092e\u091d\u093e\u090f\u0901\u0964 \u0939\u092e\u093e\u0930\u0947 \u0924\u094d\u092f\u094b\u0939\u093e\u0930, \u0939\u092e\u093e\u0930\u0940 \u092d\u093e\u0937\u093e, \u0939\u092e\u093e\u0930\u0940 \u092a\u0930\u0902\u092a\u0930\u093e, \u0939\u092e\u093e\u0930\u0940 \u0938\u0902\u0938\u094d\u0915\u0943\u0924\u093f - \u0939\u0940\u0928\u0924\u093e \u0928\u0939\u0940\u0902, \u0917\u0930\u094d\u0935 \u0915\u0940 \u0935\u093f\u0937\u092f\u0935\u0938\u094d\u0924\u0941 \u0939\u0948\u0902\u0964 As we celebrate the auspicious occasion of Makar Sankranti, when the sun begins its northward journey (Uttarayan), let us draw inspiration from this symbol of progress, renewal, and growth. This festival reminds us to embrace change, rise above challenges, and strive for new aspirations. Much like the sun\u2019s steady path, our commitment to advancing knowledge, innovation, and societal impact continues to illuminate the way forward. At IIT Jodhpur, we are driven to explore transformative solutions, foster excellence, and shape a sustainable and inclusive future for ourselves, and the nation. Let us make use of this occasion to reflect on our achievements and renew our dedication to the goals that lie ahead. Together, as a community, we can reach greater heights and leave an enduring legacy for generations to come. May this festive season bring joy, prosperity, and inspiration to you and your families. Let us soar higher, united in our purpose and vision. -- Affectionately Yours..... With warm regards..... Prof. Avinash Kumar Agarwal, FTWAS, FAAAS, FCI, FSAE, FASME, FRSC, FNAE, FNASc, FISEES Director, IIT Jodhpur & Sir J C Bose National Fellow Tel: +91 291 2801011 (Off) Wikipedia: tinyurl.com/bdhe89ew | Scopus: https://tinyurl.com/mwccdcc4 | Google Scholar: https://tinyurl.com/mtbyv7w4 | FUE",
|
16 |
+
"message_id": "<CADCv5WgfRV2jFQf2=gfVHw1xfyE87tdqHSVtGQq3S4dhNmwEdA@mail.gmail.com>"
|
17 |
+
},
|
18 |
+
{
|
19 |
+
"date": "25-Jan-2025",
|
20 |
+
"time": "19:48:33",
|
21 |
+
"subject": "Happy Republic Day-2025",
|
22 |
+
"content": "My Dear Students, Faculty and Staff members, \ud83c\uddee\ud83c\uddf3 Greetings on the occasion of the 76th Republic Day! \ud83c\uddee\ud83c\uddf3 As we approach the 26th of January, we unite to celebrate not only the adoption of our Constitution but also the enduring principles of democracy, justice, and equality that define us as individuals and as an institution. This momentous day inspires us to reaffirm our collective commitment to shaping the future of our nation. At IIT Jodhpur, we hold a pivotal role in this journey of progress and innovation. As proud members of this esteemed institution, we bear the responsibility of fostering a culture rooted in innovation, academic excellence, and ethical leadership. Republic Day serves as a powerful reminder of our individual and collective contributions to IIT Jodhpur and the nation\u2014 not only through our collective professional accomplishments but also through the values we instil in our students and the spirit of collaboration and excellence we cultivate among ourselves. On this Republic Day, let us focus on: \u2705 Strengthening research and teaching excellence in our Institute, \u2705 Enhancing our infrastructure, and \u2705 Building a more inclusive and supportive environment for all members of our community. It is through these efforts that we will continue to push the frontiers of knowledge, innovation and excellence, contributing meaningfully to our nation and beyond. I encourage everyone to actively participate in the Republic Day celebrations tomorrow morning and reflect on how we can collectively elevate IIT Jodhpur\u2019s legacy. Together, let us uphold the values of integrity, diversity, and excellence\u2014the core pillars of our nation and our Institute. Wishing you and yours a thoughtful, inspiring, and joyous Republic Day 2025. Jai Hind. Jai Bharat. With warm regards and affection, Prof. Avinash Kumar Agarwal, FTWAS, FAAAS, FCI, FSAE, FASME, FRSC, FNAE, FNASc, FISEES Director, IIT Jodhpur & Sir J C Bose National Fellow Tel: +91 291 2801011 (Off) Wikipedia: tinyurl.com/bd",
|
23 |
+
"message_id": "<CADCv5WiULZvioxbVYrmR7mdmZHtB4jQ2P2cG_+S-Sdas1hNyew@mail.gmail.com>"
|
24 |
+
},
|
25 |
+
{
|
26 |
+
"date": "13-Feb-2025",
|
27 |
+
"time": "11:37:07",
|
28 |
+
"subject": "=?UTF-8?Q?Re=3A_=5Bfaculty=5D_Invitation_to_Hamira_Manganiyar_Group=27?=\r\n\t=?UTF-8?Q?s_Rajasthani_Folk_Music_Performance_Today_=E2=80=93_VIRASAT_2025?=",
|
29 |
+
"content": "Dear All, The Institute is organising Virasat 2025, and renowned artists will descend on our campus over the next five days. We must take this opportunity to learn about our cultural heritage and musical\u00a0performances during this period. I will strongly encourage all constituents of our campus community, including students, faculty and staff members and their families, and project staff members, to attend all these programs in the evening over the next five days and enjoy the cultural performances. Best wishes Avinash Kumar Agarwal On Thu, Feb 13, 2025 at 11:26\u202fAM Sherin Sabu < [email protected] > wrote: Dear all, We are delighted to invite you to an enchanting evening of Rajasthani folk music as part of VIRASAT 2025 , organized by IIT Jodhpur in collaboration with SPIC MACAY. \ud83c\udfb6 Performance Details: \ud83d\udccd Venue: Jodhpur Club, IIT Jodhpur \ud83c\udfa4 Artist: Hamira Manganiyar Group (Rajasthani Folk Music) \ud83d\udcc5 Date: Today: 13th February 2025 \u23f0 Time: 7:30 PM Immerse yourself in the vibrant and soulful rhythms of Rajasthan as the Hamira Manganiyar Group brings to life the rich musical traditions of the desert. This performance is a rare opportunity to experience the deep-rooted heritage of folk music passed down through generations. We warmly invite you to join us for this unforgettable musical evening.\u00a0\r\n\r\nPlease bring your family along to share in this cultural celebration! \ud83d\udccc Find attached the official event poster for more details. Looking forward to your presence! Warm Regards, Team Virasat 2025 IIT Jodhpur -- Dr Sherin Sabu Assistant Professor (Sociology), School of Liberal Arts (SoLA) Affiliate Faculty, Center for Emerging Technologies for Sustainable Development (CETSD) IIT Jodhpur",
|
30 |
+
"message_id": "<CADCv5WiZNk6BBrYPXhaaN0K_9N-L27ufUfg5JyWTrrJdbnwM=w@mail.gmail.com>"
|
31 |
+
},
|
32 |
+
{
|
33 |
+
"date": "26-Feb-2025",
|
34 |
+
"time": "19:54:24",
|
35 |
+
"subject": "Greetings on Mahashivratri!",
|
36 |
+
"content": "Dear all, Wishing you all a blessed and joyous Mahashivratri! I extend my warmest greetings to all of you and your family members. Mahashivratri is a time of deep spiritual reflection, inner growth, and devotion. This sacred festival symbolizes the triumph of wisdom, devotion, and inner strength, inspiring us to pursue knowledge and morality in all our endeavors. As we celebrate this day with devotion and reflection, let us also reaffirm our commitment to excellence, innovation, and the collective growth of IIT Jodhpur. Together, through dedication and hard work, we should continue to make meaningful contributions to knowledge, technology, and society. With warm regards, Prof. Avinash Kumar Agarwal ..",
|
37 |
+
"message_id": "<CADCv5WhSG91tOjiv_+XUUrxvqeOQv4xMocQNsgAC_EuUTQ87jw@mail.gmail.com>"
|
38 |
+
},
|
39 |
+
{
|
40 |
+
"date": "28-Feb-2025",
|
41 |
+
"time": "12:05:48",
|
42 |
+
"subject": "Re: [faculty] Invitation to celebrate \"National Science Day\" on 28th\r\n February 2025 at IIT Jodhpur",
|
43 |
+
"content": "Dear All, Hearty Congratulations to all of you on the occasion of National Science Day 2025. I urge all of you to attend this celebration of National Science Day. Sh Sharad Sarraf, BoG Chairman of IIT Mumbai and Jammu, is the Speaker and the chief guest. He is a strong well-wisher of IIT Jodhpur and we will enrich ourselves by listening to his words, full of wisdom. Best regards Avinash Kumar Agarwal On Wed, Feb 26, 2025 at 6:02\u202fPM Committee for Celebration of Commemorative Days < [email protected] > wrote: Dear All, \u092e\u0939\u093e\u0936\u093f\u0935\u0930\u093e\u0924\u094d\u0930\u093f\u00a0 \u0915\u0940 \u0939\u093e\u0930\u094d\u0926\u093f\u0915 \u0936\u0941\u092d\u0915\u093e\u092e\u0928\u093e\u090f\u0902 / Happy MahaShivratri....! \u0938\u094d\u092e\u093e\u0930\u0915 \u0926\u093f\u0935\u0938 \u0938\u092e\u093e\u0930\u094b\u0939 \u0938\u092e\u093f\u0924\u093f (\u0938\u0940\u0938\u0940\u0938\u0940\u0921\u0940) \u0915\u0940 \u0913\u0930 \u0938\u0947, \u092d\u093e\u0930\u0924\u0940\u092f \u092a\u094d\u0930\u094c\u0926\u094d\u092f\u094b\u0917\u093f\u0915\u0940 \u0938\u0902\u0938\u094d\u0925\u093e\u0928 \u091c\u094b\u0927\u092a\u0941\u0930 \u092e\u0947\u0902 28 \u092b\u0930\u0935\u0930\u0940, 2025 (\u0936\u0941\u0915\u094d\u0930\u0935\u093e\u0930) \u0915\u094b \u0930\u093e\u0937\u094d\u091f\u094d\u0930\u0940\u092f \u0935\u093f\u091c\u094d\u091e\u093e\u0928 \u0926\u093f\u0935\u0938\u00a0 2025 \u0915\u0947 \u0905\u0935\u0938\u0930 \u092a\u0930 \u0939\u092e \u0906\u092a\u0915\u094b \u00a0\u0939\u093e\u0930\u094d\u0926\u093f\u0915 \u0928\u093f\u092e\u0902\u0924\u094d\u0930\u0923 \u0926\u0947\u0924\u0947\u00a0 \u0939\u0948\u0902 \u0964 \u0939\u092e\u00a0\u00a0\u0907\u0938 \u092e\u0939\u0924\u094d\u0935\u092a\u0942\u0930\u094d\u0923 \u0915\u093e\u0930\u094d\u092f\u0915\u094d\u0930\u092e \u092e\u0947\u0902 \u0906\u092a\u0915\u0947 \u0936\u093e\u092e\u093f\u0932 \u0939\u094b\u0928\u0947 \u0915\u0947 \u0938\u092e\u094d\u092e\u093e\u0928 \u0915\u0940 \u0909\u0924\u094d\u0938\u0941\u0915\u0924\u093e \u0938\u0947 \u092a\u094d\u0930\u0924\u0940\u0915\u094d\u0937\u093e \u0915\u0930\u0947\u0902\u0917\u0947\u0964 On behalf of the Committee for Celebration of Commemorative Days (CCCD) at IIT Jodhpur, we cordially invite you to join us in commemorating the National Science Day 2025 on February 28, 2025 (Friday) . We eagerly anticipate the honour of having you at this momentous event. Program: National Science Day 2025 Date: 28th February 2025 (Friday) Venue: Jodhpur Club Time: 5:45 PM onwards Program details: Time Event 05:45 \u2013 06:05 PM Scientific Demonstration & Tea -Refreshments 06:05 \u2013 06:10 PM Lamp Lighting & felicitation 06:10 \u2013 06:20 PM Welcome address by the Director 06:20 \u2013 06:45 PM Talk and interaction by the Chief Guest 06:45 \u2013 06:50 PM Felicitation of Guests 06:50 \u2013 07:00 PM Library App Release 07:00 \u2013 07:05 PM Quiz Session 07:05 \u2013 07:15 PM Felicitation to ACAC students 07:15 \u2013 07:20 PM Vote of Thanks 07:20 PM National Anthem Your presence and active participation will contribute significantly to the success of this celebration. With warm regards, Himmat Singh Assistant Registrar ___________________________________________________ \u0938\u094d\u092e\u093e\u0930\u0915 \u0926\u093f\u0935\u0938 \u0938\u092e\u093e\u0930\u094b\u0939 \u0938\u092e\u093f\u0924\u093f / Committee for Celebration of Commemorative Days (CCCD) \u0906\u0908\u0906\u0908",
|
44 |
+
"message_id": "<CADCv5WjLQTBkWxUB7XuOmQEKmNJftW5Orw8rnnjX-cAQ4bPFuw@mail.gmail.com>"
|
45 |
+
},
|
46 |
+
{
|
47 |
+
"date": "01-Apr-2025",
|
48 |
+
"time": "10:53:52",
|
49 |
+
"subject": "Fwd: FY Closure and Updates",
|
50 |
+
"content": "Dear Students, Today, April 1, marks an important day because we are taking two important steps in the direction of our evolution as a mature institute of higher learning. 1. Today, our Health Center starts working in autonomous mode, managed and operated by OUR OWN team. 2. Today, our transport services also start operating in autonomous mode, managed and operated by OUR OWN team. These two are definitely two big steps in the evolution of our institute. I would also like to put on record my deep appreciation of the teams of the Health Center, led by Prof. Anil Tiwari and Dr Neha Sharma, and the transport team, led by Prof. Shree Prakash Tiwari and Sandeep Chandel. Please join me in congratulating them for a good start. While these are big transitions, it is\u00a0possible that there might be some perturbations in services in the initial period. Please give your feedback to the process owners, and actions will be taken to minimise the inconveniences and meet all genuine expectations. Best regards -- With warm regards..... Prof. Avinash Kumar Agarwal, FTWAS, FAAAS, FCI, FSAE, FASME, FRSC, FNAE, FNASc, FISEES Director, IIT Jodhpur & Sir J C Bose National Fellow Tel: +91 291 2801011 (Off) Wikipedia: tinyurl.com/bdhe89ew | Scopus: https://tinyurl.com/mwccdcc4 | Google Scholar: https://tinyurl.com/mtbyv7w4 | FUEL: https://tinyurl.com/bdzn4r28 | Orcid: https://tinyurl.com/537m3tad ------------------------------ ------------------------------ ---------------- \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of The World Academy of Science \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Combustion Institute, USA \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of American Association for the Advancement of Science \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of American Society of Mechanical Engineers \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Society of Automotive Engineers International, USA \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of World Society for Sustainable Energy Technologies, UK \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Royal Society of Chemistry, UK \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of National Academy of Sciences India \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Indian National Academy of Engineering \u2022\u00a0 \u00a0 \u00a0 \u00a0F",
|
51 |
+
"message_id": "<CADCv5WhGgVj0LCjvkTKda_mNxQ6WqQdGmP1afV=sj2v=WSBwow@mail.gmail.com>"
|
52 |
+
},
|
53 |
+
{
|
54 |
+
"date": "23-Apr-2025",
|
55 |
+
"time": "20:27:50",
|
56 |
+
"subject": "Directorate Shifted to Chankya Complex",
|
57 |
+
"content": "Dear All, This is to inform you that all the offices of the Deans, Registrar, DD and D have moved back to Chanamkya\u00a0Complex. -- With warm regards..... Prof. Avinash Kumar Agarwal, FTWAS, FAAAS, FCI, FSAE, FASME, FRSC, FNAE, FNASc, FISEES Director, IIT Jodhpur & Sir J C Bose National Fellow Tel: +91 291 2801011 (Off) Wikipedia: tinyurl.com/bdhe89ew | Scopus: https://tinyurl.com/mwccdcc4 | Google Scholar: https://tinyurl.com/mtbyv7w4 | FUEL: https://tinyurl.com/bdzn4r28 | Orcid: https://tinyurl.com/537m3tad ------------------------------ ------------------------------ ---------------- \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of The World Academy of Science \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Combustion Institute, USA \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of American Association for the Advancement of Science \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of American Society of Mechanical Engineers \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Society of Automotive Engineers International, USA \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of World Society for Sustainable Energy Technologies, UK \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Royal Society of Chemistry, UK \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of National Academy of Sciences India \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Indian National Academy of Engineering \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of International Society of Energy, Environment, and Sustainability ------------------------------ ------------------------------ ------------- \u2022\u00a0 \u00a0 \u00a0 \u00a0Shanti Swarup Bhatnagar Award-2016 \u2022\u00a0 \u00a0 \u00a0 \u00a0Editor of FUEL \u2022\u00a0 \u00a0 \u00a0 \u00a0Associate Editor of ASME Open Journal of Engineering \u2022\u00a0 \u00a0 \u00a0 \u00a0Associate Editor of SAE International Journal of Engines ------------------------------ ------------------------------ --------------",
|
58 |
+
"message_id": "<CADCv5WhB=aoNjykLwPj9wY-ZNTCxnBp5EFsPriDs+mpH9Fi-WA@mail.gmail.com>"
|
59 |
+
},
|
60 |
+
{
|
61 |
+
"date": "01-May-2025",
|
62 |
+
"time": "12:20:20",
|
63 |
+
"subject": "Thank You",
|
64 |
+
"content": "Dear\r\nColleagues I want to thank all the stakeholders for their kind cooperation,\r\nenabling me to complete one year as Director of IIT Jodhpur. I joined the\r\nInstitute on 1 st May 2024. I realised this Institute has great potential and can break into the top echelons of\r\nranking among engineering institutions in the country and the world. However,\r\nto achieve this, all of us must work as a unified team. From my\r\nside, I assure you that I will make all possible efforts to ensure that fair\r\nand transparent governance processes are in place and we, as a team, make all\r\nthe efforts in the right direction. In the last\r\nyear, extra-mural research grants to IIT jodhpur have doubled, and project endorsements\r\nand publications have significantly increased; however, there are miles to go. I hope we\r\nall continue to work relentlessly to pursue excellence in our activities, be\r\nloyal to the Institute, and do all our duties with dedication, sincerity and\r\nhonesty. This Institute cannot have any room for corruption, nepotism and\r\nregionalism. As IIT Jodhpur stakeholders, we must commit to having excellent\r\nconduct and setting an example for others to follow. Wishing you\r\nall the very best Affectionately\r\nyours Avinash Kumar Agarwal, FTWAS, FAAAS, FCI, FSAE, FASME, FRSC, FNAE, FNASc, FISEES Director, IIT Jodhpur & Sir J C Bose National Fellow Tel: +91 291 2801011 (Off) Wikipedia: tinyurl.com/bdhe89ew | Scopus: https://tinyurl.com/mwccdcc4 | Google Scholar: https://tinyurl.com/mtbyv7w4 | FUEL: https://tinyurl.com/bdzn4r28 | Orcid: https://tinyurl.com/537m3tad ------------------------------ ------------------------------ ---------------- \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of The World Academy of Science \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Combustion Institute, USA \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of American Association for the Advancement of Science \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of American Society of Mechanical Engineers \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Society of Automotive Engineers International, USA \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of World Society for Sustainable Energy Technol",
|
65 |
+
"message_id": "<CADCv5Wjxn7Pj2XXBf4n2S0PyJQapU1aOOvfHfjjtr4xfSWbeKw@mail.gmail.com>"
|
66 |
+
},
|
67 |
+
{
|
68 |
+
"date": "07-May-2025",
|
69 |
+
"time": "20:56:02",
|
70 |
+
"subject": "Instruction for Next few days: Urgent attention",
|
71 |
+
"content": "Dear Faculty Members, Staff Members, Students and Other Constituents of our campus community You are aware that we are passing through a tough time, as far as national security is concerned. Today, we have done a drill for the evacuation of the campus community in the event of an air strike by our nemesis. We will have less than 5 minutes for blackout and evacuation into the tunnels. The next few days and nights are very critical, and we may be one of the soft targets. Hence, adequate care and precaution are important to all of us. Please note: 1. There will be no more drills. In case you hear a siren (Oscillating), it means an impending attack, and we will have less than 5 minutes to get into the tunnel hideouts.\u00a0The flat siren will mean that the danger has passed, and now it is safe to venture out. Enemy fighters\u00a0and missiles will reach us in 5-10 minutes after crossing the border, and that's all we would have to ensure our safety. 2. Today evening, we will do black out\u00a0drill between 10-10.15 PM. There will be no siren; hence, all of you are to follow the procedure voluntarily on your own. The power will be cut (If it is resumed by JVVNL). Every possible light source must be turned off. The lights can be put on again after 15 minutes, at 10.15 PM tonight. 3. In the event of an impending attack, the lights will be cut off centrally after the siren goes off, in the next 2-3 minutes.\u00a0That's all the time, you will have to come down the roads and move into the tunnels. It is\u00a0advised to carry your own water bottle in such an event. 4. There should be no live streaming, photographs posted on social media for these or sharing of this email on any platform. This will put all of us in danger. On each tunnel entry point, we will post security guards to\u00a0guide\u00a0you safely. Please ensure that you do not panic and move in a\u00a0disciplined manner into the tunnels,\u00a0when and if required. If you have already posted the photos and videos of tunnels on your social media accounts, please d",
|
72 |
+
"message_id": "<CADCv5WiK10w-aj0Vn2vq+bT1qViromAsfpwd+DPWGeYx2zspXA@mail.gmail.com>"
|
73 |
+
},
|
74 |
+
{
|
75 |
+
"date": "07-May-2025",
|
76 |
+
"time": "22:28:02",
|
77 |
+
"subject": "Re: Instruction for Next few days: Urgent attention",
|
78 |
+
"content": "Dear All, Thanks. Black out drill was an outstanding success and we figured out some lapses, which have been fixed. Be alert and all of us know the steps, in case required, to keep us safe. Be calm and hope that our forces will keep our nemesis at bay and we are not required to be in the hideout. In any case, now we all know the next steps and hopefully we will sleep peacefully. Best regards Avinash Kumar Agarwal On Wed, 7 May, 2025, 20:56 Director, IIT Jodhpur, < [email protected] > wrote: Dear Faculty Members, Staff Members, Students and Other Constituents of our campus community You are aware that we are passing through a tough time, as far as national security is concerned. Today, we have done a drill for the evacuation of the campus community in the event of an air strike by our nemesis. We will have less than 5 minutes for blackout and evacuation into the tunnels. The next few days and nights are very critical, and we may be one of the soft targets. Hence, adequate care and precaution are important to all of us. Please note: 1. There will be no more drills. In case you hear a siren (Oscillating), it means an impending attack, and we will have less than 5 minutes to get into the tunnel hideouts.\u00a0The flat siren will mean that the danger has passed, and now it is safe to venture out. Enemy fighters\u00a0and missiles will reach us in 5-10 minutes after crossing the border, and that's all we would have to ensure our safety. 2. Today evening, we will do black out\u00a0drill between 10-10.15 PM. There will be no siren; hence, all of you are to follow the procedure voluntarily on your own. The power will be cut (If it is resumed by JVVNL). Every possible light source must be turned off. The lights can be put on again after 15 minutes, at 10.15 PM tonight. 3. In the event of an impending attack, the lights will be cut off centrally after the siren goes off, in the next 2-3 minutes.\u00a0That's all the time, you will have to come down the roads and move into the tunnels. It is\u00a0advis",
|
79 |
+
"message_id": "<CADCv5Wh8x5L+Z=5bNYG_f=tbX=Lo0HH=1cT3yTw9Huv4Kr+iWQ@mail.gmail.com>"
|
80 |
+
},
|
81 |
+
{
|
82 |
+
"date": "08-May-2025",
|
83 |
+
"time": "11:06:17",
|
84 |
+
"subject": "Re: [faculty] Re: Instruction for Next few days: Urgent attention",
|
85 |
+
"content": "Dear All, There was a complete blackout in the entire city last night from 12-4 AM as all feeders were shut down by the district administration, and there was no power supply anywhere in the city. This might have led to some inconveniences\u00a0for all of us in these difficult times. These directions of complete blackout\u00a0are likely to be given again by the district administration over the next few days, depending on threat perception and intel inputs. I am trying to get our electricity supplies uninterrupted by discussing with the district admin so that the campus community stays indoors during these long and declared blackout periods. It is likely that we will keep all our street lights and public lights off starting the evenings over the next few days. The campus community is advised to ensure that they have all lights off during the declared blackout periods,\u00a0without any defaults. Any defaults may lead to our staying without electricity,\u00a0at par with the rest of the city. In addition, in the event of a siren going off, everyone needs to rush to the hideouts, as per our previous drill. Siren will indicate an upcoming aerial raid. I would also like to reiterate that there is no specific additional threat to the IITJ community. The threat to us is similar to that of any other part of the country, and there is no specific need for any panic or concern. We are all in this situation, as a united Bharat, and we must all face it bravely. There is no need for any anxiety or nervousness by seeing our emails about the safety protocols. These are just to ensure that in the event of any adverse action by our nemesis, our campus community stays safe, and all these measures taken by IITJ and drills were part of precautionary measures taken on the directions of the district administration. You may please contact Prof. Bhabani Satapathi, Prof. S R Vadera or Col Virendra Singh Rathore in case of any genuine concerns. Best wishes Avinash Kumar Agarwal On Wed, May 7, 2025 at 11:32\u202fPM Avin",
|
86 |
+
"message_id": "<CADCv5WixMeCadxAfjoOoNrBR2WotkVyw-678FsLfEODX5KRisA@mail.gmail.com>"
|
87 |
+
},
|
88 |
+
{
|
89 |
+
"date": "08-May-2025",
|
90 |
+
"time": "21:42:20",
|
91 |
+
"subject": "Re: Important Notice: Citywide Blackout and Campus Power Supply Instructions",
|
92 |
+
"content": "Evacuation to tunnels immediately On Thu, 8 May, 2025, 21:32 Deputy Director IIT Jodhpur, < [email protected] > wrote: Dear All, As per instructions from the District Administration, there will be a blackout and no power supply tonight across the entire city of Jodhpur. However, following discussions between our Director and the city administration, a special provision has been made to allow limited power supply within our campus\u2014only to Type C, Type B, and Hostel areas\u2014provided that the campus community strictly adheres to the following: \u2022\tRemain indoors throughout the blackout period. \u2022\tKeep all lights switched off; only fans may be used. \u2022\tNo lights should be visible from outside under any circumstance. Please note, no power supply will be provided to any other areas of the campus apart from the three mentioned above. Your cooperation is essential in ensuring compliance with this directive and maintaining safety for all. Warm regards, Prof. Bhabani Kumar Satapathy",
|
93 |
+
"message_id": "<CADCv5WiFEi5Qbim6PqHk7E-fu=qv4XP5ZDbg3uQgBAHou3Tzmw@mail.gmail.com>"
|
94 |
+
},
|
95 |
+
{
|
96 |
+
"date": "10-May-2025",
|
97 |
+
"time": "18:33:45",
|
98 |
+
"subject": "Updates",
|
99 |
+
"content": "Dear All, We should be aware that the threat is now over, and we can resume our \"Business as usual\". Those who are planning to go should not, and those who have already left the campus can make their plans to return, as per their convenience. Congratulations to all for showing an absolute resolve to tackle this national threat and showing that we are Bharat of the 21st century, a \"Naya Bharat\". This also calls for all IITJ constituents to work actively towards the national defence and offence capabilities. Jai Hind and Jai Bharat. Best regards Avinash Kumar AGarwal",
|
100 |
+
"message_id": "<CADCv5Wg8YogC8kG2DH7w=GmpZiKb80_nXMhBFRJjNTUZBVGQVQ@mail.gmail.com>"
|
101 |
+
},
|
102 |
+
{
|
103 |
+
"date": "24-May-2025",
|
104 |
+
"time": "21:46:02",
|
105 |
+
"subject": "Great News",
|
106 |
+
"content": "Dear All, I am delighted to share with you some fantastic news. Our Jaipur campus has come one step closer to realisation with the Government of Rajasthan agreeing \"in principle\" to allocate us land and buildings. Now we have secured a letter of intent from the GoR, which now needs to be taken up with the Ministry of Education and the Ministry of Finance, Government of India. Once these approvals are secured, we will realise our dream of having a Jaipur campus, apart from our main campus in Jodhpur. We are also beginning to work on our small footprint\u00a0campus in Jaisalmer to\u00a0complete our dream of IITJ3. This is a big feat for us as an institute to get the GoR to agree to our proposal. Hopefully, more good things will follow. -- With warm regards..... Prof. Avinash Kumar Agarwal, FTWAS, FAAAS, FCI, FSAE, FASME, FRSC, FNAE, FNASc, FISEES Director, IIT Jodhpur & Sir J C Bose National Fellow Tel: +91 291 2801011 (Off) Wikipedia: tinyurl.com/bdhe89ew | Scopus: https://tinyurl.com/mwccdcc4 | Google Scholar: https://tinyurl.com/mtbyv7w4 | FUEL: https://tinyurl.com/bdzn4r28 | Orcid: https://tinyurl.com/537m3tad ------------------------------ ------------------------------ ---------------- \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of The World Academy of Science \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Combustion Institute, USA \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of American Association for the Advancement of Science \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of American Society of Mechanical Engineers \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Society of Automotive Engineers International, USA \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of World Society for Sustainable Energy Technologies, UK \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Royal Society of Chemistry, UK \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of National Academy of Sciences India \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of Indian National Academy of Engineering \u2022\u00a0 \u00a0 \u00a0 \u00a0Fellow of International Society of Energy, Environment, and Sustainability ------------------------------ ------------------------------ ------------- \u2022\u00a0 \u00a0 \u00a0 \u00a0Shanti Swarup Bhatnagar Award-2016 \u2022\u00a0 \u00a0 \u00a0 \u00a0Editor of FUEL \u2022\u00a0 \u00a0 \u00a0 \u00a0Associate Editor of ASME Open Journal of Enginee",
|
107 |
+
"message_id": "<CADCv5Wik-=UY6XFVqbtHBPNYXGe3gWkYE82qV286AySDp1qL_w@mail.gmail.com>"
|
108 |
+
},
|
109 |
+
{
|
110 |
+
"date": "07-Jun-2025",
|
111 |
+
"time": "12:44:03",
|
112 |
+
"subject": "Greetings on the occasion of Eid al-Adha!",
|
113 |
+
"content": "Dear All, On the joyous occasion of Eid al-Adha, I extend my warmest greetings to all members of IITJ. This festival, rooted in the values of personal sacrifices, compassion, empathy and unity, inspires us to strengthen our bonds and work together for the greater good. At IIT Jodhpur, we are committed to nurturing an environment of compassion, empathy, honesty, collaboration, innovation, and integrity. As we celebrate this auspicious day, let us reaffirm our dedication to positive growth, unite in our pursuit of excellence, and resolve to uphold transparency. May this festival bring peace, prosperity, and harmony to our vibrant campus community and its constituents. Best wishes, Affectionately Yours Avinash Kumar Agarwal Director",
|
114 |
+
"message_id": "<CADCv5Wj4J-FCNitA2r_m9uT5pFZNz-OQFXTwQM1em+ki69=9jQ@mail.gmail.com>"
|
115 |
+
}
|
116 |
+
],
|
117 |
+
"last_scraped": "07-Jun-2025"
|
118 |
+
}
|
119 |
+
}
|
server/email_scraper.py
ADDED
@@ -0,0 +1,267 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
Enhanced Email Scraper with Intelligent Caching
|
4 |
+
"""
|
5 |
+
|
6 |
+
import os
|
7 |
+
import imaplib
|
8 |
+
import json
|
9 |
+
from email import message_from_bytes
|
10 |
+
from bs4 import BeautifulSoup
|
11 |
+
from datetime import datetime, timedelta
|
12 |
+
from dotenv import load_dotenv
|
13 |
+
from zoneinfo import ZoneInfo
|
14 |
+
from email.utils import parsedate_to_datetime
|
15 |
+
from typing import List, Dict
|
16 |
+
|
17 |
+
load_dotenv()
|
18 |
+
|
19 |
+
# Email credentials
|
20 |
+
APP_PASSWORD = os.getenv("APP_PASSWORD")
|
21 |
+
EMAIL_ID = os.getenv("EMAIL_ID")
|
22 |
+
EMAIL_DB_FILE = "email_db.json"
|
23 |
+
|
24 |
+
def _imap_connect():
|
25 |
+
"""Connect to Gmail IMAP server"""
|
26 |
+
try:
|
27 |
+
mail = imaplib.IMAP4_SSL("imap.gmail.com")
|
28 |
+
mail.login(EMAIL_ID, APP_PASSWORD)
|
29 |
+
mail.select('"[Gmail]/All Mail"')
|
30 |
+
return mail
|
31 |
+
except Exception as e:
|
32 |
+
print(f"IMAP connection failed: {e}")
|
33 |
+
raise
|
34 |
+
|
35 |
+
def _email_to_clean_text(msg):
|
36 |
+
"""Extract clean text from email message"""
|
37 |
+
# Try HTML first
|
38 |
+
html_content = None
|
39 |
+
text_content = None
|
40 |
+
|
41 |
+
if msg.is_multipart():
|
42 |
+
for part in msg.walk():
|
43 |
+
content_type = part.get_content_type()
|
44 |
+
if content_type == "text/html":
|
45 |
+
try:
|
46 |
+
html_content = part.get_payload(decode=True).decode(errors="ignore")
|
47 |
+
except:
|
48 |
+
continue
|
49 |
+
elif content_type == "text/plain":
|
50 |
+
try:
|
51 |
+
text_content = part.get_payload(decode=True).decode(errors="ignore")
|
52 |
+
except:
|
53 |
+
continue
|
54 |
+
else:
|
55 |
+
# Non-multipart message
|
56 |
+
content_type = msg.get_content_type()
|
57 |
+
try:
|
58 |
+
content = msg.get_payload(decode=True).decode(errors="ignore")
|
59 |
+
if content_type == "text/html":
|
60 |
+
html_content = content
|
61 |
+
else:
|
62 |
+
text_content = content
|
63 |
+
except:
|
64 |
+
pass
|
65 |
+
|
66 |
+
# Clean HTML content
|
67 |
+
if html_content:
|
68 |
+
soup = BeautifulSoup(html_content, "html.parser")
|
69 |
+
# Remove script and style elements
|
70 |
+
for script in soup(["script", "style"]):
|
71 |
+
script.decompose()
|
72 |
+
return soup.get_text(separator=' ', strip=True)
|
73 |
+
elif text_content:
|
74 |
+
return text_content.strip()
|
75 |
+
else:
|
76 |
+
return ""
|
77 |
+
|
78 |
+
def _load_email_db() -> Dict:
|
79 |
+
"""Load email database from file"""
|
80 |
+
if not os.path.exists(EMAIL_DB_FILE):
|
81 |
+
return {}
|
82 |
+
try:
|
83 |
+
with open(EMAIL_DB_FILE, "r") as f:
|
84 |
+
return json.load(f)
|
85 |
+
except (json.JSONDecodeError, IOError):
|
86 |
+
print(f"Warning: Could not load {EMAIL_DB_FILE}, starting with empty database")
|
87 |
+
return {}
|
88 |
+
|
89 |
+
def _save_email_db(db: Dict):
|
90 |
+
"""Save email database to file"""
|
91 |
+
try:
|
92 |
+
with open(EMAIL_DB_FILE, "w") as f:
|
93 |
+
json.dump(db, f, indent=2)
|
94 |
+
except IOError as e:
|
95 |
+
print(f"Error saving database: {e}")
|
96 |
+
raise
|
97 |
+
|
98 |
+
def _date_to_imap_format(date_str: str) -> str:
|
99 |
+
"""Convert DD-MMM-YYYY to IMAP date format"""
|
100 |
+
try:
|
101 |
+
dt = datetime.strptime(date_str, "%d-%b-%Y")
|
102 |
+
return dt.strftime("%d-%b-%Y")
|
103 |
+
except ValueError:
|
104 |
+
raise ValueError(f"Invalid date format: {date_str}. Expected DD-MMM-YYYY")
|
105 |
+
|
106 |
+
def _is_date_in_range(email_date: str, start_date: str, end_date: str) -> bool:
|
107 |
+
"""Check if email date is within the specified range"""
|
108 |
+
try:
|
109 |
+
email_dt = datetime.strptime(email_date, "%d-%b-%Y")
|
110 |
+
start_dt = datetime.strptime(start_date, "%d-%b-%Y")
|
111 |
+
end_dt = datetime.strptime(end_date, "%d-%b-%Y")
|
112 |
+
return start_dt <= email_dt <= end_dt
|
113 |
+
except ValueError:
|
114 |
+
return False
|
115 |
+
|
116 |
+
def scrape_emails_from_sender(sender_email: str, start_date: str, end_date: str) -> List[Dict]:
|
117 |
+
"""
|
118 |
+
Scrape emails from specific sender within date range
|
119 |
+
Uses intelligent caching to avoid re-scraping
|
120 |
+
"""
|
121 |
+
print(f"Scraping emails from {sender_email} between {start_date} and {end_date}")
|
122 |
+
|
123 |
+
# Load existing database
|
124 |
+
db = _load_email_db()
|
125 |
+
sender_email = sender_email.lower().strip()
|
126 |
+
|
127 |
+
# Check if we have cached emails for this sender
|
128 |
+
if sender_email in db:
|
129 |
+
cached_emails = db[sender_email].get("emails", [])
|
130 |
+
|
131 |
+
# Filter cached emails by date range
|
132 |
+
filtered_emails = [
|
133 |
+
email for email in cached_emails
|
134 |
+
if _is_date_in_range(email["date"], start_date, end_date)
|
135 |
+
]
|
136 |
+
|
137 |
+
# Check if we need to scrape more recent emails
|
138 |
+
last_scraped = db[sender_email].get("last_scraped", "01-Jan-2020")
|
139 |
+
today = datetime.today().strftime("%d-%b-%Y")
|
140 |
+
|
141 |
+
if last_scraped == today and filtered_emails:
|
142 |
+
print(f"Using cached emails (last scraped: {last_scraped})")
|
143 |
+
return filtered_emails
|
144 |
+
|
145 |
+
# Need to scrape emails
|
146 |
+
try:
|
147 |
+
mail = _imap_connect()
|
148 |
+
|
149 |
+
# Prepare IMAP search criteria
|
150 |
+
start_imap = _date_to_imap_format(start_date)
|
151 |
+
# Add one day to end_date for BEFORE criteria (IMAP BEFORE is exclusive)
|
152 |
+
end_dt = datetime.strptime(end_date, "%d-%b-%Y") + timedelta(days=1)
|
153 |
+
end_imap = end_dt.strftime("%d-%b-%Y")
|
154 |
+
|
155 |
+
search_criteria = f'(FROM "{sender_email}") SINCE "{start_imap}" BEFORE "{end_imap}"'
|
156 |
+
print(f"IMAP search: {search_criteria}")
|
157 |
+
|
158 |
+
# Search for emails
|
159 |
+
status, data = mail.search(None, search_criteria)
|
160 |
+
if status != 'OK':
|
161 |
+
raise Exception(f"IMAP search failed: {status}")
|
162 |
+
|
163 |
+
email_ids = data[0].split()
|
164 |
+
print(f"Found {len(email_ids)} emails")
|
165 |
+
|
166 |
+
scraped_emails = []
|
167 |
+
|
168 |
+
# Process each email
|
169 |
+
for i, email_id in enumerate(email_ids):
|
170 |
+
try:
|
171 |
+
print(f"Processing email {i+1}/{len(email_ids)}")
|
172 |
+
|
173 |
+
# Fetch email
|
174 |
+
status, msg_data = mail.fetch(email_id, "(RFC822)")
|
175 |
+
if status != 'OK':
|
176 |
+
continue
|
177 |
+
|
178 |
+
# Parse email
|
179 |
+
msg = message_from_bytes(msg_data[0][1])
|
180 |
+
|
181 |
+
# Extract information
|
182 |
+
subject = msg.get("Subject", "No Subject")
|
183 |
+
content = _email_to_clean_text(msg)
|
184 |
+
|
185 |
+
# Parse date
|
186 |
+
date_header = msg.get("Date", "")
|
187 |
+
if date_header:
|
188 |
+
try:
|
189 |
+
dt_obj = parsedate_to_datetime(date_header)
|
190 |
+
# Convert to IST
|
191 |
+
ist_dt = dt_obj.astimezone(ZoneInfo("Asia/Kolkata"))
|
192 |
+
email_date = ist_dt.strftime("%d-%b-%Y")
|
193 |
+
email_time = ist_dt.strftime("%H:%M:%S")
|
194 |
+
except:
|
195 |
+
email_date = datetime.today().strftime("%d-%b-%Y")
|
196 |
+
email_time = "00:00:00"
|
197 |
+
else:
|
198 |
+
email_date = datetime.today().strftime("%d-%b-%Y")
|
199 |
+
email_time = "00:00:00"
|
200 |
+
|
201 |
+
# Get message ID for deduplication
|
202 |
+
message_id = msg.get("Message-ID", f"missing-{email_id.decode()}")
|
203 |
+
|
204 |
+
scraped_emails.append({
|
205 |
+
"date": email_date,
|
206 |
+
"time": email_time,
|
207 |
+
"subject": subject,
|
208 |
+
"content": content[:2000], # Limit content length
|
209 |
+
"message_id": message_id
|
210 |
+
})
|
211 |
+
|
212 |
+
except Exception as e:
|
213 |
+
print(f"Error processing email {email_id}: {e}")
|
214 |
+
continue
|
215 |
+
|
216 |
+
mail.logout()
|
217 |
+
|
218 |
+
# Update database
|
219 |
+
if sender_email not in db:
|
220 |
+
db[sender_email] = {"emails": [], "last_scraped": ""}
|
221 |
+
|
222 |
+
# Merge with existing emails (avoid duplicates)
|
223 |
+
existing_emails = db[sender_email].get("emails", [])
|
224 |
+
existing_ids = {email.get("message_id") for email in existing_emails}
|
225 |
+
|
226 |
+
new_emails = [
|
227 |
+
email for email in scraped_emails
|
228 |
+
if email["message_id"] not in existing_ids
|
229 |
+
]
|
230 |
+
|
231 |
+
# Update database
|
232 |
+
db[sender_email]["emails"] = existing_emails + new_emails
|
233 |
+
db[sender_email]["last_scraped"] = datetime.today().strftime("%d-%b-%Y")
|
234 |
+
|
235 |
+
# Save database
|
236 |
+
_save_email_db(db)
|
237 |
+
|
238 |
+
# Return filtered results
|
239 |
+
all_emails = db[sender_email]["emails"]
|
240 |
+
filtered_emails = [
|
241 |
+
email for email in all_emails
|
242 |
+
if _is_date_in_range(email["date"], start_date, end_date)
|
243 |
+
]
|
244 |
+
|
245 |
+
print(f"Scraped {len(new_emails)} new emails, returning {len(filtered_emails)} in date range")
|
246 |
+
return filtered_emails
|
247 |
+
|
248 |
+
except Exception as e:
|
249 |
+
print(f"Email scraping failed: {e}")
|
250 |
+
raise
|
251 |
+
|
252 |
+
# Test the scraper
|
253 |
+
if __name__ == "__main__":
|
254 |
+
# Test scraping
|
255 |
+
try:
|
256 |
+
emails = scrape_emails_from_sender(
|
257 |
+
"[email protected]",
|
258 |
+
"01-Jun-2025",
|
259 |
+
"07-Jun-2025"
|
260 |
+
)
|
261 |
+
|
262 |
+
print(f"\nFound {len(emails)} emails:")
|
263 |
+
for email in emails[:3]: # Show first 3
|
264 |
+
print(f"- {email['date']} {email['time']}: {email['subject']}")
|
265 |
+
|
266 |
+
except Exception as e:
|
267 |
+
print(f"Test failed: {e}")
|
server/main.py
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI
|
2 |
+
from fastapi.middleware.cors import CORSMiddleware
|
3 |
+
from routes import router
|
4 |
+
|
5 |
+
app = FastAPI(
|
6 |
+
title="Email Query System",
|
7 |
+
description="Natural language email querying with intent classification",
|
8 |
+
version="1.0.0"
|
9 |
+
)
|
10 |
+
|
11 |
+
# Add CORS middleware
|
12 |
+
app.add_middleware(
|
13 |
+
CORSMiddleware,
|
14 |
+
allow_origins=["*"],
|
15 |
+
allow_credentials=True,
|
16 |
+
allow_methods=["*"],
|
17 |
+
allow_headers=["*"],
|
18 |
+
)
|
19 |
+
|
20 |
+
# Include routes
|
21 |
+
app.include_router(router, prefix="/api/v1")
|
22 |
+
|
23 |
+
@app.get("/")
|
24 |
+
def root():
|
25 |
+
return {
|
26 |
+
"message": "Email Query System API",
|
27 |
+
"docs": "/docs",
|
28 |
+
"health": "/api/v1/health"
|
29 |
+
}
|
server/query_parser.py
ADDED
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
Query Parser with Intent Classification and Name-to-Email Resolution
|
4 |
+
"""
|
5 |
+
|
6 |
+
import json
|
7 |
+
import os
|
8 |
+
from datetime import datetime, timedelta
|
9 |
+
from openai import OpenAI
|
10 |
+
from typing import Dict, Optional, Tuple
|
11 |
+
from dotenv import load_dotenv # <-- Add this
|
12 |
+
|
13 |
+
# Load environment variables from .env file
|
14 |
+
load_dotenv() # <-- Add this
|
15 |
+
|
16 |
+
# Initialize OpenAI client
|
17 |
+
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
18 |
+
# File paths
|
19 |
+
NAME_MAPPING_FILE = "name_mapping.json"
|
20 |
+
EMAIL_DB_FILE = "email_db.json"
|
21 |
+
|
22 |
+
def _llm(messages, model="gpt-4o-mini", temperature=0):
|
23 |
+
"""Helper function to call OpenAI API"""
|
24 |
+
rsp = client.chat.completions.create(
|
25 |
+
model=model,
|
26 |
+
temperature=temperature,
|
27 |
+
messages=messages,
|
28 |
+
)
|
29 |
+
return rsp.choices[0].message.content.strip()
|
30 |
+
|
31 |
+
def _load_name_mapping() -> Dict[str, str]:
|
32 |
+
"""Load name to email mapping from JSON file"""
|
33 |
+
if not os.path.exists(NAME_MAPPING_FILE):
|
34 |
+
return {}
|
35 |
+
try:
|
36 |
+
with open(NAME_MAPPING_FILE, "r") as f:
|
37 |
+
return json.load(f)
|
38 |
+
except (json.JSONDecodeError, IOError):
|
39 |
+
return {}
|
40 |
+
|
41 |
+
def _save_name_mapping(mapping: Dict[str, str]):
|
42 |
+
"""Save name to email mapping to JSON file"""
|
43 |
+
with open(NAME_MAPPING_FILE, "w") as f:
|
44 |
+
json.dump(mapping, f, indent=2)
|
45 |
+
|
46 |
+
def _load_email_db() -> Dict:
|
47 |
+
"""Load email database"""
|
48 |
+
if not os.path.exists(EMAIL_DB_FILE):
|
49 |
+
return {}
|
50 |
+
try:
|
51 |
+
with open(EMAIL_DB_FILE, "r") as f:
|
52 |
+
return json.load(f)
|
53 |
+
except (json.JSONDecodeError, IOError):
|
54 |
+
return {}
|
55 |
+
|
56 |
+
def _save_email_db(db: Dict):
|
57 |
+
"""Save email database"""
|
58 |
+
with open(EMAIL_DB_FILE, "w") as f:
|
59 |
+
json.dump(db, f, indent=2)
|
60 |
+
|
61 |
+
def extract_query_info(query: str) -> Dict:
|
62 |
+
"""
|
63 |
+
Extract intent and date range from user query using LLM
|
64 |
+
"""
|
65 |
+
today_str = datetime.today().strftime("%d-%b-%Y")
|
66 |
+
|
67 |
+
system_prompt = f"""
|
68 |
+
You are an email query parser. Today is {today_str}.
|
69 |
+
|
70 |
+
Given a user query, extract:
|
71 |
+
1. sender_intent: The person/entity they want emails from (could be name or email)
|
72 |
+
2. start_date and end_date: Date range in DD-MMM-YYYY format
|
73 |
+
|
74 |
+
For relative dates:
|
75 |
+
- "last week" = 7 days ago to today
|
76 |
+
- "yesterday" = yesterday only
|
77 |
+
- "last month" = 30 days ago to today
|
78 |
+
- "last 3 days" = 3 days ago to today
|
79 |
+
|
80 |
+
Examples:
|
81 |
+
- "emails from dev agarwal last week" → sender_intent: "dev agarwal"
|
82 |
+
- "show amazon emails from last month" → sender_intent: "amazon"
|
83 |
+
- "emails from [email protected] yesterday" → sender_intent: "[email protected]"
|
84 |
+
|
85 |
+
Return ONLY valid JSON:
|
86 |
+
{{
|
87 |
+
"sender_intent": "extracted name or email",
|
88 |
+
"start_date": "DD-MMM-YYYY",
|
89 |
+
"end_date": "DD-MMM-YYYY"
|
90 |
+
}}
|
91 |
+
"""
|
92 |
+
|
93 |
+
messages = [
|
94 |
+
{"role": "system", "content": system_prompt},
|
95 |
+
{"role": "user", "content": query}
|
96 |
+
]
|
97 |
+
|
98 |
+
result = _llm(messages)
|
99 |
+
return json.loads(result)
|
100 |
+
|
101 |
+
def resolve_sender_email(sender_intent: str) -> Tuple[Optional[str], bool]:
|
102 |
+
"""
|
103 |
+
Resolve sender intent to actual email address
|
104 |
+
Returns: (email_address, needs_user_input)
|
105 |
+
"""
|
106 |
+
# Check if it's already an email address
|
107 |
+
if "@" in sender_intent:
|
108 |
+
return sender_intent.lower(), False
|
109 |
+
|
110 |
+
# Load name mapping
|
111 |
+
name_mapping = _load_name_mapping()
|
112 |
+
|
113 |
+
# Normalize the intent (lowercase for comparison)
|
114 |
+
normalized_intent = sender_intent.lower().strip()
|
115 |
+
|
116 |
+
# Check direct match
|
117 |
+
if normalized_intent in name_mapping:
|
118 |
+
return name_mapping[normalized_intent], False
|
119 |
+
|
120 |
+
# Check partial matches (fuzzy matching)
|
121 |
+
for name, email in name_mapping.items():
|
122 |
+
if normalized_intent in name.lower() or name.lower() in normalized_intent:
|
123 |
+
return email, False
|
124 |
+
|
125 |
+
# No match found
|
126 |
+
return None, True
|
127 |
+
|
128 |
+
def store_name_email_mapping(name: str, email: str):
|
129 |
+
"""Store new name to email mapping"""
|
130 |
+
name_mapping = _load_name_mapping()
|
131 |
+
name_mapping[name.lower().strip()] = email.lower().strip()
|
132 |
+
_save_name_mapping(name_mapping)
|
133 |
+
|
134 |
+
def parse_email_query(query: str) -> Dict:
|
135 |
+
"""
|
136 |
+
Main function to parse email query
|
137 |
+
Returns structured response with next steps
|
138 |
+
"""
|
139 |
+
try:
|
140 |
+
# Step 1: Extract intent and dates
|
141 |
+
query_info = extract_query_info(query)
|
142 |
+
sender_intent = query_info["sender_intent"]
|
143 |
+
start_date = query_info["start_date"]
|
144 |
+
end_date = query_info["end_date"]
|
145 |
+
|
146 |
+
# Step 2: Resolve sender email
|
147 |
+
email_address, needs_input = resolve_sender_email(sender_intent)
|
148 |
+
|
149 |
+
if needs_input:
|
150 |
+
# Need to ask user for email address
|
151 |
+
return {
|
152 |
+
"status": "need_email_input",
|
153 |
+
"sender_intent": sender_intent,
|
154 |
+
"start_date": start_date,
|
155 |
+
"end_date": end_date,
|
156 |
+
"message": f"I don't have an email address for '{sender_intent}'. Please provide the email address."
|
157 |
+
}
|
158 |
+
else:
|
159 |
+
# Ready to proceed with email scraping
|
160 |
+
return {
|
161 |
+
"status": "ready_to_scrape",
|
162 |
+
"sender_intent": sender_intent,
|
163 |
+
"resolved_email": email_address,
|
164 |
+
"start_date": start_date,
|
165 |
+
"end_date": end_date,
|
166 |
+
"message": f"Found email: {email_address} for '{sender_intent}'"
|
167 |
+
}
|
168 |
+
|
169 |
+
except Exception as e:
|
170 |
+
return {
|
171 |
+
"status": "error",
|
172 |
+
"error": str(e),
|
173 |
+
"message": "Failed to parse query"
|
174 |
+
}
|
175 |
+
|
176 |
+
# Test the parser
|
177 |
+
if __name__ == "__main__":
|
178 |
+
# Test cases
|
179 |
+
test_queries = [
|
180 |
+
"Show me emails from dev agarwal last week",
|
181 |
+
"emails from amazon in the last month",
|
182 |
+
"get [email protected] emails yesterday",
|
183 |
+
"emails from new person last 3 days"
|
184 |
+
]
|
185 |
+
|
186 |
+
for query in test_queries:
|
187 |
+
print(f"\nQuery: {query}")
|
188 |
+
result = parse_email_query(query)
|
189 |
+
print(f"Result: {json.dumps(result, indent=2)}")
|
server/routes.py
ADDED
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
FastAPI Routes for Email Query System
|
4 |
+
"""
|
5 |
+
|
6 |
+
from fastapi import APIRouter, HTTPException
|
7 |
+
from pydantic import BaseModel, EmailStr
|
8 |
+
from typing import List, Dict, Optional
|
9 |
+
import json
|
10 |
+
|
11 |
+
# Import our modules
|
12 |
+
from query_parser import parse_email_query, store_name_email_mapping
|
13 |
+
from email_scraper import scrape_emails_from_sender
|
14 |
+
|
15 |
+
router = APIRouter()
|
16 |
+
|
17 |
+
# Pydantic models
|
18 |
+
class NaturalQuery(BaseModel):
|
19 |
+
query: str
|
20 |
+
|
21 |
+
class EmailMappingInput(BaseModel):
|
22 |
+
name: str
|
23 |
+
email: EmailStr
|
24 |
+
|
25 |
+
class EmailResponse(BaseModel):
|
26 |
+
date: str
|
27 |
+
time: str
|
28 |
+
subject: str
|
29 |
+
content: str
|
30 |
+
message_id: str
|
31 |
+
|
32 |
+
class QueryParseResponse(BaseModel):
|
33 |
+
status: str
|
34 |
+
sender_intent: Optional[str] = None
|
35 |
+
resolved_email: Optional[str] = None
|
36 |
+
start_date: Optional[str] = None
|
37 |
+
end_date: Optional[str] = None
|
38 |
+
message: str
|
39 |
+
error: Optional[str] = None
|
40 |
+
|
41 |
+
class EmailsResponse(BaseModel):
|
42 |
+
status: str
|
43 |
+
sender_intent: str
|
44 |
+
resolved_email: str
|
45 |
+
start_date: str
|
46 |
+
end_date: str
|
47 |
+
total_emails: int
|
48 |
+
emails: List[EmailResponse]
|
49 |
+
message: str
|
50 |
+
|
51 |
+
@router.post("/parse_query", response_model=QueryParseResponse)
|
52 |
+
def parse_email_query_endpoint(input_data: NaturalQuery):
|
53 |
+
"""
|
54 |
+
Parse natural language query to extract intent and dates
|
55 |
+
"""
|
56 |
+
try:
|
57 |
+
result = parse_email_query(input_data.query)
|
58 |
+
return QueryParseResponse(**result)
|
59 |
+
except Exception as e:
|
60 |
+
raise HTTPException(status_code=400, detail=f"Query parsing failed: {str(e)}")
|
61 |
+
|
62 |
+
@router.post("/add_email_mapping")
|
63 |
+
def add_email_mapping(mapping: EmailMappingInput):
|
64 |
+
"""
|
65 |
+
Add new name to email mapping
|
66 |
+
"""
|
67 |
+
try:
|
68 |
+
store_name_email_mapping(mapping.name, mapping.email)
|
69 |
+
return {
|
70 |
+
"status": "success",
|
71 |
+
"message": f"Mapping added: '{mapping.name}' → '{mapping.email}'"
|
72 |
+
}
|
73 |
+
except Exception as e:
|
74 |
+
raise HTTPException(status_code=400, detail=f"Failed to add mapping: {str(e)}")
|
75 |
+
|
76 |
+
@router.post("/get_emails", response_model=EmailsResponse)
|
77 |
+
def get_emails_from_query(input_data: NaturalQuery):
|
78 |
+
"""
|
79 |
+
Complete flow: Parse query → Resolve email → Scrape emails
|
80 |
+
"""
|
81 |
+
try:
|
82 |
+
# Step 1: Parse the query
|
83 |
+
parsed_result = parse_email_query(input_data.query)
|
84 |
+
|
85 |
+
if parsed_result["status"] == "need_email_input":
|
86 |
+
raise HTTPException(
|
87 |
+
status_code=400,
|
88 |
+
detail={
|
89 |
+
"type": "need_email_input",
|
90 |
+
"sender_intent": parsed_result["sender_intent"],
|
91 |
+
"message": parsed_result["message"]
|
92 |
+
}
|
93 |
+
)
|
94 |
+
elif parsed_result["status"] == "error":
|
95 |
+
raise HTTPException(status_code=400, detail=parsed_result["message"])
|
96 |
+
|
97 |
+
# Step 2: Scrape emails
|
98 |
+
emails = scrape_emails_from_sender(
|
99 |
+
parsed_result["resolved_email"],
|
100 |
+
parsed_result["start_date"],
|
101 |
+
parsed_result["end_date"]
|
102 |
+
)
|
103 |
+
|
104 |
+
# Step 3: Format response
|
105 |
+
email_responses = [
|
106 |
+
EmailResponse(
|
107 |
+
date=email["date"],
|
108 |
+
time=email["time"],
|
109 |
+
subject=email["subject"],
|
110 |
+
content=email["content"],
|
111 |
+
message_id=email["message_id"]
|
112 |
+
)
|
113 |
+
for email in emails
|
114 |
+
]
|
115 |
+
|
116 |
+
return EmailsResponse(
|
117 |
+
status="success",
|
118 |
+
sender_intent=parsed_result["sender_intent"],
|
119 |
+
resolved_email=parsed_result["resolved_email"],
|
120 |
+
start_date=parsed_result["start_date"],
|
121 |
+
end_date=parsed_result["end_date"],
|
122 |
+
total_emails=len(emails),
|
123 |
+
emails=email_responses,
|
124 |
+
message=f"Found {len(emails)} emails from {parsed_result['resolved_email']}"
|
125 |
+
)
|
126 |
+
|
127 |
+
except HTTPException:
|
128 |
+
raise
|
129 |
+
except Exception as e:
|
130 |
+
raise HTTPException(status_code=500, detail=f"Email retrieval failed: {str(e)}")
|
131 |
+
|
132 |
+
@router.get("/view_mappings")
|
133 |
+
def view_name_mappings():
|
134 |
+
"""
|
135 |
+
View all stored name to email mappings
|
136 |
+
"""
|
137 |
+
try:
|
138 |
+
from query_parser import _load_name_mapping
|
139 |
+
mappings = _load_name_mapping()
|
140 |
+
return {
|
141 |
+
"status": "success",
|
142 |
+
"total_mappings": len(mappings),
|
143 |
+
"mappings": mappings
|
144 |
+
}
|
145 |
+
except Exception as e:
|
146 |
+
raise HTTPException(status_code=500, detail=f"Failed to load mappings: {str(e)}")
|
147 |
+
|
148 |
+
@router.get("/health")
|
149 |
+
def health_check():
|
150 |
+
"""
|
151 |
+
Health check endpoint
|
152 |
+
"""
|
153 |
+
return {
|
154 |
+
"status": "healthy",
|
155 |
+
"message": "Email query system is running"
|
156 |
+
}
|
157 |
+
|
158 |
+
# For testing - manual endpoint to add mapping and then query
|
159 |
+
@router.post("/complete_flow")
|
160 |
+
def complete_email_flow(input_data: dict):
|
161 |
+
"""
|
162 |
+
Test endpoint for complete flow with optional mapping
|
163 |
+
Expected input:
|
164 |
+
{
|
165 |
+
"query": "emails from john last week",
|
166 |
+
"mapping": {"name": "john", "email": "[email protected]"} # optional
|
167 |
+
}
|
168 |
+
"""
|
169 |
+
try:
|
170 |
+
query = input_data.get("query")
|
171 |
+
mapping = input_data.get("mapping")
|
172 |
+
|
173 |
+
if not query:
|
174 |
+
raise HTTPException(status_code=400, detail="Query is required")
|
175 |
+
|
176 |
+
# Add mapping if provided
|
177 |
+
if mapping:
|
178 |
+
store_name_email_mapping(mapping["name"], mapping["email"])
|
179 |
+
|
180 |
+
# Parse and get emails
|
181 |
+
parsed_result = parse_email_query(query)
|
182 |
+
|
183 |
+
if parsed_result["status"] == "need_email_input":
|
184 |
+
return {
|
185 |
+
"status": "need_mapping",
|
186 |
+
"message": parsed_result["message"],
|
187 |
+
"sender_intent": parsed_result["sender_intent"]
|
188 |
+
}
|
189 |
+
|
190 |
+
# Get emails
|
191 |
+
emails = scrape_emails_from_sender(
|
192 |
+
parsed_result["resolved_email"],
|
193 |
+
parsed_result["start_date"],
|
194 |
+
parsed_result["end_date"]
|
195 |
+
)
|
196 |
+
|
197 |
+
return {
|
198 |
+
"status": "success",
|
199 |
+
"query": query,
|
200 |
+
"parsed": parsed_result,
|
201 |
+
"total_emails": len(emails),
|
202 |
+
"emails": emails[:5] # Return first 5 emails
|
203 |
+
}
|
204 |
+
|
205 |
+
except Exception as e:
|
206 |
+
raise HTTPException(status_code=500, detail=str(e))
|