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>
This commit is contained in:
Gitead
2026-05-25 00:29:04 -04:00
parent cd1381e9c6
commit 1e581c193c
14 changed files with 2100 additions and 95 deletions

View File

@@ -21,6 +21,8 @@ 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
@@ -628,6 +630,90 @@ async def ddma_claim(request: Request):
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
@@ -692,6 +778,10 @@ async def submit_otp(request: Request):
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")
@@ -719,6 +809,10 @@ async def session_status(sid: str):
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":