Skip to content

feat: paginate Strava following feed for 50–100 activities#34

Merged
bin101 merged 1 commit into
mainfrom
feat/feed-pagination
Jul 1, 2026
Merged

feat: paginate Strava following feed for 50–100 activities#34
bin101 merged 1 commit into
mainfrom
feat/feed-pagination

Conversation

@bin101

@bin101 bin101 commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • Bugfix/feature: Previously only ~13–16 activities were loaded because the JSON-XHR feed rewrite (commit 1bcfb83) dropped the quantity control of the old HTML parser and never read pagination.hasMore.
  • Cursor pagination added: Strava's /dashboard/feed endpoint uses cursor-based pagination (verified via HAR capture, 2026-07-01). Each response entry carries cursorData: {updated_at, rank}; the next page is fetched with before=updated_at and cursor=int(rank) of the last entry.
  • Load target: Loop runs while hasMore is true, up to _FEED_MAX_PAGES = 6 pages or _FEED_TARGET_ACTIVITIES = 100 unique activities.
  • Human-like pacing: A random 0.5–1.5 s sleep between pages (RNG/sleep injectable for tests).
  • No caller changes: engine.py and routes.py are untouched; FeedParser.parse() receives the same single-dict interface (merged pages stitched into one {"entries": [...], "pagination": {...}}).

Test plan

  • TDD: 12 new tests written first, then implementation
  • Multi-page merge (3 pages β†’ all activities present)
  • Stop on hasMore=false (exact 1 request)
  • Stop at _FEED_MAX_PAGES cap (6 requests max even if hasMore stays true)
  • First request has no cursor params (only feed_type + athlete_id)
  • Second request carries before/cursor from previous page's last entry
  • Deduplication (same activity id on two pages β†’ appears once)
  • Graceful stop when cursorData is missing despite hasMore=true
  • Sleep called exactly n_pages – 1 times
  • All 380 tests green, coverage 87% (β‰₯85% target), ruff + mypy clean

πŸ€– Generated with Claude Code

Previously only a single page (~13–16 entries) was fetched, because the
JSON-XHR rewrite dropped the quantity control that the old HTML parser had
and never consumed pagination.hasMore.

Strava's /dashboard/feed endpoint uses cursor pagination (verified via HAR):
each entry carries cursorData.updated_at / rank; the next page is requested
with before=updated_at and cursor=int(rank) of the last entry. The loop runs
while hasMore is true, up to _FEED_MAX_PAGES=6 pages or 100 unique activities.

A short random pause (0.5–1.5 s, sleep/rng injectable for tests) is inserted
between page requests to keep load human-like.

Callers (engine.py, routes.py) and FeedParser.parse() are unchanged: the merged
result is returned as a single {"entries": [...], "pagination": {...}} dict.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bin101 bin101 merged commit e5056e3 into main Jul 1, 2026
1 check passed
@bin101 bin101 deleted the feat/feed-pagination branch July 1, 2026 08:26
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