Skip to content

Commit

Permalink
Add test cases for SyncAgent class and update README.md with synchron…
Browse files Browse the repository at this point in the history
…ization agent details

* **tests/test_sync_agent.py**
  - Add test cases for `get_workouts_from_strava`, `push_workouts_to_trainingpeaks`, `sync_workouts_for_week`, `handle_api_rate_limits`, and `schedule_weekly_sync` methods.
  - Mock necessary modules and methods for testing.

* **README.md**
  - Add an overview section for the synchronization agent.
  - Update setup instructions with environment variables and dependency installation.
  - Add usage instructions for running the synchronization agent.
  • Loading branch information
Lucs1590 committed Jan 6, 2025
1 parent 16bea6c commit 63d6b00
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 35 deletions.
32 changes: 25 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,26 +126,44 @@ python interactive_setup.py

## Synchronization Agent

The synchronization agent automates the synchronization of all workouts for a given athlete from Strava to TrainingPeaks for an entire week.
### Overview

The synchronization agent automates the process of synchronizing all workouts for a given athlete from Strava to TrainingPeaks for an entire week. It uses LangChain to manage synchronization tasks and integrates with Strava and TrainingPeaks APIs.

### Features

- Handles API rate limits with error handling, logging, and scheduling mechanisms.
- Automates the synchronization process with minimal manual intervention.
- Automates synchronization of workouts for an entire week.
- Uses LangChain to manage synchronization tasks.
- Integrates with Strava and TrainingPeaks APIs.
- Optimized for scalability to support multiple athletes.

### Setup and Usage
### Setup

1. Ensure you have the necessary environment variables set up in a `.env` file:

1. Ensure you have the necessary API keys for Strava and TrainingPeaks stored in environment variables (`STRAVA_API_KEY` and `TRAININGPEAKS_API_KEY`).
```
STRAVA_API_KEY=your_strava_api_key
TRAININGPEAKS_USERNAME=your_trainingpeaks_username
TRAININGPEAKS_PASSWORD=your_trainingpeaks_password
OPENAI_API_KEY=your_openai_api_key
```

2. Run the project:
2. Install the required dependencies:

```bash
strava-to-trainingpeaks
pip install -r requirements.txt
```

3. The synchronization agent will automatically handle the synchronization process.
3. Run the synchronization agent:

```bash
python src/main.py
```

### Usage

The synchronization agent will run the sync process weekly, distributing API requests over time to reduce the likelihood of hitting rate limits. It logs all synchronization attempts and their outcomes for monitoring and debugging purposes.

## License

Expand Down
67 changes: 39 additions & 28 deletions tests/test_sync_agent.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import unittest
from unittest.mock import patch, MagicMock
from src.sync_agent import SyncAgent
from datetime import datetime, timedelta


class TestSyncAgent(unittest.TestCase):

Expand All @@ -12,62 +14,71 @@ def test_get_workouts_from_strava(self, mock_get):
mock_get.return_value = mock_response

agent = SyncAgent()
workouts = agent.get_workouts_from_strava("athlete_id", "start_date", "end_date")
start_date = datetime.now() - timedelta(days=7)
end_date = datetime.now()
workouts = agent.get_workouts_from_strava("athlete_id", start_date, end_date)

self.assertEqual(len(workouts), 1)
self.assertEqual(workouts[0]["name"], "Workout 1")

@patch('src.sync_agent.requests.post')
def test_push_workouts_to_trainingpeaks(self, mock_post):
@patch('src.sync_agent.requests.get')
def test_get_workouts_from_strava_error(self, mock_get):
mock_response = MagicMock()
mock_response.status_code = 201
mock_post.return_value = mock_response
mock_response.status_code = 500
mock_get.return_value = mock_response

agent = SyncAgent()
workouts = [{"id": 1, "name": "Workout 1"}]
agent.push_workouts_to_trainingpeaks(workouts)
start_date = datetime.now() - timedelta(days=7)
end_date = datetime.now()
workouts = agent.get_workouts_from_strava("athlete_id", start_date, end_date)

self.assertEqual(mock_post.call_count, 1)
self.assertEqual(len(workouts), 0)

@patch('src.sync_agent.requests.get')
@patch('src.sync_agent.requests.post')
def test_sync_workouts_for_week(self, mock_post, mock_get):
mock_get_response = MagicMock()
mock_get_response.status_code = 200
mock_get_response.json.return_value = [{"id": 1, "name": "Workout 1"}]
mock_get.return_value = mock_get_response
@patch('src.sync_agent.webdriver.Chrome')
def test_push_workouts_to_trainingpeaks(self, mock_chrome):
mock_driver = MagicMock()
mock_chrome.return_value = mock_driver

agent = SyncAgent()
workouts = [{"id": 1, "tcx_file_path": "path/to/file.tcx"}]
agent.push_workouts_to_trainingpeaks(workouts)

mock_post_response = MagicMock()
mock_post_response.status_code = 201
mock_post.return_value = mock_post_response
self.assertTrue(mock_driver.get.called)
self.assertTrue(mock_driver.find_element.called)

@patch('src.sync_agent.SyncAgent.get_workouts_from_strava')
@patch('src.sync_agent.SyncAgent.push_workouts_to_trainingpeaks')
def test_sync_workouts_for_week(self, mock_push, mock_get):
mock_get.return_value = [{"id": 1, "name": "Workout 1"}]

agent = SyncAgent()
agent.sync_workouts_for_week("athlete_id")

self.assertEqual(mock_get.call_count, 1)
self.assertEqual(mock_post.call_count, 1)
self.assertTrue(mock_get.called)
self.assertTrue(mock_push.called)

@patch('src.sync_agent.requests.get')
def test_handle_api_rate_limits(self, mock_get):
mock_get.side_effect = [requests.exceptions.RequestException("Error"), MagicMock(status_code=200, json=lambda: [{"id": 1, "name": "Workout 1"}])]
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = [{"id": 1, "name": "Workout 1"}]
mock_get.return_value = mock_response

agent = SyncAgent()
result = agent.handle_api_rate_limits(agent.get_workouts_from_strava, "athlete_id", "start_date", "end_date")
result = agent.handle_api_rate_limits(agent.get_workouts_from_strava, "athlete_id", datetime.now(), datetime.now())

self.assertIsNotNone(result)
self.assertEqual(len(result), 1)
self.assertEqual(result[0]["name"], "Workout 1")

@patch('src.sync_agent.schedule.run_pending')
@patch('src.sync_agent.schedule.every')
def test_schedule_weekly_sync(self, mock_every):
mock_job = MagicMock()
mock_every.return_value.week.do.return_value = mock_job

def test_schedule_weekly_sync(self, mock_every, mock_run_pending):
agent = SyncAgent()
agent.schedule_weekly_sync("athlete_id")

self.assertTrue(mock_every.called)
self.assertTrue(mock_every.return_value.week.do.called)
self.assertTrue(mock_run_pending.called)


if __name__ == '__main__':
unittest.main()

0 comments on commit 63d6b00

Please sign in to comment.