Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,9 +4,78 @@ import os
|
|
| 4 |
import pandas as pd
|
| 5 |
from contextlib import asynccontextmanager
|
| 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
# File path (mounted bucket)
|
| 8 |
FILE_PATH = "/data/TGDATA BY DEADLOX P4.parquet"
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
@asynccontextmanager
|
| 11 |
async def lifespan(app: FastAPI):
|
| 12 |
# Startup
|
|
|
|
| 4 |
import pandas as pd
|
| 5 |
from contextlib import asynccontextmanager
|
| 6 |
|
| 7 |
+
# File path (mounted bucket)
|
| 8 |
+
FILE_PATH = "/import duckdb
|
| 9 |
+
from fastapi import FastAPI, Query
|
| 10 |
+
import os
|
| 11 |
+
import pandas as pd
|
| 12 |
+
from contextlib import asynccontextmanager
|
| 13 |
+
|
| 14 |
# File path (mounted bucket)
|
| 15 |
FILE_PATH = "/data/TGDATA BY DEADLOX P4.parquet"
|
| 16 |
|
| 17 |
+
@asynccontextmanager
|
| 18 |
+
async def lifespan(app: FastAPI):
|
| 19 |
+
# Startup
|
| 20 |
+
if os.path.exists(FILE_PATH):
|
| 21 |
+
print(f"✅ File found: {FILE_PATH}")
|
| 22 |
+
size = os.path.getsize(FILE_PATH) / (1024**3)
|
| 23 |
+
print(f"📏 Size: {size:.2f} GB")
|
| 24 |
+
else:
|
| 25 |
+
print(f"❌ File NOT found at {FILE_PATH}")
|
| 26 |
+
yield
|
| 27 |
+
print("Shutting down...")
|
| 28 |
+
|
| 29 |
+
app = FastAPI(lifespan=lifespan)
|
| 30 |
+
con = duckdb.connect()
|
| 31 |
+
|
| 32 |
+
@app.get("/")
|
| 33 |
+
async def root():
|
| 34 |
+
return {
|
| 35 |
+
"message": "Parquet Search API",
|
| 36 |
+
"endpoint": "/search?q=keyword&limit=10",
|
| 37 |
+
"file_available": os.path.exists(FILE_PATH)
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
@app.get("/search")
|
| 41 |
+
async def search(q: str = Query(...), limit: int = 20):
|
| 42 |
+
if not q.strip():
|
| 43 |
+
return {"error": "Missing 'q' parameter"}
|
| 44 |
+
|
| 45 |
+
try:
|
| 46 |
+
# Get column names
|
| 47 |
+
df_cols = con.execute(f"SELECT * FROM '{FILE_PATH}' LIMIT 0").fetchdf()
|
| 48 |
+
columns = list(df_cols.columns)
|
| 49 |
+
|
| 50 |
+
# Build dynamic WHERE clause for all columns
|
| 51 |
+
conditions = [f"CAST(\"{col}\" AS VARCHAR) LIKE '%{q}%'" for col in columns]
|
| 52 |
+
where_clause = " OR ".join(conditions)
|
| 53 |
+
|
| 54 |
+
query = f"SELECT * FROM '{FILE_PATH}' WHERE {where_clause} LIMIT {limit}"
|
| 55 |
+
results = con.execute(query).fetchdf()
|
| 56 |
+
results_dict = results.to_dict(orient='records')
|
| 57 |
+
|
| 58 |
+
# 🔥 FIX: Swap username and phone columns
|
| 59 |
+
for record in results_dict:
|
| 60 |
+
username_val = record.get('username', '')
|
| 61 |
+
phone_val = record.get('phone', '')
|
| 62 |
+
record['username'] = phone_val # Phone number goes to username field
|
| 63 |
+
record['phone'] = username_val # Username/name goes to phone field
|
| 64 |
+
|
| 65 |
+
return {
|
| 66 |
+
"query": q,
|
| 67 |
+
"count": len(results_dict),
|
| 68 |
+
"limit": limit,
|
| 69 |
+
"columns": columns,
|
| 70 |
+
"results": results_dict
|
| 71 |
+
}
|
| 72 |
+
except Exception as e:
|
| 73 |
+
return {"error": str(e)}
|
| 74 |
+
|
| 75 |
+
if __name__ == "__main__":
|
| 76 |
+
import uvicorn
|
| 77 |
+
uvicorn.run(app, host="0.0.0.0", port=7860) BY DEADLOX P4.parquet"
|
| 78 |
+
|
| 79 |
@asynccontextmanager
|
| 80 |
async def lifespan(app: FastAPI):
|
| 81 |
# Startup
|