fix: Tufts SCO location select, simple ext → D7140, sanitize custom CDT aliases
- Tufts SCO claim/preauth: robust location dropdown selection (multiple
option selectors, retries, keyboard fallback, HTML dump on failure)
- CDT lookup: map ext/extraction/simple ext to D7140; ignore dangling
"on/of/for/at" left after stripping tooth number
- Custom CDT aliases: keep only the Dxxxx code ("D7140 #25" → "D7140") and
drop tooth numbers from phrases, on save and at lookup — fixes $0 fee
from code "D7140#25"
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -245,6 +245,93 @@ class AutomationTuftsSCOClaimSubmit:
|
||||
|
||||
# ── Step 1: Search patient (mirrors DentaQuest eligibility step1) ──────────
|
||||
|
||||
def _select_location(self, tag):
|
||||
"""Open the Location dropdown on member search and pick the first option."""
|
||||
btn_xpath = '//button[@data-testid="member-search_location_select-btn"]'
|
||||
option_xpaths = [
|
||||
"//*[@role='listbox']//*[@role='option']",
|
||||
"//li[@role='option']",
|
||||
"//*[@role='option']",
|
||||
"//*[@role='listbox']//li",
|
||||
"//*[@role='menu']//*[@role='menuitem' or @role='menuitemradio']",
|
||||
]
|
||||
|
||||
def js_click(el):
|
||||
self.driver.execute_script("arguments[0].click();", el)
|
||||
|
||||
for attempt in range(3):
|
||||
try:
|
||||
trigger = WebDriverWait(self.driver, 15).until(
|
||||
EC.element_to_be_clickable((By.XPATH, btn_xpath))
|
||||
)
|
||||
except TimeoutException:
|
||||
print(f"[{tag}] Warning: Location dropdown button not found")
|
||||
return False
|
||||
|
||||
before = (trigger.text or "").strip()
|
||||
try:
|
||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", trigger)
|
||||
if attempt == 0:
|
||||
trigger.click()
|
||||
else:
|
||||
js_click(trigger)
|
||||
except Exception:
|
||||
js_click(trigger)
|
||||
print(f"[{tag}] Clicked location dropdown (attempt {attempt + 1})")
|
||||
|
||||
# Wait for any visible option
|
||||
option = None
|
||||
deadline = time.time() + 5
|
||||
while option is None and time.time() < deadline:
|
||||
for xp in option_xpaths:
|
||||
visible = [e for e in self.driver.find_elements(By.XPATH, xp) if e.is_displayed()]
|
||||
if visible:
|
||||
option = visible[0]
|
||||
break
|
||||
if option is None:
|
||||
time.sleep(0.3)
|
||||
|
||||
if option is not None:
|
||||
opt_text = option.get_attribute("aria-label") or option.text.strip()
|
||||
try:
|
||||
option.click()
|
||||
except Exception:
|
||||
js_click(option)
|
||||
time.sleep(0.5)
|
||||
print(f"[{tag}] Selected location: {opt_text[:60]}")
|
||||
return True
|
||||
|
||||
# Keyboard fallback: open/highlight the first option and confirm
|
||||
try:
|
||||
trigger.send_keys(Keys.ARROW_DOWN)
|
||||
time.sleep(0.3)
|
||||
ActionChains(self.driver).send_keys(Keys.ENTER).perform()
|
||||
time.sleep(0.5)
|
||||
after = (self.driver.find_element(By.XPATH, btn_xpath).text or "").strip()
|
||||
if after and after != before:
|
||||
print(f"[{tag}] Selected location via keyboard: {after[:60]}")
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(1)
|
||||
|
||||
# Dump dropdown markup so selector changes on the portal can be diagnosed
|
||||
try:
|
||||
html = self.driver.execute_script(
|
||||
"var lb=document.querySelector('[role=listbox]');"
|
||||
"var b=document.querySelector('[data-testid=\"member-search_location_select-btn\"]');"
|
||||
"return (lb||(b&&b.parentElement.parentElement)||{}).outerHTML||'';"
|
||||
)
|
||||
print(f"[{tag}] Location dropdown HTML: {html[:2000]}")
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
def step1_search_patient(self):
|
||||
"""Navigate to member search and find the patient by Member ID + DOB."""
|
||||
wait = WebDriverWait(self.driver, 30)
|
||||
@@ -276,25 +363,8 @@ class AutomationTuftsSCOClaimSubmit:
|
||||
return f"ERROR: step1 DOB parse failed: {e}"
|
||||
|
||||
# 1. Select Location from dropdown (required field on DentaQuest portal)
|
||||
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("[TuftsSCO Claim step1] Clicked location dropdown")
|
||||
time.sleep(0.5)
|
||||
first_option = WebDriverWait(self.driver, 5).until(
|
||||
EC.element_to_be_clickable((By.XPATH, "(//li[@role='option'])[1]"))
|
||||
)
|
||||
opt_text = first_option.get_attribute("aria-label") or first_option.text.strip()
|
||||
first_option.click()
|
||||
print(f"[TuftsSCO Claim step1] Selected location: {opt_text[:60]}")
|
||||
time.sleep(0.3)
|
||||
except TimeoutException:
|
||||
print("[TuftsSCO Claim step1] Warning: Location dropdown not found (continuing)")
|
||||
except Exception as e:
|
||||
print(f"[TuftsSCO Claim step1] Warning: Location select failed: {e}")
|
||||
if not self._select_location("TuftsSCO Claim step1"):
|
||||
print("[TuftsSCO Claim step1] Warning: Location not selected (continuing)")
|
||||
|
||||
# 2. Fill DOB
|
||||
try:
|
||||
|
||||
@@ -250,6 +250,93 @@ class AutomationTuftsSCOPreAuth:
|
||||
|
||||
# ── Step 1: Search patient ─────────────────────────────────────────────────
|
||||
|
||||
def _select_location(self, tag):
|
||||
"""Open the Location dropdown on member search and pick the first option."""
|
||||
btn_xpath = '//button[@data-testid="member-search_location_select-btn"]'
|
||||
option_xpaths = [
|
||||
"//*[@role='listbox']//*[@role='option']",
|
||||
"//li[@role='option']",
|
||||
"//*[@role='option']",
|
||||
"//*[@role='listbox']//li",
|
||||
"//*[@role='menu']//*[@role='menuitem' or @role='menuitemradio']",
|
||||
]
|
||||
|
||||
def js_click(el):
|
||||
self.driver.execute_script("arguments[0].click();", el)
|
||||
|
||||
for attempt in range(3):
|
||||
try:
|
||||
trigger = WebDriverWait(self.driver, 15).until(
|
||||
EC.element_to_be_clickable((By.XPATH, btn_xpath))
|
||||
)
|
||||
except TimeoutException:
|
||||
print(f"[{tag}] Warning: Location dropdown button not found")
|
||||
return False
|
||||
|
||||
before = (trigger.text or "").strip()
|
||||
try:
|
||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", trigger)
|
||||
if attempt == 0:
|
||||
trigger.click()
|
||||
else:
|
||||
js_click(trigger)
|
||||
except Exception:
|
||||
js_click(trigger)
|
||||
print(f"[{tag}] Clicked location dropdown (attempt {attempt + 1})")
|
||||
|
||||
# Wait for any visible option
|
||||
option = None
|
||||
deadline = time.time() + 5
|
||||
while option is None and time.time() < deadline:
|
||||
for xp in option_xpaths:
|
||||
visible = [e for e in self.driver.find_elements(By.XPATH, xp) if e.is_displayed()]
|
||||
if visible:
|
||||
option = visible[0]
|
||||
break
|
||||
if option is None:
|
||||
time.sleep(0.3)
|
||||
|
||||
if option is not None:
|
||||
opt_text = option.get_attribute("aria-label") or option.text.strip()
|
||||
try:
|
||||
option.click()
|
||||
except Exception:
|
||||
js_click(option)
|
||||
time.sleep(0.5)
|
||||
print(f"[{tag}] Selected location: {opt_text[:60]}")
|
||||
return True
|
||||
|
||||
# Keyboard fallback: open/highlight the first option and confirm
|
||||
try:
|
||||
trigger.send_keys(Keys.ARROW_DOWN)
|
||||
time.sleep(0.3)
|
||||
ActionChains(self.driver).send_keys(Keys.ENTER).perform()
|
||||
time.sleep(0.5)
|
||||
after = (self.driver.find_element(By.XPATH, btn_xpath).text or "").strip()
|
||||
if after and after != before:
|
||||
print(f"[{tag}] Selected location via keyboard: {after[:60]}")
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(1)
|
||||
|
||||
# Dump dropdown markup so selector changes on the portal can be diagnosed
|
||||
try:
|
||||
html = self.driver.execute_script(
|
||||
"var lb=document.querySelector('[role=listbox]');"
|
||||
"var b=document.querySelector('[data-testid=\"member-search_location_select-btn\"]');"
|
||||
"return (lb||(b&&b.parentElement.parentElement)||{}).outerHTML||'';"
|
||||
)
|
||||
print(f"[{tag}] Location dropdown HTML: {html[:2000]}")
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
def step1_search_patient(self):
|
||||
"""Navigate to member search and find the patient by Member ID + DOB."""
|
||||
wait = WebDriverWait(self.driver, 30)
|
||||
@@ -280,25 +367,8 @@ class AutomationTuftsSCOPreAuth:
|
||||
return f"ERROR: step1 DOB parse failed: {e}"
|
||||
|
||||
# Select Location from dropdown
|
||||
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("[TuftsSCO PreAuth step1] Clicked location dropdown")
|
||||
time.sleep(0.5)
|
||||
first_option = WebDriverWait(self.driver, 5).until(
|
||||
EC.element_to_be_clickable((By.XPATH, "(//li[@role='option'])[1]"))
|
||||
)
|
||||
opt_text = first_option.get_attribute("aria-label") or first_option.text.strip()
|
||||
first_option.click()
|
||||
print(f"[TuftsSCO PreAuth step1] Selected location: {opt_text[:60]}")
|
||||
time.sleep(0.3)
|
||||
except TimeoutException:
|
||||
print("[TuftsSCO PreAuth step1] Warning: Location dropdown not found (continuing)")
|
||||
except Exception as e:
|
||||
print(f"[TuftsSCO PreAuth step1] Warning: Location select failed: {e}")
|
||||
if not self._select_location("TuftsSCO PreAuth step1"):
|
||||
print("[TuftsSCO PreAuth step1] Warning: Location not selected (continuing)")
|
||||
|
||||
# Fill DOB
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user