mirror of
https://github.com/joaovitoriasilva/endurain.git
synced 2026-01-09 15:57:59 -05:00
Added missing attribute pace to waypoints array when processing Strava activities.
This commit is contained in:
@@ -10,6 +10,7 @@ from sqlalchemy import func
|
||||
from stravalib.client import Client
|
||||
from pint import Quantity
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from math import radians, sin, cos, sqrt, atan2
|
||||
import logging
|
||||
import requests
|
||||
|
||||
@@ -240,20 +241,36 @@ def get_strava_activities(start_date: datetime):
|
||||
# response.raise_for_status() # Raise an error for bad responses
|
||||
|
||||
# store_strava_activities_stravaLib(response.json(), user.id, user.strava_token)
|
||||
#store_strava_activities_stravaLib(user.id, user.strava_token, start_date)
|
||||
# store_strava_activities_stravaLib(user.id, user.strava_token, start_date)
|
||||
|
||||
stravaClient = Client(access_token=user.strava_token)
|
||||
|
||||
strava_activities = list(stravaClient.get_activities(after=start_date))
|
||||
chunk_size = len(strava_activities) // 4 # Adjust the number of threads as needed
|
||||
activity_chunks = [strava_activities[i:i + chunk_size] for i in range(0, len(strava_activities), chunk_size)]
|
||||
|
||||
strava_activities = list(
|
||||
stravaClient.get_activities(after=start_date)
|
||||
)
|
||||
chunk_size = (
|
||||
len(strava_activities) // 4
|
||||
) # Adjust the number of threads as needed
|
||||
activity_chunks = [
|
||||
strava_activities[i : i + chunk_size]
|
||||
for i in range(0, len(strava_activities), chunk_size)
|
||||
]
|
||||
|
||||
with ThreadPoolExecutor() as executor:
|
||||
# Process each chunk of activities using threads
|
||||
results = list(executor.map(lambda chunk: process_activities(chunk, user.id, stravaClient), activity_chunks))
|
||||
results = list(
|
||||
executor.map(
|
||||
lambda chunk: process_activities(
|
||||
chunk, user.id, stravaClient
|
||||
),
|
||||
activity_chunks,
|
||||
)
|
||||
)
|
||||
|
||||
# Flatten the list of results
|
||||
activities_to_insert = [activity for sublist in results for activity in sublist]
|
||||
activities_to_insert = [
|
||||
activity for sublist in results for activity in sublist
|
||||
]
|
||||
|
||||
# Bulk insert all activities
|
||||
with get_db_session() as db_session:
|
||||
@@ -269,6 +286,7 @@ def get_strava_activities(start_date: datetime):
|
||||
except NameError as db_err:
|
||||
logger.error(f"Database error: {db_err}")
|
||||
|
||||
|
||||
def process_activities(strava_activities, user_id, stravaClient):
|
||||
activities_to_insert = []
|
||||
|
||||
@@ -289,6 +307,7 @@ def process_activities(strava_activities, user_id, stravaClient):
|
||||
|
||||
return activities_to_insert
|
||||
|
||||
|
||||
def process_activity(activity, user_id, stravaClient):
|
||||
start_date_parsed = activity.start_date
|
||||
# Ensure activity.elapsed_time is a numerical value
|
||||
@@ -297,9 +316,7 @@ def process_activity(activity, user_id, stravaClient):
|
||||
if isinstance(activity.elapsed_time, timedelta)
|
||||
else activity.elapsed_time
|
||||
)
|
||||
end_date_parsed = start_date_parsed + timedelta(
|
||||
seconds=elapsed_time_seconds
|
||||
)
|
||||
end_date_parsed = start_date_parsed + timedelta(seconds=elapsed_time_seconds)
|
||||
|
||||
latitude = 0
|
||||
longitude = 0
|
||||
@@ -362,9 +379,7 @@ def process_activity(activity, user_id, stravaClient):
|
||||
heart_rates = streams["heartrate"].data if "heartrate" in streams else []
|
||||
cadences = streams["cadence"].data if "cadence" in streams else []
|
||||
powers = streams["watts"].data if "watts" in streams else []
|
||||
velocities = (
|
||||
streams["velocity_smooth"].data if "velocity_smooth" in streams else []
|
||||
)
|
||||
velocities = streams["velocity_smooth"].data if "velocity_smooth" in streams else []
|
||||
|
||||
for i in range(len(heart_rates)):
|
||||
waypoint = {
|
||||
@@ -376,6 +391,7 @@ def process_activity(activity, user_id, stravaClient):
|
||||
"cad": cadences[i] if i < len(cadences) else None,
|
||||
"power": powers[i] if i < len(powers) else None,
|
||||
"vel": velocities[i] if i < len(velocities) else None,
|
||||
"pace": 1/velocities[i] if i < len(velocities) and velocities[i] != 0 else None,
|
||||
# Add other relevant fields based on your requirements
|
||||
}
|
||||
|
||||
@@ -445,7 +461,7 @@ def process_activity(activity, user_id, stravaClient):
|
||||
city=city,
|
||||
town=town,
|
||||
country=country,
|
||||
#created_at=func.now(), # Use func.now() to set 'created_at' to the current timestamp
|
||||
# created_at=func.now(), # Use func.now() to set 'created_at' to the current timestamp
|
||||
created_at=datetime.utcnow(),
|
||||
waypoints=waypoints,
|
||||
elevation_gain=elevation_gain,
|
||||
@@ -456,451 +472,4 @@ def process_activity(activity, user_id, stravaClient):
|
||||
strava_activity_id=activity.id,
|
||||
)
|
||||
|
||||
return newActivity
|
||||
|
||||
|
||||
def store_strava_activities_stravaLib(user_id, strava_token, start_date):
|
||||
stravaClient = Client(access_token=strava_token)
|
||||
#start_date = (datetime.utcnow() - timedelta(days=7)).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
for activity in stravaClient.get_activities(after=start_date):
|
||||
with get_db_session() as db_session:
|
||||
# Use SQLAlchemy to query the gear record by ID
|
||||
activity_record = (
|
||||
db_session.query(Activity)
|
||||
.filter(Activity.strava_activity_id == activity.id)
|
||||
.first()
|
||||
)
|
||||
|
||||
if activity_record:
|
||||
# Skip to the next iteration
|
||||
continue
|
||||
|
||||
start_date_parsed = activity.start_date
|
||||
# Ensure activity.elapsed_time is a numerical value
|
||||
elapsed_time_seconds = (
|
||||
activity.elapsed_time.total_seconds()
|
||||
if isinstance(activity.elapsed_time, timedelta)
|
||||
else activity.elapsed_time
|
||||
)
|
||||
end_date_parsed = start_date_parsed + timedelta(
|
||||
seconds=elapsed_time_seconds
|
||||
)
|
||||
|
||||
latitude = 0
|
||||
longitude = 0
|
||||
|
||||
if hasattr(activity, "start_latlng") and activity.start_latlng is not None:
|
||||
latitude = activity.start_latlng.lat
|
||||
longitude = activity.start_latlng.lon
|
||||
|
||||
city = None
|
||||
town = None
|
||||
country = None
|
||||
if latitude != 0 and longitude != 0:
|
||||
url = f"https://geocode.maps.co/reverse?lat={latitude}&lon={longitude}"
|
||||
try:
|
||||
# Make a GET request
|
||||
response = requests.get(url)
|
||||
|
||||
# Check if the request was successful (status code 200)
|
||||
if response.status_code == 200:
|
||||
# Parse the JSON response
|
||||
data = response.json()
|
||||
|
||||
# Extract the town and country from the address components
|
||||
city = data.get("address", {}).get("city", None)
|
||||
town = data.get("address", {}).get("town", None)
|
||||
country = data.get("address", {}).get("country", None)
|
||||
else:
|
||||
print(f"Error location: {response.status_code}")
|
||||
print(f"Error location: {url}")
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
# List to store constructed waypoints
|
||||
waypoints = []
|
||||
|
||||
# Initialize variables for elevation gain and loss
|
||||
elevation_gain = 0
|
||||
elevation_loss = 0
|
||||
previous_elevation = None
|
||||
|
||||
# Get streams for the activity
|
||||
streams = stravaClient.get_activity_streams(
|
||||
activity.id,
|
||||
types=[
|
||||
"latlng",
|
||||
"altitude",
|
||||
"time",
|
||||
"heartrate",
|
||||
"cadence",
|
||||
"watts",
|
||||
"velocity_smooth",
|
||||
],
|
||||
)
|
||||
|
||||
# Extract data from streams
|
||||
latitudes = streams["latlng"].data if "latlng" in streams else []
|
||||
longitudes = streams["latlng"].data if "latlng" in streams else []
|
||||
elevations = streams["altitude"].data if "altitude" in streams else []
|
||||
times = streams["time"].data if "time" in streams else []
|
||||
heart_rates = streams["heartrate"].data if "heartrate" in streams else []
|
||||
cadences = streams["cadence"].data if "cadence" in streams else []
|
||||
powers = streams["watts"].data if "watts" in streams else []
|
||||
velocities = (
|
||||
streams["velocity_smooth"].data if "velocity_smooth" in streams else []
|
||||
)
|
||||
|
||||
for i in range(len(heart_rates)):
|
||||
waypoint = {
|
||||
"lat": latitudes[i] if i < len(latitudes) else None,
|
||||
"lon": longitudes[i] if i < len(longitudes) else None,
|
||||
"ele": elevations[i] if i < len(elevations) else None,
|
||||
"time": times[i] if i < len(times) else None,
|
||||
"hr": heart_rates[i] if i < len(heart_rates) else None,
|
||||
"cad": cadences[i] if i < len(cadences) else None,
|
||||
"power": powers[i] if i < len(powers) else None,
|
||||
"vel": velocities[i] if i < len(velocities) else None,
|
||||
# Add other relevant fields based on your requirements
|
||||
}
|
||||
|
||||
# Calculate elevation gain and loss on-the-fly
|
||||
current_elevation = elevations[i] if i < len(elevations) else None
|
||||
|
||||
if current_elevation is not None:
|
||||
if previous_elevation is not None:
|
||||
elevation_change = current_elevation - previous_elevation
|
||||
|
||||
if elevation_change > 0:
|
||||
elevation_gain += elevation_change
|
||||
else:
|
||||
elevation_loss += abs(elevation_change)
|
||||
|
||||
previous_elevation = current_elevation
|
||||
|
||||
# Append the constructed waypoint to the waypoints list
|
||||
waypoints.append(waypoint)
|
||||
|
||||
average_speed = 0
|
||||
if activity.average_speed is not None:
|
||||
average_speed = (
|
||||
float(activity.average_speed.magnitude)
|
||||
if isinstance(activity.average_speed, Quantity)
|
||||
else activity.average_speed
|
||||
)
|
||||
|
||||
average_pace = 1 / average_speed if average_speed != 0 else 0
|
||||
|
||||
average_watts = 0
|
||||
if activity.average_watts is not None:
|
||||
average_watts = activity.average_watts
|
||||
|
||||
auxType = 10 # Default value
|
||||
type_mapping = {
|
||||
"running": 1,
|
||||
"Run": 1,
|
||||
"trail running": 2,
|
||||
"TrailRun": 2,
|
||||
"VirtualRun": 3,
|
||||
"cycling": 4,
|
||||
"Ride": 4,
|
||||
"GravelRide": 5,
|
||||
"EBikeRide": 6,
|
||||
"EMountainBikeRide": 6,
|
||||
"VirtualRide": 7,
|
||||
"virtual_ride": 7,
|
||||
"MountainBikeRide": 8,
|
||||
"swimming": 9,
|
||||
"Swim": 9,
|
||||
"open_water_swimming": 9,
|
||||
"Workout": 10,
|
||||
}
|
||||
auxType = type_mapping.get(activity.sport_type, 10)
|
||||
|
||||
# Create a new Activity record
|
||||
activity = Activity(
|
||||
user_id=user_id,
|
||||
name=activity.name,
|
||||
distance=round(float(activity.distance))
|
||||
if isinstance(activity.distance, Quantity)
|
||||
else round(activity.distance),
|
||||
activity_type=auxType,
|
||||
start_time=start_date_parsed,
|
||||
end_time=end_date_parsed,
|
||||
city=city,
|
||||
town=town,
|
||||
country=country,
|
||||
created_at=func.now(), # Use func.now() to set 'created_at' to the current timestamp
|
||||
waypoints=waypoints,
|
||||
elevation_gain=elevation_gain,
|
||||
elevation_loss=elevation_loss,
|
||||
pace=average_pace,
|
||||
average_speed=average_speed,
|
||||
average_power=average_watts,
|
||||
strava_activity_id=activity.id,
|
||||
)
|
||||
|
||||
try:
|
||||
# Store the Activity record in the database
|
||||
with get_db_session() as db_session:
|
||||
db_session.add(activity)
|
||||
db_session.commit()
|
||||
db_session.refresh(
|
||||
activity
|
||||
) # This will ensure that the activity object is updated with the ID from the database
|
||||
return {"message": "Activities retrieved"}
|
||||
except Exception as err:
|
||||
print(err)
|
||||
logger.error(err)
|
||||
|
||||
|
||||
def store_strava_activities(strava_activities, user_id, strava_token):
|
||||
from . import activitiesController
|
||||
|
||||
try:
|
||||
params = {
|
||||
"access_token": strava_token,
|
||||
}
|
||||
|
||||
# start_date = (datetime.utcnow() - timedelta(days=7)).strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
# strava_activities = get_strava_activities(start_date)
|
||||
if strava_activities:
|
||||
for activity in strava_activities:
|
||||
try:
|
||||
with get_db_session() as db_session:
|
||||
# Use SQLAlchemy to query the gear record by ID
|
||||
activity_record = (
|
||||
db_session.query(Activity)
|
||||
.filter(Activity.strava_activity_id == activity.get("id"))
|
||||
.first()
|
||||
)
|
||||
|
||||
if activity_record:
|
||||
# Skip to the next iteration
|
||||
continue
|
||||
|
||||
start_date_parsed = activitiesController.parse_timestamp(
|
||||
activity.get("start_date")
|
||||
)
|
||||
end_date_parsed = start_date_parsed + timedelta(
|
||||
seconds=activity.get("elapsed_time")
|
||||
)
|
||||
|
||||
latitude, longitude = activity.get("start_latlng", [0, 0])
|
||||
city = None
|
||||
town = None
|
||||
country = None
|
||||
if latitude != 0 and longitude != 0:
|
||||
url = f"https://geocode.maps.co/reverse?lat={latitude}&lon={longitude}"
|
||||
try:
|
||||
# Make a GET request
|
||||
response = requests.get(url)
|
||||
|
||||
# Check if the request was successful (status code 200)
|
||||
if response.status_code == 200:
|
||||
# Parse the JSON response
|
||||
data = response.json()
|
||||
|
||||
# Extract the town and country from the address components
|
||||
city = data.get("address", {}).get("city", None)
|
||||
town = data.get("address", {}).get("town", None)
|
||||
country = data.get("address", {}).get("country", None)
|
||||
else:
|
||||
print(f"Error location: {response.status_code}")
|
||||
print(f"Error location: {url}")
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
activity_id = activity.get("id")
|
||||
strava_api_url_streams = f"https://www.strava.com/api/v3/activities/{activity_id}/streams"
|
||||
|
||||
# List to store constructed waypoints
|
||||
waypoints = []
|
||||
|
||||
# Initialize variables for elevation gain and loss
|
||||
elevation_gain = 0
|
||||
elevation_loss = 0
|
||||
|
||||
try:
|
||||
# Make a GET request to retrieve activity streams
|
||||
response = requests.get(strava_api_url_streams, params=params)
|
||||
|
||||
# Check if the request was successful (status code 200)
|
||||
if response.status_code == 200:
|
||||
# Parse the JSON response
|
||||
activity_streams = response.json()
|
||||
|
||||
# Extract relevant streams data
|
||||
latlng_stream = activity_streams.get("latlng", {}).get(
|
||||
"data", []
|
||||
)
|
||||
time_stream = activity_streams.get("time", {}).get(
|
||||
"data", []
|
||||
)
|
||||
elevation_stream = activity_streams.get("altitude", {}).get(
|
||||
"data", []
|
||||
)
|
||||
heart_rate_stream = activity_streams.get(
|
||||
"heartrate", {}
|
||||
).get("data", [])
|
||||
cadence_stream = activity_streams.get("cadence", {}).get(
|
||||
"data", []
|
||||
)
|
||||
power_stream = activity_streams.get("watts", {}).get(
|
||||
"data", []
|
||||
)
|
||||
velocity_stream = activity_streams.get(
|
||||
"velocity_smooth", {}
|
||||
).get("data", [])
|
||||
|
||||
# Ensure all streams have the same length (adjust as needed)
|
||||
stream_length = min(
|
||||
len(latlng_stream),
|
||||
len(time_stream),
|
||||
len(elevation_stream),
|
||||
len(heart_rate_stream),
|
||||
len(cadence_stream),
|
||||
len(power_stream),
|
||||
len(velocity_stream),
|
||||
)
|
||||
|
||||
# Iterate over the streams and construct waypoints
|
||||
for i in range(stream_length):
|
||||
latitude, longitude = (
|
||||
latlng_stream[i]
|
||||
if i < len(latlng_stream)
|
||||
else (0, 0)
|
||||
)
|
||||
time = time_stream[i] if i < len(time_stream) else ""
|
||||
elevation = (
|
||||
elevation_stream[i]
|
||||
if i < len(elevation_stream)
|
||||
else 0
|
||||
)
|
||||
heart_rate = (
|
||||
heart_rate_stream[i]
|
||||
if i < len(heart_rate_stream)
|
||||
else 0
|
||||
)
|
||||
cadence = (
|
||||
cadence_stream[i] if i < len(cadence_stream) else 0
|
||||
)
|
||||
power = power_stream[i] if i < len(power_stream) else 0
|
||||
velocity = (
|
||||
velocity_stream[i]
|
||||
if i < len(velocity_stream)
|
||||
else 0
|
||||
)
|
||||
|
||||
elevation_current = (
|
||||
elevation_stream[i]
|
||||
if i < len(elevation_stream)
|
||||
else 0
|
||||
)
|
||||
elevation_previous = (
|
||||
elevation_stream[i - 1]
|
||||
if i - 1 < len(elevation_stream)
|
||||
else 0
|
||||
)
|
||||
|
||||
# Calculate the difference in elevation
|
||||
elevation_difference = (
|
||||
elevation_current - elevation_previous
|
||||
)
|
||||
|
||||
# Update elevation gain and loss based on the difference
|
||||
if elevation_difference > 0:
|
||||
elevation_gain += elevation_difference
|
||||
elif elevation_difference < 0:
|
||||
elevation_loss += abs(elevation_difference)
|
||||
|
||||
# Construct the waypoint dictionary
|
||||
waypoint = {
|
||||
"lat": latitude,
|
||||
"lon": longitude,
|
||||
"ele": elevation,
|
||||
"time": time,
|
||||
"hr": heart_rate,
|
||||
"cad": cadence,
|
||||
"power": power,
|
||||
"vel": velocity,
|
||||
# Add other relevant fields based on your requirements
|
||||
}
|
||||
|
||||
# Append the constructed waypoint to the waypoints list
|
||||
waypoints.append(waypoint)
|
||||
|
||||
else:
|
||||
print(f"Error streams: {response.status_code}")
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
average_pace = (
|
||||
1 / activity.get("average_speed", 0)
|
||||
if activity.get("average_speed", 0) != 0
|
||||
else 0
|
||||
)
|
||||
|
||||
# activitiesController.create_activity([activity.get("distance", 0), activity.get("name", "Unnamed activity"), activity.get("type", "Workout"), start_date_parsed, end_date_parsed, city, town, country, waypoints, elevation_gain, elevation_loss, average_pace, activity.get("average_speed", 0), activity.get("average_watts", 0), activity.get("id")],token)
|
||||
|
||||
auxType = 10 # Default value
|
||||
type_mapping = {
|
||||
"running": 1,
|
||||
"Run": 1,
|
||||
"trail running": 2,
|
||||
"TrailRun": 2,
|
||||
"VirtualRun": 3,
|
||||
"cycling": 4,
|
||||
"Ride": 4,
|
||||
"GravelRide": 5,
|
||||
"EBikeRide": 6,
|
||||
"EMountainBikeRide": 6,
|
||||
"VirtualRide": 7,
|
||||
"virtual_ride": 7,
|
||||
"MountainBikeRide": 8,
|
||||
"swimming": 9,
|
||||
"open_water_swimming": 9,
|
||||
"Workout": 10,
|
||||
}
|
||||
auxType = type_mapping.get(activity.get("sport_type", "Workout"), 10)
|
||||
|
||||
aux = activity.get("sport_type")
|
||||
logger.info(f"No Strava activities returned {aux}")
|
||||
|
||||
# Create a new Activity record
|
||||
activity = Activity(
|
||||
user_id=user_id,
|
||||
name=activity.get("name", "Unnamed activity"),
|
||||
distance=round(activity.get("distance", 0)),
|
||||
activity_type=auxType,
|
||||
start_time=start_date_parsed,
|
||||
end_time=end_date_parsed,
|
||||
city=city,
|
||||
town=town,
|
||||
country=country,
|
||||
created_at=func.now(), # Use func.now() to set 'created_at' to the current timestamp
|
||||
waypoints=waypoints,
|
||||
elevation_gain=elevation_gain,
|
||||
elevation_loss=elevation_loss,
|
||||
pace=average_pace,
|
||||
average_speed=activity.get("average_speed", 0),
|
||||
average_power=activity.get("average_watts", 0),
|
||||
strava_activity_id=activity.get("id"),
|
||||
)
|
||||
|
||||
# Store the Activity record in the database
|
||||
with get_db_session() as db_session:
|
||||
db_session.add(activity)
|
||||
db_session.commit()
|
||||
db_session.refresh(
|
||||
activity
|
||||
) # This will ensure that the activity object is updated with the ID from the database
|
||||
return {"message": "Activities retrieved"}
|
||||
except Exception as err:
|
||||
print(err)
|
||||
logger.error(err)
|
||||
else:
|
||||
logger.info("No Strava activities returned")
|
||||
except JWTError:
|
||||
raise HTTPException(status_code=401, detail="Unauthorized")
|
||||
return newActivity
|
||||
Reference in New Issue
Block a user