feat: DentaQuest eligibility — PLAN_NOT_ACCEPTED status, DOB save, no retries

- Add PLAN_NOT_ACCEPTED to PatientStatus enum (prisma schema + db push)
- Selenium: return "plan not accepted" eligibility text instead of collapsing to inactive
- Backend processor: map "plan not accepted" → PLAN_NOT_ACCEPTED, fix insuranceProvider label
- _shared.ts: save DOB for existing patients when field is currently empty
- Frontend: show amber "Plan Not Accepted" badge in patient table and detail panel
- patient-form.tsx: display "Plan Not Accepted" label in status dropdown
- BullMQ: set attempts=1 (no retry on selenium failure)
- DDMA: remove first/last name from search (member ID + DOB only)
- patient-types.ts: allow alphanumeric insurance IDs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gitead
2026-04-17 23:47:50 -04:00
parent f5ec4a1480
commit 4505d5db85
12 changed files with 141 additions and 104 deletions

View File

@@ -305,65 +305,40 @@ class AutomationDentaQuestEligibilityCheck:
print(f"[DentaQuest step1] Error filling {field_name}: {e}")
return False
# 1. Select Provider from dropdown (required field)
# 1. Select Location from dropdown (required field before search)
try:
print("[DentaQuest step1] Selecting Provider...")
# Try to find and click Provider dropdown
provider_selectors = [
"//label[contains(text(),'Provider')]/following-sibling::*//div[contains(@class,'select')]",
"//div[contains(@data-testid,'provider')]//div[contains(@class,'select')]",
"//*[@aria-label='Provider']",
"//select[contains(@name,'provider') or contains(@id,'provider')]",
"//div[contains(@class,'provider')]//input",
"//label[contains(text(),'Provider')]/..//div[contains(@class,'control')]"
]
provider_clicked = False
for selector in provider_selectors:
print("[DentaQuest step1] Selecting Location...")
# Click the Location dropdown button (stable data-testid)
location_clicked = False
try:
trigger = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.XPATH, '//button[@data-testid="member-search_location_select-btn"]'))
)
trigger.click()
print("[DentaQuest step1] Clicked location dropdown button")
time.sleep(0.5)
location_clicked = True
except TimeoutException:
print("[DentaQuest step1] Warning: Location button not found by data-testid")
if location_clicked:
# Wait for options to appear and click the first one (role="option")
try:
provider_dropdown = WebDriverWait(self.driver, 3).until(
EC.element_to_be_clickable((By.XPATH, selector))
first_option = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.XPATH, "(//li[@role='option'])[1]"))
)
provider_dropdown.click()
print(f"[DentaQuest step1] Clicked provider dropdown with selector: {selector}")
time.sleep(0.5)
provider_clicked = True
break
opt_text = first_option.get_attribute("aria-label") or first_option.text.strip()
first_option.click()
print(f"[DentaQuest step1] Selected location: {opt_text[:60]}")
time.sleep(0.3)
except TimeoutException:
continue
if provider_clicked:
# Select first available provider option
option_selectors = [
"//div[contains(@class,'option') and not(contains(@class,'disabled'))]",
"//li[contains(@class,'option')]",
"//option[not(@disabled)]",
"//*[@role='option']"
]
for opt_selector in option_selectors:
try:
options = self.driver.find_elements(By.XPATH, opt_selector)
if options:
# Select first non-placeholder option
for opt in options:
opt_text = opt.text.strip()
if opt_text and "select" not in opt_text.lower():
opt.click()
print(f"[DentaQuest step1] Selected provider: {opt_text}")
break
break
except:
continue
# Close dropdown if still open
ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
time.sleep(0.3)
print("[DentaQuest step1] Warning: Location options did not appear")
else:
print("[DentaQuest step1] Warning: Could not find Provider dropdown")
print("[DentaQuest step1] Warning: Could not find Location dropdown trigger")
except Exception as e:
print(f"[DentaQuest step1] Error selecting provider: {e}")
print(f"[DentaQuest step1] Error selecting location: {e}")
time.sleep(0.3)
@@ -501,29 +476,26 @@ class AutomationDentaQuestEligibilityCheck:
if self.memberId:
foundMemberId = self.memberId
# Extract eligibility status
status_selectors = [
"(//tbody//tr)[1]//a[contains(@href, 'eligibility')]",
"//a[contains(@href,'eligibility')]",
"//*[contains(@class,'status')]",
"//*[contains(text(),'Active') or contains(text(),'Inactive') or contains(text(),'Eligible')]"
]
for selector in status_selectors:
try:
status_elem = self.driver.find_element(By.XPATH, selector)
status_text = status_elem.text.strip().lower()
if status_text:
print(f"[DentaQuest step2] Found status with selector '{selector}': {status_text}")
if "active" in status_text or "eligible" in status_text:
eligibilityText = "active"
break
elif "inactive" in status_text or "ineligible" in status_text:
eligibilityText = "inactive"
break
except:
continue
# Extract eligibility status from first result row
try:
status_elem = self.driver.find_element(By.XPATH,
"(//tbody//tr)[1]//*[self::span or self::a or self::div]["
"contains(text(),'Active') or contains(text(),'Inactive') or "
"contains(text(),'Eligible') or contains(text(),'Ineligible') or "
"contains(text(),'Plan not accepted') or contains(text(),'Not eligible')"
"]"
)
status_text = status_elem.text.strip().lower()
print(f"[DentaQuest step2] Found eligibility status: '{status_text}'")
if "active" in status_text or ("eligible" in status_text and "ineligible" not in status_text):
eligibilityText = "active"
elif "plan not accepted" in status_text:
eligibilityText = "plan not accepted"
else:
eligibilityText = "inactive"
except Exception as e:
print(f"[DentaQuest step2] Could not find eligibility status: {e}")
print(f"[DentaQuest step2] Final eligibility status: {eligibilityText}")
# 2) Find the patient detail link and navigate DIRECTLY to it
@@ -685,6 +657,11 @@ class AutomationDentaQuestEligibilityCheck:
if not patientName:
print("[DentaQuest step2] Could not extract patient name")
else:
# Strip any trailing date (e.g. "PRINCILLA WALKER 01/09/2026")
import re as _re
patientName = _re.sub(r'\s*\d{1,2}/\d{1,2}/\d{2,4}\s*$', '', patientName).strip()
# Also strip "DOB: ..." prefix/suffix
patientName = _re.sub(r'\s*DOB[:\s]*\d{1,2}/\d{1,2}/\d{2,4}\s*', '', patientName, flags=_re.IGNORECASE).strip()
print(f"[DentaQuest step2] Patient name: {patientName}")
# Wait for page to fully load before generating PDF
@@ -721,14 +698,14 @@ class AutomationDentaQuestEligibilityCheck:
f.write(pdf_data)
print(f"[DentaQuest step2] PDF saved: {pdf_path}")
# Close the browser window after PDF generation
# Close browser after PDF (session preserved in profile)
try:
from dentaquest_browser_manager import get_browser_manager
get_browser_manager().quit_driver()
print("[DentaQuest step2] Browser closed")
except Exception as e:
print(f"[DentaQuest step2] Error closing browser: {e}")
output = {
"status": "success",
"eligibility": eligibilityText,