Retry RunnerUp verification every 15 min, and stop hiding why it fails

The auto-fill scan only ran once, at lock-screen login; a workout was
uncreditable for the rest of the day if the phone wasn't adb-visible at
that exact instant. Reuse the existing 15-minute workout-sync.timer to
retry it. Also upgrade the RunnerUp verification failure paths from
info to warning with the actual reason (no TCX export found, or root
DB access unavailable on this non-rooted device) instead of a vague or
invisible message.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013SH7XBkpEqQX4KghHdgx9T
This commit is contained in:
Krzysztof Rudnicki 2026-08-12 19:17:29 +02:00
parent aab51eed2d
commit 507030d63b
3 changed files with 38 additions and 3 deletions

View File

@ -57,7 +57,14 @@ class RunnerUpDbMixin:
root=True,
)
if not ok:
_logger.info("Failed to copy RunnerUp DB to sdcard: %s", err)
_logger.warning(
"Root DB pull failed for %s (%s) — this device likely has no "
"root/su access, so this fallback path cannot work here. "
"RunnerUp workouts can only be verified via TCX export files; "
"enable RunnerUp's File Synchronizer auto-export on-device.",
pkg,
err,
)
shutil.rmtree(tmp_dir, ignore_errors=True)
return None
@ -142,7 +149,12 @@ class RunnerUpDbMixin:
"""Verify today's run via root DB pull (fallback path)."""
db_path = self._pull_runnerup_db()
if db_path is None:
return "not_verified", "Could not retrieve RunnerUp database from phone"
return (
"not_verified",
"No TCX export found and root DB access is unavailable on this "
"device — enable RunnerUp's File Synchronizer auto-export so "
"runs land in /sdcard/Documents/RunnerUp/",
)
try:
run_data = self._query_todays_run(db_path)

View File

@ -138,6 +138,14 @@ class RunnerUpVerificationMixin(RunnerUpDbMixin):
today = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d")
exports = self._find_runnerup_exports_for_date(today)
if not exports:
_logger.warning(
"No RunnerUp TCX export found for %s in %s — a run today will "
"not be verified unless RunnerUp's File Synchronizer "
"auto-export is enabled and the app stays enabled long enough "
"to sync (check it isn't disabled by focus mode).",
today,
RUNNERUP_EXPORT_DIRS,
)
return None
# Try each file; return the best result (verified > validation error).
@ -174,6 +182,13 @@ class RunnerUpVerificationMixin(RunnerUpDbMixin):
continue
status, msg = self._validate_runnerup_data(data)
if status != "verified":
_logger.warning(
"RunnerUp export %s for %s did not qualify (%s): %s",
remote,
date_str,
status,
msg,
)
continue
workout_data = {
"type": "runnerup_verified",
@ -285,5 +300,6 @@ class RunnerUpVerificationMixin(RunnerUpDbMixin):
return file_result
# Path 2: root DB pull (fallback when no export files found yet).
_logger.info("No TCX exports found today; trying root DB pull...")
# On a non-rooted device this path cannot succeed; _verify_runnerup_via_db
# reports that plainly rather than as a generic failure.
return self._verify_runnerup_via_db()

View File

@ -234,8 +234,15 @@ class ScreenLocker(
``workout-sync.timer`` unit calls. Sync used to happen only inside the
locker's startup path, so a workout finished after login stayed
invisible until the next login.
Also re-runs the RunnerUp TCX backfill here, not just at login: the
login-time scan gets exactly one shot, and if the phone isn't
adb-visible at that instant (e.g. USB debugging not yet
authorized), a same-day run stays uncredited until the next login.
Repeating it every 15 minutes closes that gap.
"""
self._ingest_synced_manual_workouts()
self._auto_fill_week_runnerup_bonus()
def _ingest_synced_manual_workouts(self) -> None:
"""Sync manual workouts: publish this PC's, ingest everyone else's.