learnonix commited on
Commit
b1bf651
Β·
verified Β·
1 Parent(s): 167b129

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +265 -0
app.py ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import logging
4
+ import duckdb
5
+ from fastapi import FastAPI, HTTPException, Query
6
+ from fastapi.responses import JSONResponse
7
+ from datetime import datetime, timedelta
8
+ from dotenv import load_dotenv
9
+ from typing import Optional
10
+ import gradio as gr
11
+ import spaces # πŸ”₯ ZeroGPU ke liye
12
+
13
+ # ========== LOGGING ==========
14
+ logging.basicConfig(
15
+ level=logging.INFO,
16
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
17
+ )
18
+ logger = logging.getLogger(__name__)
19
+ logger.info(f"🐍 Python version: {sys.version}")
20
+
21
+ # ========== LOAD ENV ==========
22
+ load_dotenv()
23
+
24
+ # ========== CONFIG ==========
25
+ API_KEY = os.getenv("API_KEY", "@Aerivue")
26
+ DATASET_URL = os.getenv("DATASET_URL", "https://huggingface.co/datasets/learnonix/icmr/resolve/main/icmr.parquet")
27
+ DEPLOYMENT_DATE = datetime.strptime(os.getenv("DEPLOYMENT_DATE", "2026-06-27"), "%Y-%m-%d")
28
+ EXPIRY_DAYS = int(os.getenv("EXPIRY_DAYS", 21))
29
+ MEMORY_LIMIT = os.getenv("MEMORY_LIMIT", "400MB")
30
+
31
+ def is_api_valid():
32
+ return datetime.now() < DEPLOYMENT_DATE + timedelta(days=EXPIRY_DAYS)
33
+
34
+ # ========== FASTAPI APP ==========
35
+ app = FastAPI(title="ICMR Data Search API - ZeroGPU", version="3.0.0")
36
+
37
+ # ========== GLOBALS ==========
38
+ con = None
39
+ TABLE_EXISTS = False
40
+ TABLE_LOADING = False
41
+
42
+ # ========== STARTUP (CPU PAR) ==========
43
+ @app.on_event("startup")
44
+ async def startup_event():
45
+ global con, TABLE_EXISTS, TABLE_LOADING
46
+ logger.info("🟒 Initializing DuckDB on CPU...")
47
+
48
+ try:
49
+ con = duckdb.connect()
50
+ con.execute(f"PRAGMA memory_limit='{MEMORY_LIMIT}'")
51
+ con.execute("SET enable_object_cache=true")
52
+ con.execute("INSTALL httpfs; LOAD httpfs;")
53
+
54
+ TABLE_LOADING = True
55
+ logger.info("πŸ“₯ Loading dataset (CPU)...")
56
+
57
+ con.execute(f"""
58
+ CREATE OR REPLACE TABLE icmr_data AS
59
+ SELECT * FROM read_parquet('{DATASET_URL}')
60
+ """)
61
+ con.execute("CREATE INDEX IF NOT EXISTS idx_phone ON icmr_data(phoneNumber)")
62
+ con.execute("CREATE INDEX IF NOT EXISTS idx_aadhar ON icmr_data(aadharNumber)")
63
+
64
+ count = con.execute("SELECT COUNT(*) FROM icmr_data").fetchone()[0]
65
+ TABLE_EXISTS = True
66
+ logger.info(f"βœ… Dataset loaded! Rows: {count:,}")
67
+
68
+ except Exception as e:
69
+ logger.error(f"❌ Dataset load failed: {e}")
70
+ TABLE_EXISTS = False
71
+ finally:
72
+ TABLE_LOADING = False
73
+
74
+ # ========== FASTAPI ROUTES (CPU) ==========
75
+ @app.get("/")
76
+ async def home(key: str = Query(...)):
77
+ if key != API_KEY:
78
+ raise HTTPException(401, "Invalid API key")
79
+ return {
80
+ "status": "success",
81
+ "message": "API Live with ZeroGPU!",
82
+ "data_loaded": TABLE_EXISTS,
83
+ "hardware": "CPU + ZeroGPU (on-demand)",
84
+ "developer": {"name": "@Aerivue", "team": "TEAM DARK KNIGHT APIs"}
85
+ }
86
+
87
+ @app.get("/search")
88
+ async def search_data(
89
+ key: str = Query(...),
90
+ number: Optional[str] = Query(None),
91
+ aadhar: Optional[str] = Query(None),
92
+ q: Optional[str] = Query(None)
93
+ ):
94
+ """Search endpoint - redirects to GPU via Gradio function"""
95
+ if key != API_KEY:
96
+ raise HTTPException(401, "Invalid API key")
97
+ if not is_api_valid():
98
+ raise HTTPException(403, "API expired")
99
+ if not number and not aadhar and not q:
100
+ raise HTTPException(400, "Provide number, aadhar, or q")
101
+
102
+ if TABLE_LOADING:
103
+ return {"status": "error", "message": "Dataset loading, please wait..."}
104
+ if not TABLE_EXISTS:
105
+ return {"status": "error", "message": "Dataset not available"}
106
+
107
+ search_term = q or number or aadhar
108
+ logger.info(f"πŸ” Searching for: {search_term} (via GPU)")
109
+
110
+ # πŸ”₯ Call GPU function
111
+ result = search_dataset_gpu(search_term)
112
+
113
+ if not result:
114
+ return {"status": "error", "message": f"'{search_term}' not found"}
115
+
116
+ return {
117
+ "status": "success",
118
+ "data": result,
119
+ "hardware": "GPU (ZeroGPU)"
120
+ }
121
+
122
+ @app.get("/stats")
123
+ async def stats(key: str = Query(...)):
124
+ if key != API_KEY:
125
+ raise HTTPException(401, "Invalid API key")
126
+ row_count = 0
127
+ if TABLE_EXISTS:
128
+ row_count = con.execute("SELECT COUNT(*) FROM icmr_data").fetchone()[0]
129
+ return {
130
+ "status": "success",
131
+ "stats": {
132
+ "data_loaded": TABLE_EXISTS,
133
+ "total_rows": row_count,
134
+ "expires_on": (DEPLOYMENT_DATE + timedelta(days=EXPIRY_DAYS)).strftime("%Y-%m-%d"),
135
+ "gpu_enabled": True,
136
+ "memory_limit": MEMORY_LIMIT
137
+ },
138
+ "developer": {"name": "@Aerivue", "team": "TEAM DARK KNIGHT APIs"}
139
+ }
140
+
141
+ # ========== πŸ”₯ ZEROGPU SEARCH FUNCTION ==========
142
+ @spaces.GPU(duration=20) # 20 seconds GPU time per call
143
+ def search_dataset_gpu(search_term: str):
144
+ """GPU-accelerated search function"""
145
+ global con
146
+
147
+ logger.info(f"🟣 GPU Activated for search: {search_term}")
148
+
149
+ query = """
150
+ SELECT name, fathersName, phoneNumber, aadharNumber,
151
+ address, district, pincode, state, town
152
+ FROM icmr_data
153
+ WHERE phoneNumber = ?
154
+ OR aadharNumber = ?
155
+ OR LOWER(name) LIKE LOWER(?)
156
+ OR LOWER(district) LIKE LOWER(?)
157
+ LIMIT 5
158
+ """
159
+
160
+ like_term = f"%{search_term}%"
161
+ result = con.execute(query, [
162
+ search_term,
163
+ search_term,
164
+ like_term,
165
+ like_term
166
+ ]).fetchall()
167
+
168
+ if not result:
169
+ return None
170
+
171
+ columns = [desc[0] for desc in con.description]
172
+ return [dict(zip(columns, row)) for row in result]
173
+
174
+ # ========== GRADIO INTERFACE ==========
175
+ def gradio_search(key: str, search_term: str):
176
+ """Gradio interface with GPU support"""
177
+ if not key or not search_term:
178
+ return "❌ Please provide both API key and search term"
179
+
180
+ if key != API_KEY:
181
+ return "❌ Invalid API key"
182
+
183
+ if TABLE_LOADING:
184
+ return "⏳ Dataset is loading, please wait 30-60 seconds..."
185
+
186
+ if not TABLE_EXISTS:
187
+ return "❌ Dataset not available. Check logs."
188
+
189
+ try:
190
+ # πŸ”₯ This call goes to GPU
191
+ results = search_dataset_gpu(search_term)
192
+
193
+ if not results:
194
+ return f"❌ '{search_term}' not found in database"
195
+
196
+ output = "βœ… **Search Results (GPU Accelerated):**\n\n"
197
+ for i, data in enumerate(results, 1):
198
+ output += f"**Result {i}**\n"
199
+ output += f"πŸ‘€ **Name:** {data.get('name', 'N/A')}\n"
200
+ output += f"πŸ“ž **Phone:** {data.get('phoneNumber', 'N/A')}\n"
201
+ output += f"πŸ†” **Aadhar:** {data.get('aadharNumber', 'N/A')}\n"
202
+ output += f"🏠 **Address:** {data.get('address', 'N/A')}\n"
203
+ output += f"πŸ›οΈ **District:** {data.get('district', 'N/A')}\n"
204
+ output += f"πŸ“ **State:** {data.get('state', 'N/A')}\n"
205
+ output += "---\n"
206
+
207
+ output += f"\n⚑ Powered by ZeroGPU (H200)"
208
+ return output
209
+
210
+ except Exception as e:
211
+ logger.error(f"GPU Search error: {e}")
212
+ return f"❌ Error: {str(e)}"
213
+
214
+ # Create Gradio interface
215
+ demo = gr.Interface(
216
+ fn=gradio_search,
217
+ inputs=[
218
+ gr.Textbox(
219
+ label="πŸ”‘ API Key",
220
+ placeholder="Enter your API key",
221
+ type="password",
222
+ value=""
223
+ ),
224
+ gr.Textbox(
225
+ label="πŸ” Search Term",
226
+ placeholder="Phone, Aadhar, Name, or District",
227
+ value=""
228
+ )
229
+ ],
230
+ outputs=gr.Textbox(label="πŸ“Š Results", lines=20),
231
+ title="πŸ” ICMR Data Search - ZeroGPU Edition",
232
+ description="Search ICMR dataset using GPU acceleration",
233
+ examples=[
234
+ ["@Aerivue", "9876543210"],
235
+ ["@Aerivue", "Rahul"],
236
+ ["@Aerivue", "Mumbai"],
237
+ ["@Aerivue", "Delhi"]
238
+ ],
239
+ cache_examples=False # Keep False for ZeroGPU
240
+ )
241
+
242
+ # ========== MOUNT GRADIO ON FASTAPI ==========
243
+ app = gr.mount_gradio_app(app, demo, path="/")
244
+
245
+ # ========== HEALTH CHECK ==========
246
+ @app.get("/health")
247
+ async def health():
248
+ return {
249
+ "status": "healthy" if TABLE_EXISTS else "loading",
250
+ "data_loaded": TABLE_EXISTS,
251
+ "data_loading": TABLE_LOADING,
252
+ "database_connected": con is not None,
253
+ "gpu_available": True
254
+ }
255
+
256
+ # ========== RUN ==========
257
+ if __name__ == "__main__":
258
+ import uvicorn
259
+ port = int(os.getenv("PORT", 7860))
260
+ uvicorn.run(
261
+ app,
262
+ host="0.0.0.0",
263
+ port=port,
264
+ log_level="info"
265
+ )