import os import json import logging import traceback import time import psutil import polars as pl from datetime import datetime from dotenv import load_dotenv from fastapi import FastAPI, UploadFile, File, Form, HTTPException from fastapi.middleware.cors import CORSMiddleware load_dotenv() app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) # Vercel filesystem is read-only except for /tmp IS_VERCEL = "VERCEL" in os.environ UPLOAD_ROOT = "/tmp/uploads" if IS_VERCEL else "uploads" EXPORT_ROOT = "/tmp/exports" if IS_VERCEL else "exports" def get_dated_path(root): path = os.path.join(root, datetime.now().strftime("%Y-%m-%d")) os.makedirs(path, exist_ok=True) return path @app.post("/upload") async def upload_file(file: UploadFile = File(...)): try: dir_path = get_dated_path(UPLOAD_ROOT) ext = os.path.splitext(file.filename)[1] now = datetime.now() new_filename = f"{now.strftime('%Y%m%d_%H%M%S')}_{int(now.timestamp())}{ext}" file_path = os.path.join(dir_path, new_filename) content = await file.read() with open(file_path, "wb") as f: f.write(content) df = pl.read_excel(file_path) if new_filename.endswith(('.xlsx', '.xls')) else pl.read_csv(file_path) return {"filename": new_filename, "path": file_path, "columns": df.columns, "total_rows": len(df)} except Exception as e: logging.error(traceback.format_exc()) raise HTTPException(status_code=400, detail=str(e)) @app.post("/filter") async def filter_data(file_path: str = Form(...), filters: str = Form(...), sorts: str = Form(...)): start_time = time.time() try: process = psutil.Process(os.getpid()) df = pl.read_excel(file_path) if file_path.endswith(('.xlsx', '.xls')) else pl.read_csv(file_path) # Filtering for f in json.loads(filters): col, op, val = f['col'], f['op'], f['val'] if not val: continue selector = pl.col(col) if op == "=": df = df.filter(selector.cast(pl.Utf8) == str(val)) elif op == "!=": df = df.filter(selector.cast(pl.Utf8) != str(val)) elif op == "like": df = df.filter(selector.cast(pl.Utf8).str.contains(str(val))) # Sorting sort_list = json.loads(sorts) if sort_list: df = df.sort([s['col'] for s in sort_list], descending=[s['desc'] for s in sort_list]) if len(df) > 0: if "S.No" in df.columns: df = df.drop("S.No") df = df.insert_column(0, pl.Series("S.No", list(range(1, len(df) + 1)))) return { "data": df.to_dicts(), "performance": { "execution_time": f"{round(time.time() - start_time, 4)}s", "memory_usage": f"{round(process.memory_info().rss / (1024 * 1024), 2)} MB", "cpu_utilization": f"{psutil.cpu_percent()}%", "physical_cores": psutil.cpu_count(logical=False), "logical_cores": psutil.cpu_count(logical=True), "total_records": len(df) } } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @app.post("/export") async def export_data(data: str = Form(...)): try: df = pl.from_dicts(json.loads(data)) dir_path = get_dated_path(EXPORT_ROOT) path = os.path.join(dir_path, f"export_{datetime.now().strftime('%H%M%S')}.xlsx") df.write_excel(path) return {"path": path} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) if __name__ == "__main__": import uvicorn uvicorn.run(app, host=os.getenv("BACKEND_HOST", "172.18.177.164"), port=int(os.getenv("BACKEND_PORT", 8000)))