Files
DentalManagementMH06/apps/SeleniumService/agent.py
Gitead 1e581c193c feat: United/DentalHub claim submission automation and patient list sync
- Add full Selenium automation for United/DentalHub claim submission
  (steps 1-8: login, OTP, patient search, practitioner page, code entry,
  other coverage No, attachments, submit, Status & History PDF)
- Consolidate UnitedDH siteKey to UNITED_SCO throughout app
- Fix procedure date overwrite with Ctrl+A+Delete before typing service date
- Fix OTP popup reliability: emit every poll (no throttle)
- Fix Chrome session persistence: only clear cookies on startup
- Add touchPatient() to storage: claim submission now pushes patient to
  top of list across eligibility, claims, and documents pages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 00:29:04 -04:00

889 lines
29 KiB
Python
Executable File

from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import RedirectResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
import uvicorn
import asyncio
from selenium_claimSubmitWorker import AutomationMassHealthClaimsLogin
from selenium_eligibilityCheckWorker import AutomationMassHealthEligibilityCheck
from selenium_MH_eligibilityHistoryCheckWorker import AutomationMassHealthEligibilityHistoryCheck
from selenium_CMSP_eligibilityHistoryRemainingCheckWorker import AutomationCMSPEligibilityHistoryRemainingCheck
from selenium_claimStatusCheckWorker import AutomationMassHealthClaimStatusCheck
from selenium_preAuthWorker import AutomationMassHealthPreAuth
from selenium_MHPaymentCheckWorker import AutomationMassHealthPaymentCheck
import os
import time
import helpers_ddma_eligibility as hddma
import helpers_deltains_eligibility as hdeltains
import helpers_unitedsco_eligibility as hunitedsco
import helpers_dentaquest_eligibility as hdentaquest
import helpers_cca_eligibility as hcca
import helpers_cca_claim as hcca_claim
import helpers_cca_preauth as hcca_preauth
import helpers_ddma_claim as hddma_claim
import helpers_uniteddh_claim as huniteddh_claim
import helpers_tuftssco_claim as htuftssco_claim
# Import startup session-clear functions
from ddma_browser_manager import clear_ddma_session_on_startup
from deltains_browser_manager import clear_deltains_session_on_startup
from unitedsco_browser_manager import clear_unitedsco_session_on_startup
from dentaquest_browser_manager import clear_dentaquest_session_on_startup
from cca_browser_manager import clear_cca_session_on_startup
from dotenv import load_dotenv
load_dotenv()
# Clear sessions on startup so fresh login is required after PC restart.
print("=" * 50)
print("SELENIUM AGENT STARTING - CLEARING SESSIONS")
print("=" * 50)
clear_ddma_session_on_startup()
clear_deltains_session_on_startup()
clear_unitedsco_session_on_startup()
clear_dentaquest_session_on_startup()
clear_cca_session_on_startup()
print("=" * 50)
print("SESSION CLEAR COMPLETE")
print("=" * 50)
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=["*"],
)
# Mount static files for serving PDFs (must be after middleware)
DOWNLOAD_DIR = os.path.join(os.path.dirname(__file__), "downloads")
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
app.mount("/downloads", StaticFiles(directory=DOWNLOAD_DIR), name="downloads")
# 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 = AutomationMassHealthClaimsLogin(data)
# result = bot.main_workflow("https://provider.masshealth-dental.org/mh_provider_login")
result = bot.run()
if result.get("status") != "success":
return {"status": "error", "message": result.get("message")}
# Convert pdf_path to pdf_url
if result.get("pdf_path"):
filename = os.path.basename(result["pdf_path"])
port = os.getenv("PORT", "5002")
url_host = os.getenv("HOST", "localhost")
result["pdf_url"] = f"http://{url_host}:{port}/downloads/{filename}"
print(f"DEBUG: Generated pdf_url = {result['pdf_url']}")
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://provider.masshealth-dental.org/mh_provider_login")
if result.get("status") != "success":
return {"status": "error", "message": result.get("message")}
# Convert pdf_path to pdf_url
if result.get("pdf_path"):
filename = os.path.basename(result["pdf_path"])
port = os.getenv("PORT", "5002")
url_host = os.getenv("HOST", "localhost")
result["pdf_url"] = f"http://{url_host}:{port}/downloads/{filename}"
print(f"DEBUG: Generated pdf_url = {result['pdf_url']}")
return result
except Exception as e:
return {"status": "error", "message": str(e)}
finally:
async with lock:
active_jobs -= 1
# Endpoint: 2a — MH Eligibility + Service History
@app.post("/mh-eligibility-history-check")
async def mh_eligibility_history_check(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 = AutomationMassHealthEligibilityHistoryCheck(data)
result = bot.main_workflow("https://provider.masshealth-dental.org/mh_provider_login")
if result.get("status") == "error":
return {"status": "error", "message": result.get("message")}
port = os.getenv("PORT", "5002")
url_host = os.getenv("HOST", "localhost")
if result.get("pdf_path"):
filename = os.path.basename(result["pdf_path"])
result["pdf_url"] = f"http://{url_host}:{port}/downloads/{filename}"
if result.get("history_pdf_path"):
filename = os.path.basename(result["history_pdf_path"])
result["history_pdf_url"] = f"http://{url_host}:{port}/downloads/{filename}"
return result
except Exception as e:
return {"status": "error", "message": str(e)}
finally:
async with lock:
active_jobs -= 1
# Endpoint: 2b — CMSP Eligibility + Service History + Accumulator (Remaining)
@app.post("/cmsp-eligibility-history-remaining-check")
async def cmsp_eligibility_history_remaining_check(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 = AutomationCMSPEligibilityHistoryRemainingCheck(data)
result = bot.main_workflow("https://provider.masshealth-dental.org/mh_provider_login")
if result.get("status") == "error":
return {"status": "error", "message": result.get("message")}
port = os.getenv("PORT", "5002")
url_host = os.getenv("HOST", "localhost")
for key, url_key in [
("pdf_path", "pdf_url"),
("history_pdf_path", "history_pdf_url"),
("accumulator_pdf_path", "accumulator_pdf_url"),
]:
if result.get(key):
filename = os.path.basename(result[key])
result[url_key] = f"http://{url_host}:{port}/downloads/{filename}"
return result
except Exception as e:
return {"status": "error", "message": str(e)}
finally:
async with lock:
active_jobs -= 1
# Endpoint: 2.1 — Start the automation for Claims login (open browser and log in)
@app.post("/claims-login")
async def start_claims_login(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 = AutomationMassHealthClaimsLogin(data)
result = bot.run()
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://provider.masshealth-dental.org/mh_provider_login")
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 — Check MassHealth payment for a given claim number
@app.post("/mh-payment-check")
async def mh_payment_check(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 = AutomationMassHealthPaymentCheck(data)
result = bot.main_workflow("https://provider.masshealth-dental.org/mh_provider_login")
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://provider.masshealth-dental.org/mh_provider_login")
if result.get("status") != "success":
return {"status": "error", "message": result.get("message")}
# Convert pdf_path to pdf_url so the frontend can fetch it
if result.get("pdf_path"):
filename = os.path.basename(result["pdf_path"])
port = os.getenv("PORT", "5002")
url_host = os.getenv("HOST", "localhost")
result["pdf_url"] = f"http://{url_host}:{port}/downloads/{filename}"
print(f"DEBUG: Generated pdf_url = {result['pdf_url']}")
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: "<uuid>" }
"""
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}
async def _deltains_worker_wrapper(sid: str, data: dict, url: str):
"""Background worker for DeltaIns — acquires semaphore, updates counters."""
global active_jobs, waiting_jobs
async with semaphore:
async with lock:
waiting_jobs -= 1
active_jobs += 1
try:
await hdeltains.start_deltains_run(sid, data, url)
finally:
async with lock:
active_jobs -= 1
@app.post("/deltains-eligibility")
async def deltains_eligibility(request: Request):
"""
Starts a DeltaIns eligibility session in the background.
Body: { "data": { ... } }
Returns: { status: "started", session_id: "<uuid>" }
"""
global waiting_jobs
body = await request.json()
data = body.get("data", {})
sid = hdeltains.make_session_entry()
hdeltains.sessions[sid]["type"] = "deltains_eligibility"
hdeltains.sessions[sid]["last_activity"] = time.time()
async with lock:
waiting_jobs += 1
asyncio.create_task(_deltains_worker_wrapper(
sid, data,
url="https://www.deltadentalins.com/ciam/login?TARGET=%2Fprovider-tools%2Fv2"
))
return {"status": "started", "session_id": sid}
async def _unitedsco_worker_wrapper(sid: str, data: dict, url: str):
"""Background worker for UnitedSCO — acquires semaphore, updates counters."""
global active_jobs, waiting_jobs
async with semaphore:
async with lock:
waiting_jobs -= 1
active_jobs += 1
try:
await hunitedsco.start_unitedsco_run(sid, data, url)
finally:
async with lock:
active_jobs -= 1
@app.post("/unitedsco-eligibility")
async def unitedsco_eligibility(request: Request):
"""
Starts a UnitedSCO eligibility session in the background.
Body: { "data": { ... } }
Returns: { status: "started", session_id: "<uuid>" }
"""
global waiting_jobs
body = await request.json()
data = body.get("data", {})
sid = hunitedsco.make_session_entry()
hunitedsco.sessions[sid]["type"] = "unitedsco_eligibility"
hunitedsco.sessions[sid]["last_activity"] = time.time()
async with lock:
waiting_jobs += 1
asyncio.create_task(_unitedsco_worker_wrapper(
sid, data,
url="https://app.dentalhub.com/app/login"
))
return {"status": "started", "session_id": sid}
async def _dentaquest_worker_wrapper(sid: str, data: dict, url: str):
"""Background worker for DentaQuest (Tufts SCO) — acquires semaphore, updates counters."""
global active_jobs, waiting_jobs
async with semaphore:
async with lock:
waiting_jobs -= 1
active_jobs += 1
try:
await hdentaquest.start_dentaquest_run(sid, data, url)
finally:
async with lock:
active_jobs -= 1
@app.post("/dentaquest-eligibility")
async def dentaquest_eligibility(request: Request):
"""
Starts a DentaQuest (Tufts SCO) eligibility session in the background.
Body: { "data": { ... } }
Returns: { status: "started", session_id: "<uuid>" }
"""
global waiting_jobs
body = await request.json()
data = body.get("data", {})
sid = hdentaquest.make_session_entry()
hdentaquest.sessions[sid]["type"] = "dentaquest_eligibility"
hdentaquest.sessions[sid]["last_activity"] = time.time()
async with lock:
waiting_jobs += 1
asyncio.create_task(_dentaquest_worker_wrapper(
sid, data,
url="https://providers.dentaquest.com/"
))
return {"status": "started", "session_id": sid}
async def _cca_worker_wrapper(sid: str, data: dict, url: str):
"""Background worker for CCA — acquires semaphore, updates counters. No OTP."""
global active_jobs, waiting_jobs
async with semaphore:
async with lock:
waiting_jobs -= 1
active_jobs += 1
try:
await hcca.start_cca_run(sid, data, url)
finally:
async with lock:
active_jobs -= 1
@app.post("/cca-eligibility")
async def cca_eligibility(request: Request):
"""
Starts a CCA eligibility session in the background (no OTP).
Body: { "data": { ... } }
Returns: { status: "started", session_id: "<uuid>" }
"""
global waiting_jobs
body = await request.json()
data = body.get("data", {})
sid = hcca.make_session_entry()
hcca.sessions[sid]["type"] = "cca_eligibility"
hcca.sessions[sid]["last_activity"] = time.time()
async with lock:
waiting_jobs += 1
asyncio.create_task(_cca_worker_wrapper(
sid, data,
url="https://pwp.sciondental.com/PWP/Landing"
))
return {"status": "started", "session_id": sid}
async def _cca_claim_worker_wrapper(sid: str, data: dict, url: str):
"""Background worker for CCA claim submission."""
global active_jobs, waiting_jobs
async with semaphore:
async with lock:
waiting_jobs -= 1
active_jobs += 1
try:
await hcca_claim.start_cca_claim_run(sid, data, url)
finally:
async with lock:
active_jobs -= 1
@app.post("/cca-claim")
async def cca_claim(request: Request):
"""
Starts a CCA claim submission session in the background.
Logs in, navigates Claims > Submit Claims, opens claim entry page.
Body: { "claim": { "cca_username": "...", "cca_password": "...", ... } }
Returns: { status: "started", session_id: "<uuid>" }
"""
global waiting_jobs
body = await request.json()
sid = hcca_claim.make_session_entry()
hcca_claim.sessions[sid]["type"] = "cca_claim"
hcca_claim.sessions[sid]["last_activity"] = time.time()
async with lock:
waiting_jobs += 1
asyncio.create_task(_cca_claim_worker_wrapper(
sid, body,
url="https://pwp.sciondental.com/PWP/Landing"
))
return {"status": "started", "session_id": sid}
async def _ddma_claim_worker_wrapper(sid: str, data: dict, url: str):
"""Background worker for DDMA claim submission."""
global active_jobs, waiting_jobs
async with semaphore:
async with lock:
waiting_jobs -= 1
active_jobs += 1
try:
await hddma_claim.start_ddma_claim_run(sid, data, url)
finally:
async with lock:
active_jobs -= 1
@app.post("/ddma-claim")
async def ddma_claim(request: Request):
"""
Starts a DDMA claim submission session in the background.
Logs in, searches patient, opens Member Information page, clicks Create claim,
fills service date and procedure code.
Body: { "claim": { "massddmaUsername": "...", "massddmaPassword": "...", ... } }
Returns: { status: "started", session_id: "<uuid>" }
"""
global waiting_jobs
body = await request.json()
sid = hddma_claim.make_session_entry()
hddma_claim.sessions[sid]["type"] = "ddma_claim"
hddma_claim.sessions[sid]["last_activity"] = time.time()
async with lock:
waiting_jobs += 1
asyncio.create_task(_ddma_claim_worker_wrapper(
sid, body,
url="https://providers.deltadentalma.com/onboarding/start/"
))
return {"status": "started", "session_id": sid}
async def _uniteddh_claim_worker_wrapper(sid: str, data: dict, url: str):
"""Background worker for United/DentalHub claim submission."""
global active_jobs, waiting_jobs
async with semaphore:
async with lock:
waiting_jobs -= 1
active_jobs += 1
try:
await huniteddh_claim.start_uniteddh_claim_run(sid, data, url)
finally:
async with lock:
active_jobs -= 1
@app.post("/uniteddh-claim")
async def uniteddh_claim(request: Request):
"""
Starts a United/DentalHub claim submission session in the background.
Logs in, searches patient, opens Member Information page, clicks Create claim,
fills service date and procedure codes.
Body: { "claim": { "uniteddhUsername": "...", "uniteddhPassword": "...", ... } }
Returns: { status: "started", session_id: "<uuid>" }
"""
global waiting_jobs
body = await request.json()
sid = huniteddh_claim.make_session_entry()
huniteddh_claim.sessions[sid]["type"] = "uniteddh_claim"
huniteddh_claim.sessions[sid]["last_activity"] = time.time()
async with lock:
waiting_jobs += 1
asyncio.create_task(_uniteddh_claim_worker_wrapper(
sid, body,
url="https://app.dentalhub.com/app/login"
))
return {"status": "started", "session_id": sid}
async def _tuftssco_claim_worker_wrapper(sid: str, data: dict, url: str):
"""Background worker for Tufts SCO (DentaQuest) claim submission."""
global active_jobs, waiting_jobs
async with semaphore:
async with lock:
waiting_jobs -= 1
active_jobs += 1
try:
await htuftssco_claim.start_tuftssco_claim_run(sid, data, url)
finally:
async with lock:
active_jobs -= 1
@app.post("/tuftssco-claim")
async def tuftssco_claim(request: Request):
"""
Starts a Tufts SCO (DentaQuest) claim submission session in the background.
Logs in, searches patient, opens Member Information page, clicks Create claim,
fills service date and procedure codes.
Body: { "claim": { "dentaquestUsername": "...", "dentaquestPassword": "...", ... } }
Returns: { status: "started", session_id: "<uuid>" }
"""
global waiting_jobs
body = await request.json()
sid = htuftssco_claim.make_session_entry()
htuftssco_claim.sessions[sid]["type"] = "tuftssco_claim"
htuftssco_claim.sessions[sid]["last_activity"] = time.time()
async with lock:
waiting_jobs += 1
asyncio.create_task(_tuftssco_claim_worker_wrapper(
sid, body,
url="https://providers.dentaquest.com/"
))
return {"status": "started", "session_id": sid}
async def _cca_preauth_worker_wrapper(sid: str, data: dict, url: str):
"""Background worker for CCA pre-authorization submission."""
global active_jobs, waiting_jobs
async with semaphore:
async with lock:
waiting_jobs -= 1
active_jobs += 1
try:
await hcca_preauth.start_cca_preauth_run(sid, data, url)
finally:
async with lock:
active_jobs -= 1
@app.post("/cca-preauth")
async def cca_preauth(request: Request):
"""
Starts a CCA pre-authorization session in the background.
Logs in, navigates to Authorization Entry, fills the form and submits.
Body: { "claim": { "cca_username": "...", "cca_password": "...", ... } }
Returns: { status: "started", session_id: "<uuid>" }
"""
global waiting_jobs
body = await request.json()
sid = hcca_preauth.make_session_entry()
hcca_preauth.sessions[sid]["type"] = "cca_preauth"
hcca_preauth.sessions[sid]["last_activity"] = time.time()
async with lock:
waiting_jobs += 1
asyncio.create_task(_cca_preauth_worker_wrapper(
sid, body,
url="https://pwp.sciondental.com/PWP/Landing"
))
return {"status": "started", "session_id": sid}
@app.post("/submit-otp")
async def submit_otp(request: Request):
"""
Body: { "session_id": "<sid>", "otp": "123456" }
Tries each session store in order (CCA has no OTP but included for completeness).
"""
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")
# Try each session store in order
if sid in hddma.sessions:
res = hddma.submit_otp(sid, otp)
elif sid in hdeltains.sessions:
res = hdeltains.submit_otp(sid, otp)
elif sid in hunitedsco.sessions:
res = hunitedsco.submit_otp(sid, otp)
elif sid in hdentaquest.sessions:
res = hdentaquest.submit_otp(sid, otp)
elif sid in hddma_claim.sessions:
res = hddma_claim.submit_otp(sid, otp)
elif sid in huniteddh_claim.sessions:
res = huniteddh_claim.submit_otp(sid, otp)
elif sid in htuftssco_claim.sessions:
res = htuftssco_claim.submit_otp(sid, otp)
else:
raise HTTPException(status_code=404, detail="session not found")
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):
# Try each session store in order
if sid in hddma.sessions:
s = hddma.get_session_status(sid)
elif sid in hdeltains.sessions:
s = hdeltains.get_session_status(sid)
elif sid in hunitedsco.sessions:
s = hunitedsco.get_session_status(sid)
elif sid in hdentaquest.sessions:
s = hdentaquest.get_session_status(sid)
elif sid in hcca.sessions:
s = hcca.get_session_status(sid)
elif sid in hcca_claim.sessions:
s = hcca_claim.get_session_status(sid)
elif sid in hcca_preauth.sessions:
s = hcca_preauth.get_session_status(sid)
elif sid in hddma_claim.sessions:
s = hddma_claim.get_session_status(sid)
elif sid in huniteddh_claim.sessions:
s = huniteddh_claim.get_session_status(sid)
elif sid in htuftssco_claim.sessions:
s = htuftssco_claim.get_session_status(sid)
else:
s = {"status": "not_found"}
if s.get("status") == "not_found":
raise HTTPException(status_code=404, detail="session not found")
return s
# ── Session management endpoints ─────────────────────────────────────────────
@app.post("/clear-ddma-session")
async def clear_ddma_session_endpoint():
"""Clears the DDMA browser session. Call when credentials are deleted or changed."""
try:
clear_ddma_session_on_startup()
return {"status": "success", "message": "DDMA session cleared"}
except Exception as e:
return {"status": "error", "message": str(e)}
@app.post("/clear-deltains-session")
async def clear_deltains_session_endpoint():
"""Clears the DeltaIns browser session. Call when credentials are deleted or changed."""
try:
clear_deltains_session_on_startup()
return {"status": "success", "message": "DeltaIns session cleared"}
except Exception as e:
return {"status": "error", "message": str(e)}
@app.post("/clear-unitedsco-session")
async def clear_unitedsco_session_endpoint():
"""Clears the UnitedSCO browser session. Call when credentials are deleted or changed."""
try:
clear_unitedsco_session_on_startup()
return {"status": "success", "message": "UnitedSCO session cleared"}
except Exception as e:
return {"status": "error", "message": str(e)}
@app.post("/clear-cca-session")
async def clear_cca_session_endpoint():
"""Clears the CCA browser session. Call when credentials are deleted or changed."""
try:
clear_cca_session_on_startup()
return {"status": "success", "message": "CCA session cleared"}
except Exception as e:
return {"status": "error", "message": str(e)}
# ✅ Health Check Endpoint
@app.get("/")
async def health_check():
return {
"status": "ok",
"service": "Selenium Service",
"message": "Service is running"
}
# ✅ 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", "0.0.0.0") # Default to 0.0.0.0 to accept connections from all interfaces
port = int(os.getenv("PORT", "5002")) # Default to 5002
print(f"Starting Selenium service on {host}:{port}")
uvicorn.run(app, host=host, port=port)