mirror of
https://github.com/joaovitoriasilva/endurain.git
synced 2026-01-10 08:17:59 -05:00
Add activity name support to file parsing functions
Introduces an optional activity_name parameter to GPX, TCX, and FIT file parsing utilities, allowing the activity name to be set from external sources (such as Garmin Connect). Updates related function signatures and usages throughout the codebase to propagate this parameter. Also adds windsurf to ActivityDistances schema and improves type annotations and error handling in activity utilities.
This commit is contained in:
@@ -71,6 +71,7 @@ class ActivityDistances(BaseModel):
|
||||
rowing: float
|
||||
snow_ski: float
|
||||
snowboard: float
|
||||
windsurf: float
|
||||
|
||||
|
||||
class ActivityEdit(BaseModel):
|
||||
|
||||
@@ -300,7 +300,9 @@ def serialize_activity(activity: activities_schema.Activity):
|
||||
dt = dt.replace(tzinfo=ZoneInfo("UTC"))
|
||||
return dt.astimezone(timezone).strftime("%Y-%m-%dT%H:%M:%S")
|
||||
|
||||
def convert_to_datetime_if_string(dt):
|
||||
def convert_to_datetime_if_string(dt: str | datetime | None) -> datetime:
|
||||
if dt is None:
|
||||
raise ValueError("Datetime cannot be None")
|
||||
if isinstance(dt, str):
|
||||
return datetime.fromisoformat(dt)
|
||||
return dt
|
||||
@@ -364,7 +366,8 @@ async def parse_and_store_activity_from_file(
|
||||
websocket_manager: websocket_schema.WebSocketManager,
|
||||
db: Session,
|
||||
from_garmin: bool = False,
|
||||
garminconnect_gear: dict = None,
|
||||
garminconnect_gear: dict | None = None,
|
||||
activity_name: str | None = None,
|
||||
):
|
||||
try:
|
||||
core_logger.print_to_log_and_console(
|
||||
@@ -403,6 +406,7 @@ async def parse_and_store_activity_from_file(
|
||||
file_extension,
|
||||
file_path,
|
||||
db,
|
||||
activity_name,
|
||||
)
|
||||
|
||||
if parsed_info is not None:
|
||||
@@ -430,8 +434,12 @@ async def parse_and_store_activity_from_file(
|
||||
split_records_by_activity,
|
||||
token_user_id,
|
||||
user_privacy_settings,
|
||||
int(garmin_connect_activity_id),
|
||||
garminconnect_gear,
|
||||
(
|
||||
int(garmin_connect_activity_id)
|
||||
if garmin_connect_activity_id
|
||||
else None
|
||||
),
|
||||
garminconnect_gear if garminconnect_gear else None,
|
||||
db,
|
||||
)
|
||||
else:
|
||||
@@ -509,6 +517,12 @@ async def parse_and_store_activity_from_uploaded_file(
|
||||
websocket_manager: websocket_schema.WebSocketManager,
|
||||
db: Session,
|
||||
):
|
||||
# Validate filename exists
|
||||
if file.filename is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Filename is required",
|
||||
)
|
||||
|
||||
# Get file extension
|
||||
_, file_extension = os.path.splitext(file.filename)
|
||||
@@ -654,7 +668,8 @@ def parse_file(
|
||||
file_extension: str,
|
||||
filename: str,
|
||||
db: Session,
|
||||
) -> dict:
|
||||
activity_name: str | None = None,
|
||||
) -> dict | None:
|
||||
try:
|
||||
if filename.lower() != "bulk_import/__init__.py":
|
||||
core_logger.print_to_log(f"Parsing file: {filename}")
|
||||
@@ -666,6 +681,7 @@ def parse_file(
|
||||
token_user_id,
|
||||
user_privacy_settings,
|
||||
db,
|
||||
activity_name,
|
||||
)
|
||||
elif file_extension.lower() == ".tcx":
|
||||
parsed_info = tcx_utils.parse_tcx_file(
|
||||
@@ -673,17 +689,17 @@ def parse_file(
|
||||
token_user_id,
|
||||
user_privacy_settings,
|
||||
db,
|
||||
activity_name,
|
||||
)
|
||||
elif file_extension.lower() == ".fit":
|
||||
# Parse the FIT file
|
||||
parsed_info = fit_utils.parse_fit_file(filename, db)
|
||||
parsed_info = fit_utils.parse_fit_file(filename, db, activity_name)
|
||||
else:
|
||||
# file extension not supported raise an HTTPException with a 406 Not Acceptable status code
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail="File extension not supported. Supported file extensions are .gpx, .fit and .tcx",
|
||||
)
|
||||
return None # Can't return parsed info if we haven't parsed anything
|
||||
return parsed_info
|
||||
else:
|
||||
return None
|
||||
@@ -708,7 +724,7 @@ async def store_activity(
|
||||
)
|
||||
|
||||
# Check if created_activity is None
|
||||
if created_activity is None:
|
||||
if created_activity is None or created_activity.id is None:
|
||||
# Log the error
|
||||
core_logger.print_to_log(
|
||||
"Error in store_activity - activity is None, error creating activity",
|
||||
|
||||
@@ -32,8 +32,8 @@ def create_activity_objects(
|
||||
sessions_records: dict,
|
||||
user_id: int,
|
||||
user_privacy_settings: users_privacy_settings_schema.UsersPrivacySettings,
|
||||
garmin_activity_id: int = None,
|
||||
garminconnect_gear: dict = None,
|
||||
garmin_activity_id: int | None = None,
|
||||
garminconnect_gear: dict | None = None,
|
||||
db: Session = None,
|
||||
) -> list:
|
||||
try:
|
||||
@@ -428,13 +428,15 @@ def split_records_by_activity(parsed_data: dict) -> dict:
|
||||
return sessions_records
|
||||
|
||||
|
||||
def parse_fit_file(file: str, db: Session) -> dict:
|
||||
def parse_fit_file(
|
||||
file: str, db: Session, activity_name_input: str | None = None
|
||||
) -> dict:
|
||||
try:
|
||||
# Initialize default values for various variables
|
||||
sessions = []
|
||||
time_offset = 0
|
||||
last_waypoint_time = None
|
||||
activity_name = "Workout"
|
||||
activity_name = activity_name_input if activity_name_input else "Workout"
|
||||
|
||||
# Arrays to store waypoint data
|
||||
lat_lon_waypoints = []
|
||||
|
||||
@@ -56,6 +56,7 @@ async def fetch_and_process_activities_by_dates(
|
||||
for activity in garmin_activities:
|
||||
# Get the activity ID
|
||||
activity_id = activity["activityId"]
|
||||
activity_name = activity["activityName"]
|
||||
|
||||
# Check if the activity is already stored in the database
|
||||
activity_db = activities_crud.get_activity_by_garminconnect_id_from_user_id(
|
||||
@@ -114,6 +115,7 @@ async def fetch_and_process_activities_by_dates(
|
||||
db,
|
||||
True,
|
||||
activity_gear,
|
||||
activity_name,
|
||||
)
|
||||
or []
|
||||
)
|
||||
@@ -205,8 +207,15 @@ async def get_user_garminconnect_activities_by_dates(
|
||||
|
||||
if garminconnect_client is not None:
|
||||
# Fetch Garmin Connect activities for the specified date range
|
||||
garminconnect_activities_processed = await fetch_and_process_activities_by_dates(
|
||||
garminconnect_client, start_date, end_date, user_id, websocket_manager, db
|
||||
garminconnect_activities_processed = (
|
||||
await fetch_and_process_activities_by_dates(
|
||||
garminconnect_client,
|
||||
start_date,
|
||||
end_date,
|
||||
user_id,
|
||||
websocket_manager,
|
||||
db,
|
||||
)
|
||||
)
|
||||
|
||||
# Log the start of the activities processing
|
||||
|
||||
@@ -22,6 +22,7 @@ def parse_gpx_file(
|
||||
user_id: int,
|
||||
user_privacy_settings: users_privacy_settings_schema.UsersPrivacySettings,
|
||||
db: Session,
|
||||
activity_name_input: str | None = None,
|
||||
) -> dict:
|
||||
try:
|
||||
# Create an instance of TimezoneFinder
|
||||
@@ -45,7 +46,7 @@ def parse_gpx_file(
|
||||
np = None
|
||||
avg_speed = None
|
||||
max_speed = None
|
||||
activity_name = "Workout"
|
||||
activity_name = activity_name_input if activity_name_input else "Workout"
|
||||
activity_description = None
|
||||
process_one_time_fields = 0
|
||||
gear_id = None
|
||||
@@ -105,7 +106,7 @@ def parse_gpx_file(
|
||||
|
||||
# Extract elevation, time, and location details
|
||||
elevation, time = point.elevation, point.time
|
||||
|
||||
|
||||
# Skip trackpoints without time data (common in some OsmAnd exports)
|
||||
if time is None:
|
||||
continue
|
||||
|
||||
@@ -12,7 +12,9 @@ import core.logger as core_logger
|
||||
import core.config as core_config
|
||||
|
||||
|
||||
def parse_tcx_file(file, user_id, user_privacy_settings, db):
|
||||
def parse_tcx_file(
|
||||
file, user_id, user_privacy_settings, db, activity_name_input: str | None = None
|
||||
) -> dict:
|
||||
tcx_file = tcxreader.TCXReader().read(file)
|
||||
trackpoints = tcx_file.trackpoints_to_dict()
|
||||
|
||||
@@ -26,7 +28,7 @@ def parse_tcx_file(file, user_id, user_privacy_settings, db):
|
||||
city = None
|
||||
town = None
|
||||
country = None
|
||||
activity_name = "Workout"
|
||||
activity_name = activity_name_input if activity_name_input else "Workout"
|
||||
avg_power = None
|
||||
max_power = None
|
||||
np = None
|
||||
|
||||
Reference in New Issue
Block a user