diff --git a/backend/src/api/__init__.py b/backend/src/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/specs/004-single-sync-job.md b/specs/004-single-sync-job.md new file mode 100644 index 0000000..8fb94da --- /dev/null +++ b/specs/004-single-sync-job.md @@ -0,0 +1,59 @@ +# 004-single-sync-job: Simplify Sync Job Management with Progress Tracking + +## Goal +To simplify the synchronization job management by allowing only one sync job to run at a time, and to provide progress tracking for this single active sync job via a polling API. This aligns with the system's single-user nature while offering better user feedback. + +## Motivation +The current system is designed for a single user. While the previous iteration aimed to remove all job management complexity, the need for user feedback on ongoing sync operations necessitates a simplified progress tracking mechanism. This approach avoids a full-fledged job queue and ID system but still provides visibility into the single active sync. + +## Proposed Changes + +### 1. Reintroduction of a Simplified Sync Job Model +* **Define `SyncJob` Model**: Reintroduce a `Pydantic` model for `SyncJob` (e.g., in a new `backend/src/sync_status.py` file or similar). This model will represent the state of the *single active* sync job and will include fields such as: + * `status: str` (e.g., "idle", "in_progress", "completed", "failed") + * `progress: float` (0.0 to 1.0) + * `start_time: datetime` + * `end_time: Optional[datetime]` + * `error_message: Optional[str]` + * `job_type: Optional[str]` (e.g., "activities", "health", "workouts") + +### 2. Introduce `CurrentSyncJobManager` +* **Create `CurrentSyncJobManager`**: Implement a singleton class or a global object (e.g., in `backend/src/sync_manager.py`) to manage the state of the `SyncJob`. This manager will hold the single `SyncJob` instance and provide methods for its lifecycle. + * `start_sync(job_type: str) -> SyncJob`: Initializes a new `SyncJob` with "in_progress" status and the given type. Sets the `start_time`. Returns the `SyncJob` instance. + * `update_progress(progress: float, details: Optional[str] = None)`: Updates the `progress` and optionally `details` of the current `SyncJob`. + * `complete_sync()`: Sets the `status` to "completed" and `end_time` for the current `SyncJob`. + * `fail_sync(error_message: str)`: Sets the `status` to "failed", `error_message`, and `end_time` for the current `SyncJob`. + * `get_current_sync_status() -> SyncJob`: Returns the current `SyncJob` instance. + * `is_sync_active() -> bool`: Returns `True` if a sync is currently "in_progress", `False` otherwise. + +### 3. Modification of Sync Service Functions +* The `_in_background` functions in `backend/src/services/garmin_health_service.py`, `backend/src/services/garmin_activity_service.py`, and `backend/src/services/garmin_workout_service.py` will be modified: + * They will no longer accept a `job_id` parameter. + * Instead, they will receive the `SyncJob` instance (or a reference to the `CurrentSyncJobManager`) for the current operation. + * They will use the `CurrentSyncJobManager`'s `update_progress`, `complete_sync`, and `fail_sync` methods to report their status. + +### 4. Modification of API Endpoints (`backend/src/api/garmin_sync.py`) + +* **Remove `job_store` and `SyncJob` imports from the old system.** +* **Import `CurrentSyncJobManager` and `SyncJob` from the new system.** +* **Remove `/status/{job_id}` endpoint**: This endpoint is no longer needed as there are no individual job IDs to query. +* **Modify sync initiation endpoints (`/garmin/activities`, `/garmin/workouts`, `/garmin/health`)**: + * Before initiating a sync, check `CurrentSyncJobManager.is_sync_active()`. If `True`, return an error response (e.g., `409 Conflict`) indicating that a sync is already in progress. + * Call `CurrentSyncJobManager.start_sync(job_type="...")` to initialize the sync job. + * Pass the `SyncJob` instance (or a reference to the `CurrentSyncJobManager`) to the background task. + * Change the `response_model` to return the initial `SyncJob` status or a simple success message. +* **Add New API Endpoint for Status Polling**: + * `GET /garmin/sync/status`: + * **Response Model**: `SyncJob` + * **Description**: Returns the current status of the single active sync job. If no sync is active, it returns an "idle" status `SyncJob`. + +### 5. Update Dependencies and Main Application +* **`backend/src/dependencies.py`**: Remove any references to the old `job_store` and `SyncStatusService`. +* **`backend/src/main.py`**: Ensure the `CurrentSyncJobManager` is properly initialized and accessible (e.g., as a global or via dependency injection if preferred). + +## Impact +* **Progress Tracking**: Users can now poll a dedicated API endpoint to get real-time progress updates for the single active sync job. +* **Clearer State**: The system's state regarding sync operations is more transparent. +* **Controlled Concurrency**: Only one sync operation can run at a time, preventing resource contention in a single-user environment. +* **Simplified API**: No need to manage multiple job IDs. The status endpoint always refers to the current operation. +* **Minimal Overhead**: Avoids the complexity of a full-blown job queue and distributed job management system, suitable for a single-user application. \ No newline at end of file