feat: fix DDMA eligibility — patient list, name extraction, PDF page, OTP session

- Filter patient list by userId so each user sees only their own patients
- Sort patients by updatedAt DESC so recently checked patients appear first
- Add updatedAt field to Patient model (DB migration via raw SQL + db:generate)
- Fix DDMA name extraction: read from detail page "Name:" label, not search
  results row text which included appended dates
- Fix PDF capture: use driver.get() instead of click() to avoid race condition
  that was saving the search results page instead of the patient detail page
- Strip trailing bare dates from extracted names (e.g. "Rodriguez 04/27/2026")
- Handle "Last, First" comma format and single-word last names in splitName
- Normalize insuranceId consistently in createOrUpdatePatientByInsuranceId
- Fix OTP persistent session: stop clearing LocalStorage/IndexedDB on startup
  (these hold the DDMA device trust token that skips OTP on subsequent logins)
- Increase post-navigation wait time for full page render before PDF generation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gitead
2026-05-01 21:40:04 -04:00
parent 24bbaed6ab
commit e26ebf7fd5
213 changed files with 1698 additions and 1425 deletions

View File

@@ -44,28 +44,26 @@ class DDMABrowserManager:
def clear_session_on_startup(self):
"""
Clear session cookies from Chrome profile on startup.
This forces a fresh login after PC restart.
Preserves device trust tokens (LocalStorage, IndexedDB) to avoid OTPs.
Clear only login cookies on startup to force credential re-entry after restart.
NEVER clears Local Storage or IndexedDB — those hold the DDMA device trust token
that allows the portal to skip OTP for recognised devices.
"""
print("[DDMA BrowserManager] Clearing session on startup...")
print("[DDMA BrowserManager] Clearing login cookies on startup (preserving device trust)...")
try:
# Clear credentials tracking file
# Reset credentials tracking so the next login re-saves the hash
if os.path.exists(self._credentials_file):
os.remove(self._credentials_file)
print("[DDMA BrowserManager] Cleared credentials tracking file")
# Clear session-related Chrome profile files
# Only remove cookie / login-data files — these expire the session so the
# user must re-enter credentials, but the device trust token is untouched.
session_files = [
"Cookies",
"Cookies-journal",
"Login Data",
"Login Data-journal",
"Web Data",
"Web Data-journal",
]
for filename in session_files:
for base in [os.path.join(self.profile_dir, "Default"), self.profile_dir]:
filepath = os.path.join(base, filename)
@@ -76,55 +74,12 @@ class DDMABrowserManager:
except Exception as e:
print(f"[DDMA BrowserManager] Could not remove {filename}: {e}")
# Clear Session Storage (contains login state)
session_storage_dir = os.path.join(self.profile_dir, "Default", "Session Storage")
if os.path.exists(session_storage_dir):
try:
shutil.rmtree(session_storage_dir)
print("[DDMA BrowserManager] Cleared Session Storage")
except Exception as e:
print(f"[DDMA BrowserManager] Could not clear Session Storage: {e}")
# Clear Local Storage (may contain auth tokens)
local_storage_dir = os.path.join(self.profile_dir, "Default", "Local Storage")
if os.path.exists(local_storage_dir):
try:
shutil.rmtree(local_storage_dir)
print("[DDMA BrowserManager] Cleared Local Storage")
except Exception as e:
print(f"[DDMA BrowserManager] Could not clear Local Storage: {e}")
# Clear IndexedDB (may contain auth tokens)
indexeddb_dir = os.path.join(self.profile_dir, "Default", "IndexedDB")
if os.path.exists(indexeddb_dir):
try:
shutil.rmtree(indexeddb_dir)
print("[DDMA BrowserManager] Cleared IndexedDB")
except Exception as e:
print(f"[DDMA BrowserManager] Could not clear IndexedDB: {e}")
# Clear browser caches
cache_dirs = [
os.path.join(self.profile_dir, "Default", "Cache"),
os.path.join(self.profile_dir, "Default", "Code Cache"),
os.path.join(self.profile_dir, "Default", "GPUCache"),
os.path.join(self.profile_dir, "Default", "Service Worker"),
os.path.join(self.profile_dir, "Cache"),
os.path.join(self.profile_dir, "Code Cache"),
os.path.join(self.profile_dir, "GPUCache"),
os.path.join(self.profile_dir, "Service Worker"),
os.path.join(self.profile_dir, "ShaderCache"),
]
for cache_dir in cache_dirs:
if os.path.exists(cache_dir):
try:
shutil.rmtree(cache_dir)
print(f"[DDMA BrowserManager] Cleared {os.path.basename(cache_dir)}")
except Exception as e:
print(f"[DDMA BrowserManager] Could not clear {os.path.basename(cache_dir)}: {e}")
# Local Storage, IndexedDB, and Session Storage are intentionally
# NOT cleared — they contain the DDMA device trust token that prevents
# OTP from being required on every login.
self._needs_session_clear = True
print("[DDMA BrowserManager] Session cleared - will require fresh login")
print("[DDMA BrowserManager] Startup clear done — device trust preserved, OTP not required")
except Exception as e:
print(f"[DDMA BrowserManager] Error clearing session: {e}")