Skip to content

fix(automation): repair Oura login and export download - #18

Open
rosebaesj wants to merge 1 commit into
EIrno:mainfrom
rosebaesj:fix/login-and-export-automation
Open

rosebaesj wants to merge 1 commit into
EIrno:mainfrom
rosebaesj:fix/login-and-export-automation

Conversation

@rosebaesj

Copy link
Copy Markdown

Four defects currently make the automated sync unusable end to end. I hit them
in order while trying to get a sync to complete, and each one blocks the next.

1. Login dies on the verification-method screen

Oura now offers a passkey alongside the e-mail code, and both buttons carry
name="selectedId". The unqualified locator in _check_otp_screen matches two
elements, so Playwright's strict mode raises before anything is clicked:

Locator.is_visible: Error: strict mode violation:
locator("button[name='selectedId']") resolved to 2 elements:
  1) <button name="selectedId" ...>Use passkey</button>
  2) <button name="selectedId" ...>Email me a code</button>

Passkeys need a platform authenticator and can never be driven from an automated
browser, so the fix drops those options and prefers an e-mail/code label among
what remains, falling back to the last option. Excluding passkeys rather than
matching the e-mail label by text keeps this working on non-English pages —
worth noting since this file already hardcodes a Finnish string elsewhere for
invalid-code detection.

This overlaps #10, which fixes the same crash by matching
(email.*code|send.*code) with an .oura-button-outline fallback. That works
on an English page; this takes the language-independent route instead. Happy to
rebase on top of #10 if you would rather land that one first.

2. login() returned None when already logged in

The UI reads .message off that response unconditionally, so a successful
login crashed the renderer with Cannot read properties of null (reading 'message'). It now always returns a status dict.

3. The export wait declared success immediately

_wait_for_processing() treated "the Request button is enabled again" as "the
export is ready". Oura leaves that button enabled right after it is clicked, and
the main button fallback matches almost any button on the page, so it returned
ready within seconds. The caller then looked for a download button that would
not exist for hours:

00:42:05  Found Request button. Clicking...
00:42:07  Export requested. Waiting for processing...
00:42:17  Download button not found.
          Full sync failed: No file downloaded.

Note Processing... (Attempt 1/30) never logged — it never waited once. It now
polls for the download control itself, the only signal that the archive exists.

This one fails for every account, with or without a passkey.

4. Opaque error when login is required

download_export() passed a status dict straight to the zip parser when the
automator could not reach the export page, surfacing as 'dict' object has no attribute 'seek' instead of saying that login was needed.

Testing

Ran against a real account: login now completes through the e-mail code path,
and the export page is reached and the request submitted. I verified the marker
selection logic separately across English, Korean, Finnish and Japanese labels,
plus the single-button and passkey-only cases.

I have not been able to observe a full multi-hour export generation complete, so
the polling loop in (3) is verified only up to the point where it correctly
keeps waiting instead of giving up after ten seconds.

Four failures currently make the automated sync unusable.

1. Login dies on the verification-method screen. Oura now offers a passkey
   alongside the e-mail code, and both buttons share name='selectedId', so the
   unqualified locator matches two elements and Playwright's strict mode raises
   "strict mode violation: ... resolved to 2 elements". Passkeys cannot be
   driven from an automated browser, so those options are dropped and an
   e-mail/code label is preferred among the rest, falling back to the last
   option. Excluding passkeys rather than matching the e-mail label keeps this
   working on non-English pages.

2. login() returned None when the session was already valid. The UI reads
   `.message` off that response unconditionally, so a successful login crashed
   the renderer with "Cannot read properties of null". It now always returns a
   status dict.

3. The export wait declared success immediately. _wait_for_processing() asked
   whether the Request button was enabled again, but Oura leaves it enabled
   right after it is clicked, and the generic 'main button' fallback matches
   almost any button on the page. It returned ready within seconds, and the
   caller then failed to find a download button that would not exist for hours,
   reporting "No file downloaded (timeout?)". It now polls for the download
   control itself, which is the only signal the archive exists.

4. download_export() passed a status dict to the zip parser when the automator
   could not reach the export page, surfacing as
   "'dict' object has no attribute 'seek'" instead of saying login was needed.

Together these mean a full sync never completes: (1) blocks accounts with a
passkey, and (3) fails every account regardless.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant