diff --git a/backend/app/garmin/router.py b/backend/app/garmin/router.py index 93448e6a5..34d7f0e36 100644 --- a/backend/app/garmin/router.py +++ b/backend/app/garmin/router.py @@ -1,4 +1,3 @@ -import logging from typing import Annotated from fastapi import APIRouter, Depends, BackgroundTasks from sqlalchemy.orm import Session diff --git a/backend/app/strava/activity_utils.py b/backend/app/strava/activity_utils.py index 76ea7bf13..838d13a69 100644 --- a/backend/app/strava/activity_utils.py +++ b/backend/app/strava/activity_utils.py @@ -38,6 +38,7 @@ from core.database import SessionLocal async def fetch_and_process_activities( strava_client: Client, start_date: datetime, + end_date: datetime, user_id: int, user_integrations: user_integrations_schema.UsersIntegrations, websocket_manager: websocket_schema.WebSocketManager, @@ -49,7 +50,9 @@ async def fetch_and_process_activities( # Fetch Strava activities after the specified start date try: - strava_activities = list(strava_client.get_activities(after=start_date)) + strava_activities = list( + strava_client.get_activities(after=start_date, before=end_date) + ) except AccessUnauthorized as auth_err: # Log a more specific error message for authentication issues core_logger.print_to_log( @@ -699,14 +702,17 @@ async def retrieve_strava_users_activities_for_days( # Get all users users = users_crud.get_all_users(db) + # Calculate the start date and end date + calculated_start_date = datetime.now(timezone.utc) - timedelta(days=days) + calculated_end_date = datetime.now(timezone.utc) + # Process the activities for each user if users: for user in users: try: - await get_user_strava_activities_by_days( - (datetime.now(timezone.utc) - timedelta(days=days)).strftime( - "%Y-%m-%dT%H:%M:%S" - ), + await get_user_garminconnect_activities_by_dates( + calculated_start_date, + calculated_end_date, user.id, None, None, @@ -764,8 +770,9 @@ async def retrieve_strava_users_activities_for_days( db.close() -async def get_user_strava_activities_by_days( +async def get_user_garminconnect_activities_by_dates( start_date: datetime, + end_date: datetime, user_id: int, websocket_manager: websocket_schema.WebSocketManager = None, db: Session = None, @@ -804,6 +811,7 @@ async def get_user_strava_activities_by_days( strava_activities_processed = await fetch_and_process_activities( strava_client, start_date, + end_date, user_id, user_integrations, websocket_manager, diff --git a/backend/app/strava/router.py b/backend/app/strava/router.py index e6eb8809e..c72222336 100644 --- a/backend/app/strava/router.py +++ b/backend/app/strava/router.py @@ -1,5 +1,5 @@ import os -from datetime import datetime, timedelta, timezone +from datetime import datetime, timedelta, timezone, date from typing import Annotated, Callable from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks, Security from sqlalchemy.orm import Session @@ -102,12 +102,13 @@ async def strava_link( @router.get( - "/activities/days/{days}", + "/activities", status_code=202, ) async def strava_retrieve_activities_days( - days: int, - validate_access_token: Annotated[ + start_date: date, + end_date: date, + _validate_access_token: Annotated[ Callable, Depends(auth_security.validate_access_token), ], @@ -126,12 +127,16 @@ async def strava_retrieve_activities_days( # db: Annotated[Session, Depends(core_database.get_db)], background_tasks: BackgroundTasks, ): + start_datetime = datetime.combine( + start_date, datetime.min.time(), tzinfo=timezone.utc + ) + end_datetime = datetime.combine(end_date, datetime.max.time(), tzinfo=timezone.utc) + # Process strava activities in the background background_tasks.add_task( - strava_activity_utils.get_user_strava_activities_by_days, - (datetime.now(timezone.utc) - timedelta(days=days)).strftime( - "%Y-%m-%dT%H:%M:%S" - ), + strava_activity_utils.get_user_garminconnect_activities_by_dates, + start_datetime, + end_datetime, token_user_id, websocket_manager, ) @@ -147,7 +152,7 @@ async def strava_retrieve_activities_days( @router.get("/gear", status_code=201) async def strava_retrieve_gear( - validate_access_token: Annotated[ + _validate_access_token: Annotated[ Callable, Depends(auth_security.validate_access_token), ], @@ -291,7 +296,7 @@ async def import_shoes_from_strava_export( @router.put("/client") async def strava_set_user_client( client: strava_schema.StravaClient, - validate_access_token: Annotated[ + _validate_access_token: Annotated[ Callable, Depends(auth_security.validate_access_token), ], @@ -319,7 +324,7 @@ async def strava_set_user_client( ) async def strava_set_user_unique_state( state: str | None, - validate_access_token: Annotated[ + _validate_access_token: Annotated[ Callable, Depends(auth_security.validate_access_token), ], @@ -342,7 +347,7 @@ async def strava_set_user_unique_state( @router.delete("/unlink") async def strava_unlink( - validate_access_token: Annotated[ + _validate_access_token: Annotated[ Callable, Depends(auth_security.validate_access_token), ], diff --git a/frontend/app/src/components/Settings/SettingsIntegrationsZone.vue b/frontend/app/src/components/Settings/SettingsIntegrationsZone.vue index 174379f3f..7a31868f4 100644 --- a/frontend/app/src/components/Settings/SettingsIntegrationsZone.vue +++ b/frontend/app/src/components/Settings/SettingsIntegrationsZone.vue @@ -48,6 +48,17 @@ >{{ $t('settingsIntegrationsZone.modalRetrieveActivitiesByDaysTitle') }} +
  • + + {{ $t('settingsIntegrationsZone.modalRetrieveActivitiesByDateRangeTitle') }} +
  • {{ @@ -219,7 +230,16 @@ :numberFieldLabel="`${t('settingsIntegrationsZone.modalRetrieveActivitiesByDaysLabel')}`" :actionButtonType="`success`" :actionButtonText="t('settingsIntegrationsZone.modalRetrieveButton')" - @numberToEmitAction="submitRetrieveStravaActivities" + @numberToEmitAction="submitRetrieveStravaActivitiesDays" + /> + + + @@ -303,6 +323,15 @@ import { INTEGRATION_LOGOS } from '@/constants/integrationLogoConstants' const authStore = useAuthStore() const { locale, t } = useI18n() +function getStartAndEndDateFromDaysAgo(days) { + const endDate = new Date() + const startDate = new Date() + startDate.setDate(endDate.getDate() - days) + const formattedStartDate = startDate.toISOString().split('T')[0] + const formattedEndDate = endDate.toISOString().split('T')[0] + return { startDate: formattedStartDate, endDate: formattedEndDate } +} + async function submitConnectStrava(stravaClient) { const array = new Uint8Array(16) window.crypto.getRandomValues(array) @@ -326,9 +355,21 @@ async function submitConnectStrava(stravaClient) { } } -async function submitRetrieveStravaActivities(daysToRetrieveStrava) { +async function submitRetrieveStravaActivitiesDays(days) { try { - await strava.getStravaActivitiesLastDays(daysToRetrieveStrava) + const dates = getStartAndEndDateFromDaysAgo(days) + await strava.getStravaActivitiesByDates(dates.startDate, dates.endDate) + push.info(t('settingsIntegrationsZone.loadingMessageRetrievingStravaActivities')) + } catch (error) { + push.error( + `${t('settingsIntegrationsZone.errorMessageUnableToGetStravaActivities')} - ${error}` + ) + } +} + +async function submitRetrieveStravaActivitiesDataRange(dateRange) { + try { + await strava.getStravaActivitiesByDates(dateRange.startDate, dateRange.endDate) push.info(t('settingsIntegrationsZone.loadingMessageRetrievingStravaActivities')) } catch (error) { push.error( @@ -363,12 +404,8 @@ async function buttonStravaUnlink() { async function submitRetrieveGarminConnectActivitiesDays(days) { try { - const endDate = new Date() - const startDate = new Date() - startDate.setDate(endDate.getDate() - days) - const formattedStartDate = startDate.toISOString().split('T')[0] - const formattedEndDate = endDate.toISOString().split('T')[0] - await garminConnect.getGarminConnectActivitiesByDates(formattedStartDate, formattedEndDate) + const dates = getStartAndEndDateFromDaysAgo(days) + await garminConnect.getGarminConnectActivitiesByDates(dates.startDate, dates.endDate) push.info(t('settingsIntegrationsZone.loadingMessageRetrievingGarminConnectActivities')) } catch (error) { push.error( @@ -412,12 +449,8 @@ async function submitRetrieveGarminConnectHealthDataDataRange(dateRange) { async function submitRetrieveGarminConnectHealthDataDays(days) { try { - const endDate = new Date() - const startDate = new Date() - startDate.setDate(endDate.getDate() - days) - const formattedStartDate = startDate.toISOString().split('T')[0] - const formattedEndDate = endDate.toISOString().split('T')[0] - await garminConnect.getGarminConnectHealthDataByDates(formattedStartDate, formattedEndDate) + const dates = getStartAndEndDateFromDaysAgo(days) + await garminConnect.getGarminConnectHealthDataByDates(dates.startDate, dates.endDate) push.info(t('settingsIntegrationsZone.loadingMessageRetrievingGarminConnectHealthData')) } catch (error) { push.error( diff --git a/frontend/app/src/services/stravaService.js b/frontend/app/src/services/stravaService.js index b6486a96f..272976cb0 100644 --- a/frontend/app/src/services/stravaService.js +++ b/frontend/app/src/services/stravaService.js @@ -35,8 +35,8 @@ export const strava = { linkStravaCallback(state, code, scope) { return fetchPutRequest(`strava/link?state=${state}&code=${code}&scope=${scope}`) }, - getStravaActivitiesLastDays(days) { - return fetchGetRequest(`strava/activities/days/${days}`) + getStravaActivitiesByDates(startDate, endDate) { + return fetchGetRequest(`strava/activities?start_date=${startDate}&end_date=${endDate}`) }, getStravaGear() { return fetchGetRequest('strava/gear')