{"id":25363585,"url":"https://github.com/ethanlee928/cycling","last_synced_at":"2025-08-24T09:41:31.728Z","repository":{"id":277390556,"uuid":"896907611","full_name":"ethanlee928/cycling","owner":"ethanlee928","description":"Exploring cycling data","archived":false,"fork":false,"pushed_at":"2025-03-01T05:56:51.000Z","size":2211,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-01T06:26:46.064Z","etag":null,"topics":["cycling","opencv","sports","tcx"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ethanlee928.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-01T15:47:04.000Z","updated_at":"2025-03-01T05:56:54.000Z","dependencies_parsed_at":"2025-02-13T18:21:44.083Z","dependency_job_id":"c52e13e5-4002-4397-86c4-f953d977f040","html_url":"https://github.com/ethanlee928/cycling","commit_stats":null,"previous_names":["ethanlee928/cycling"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethanlee928%2Fcycling","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethanlee928%2Fcycling/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethanlee928%2Fcycling/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethanlee928%2Fcycling/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ethanlee928","download_url":"https://codeload.github.com/ethanlee928/cycling/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247975280,"owners_count":21026844,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cycling","opencv","sports","tcx"],"created_at":"2025-02-14T22:35:19.463Z","updated_at":"2025-08-24T09:41:31.704Z","avatar_url":"https://github.com/ethanlee928.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cycling\n\nExtract cycling workout data from Strava, analyse the performance using Strava API.\n\n## 🖥️ Dependencies\n\n### Python Environment\n\n```bash\nuv venv .venv\nsource .venv/bin/activate\nuv pip install -r requirements.txt\n```\n\n### Use Docker\n\n```bash\nmake build\nmake run\n```\n\n## 📈 Streamlit Dashboard\n\nCheck out the Streamlit [app](./app/) for workout analysis \u0026 performance management.\n\n### Start Streamlit App\n\n```bash\ncd app/\nstreamlit run app.py\n```\n\n### Dashboard Preview\n\nAfter clicking login, you will be redirected to the Strava login page. After logging in, you will be redirected back to the app.\n\n#### Successful Login\n\n![streamlit_dashboard](./images/general-stats.png)\n\n#### Workout Summary\n\n![weekly-tss](./images/weekly-tss.png)\n\n#### Performance Management Chart\n\n![performance-management](./images/performance-management.png)\n\n### References\n\n1. [Power Zones by Strava](https://stories.strava.com/articles/feel-the-power-calculate-your-training-pacing-zones-know-what-they-feel-like)\n2. [Power Zones by Pro Cycling Coaching](https://www.procyclingcoaching.com/resources/power-training-zones-for-cycling)\n3. [Strava Guide: Features to Take Your Training to The Next Level](https://stories.strava.com/articles/strava-guide-features-to-take-your-training-to-the-next-level)\n4. [TSS, IF, NP](https://www.trainerroad.com/blog/tss-if-and-workout-levels-3-metrics-to-help-you-understand-your-training-and-get-faster/)\n5. [TSS by Peaksware](https://www.trainingpeaks.com/learn/articles/how-to-plan-your-season-with-training-stress-score/)\n6. [CTL by Peaksware](https://www.trainingpeaks.com/learn/articles/applying-the-numbers-part-1-chronic-training-load/)\n7. [A blog about CTL and ATL](https://ssp3nc3r.github.io/post/2020-05-08-calculating-training-load-in-cycling/)\n8. [More indepth CTL and ATL analysis](https://konakorgi.com/2020/01/29/entry-5-rest-and-recovery-part-1-managing-fatigue/)\n9. [A blog about CTL, ATL, and TSB in Chinese](https://zhuanlan.zhihu.com/p/389912897)\n\n## Strava API\n\n- [Strava API Developer Guide](https://developers.strava.com/docs/getting-started/)\n- `We require authentication via OAuth 2.0 to request data about any athlete.` [Authentication](https://developers.strava.com/docs/authentication/)\n\n### OAuth 2.0 with Strava\n\n1. Redirect the user to the Strava authorization page with the following parameters:\n\n   - `client_id`: Your Strava API client ID\n   - `redirect_uri`: The URL to redirect to after authorization. Must be within the callback domain specified by the application. localhost and 127.0.0.1 are white-listed.\n   - `response_type`: Set to \"code\"\n   - `scope`: The permissions you want to request (e.g., \"read,activity:read\")\n   - `approval_prompt=force`: (optional) Forces the user to approve each time\n\n   Example:\n\n   ```bash\n   https://www.strava.com/oauth/authorize?client_id=YOUR_CLIENT_ID\u0026response_type=code\u0026redirect_uri=YOUR_REDIRECT_URI\u0026approval_prompt=force\u0026scope=read,activity:read\n   ```\n\n2. After user approval, Strava redirects the user back to your specified `redirect_uri` with an authorization code in the URL query string:\n\n   ```bash\n   https://yourapp.com/callback?code=AUTHORIZATION_CODE\u0026scope=accepted_scopes\n   ```\n\n3. The backend exchanges the authorization code for tokens:\n\n   - Make a POST request to the Strava token endpoint with the following parameters:\n     - `client_id`: Your Strava API client ID\n     - `client_secret`: Your Strava API client secret\n     - `code`: The authorization code received in step 2\n     - `grant_type`: Set to \"authorization_code\"\n\n   Example:\n\n   ```bash\n   curl -X POST https://www.strava.com/oauth/token \\\n   -d \"client_id=YOUR_CLIENT_ID\" \\\n   -d \"client_secret=YOUR_CLIENT_SECRET\" \\\n   -d \"code=AUTHORIZATION_CODE\" \\\n   -d \"grant_type=authorization_code\"\n   ```\n\n4. Strava responds with an access token and a refresh token:\n\n   Strava's response includes JSON containing:\n\n   - `access_token` (short-lived)\n   - `refresh_token` (used to obtain new access tokens)\n   - User information (e.g., athlete ID).\n\n## Patch Streamlit index.html\n\nUsing `st.set_page_config` does not immediately set the title and page icon; for a fraction of a second, you will still see Streamlit's default logo and title. To avoid this, you need to patch the `index.html` file in the Streamlit source code.\n\nThe `index.html` file is located at `\u003cpython-env\u003e/lib/\u003cpython-version\u003e/site-packages/streamlit/static`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fethanlee928%2Fcycling","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fethanlee928%2Fcycling","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fethanlee928%2Fcycling/lists"}