from fastapi import FastAPI, Request, HTTPException from fastapi.middleware.cors import CORSMiddleware import uvicorn import asyncio from selenium_claimSubmitWorker import AutomationMassHealth from selenium_eligibilityCheckWorker import AutomationMassHealthEligibilityCheck from selenium_claimStatusCheckWorker import AutomationMassHealthClaimStatusCheck from selenium_preAuthWorker import AutomationMassHealthPreAuth import os import time import helpers_ddma_eligibility as hddma from dotenv import load_dotenv load_dotenv() app = FastAPI() # Allow 1 selenium session at a time semaphore = asyncio.Semaphore(1) # Manual counters to track active & queued jobs active_jobs = 0 waiting_jobs = 0 lock = asyncio.Lock() # To safely update counters app.add_middleware( CORSMiddleware, allow_origins=["*"], # Replace with your frontend domain for security allow_methods=["*"], allow_headers=["*"], ) # Endpoint: 1 — Start the automation of submitting Claim. @app.post("/claimsubmit") async def start_workflow(request: Request): global active_jobs, waiting_jobs data = await request.json() async with lock: waiting_jobs += 1 async with semaphore: async with lock: waiting_jobs -= 1 active_jobs += 1 try: bot = AutomationMassHealth(data) result = bot.main_workflow("https://providers.massdhp.com/providers_login.asp") if result.get("status") != "success": return {"status": "error", "message": result.get("message")} return result except Exception as e: return {"status": "error", "message": str(e)} finally: async with lock: active_jobs -= 1 # Endpoint: 2 — Start the automation of cheking eligibility @app.post("/eligibility-check") async def start_workflow(request: Request): global active_jobs, waiting_jobs data = await request.json() async with lock: waiting_jobs += 1 async with semaphore: async with lock: waiting_jobs -= 1 active_jobs += 1 try: bot = AutomationMassHealthEligibilityCheck(data) result = bot.main_workflow("https://providers.massdhp.com/providers_login.asp") if result.get("status") != "success": return {"status": "error", "message": result.get("message")} return result except Exception as e: return {"status": "error", "message": str(e)} finally: async with lock: active_jobs -= 1 # Endpoint: 3 — Start the automation of cheking claim status @app.post("/claim-status-check") async def start_workflow(request: Request): global active_jobs, waiting_jobs data = await request.json() async with lock: waiting_jobs += 1 async with semaphore: async with lock: waiting_jobs -= 1 active_jobs += 1 try: bot = AutomationMassHealthClaimStatusCheck(data) result = bot.main_workflow("https://providers.massdhp.com/providers_login.asp") if result.get("status") != "success": return {"status": "error", "message": result.get("message")} return result except Exception as e: return {"status": "error", "message": str(e)} finally: async with lock: active_jobs -= 1 # Endpoint: 4 — Start the automation of cheking claim pre auth @app.post("/claim-pre-auth") async def start_workflow(request: Request): global active_jobs, waiting_jobs data = await request.json() async with lock: waiting_jobs += 1 async with semaphore: async with lock: waiting_jobs -= 1 active_jobs += 1 try: bot = AutomationMassHealthPreAuth(data) result = bot.main_workflow("https://providers.massdhp.com/providers_login.asp") if result.get("status") != "success": return {"status": "error", "message": result.get("message")} return result except Exception as e: return {"status": "error", "message": str(e)} finally: async with lock: active_jobs -= 1 # Endpoint:5 - DDMA eligibility (background, OTP) async def _ddma_worker_wrapper(sid: str, data: dict, url: str): """ Background worker that: - acquires semaphore (to keep 1 selenium at a time), - updates active/queued counters, - runs the DDMA flow via helpers.start_ddma_run. """ global active_jobs, waiting_jobs async with semaphore: async with lock: waiting_jobs -= 1 active_jobs += 1 try: await hddma.start_ddma_run(sid, data, url) finally: async with lock: active_jobs -= 1 @app.post("/ddma-eligibility") async def ddma_eligibility(request: Request): """ Starts a DDMA eligibility session in the background. Body: { "data": { ... }, "url"?: string } Returns: { status: "started", session_id: "" } """ global waiting_jobs body = await request.json() data = body.get("data", {}) # create session sid = hddma.make_session_entry() hddma.sessions[sid]["type"] = "ddma_eligibility" hddma.sessions[sid]["last_activity"] = time.time() async with lock: waiting_jobs += 1 # run in background (queued under semaphore) asyncio.create_task(_ddma_worker_wrapper(sid, data, url="https://providers.deltadentalma.com/onboarding/start/")) return {"status": "started", "session_id": sid} @app.post("/submit-otp") async def submit_otp(request: Request): """ Body: { "session_id": "", "otp": "123456" } Node / frontend call this when user provides OTP. """ body = await request.json() sid = body.get("session_id") otp = body.get("otp") if not sid or not otp: raise HTTPException(status_code=400, detail="session_id and otp required") res = hddma.submit_otp(sid, otp) if res.get("status") == "error": raise HTTPException(status_code=400, detail=res.get("message")) return res @app.get("/session/{sid}/status") async def session_status(sid: str): s = hddma.get_session_status(sid) if s.get("status") == "not_found": raise HTTPException(status_code=404, detail="session not found") return s # ✅ Status Endpoint @app.get("/status") async def get_status(): async with lock: return { "active_jobs": active_jobs, "queued_jobs": waiting_jobs, "status": "busy" if active_jobs > 0 or waiting_jobs > 0 else "idle" } if __name__ == "__main__": host = os.getenv("HOST") port = int(os.getenv("PORT")) uvicorn.run(app, host=host, port=port)