mirror of
https://github.com/joaovitoriasilva/endurain.git
synced 2026-01-09 15:57:59 -05:00
BMI logic + fixes
[aux] added auxiliary scripts [backend] added error logic to error printing [backend] rollbacked changes from Datetime to Timestamp [backend] added body composition sync from Garmin [backend] added additional validations for empty .gpx files [backend] add calculate BMI logic [backend] add BMI calculation in migration [backend] added Timezone logic to Strava activity import [frontend] removed block that prevent deleting Strava or GC activity [frontend] added new generic modal for single number input [frontend] replaced modal that retrieves Strava and GC activities for number of days with new generic modal [frontend] added shoes gear in add gear to walk and hike activities [frontend] minor bug fixes
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,6 +1,9 @@
|
|||||||
# General
|
# General
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
|
|
||||||
|
# aux folder
|
||||||
|
aux_scripts/.garminconnect
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
backend/app/__pycache__/
|
backend/app/__pycache__/
|
||||||
backend/app/*/__pycache__/
|
backend/app/*/__pycache__/
|
||||||
|
|||||||
99
aux_scripts/aux_gc_getbcstats.py
Normal file
99
aux_scripts/aux_gc_getbcstats.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
from garminconnect import (
|
||||||
|
Garmin,
|
||||||
|
GarminConnectAuthenticationError,
|
||||||
|
)
|
||||||
|
import requests
|
||||||
|
import datetime
|
||||||
|
from garth.exc import GarthHTTPError
|
||||||
|
import os
|
||||||
|
from getpass import getpass
|
||||||
|
|
||||||
|
email = os.getenv("EMAIL")
|
||||||
|
password = os.getenv("PASSWORD")
|
||||||
|
tokenstore = os.getenv("GARMINTOKENS") or ".garminconnect"
|
||||||
|
api = None
|
||||||
|
today = datetime.date.today()
|
||||||
|
|
||||||
|
|
||||||
|
def get_date():
|
||||||
|
"""Get date from user input."""
|
||||||
|
|
||||||
|
return input("Enter date (dd-mm-yyyy): ")
|
||||||
|
|
||||||
|
|
||||||
|
def get_credentials():
|
||||||
|
"""Get user credentials."""
|
||||||
|
|
||||||
|
email = input("Login e-mail: ")
|
||||||
|
password = getpass("Enter password: ")
|
||||||
|
|
||||||
|
return email, password
|
||||||
|
|
||||||
|
|
||||||
|
def get_mfa():
|
||||||
|
"""Get MFA."""
|
||||||
|
|
||||||
|
return input("MFA one-time code: ")
|
||||||
|
|
||||||
|
|
||||||
|
def init_api(email, password):
|
||||||
|
"""Initialize Garmin API with your credentials."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Using Oauth1 and OAuth2 token files from directory
|
||||||
|
print(
|
||||||
|
f"Trying to login to Garmin Connect using token data from directory '{tokenstore}'...\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
garmin = Garmin()
|
||||||
|
garmin.login(tokenstore)
|
||||||
|
|
||||||
|
except (FileNotFoundError, GarthHTTPError, GarminConnectAuthenticationError):
|
||||||
|
# Session is expired. You'll need to log in again
|
||||||
|
print(
|
||||||
|
"Login tokens not present, login with your Garmin Connect credentials to generate them.\n"
|
||||||
|
f"They will be stored in '{tokenstore}' for future use.\n"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
# Ask for credentials if not set as environment variables
|
||||||
|
if not email or not password:
|
||||||
|
email, password = get_credentials()
|
||||||
|
|
||||||
|
garmin = Garmin(
|
||||||
|
email=email, password=password, is_cn=False, prompt_mfa=get_mfa
|
||||||
|
)
|
||||||
|
garmin.login()
|
||||||
|
# Save Oauth1 and Oauth2 token files to directory for next login
|
||||||
|
garmin.garth.dump(tokenstore)
|
||||||
|
print(
|
||||||
|
f"Oauth tokens stored in '{tokenstore}' directory for future use. (first method)\n"
|
||||||
|
)
|
||||||
|
except (
|
||||||
|
FileNotFoundError,
|
||||||
|
GarthHTTPError,
|
||||||
|
GarminConnectAuthenticationError,
|
||||||
|
requests.exceptions.HTTPError,
|
||||||
|
) as err:
|
||||||
|
print(err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return garmin
|
||||||
|
|
||||||
|
|
||||||
|
date = get_date()
|
||||||
|
date_object = datetime.datetime.strptime(date, "%d-%m-%Y")
|
||||||
|
|
||||||
|
if not api:
|
||||||
|
api = init_api(email, password)
|
||||||
|
|
||||||
|
if api:
|
||||||
|
garmin_bc = api.get_body_composition(date_object, date_object)
|
||||||
|
|
||||||
|
if garmin_bc is None:
|
||||||
|
# Log an informational event if no body composition were found
|
||||||
|
print(
|
||||||
|
f"User: No new Garmin Connect body composition found after {today}: garmin_bc is None"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Return the number of body compositions processed
|
||||||
|
print(garmin_bc)
|
||||||
@@ -30,7 +30,9 @@ def get_all_activities(db: Session):
|
|||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_all_activities: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in get_all_activities: {err}", "error", exc=err
|
||||||
|
)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -63,7 +65,9 @@ def get_user_activities(
|
|||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_user_activities: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in get_user_activities: {err}", "error", exc=err
|
||||||
|
)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -537,7 +541,9 @@ def get_activities_if_contains_name(name: str, user_id: int, db: Session):
|
|||||||
) from err
|
) from err
|
||||||
|
|
||||||
|
|
||||||
def create_activity(activity: activities_schema.Activity, db: Session):
|
def create_activity(
|
||||||
|
activity: activities_schema.Activity, db: Session
|
||||||
|
) -> activities_schema.Activity:
|
||||||
try:
|
try:
|
||||||
# Create a new activity
|
# Create a new activity
|
||||||
db_activity = activities_models.Activity(
|
db_activity = activities_models.Activity(
|
||||||
@@ -592,7 +598,7 @@ def create_activity(activity: activities_schema.Activity, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in create_activity: {err}", "error")
|
core_logger.print_to_log(f"Error in create_activity: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -638,7 +644,7 @@ def edit_activity(user_id: int, activity: activities_schema.Activity, db: Sessio
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in edit_activity: {err}", "error")
|
core_logger.print_to_log(f"Error in edit_activity: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -664,7 +670,9 @@ def add_gear_to_activity(activity_id: int, gear_id: int, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in add_gear_to_activity: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in add_gear_to_activity: {err}", "error", exc=err
|
||||||
|
)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -725,7 +733,7 @@ def delete_activity(activity_id: int, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in delete_activity: {err}", "error")
|
core_logger.print_to_log(f"Error in delete_activity: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from sqlalchemy import (
|
|||||||
Column,
|
Column,
|
||||||
Integer,
|
Integer,
|
||||||
String,
|
String,
|
||||||
TIMESTAMP,
|
DATETIME,
|
||||||
ForeignKey,
|
ForeignKey,
|
||||||
DECIMAL,
|
DECIMAL,
|
||||||
BigInteger,
|
BigInteger,
|
||||||
@@ -38,11 +38,13 @@ class Activity(Base):
|
|||||||
comment="Gear type (1 - mountain bike, 2 - gravel bike, ...)",
|
comment="Gear type (1 - mountain bike, 2 - gravel bike, ...)",
|
||||||
)
|
)
|
||||||
start_time = Column(
|
start_time = Column(
|
||||||
TIMESTAMP, nullable=False, comment="Activity start date (TIMESTAMP)"
|
DATETIME, nullable=False, comment="Activity start date (DATETIME)"
|
||||||
)
|
)
|
||||||
end_time = Column(TIMESTAMP, nullable=False, comment="Activity end date (TIMESTAMP)")
|
end_time = Column(DATETIME, nullable=False, comment="Activity end date (DATETIME)")
|
||||||
timezone = Column(
|
timezone = Column(
|
||||||
String(length=250), nullable=True, comment="Activity timezone (May include spaces)"
|
String(length=250),
|
||||||
|
nullable=True,
|
||||||
|
comment="Activity timezone (May include spaces)",
|
||||||
)
|
)
|
||||||
total_elapsed_time = Column(
|
total_elapsed_time = Column(
|
||||||
DECIMAL(precision=20, scale=10),
|
DECIMAL(precision=20, scale=10),
|
||||||
@@ -66,7 +68,7 @@ class Activity(Base):
|
|||||||
comment="Activity country (May include spaces)",
|
comment="Activity country (May include spaces)",
|
||||||
)
|
)
|
||||||
created_at = Column(
|
created_at = Column(
|
||||||
TIMESTAMP, nullable=False, comment="Activity creation date (TIMESTAMP)"
|
DATETIME, nullable=False, comment="Activity creation date (DATETIME)"
|
||||||
)
|
)
|
||||||
elevation_gain = Column(Integer, nullable=True, comment="Elevation gain in meters")
|
elevation_gain = Column(Integer, nullable=True, comment="Elevation gain in meters")
|
||||||
elevation_loss = Column(Integer, nullable=True, comment="Elevation loss in meters")
|
elevation_loss = Column(Integer, nullable=True, comment="Elevation loss in meters")
|
||||||
@@ -138,4 +140,4 @@ class Activity(Base):
|
|||||||
"ActivityStreams",
|
"ActivityStreams",
|
||||||
back_populates="activity",
|
back_populates="activity",
|
||||||
cascade="all, delete-orphan",
|
cascade="all, delete-orphan",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -646,10 +646,10 @@ async def delete_activity(
|
|||||||
os.remove(file)
|
os.remove(file)
|
||||||
except FileNotFoundError as err:
|
except FileNotFoundError as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"File not found {file}: {err}", "error")
|
core_logger.print_to_log(f"File not found {file}: {err}", "error", exc=err)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error deleting file {file}: {err}", "error")
|
core_logger.print_to_log(f"Error deleting file {file}: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Return success message
|
# Return success message
|
||||||
return {"detail": f"Activity {activity_id} deleted successfully"}
|
return {"detail": f"Activity {activity_id} deleted successfully"}
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ def move_file(new_dir: str, new_filename: str, file_path: str):
|
|||||||
shutil.move(file_path, new_file_path)
|
shutil.move(file_path, new_file_path)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in move_file - {str(err)}", "error")
|
core_logger.print_to_log(f"Error in move_file - {str(err)}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -267,7 +267,7 @@ def parse_file(token_user_id: int, file_extension: str, filename: str) -> dict:
|
|||||||
raise http_err
|
raise http_err
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in parse_file - {str(err)}", "error")
|
core_logger.print_to_log(f"Error in parse_file - {str(err)}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ def get_activity_streams(activity_id: int, db: Session):
|
|||||||
return activity_streams
|
return activity_streams
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_activity_streams: {err}", "error")
|
core_logger.print_to_log(f"Error in get_activity_streams: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -56,7 +56,7 @@ def get_activity_stream_by_type(activity_id: int, stream_type: int, db: Session)
|
|||||||
return activity_stream
|
return activity_stream
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_activity_stream_by_type: {err}", "error")
|
core_logger.print_to_log(f"Error in get_activity_stream_by_type: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -92,7 +92,7 @@ def create_activity_streams(
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger(f"Error in create_activity_streams: {err}", "error")
|
core_logger(f"Error in create_activity_streams: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
"""v0.6.5 migration
|
"""v0.6.5 migration
|
||||||
|
|
||||||
Revision ID: 7ba9b71fe203
|
Revision ID: 542605083c0c
|
||||||
Revises: 65a0f1d72997
|
Revises: 65a0f1d72997
|
||||||
Create Date: 2024-11-25 13:07:30.142799
|
Create Date: 2024-12-09 16:02:43.332696
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from typing import Sequence, Union
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '7ba9b71fe203'
|
revision: str = '542605083c0c'
|
||||||
down_revision: Union[str, None] = '65a0f1d72997'
|
down_revision: Union[str, None] = '65a0f1d72997'
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
@@ -20,56 +20,52 @@ depends_on: Union[str, Sequence[str], None] = None
|
|||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
op.add_column('gear', sa.Column('initial_kms', sa.DECIMAL(precision=11, scale=3), nullable=False, comment='Initial kilometers of the gear'))
|
op.add_column('activities', sa.Column('timezone', sa.String(length=250), nullable=True, comment='Activity timezone (May include spaces)'))
|
||||||
op.alter_column('activities', 'start_time',
|
op.alter_column('activities', 'start_time',
|
||||||
existing_type=sa.DATETIME(),
|
existing_type=mysql.DATETIME(),
|
||||||
type_=sa.TIMESTAMP(),
|
comment='Activity start date (DATETIME)',
|
||||||
comment='Activity start date (TIMESTAMP)',
|
|
||||||
existing_comment='Activity start date (datetime)',
|
existing_comment='Activity start date (datetime)',
|
||||||
existing_nullable=False)
|
existing_nullable=False)
|
||||||
op.alter_column('activities', 'end_time',
|
op.alter_column('activities', 'end_time',
|
||||||
existing_type=sa.DATETIME(),
|
existing_type=mysql.DATETIME(),
|
||||||
type_=sa.TIMESTAMP(),
|
comment='Activity end date (DATETIME)',
|
||||||
comment='Activity end date (TIMESTAMP)',
|
|
||||||
existing_comment='Activity end date (datetime)',
|
existing_comment='Activity end date (datetime)',
|
||||||
existing_nullable=False)
|
existing_nullable=False)
|
||||||
op.alter_column('activities', 'total_elapsed_time',
|
op.alter_column('activities', 'total_elapsed_time',
|
||||||
existing_type=sa.DECIMAL(precision=20, scale=10),
|
existing_type=mysql.DECIMAL(precision=20, scale=10),
|
||||||
comment='Activity total elapsed time (s)',
|
comment='Activity total elapsed time (s)',
|
||||||
existing_comment='Activity total elapsed time (datetime)',
|
existing_comment='Activity total elapsed time (datetime)',
|
||||||
existing_nullable=False)
|
existing_nullable=False)
|
||||||
op.alter_column('activities', 'total_timer_time',
|
op.alter_column('activities', 'total_timer_time',
|
||||||
existing_type=sa.DECIMAL(precision=20, scale=10),
|
existing_type=mysql.DECIMAL(precision=20, scale=10),
|
||||||
comment='Activity total timer time (s)',
|
comment='Activity total timer time (s)',
|
||||||
existing_comment='Activity total timer time (datetime)',
|
existing_comment='Activity total timer time (datetime)',
|
||||||
existing_nullable=False)
|
existing_nullable=False)
|
||||||
op.alter_column('activities', 'created_at',
|
op.alter_column('activities', 'created_at',
|
||||||
existing_type=sa.DATETIME(),
|
existing_type=mysql.DATETIME(),
|
||||||
type_=sa.TIMESTAMP(),
|
comment='Activity creation date (DATETIME)',
|
||||||
comment='Activity creation date (TIMESTAMP)',
|
|
||||||
existing_comment='Activity creation date (datetime)',
|
existing_comment='Activity creation date (datetime)',
|
||||||
existing_nullable=False)
|
existing_nullable=False)
|
||||||
|
op.add_column('gear', sa.Column('initial_kms', sa.DECIMAL(precision=11, scale=3), nullable=False, comment='Initial kilometers of the gear'))
|
||||||
op.alter_column('gear', 'created_at',
|
op.alter_column('gear', 'created_at',
|
||||||
existing_type=sa.DATETIME(),
|
existing_type=mysql.DATETIME(),
|
||||||
type_=sa.TIMESTAMP(),
|
comment='Gear creation date (DATETIME)',
|
||||||
comment='Gear creation date (TIMESTAMP)',
|
|
||||||
existing_comment='Gear creation date (date)',
|
existing_comment='Gear creation date (date)',
|
||||||
existing_nullable=False)
|
existing_nullable=False)
|
||||||
|
op.add_column('health_data', sa.Column('date', sa.Date(), nullable=True, comment='Health data creation date (date)'))
|
||||||
|
# Copy data from `created_at` to `date`
|
||||||
|
op.execute("""
|
||||||
|
UPDATE health_data
|
||||||
|
SET date = DATE(created_at)
|
||||||
|
""")
|
||||||
|
# Make `date` column non-nullable
|
||||||
|
op.alter_column('health_data', 'date', nullable=False, existing_type=sa.Date())
|
||||||
op.add_column('health_data', sa.Column('bmi', sa.DECIMAL(precision=10, scale=2), nullable=True, comment='Body mass index (BMI)'))
|
op.add_column('health_data', sa.Column('bmi', sa.DECIMAL(precision=10, scale=2), nullable=True, comment='Body mass index (BMI)'))
|
||||||
op.add_column('health_data', sa.Column('body_fat', sa.DECIMAL(precision=10, scale=2), nullable=True, comment='Body fat percentage'))
|
|
||||||
op.add_column('health_data', sa.Column('body_water', sa.DECIMAL(precision=10, scale=2), nullable=True, comment='Body hydration percentage'))
|
|
||||||
op.add_column('health_data', sa.Column('bone_mass', sa.DECIMAL(precision=10, scale=2), nullable=True, comment='Bone mass percentage'))
|
|
||||||
op.add_column('health_data', sa.Column('muscle_mass', sa.DECIMAL(precision=10, scale=2), nullable=True, comment='Muscle mass percentage'))
|
|
||||||
op.add_column('health_data', sa.Column('physique_rating', sa.DECIMAL(precision=10, scale=2), nullable=True, comment='Physique rating'))
|
|
||||||
op.add_column('health_data', sa.Column('visceral_fat', sa.DECIMAL(precision=10, scale=2), nullable=True, comment='Visceral fat rating'))
|
|
||||||
op.add_column('health_data', sa.Column('metabolic_age', sa.DECIMAL(precision=10, scale=2), nullable=True, comment='Metabolic age'))
|
|
||||||
op.add_column('health_data', sa.Column('garminconnect_body_composition_id', sa.String(length=45), nullable=True, comment='Garmin Connect body composition ID'))
|
op.add_column('health_data', sa.Column('garminconnect_body_composition_id', sa.String(length=45), nullable=True, comment='Garmin Connect body composition ID'))
|
||||||
op.alter_column('health_data', 'created_at',
|
op.drop_index('created_at', table_name='health_data')
|
||||||
existing_type=sa.DATE(),
|
op.create_unique_constraint(None, 'health_data', ['date'])
|
||||||
comment='Health data creation date (date)',
|
op.drop_column('health_data', 'created_at')
|
||||||
existing_comment='Health data creation date (datetime)',
|
# Add the new entry to the migrations table
|
||||||
existing_nullable=False)
|
|
||||||
op.add_column('activities', sa.Column('timezone', sa.String(length=250), nullable=True, comment='Activity timezone (May include spaces)'))
|
|
||||||
op.execute("""
|
op.execute("""
|
||||||
INSERT INTO migrations (id, name, description, executed) VALUES
|
INSERT INTO migrations (id, name, description, executed) VALUES
|
||||||
(2, 'v0.6.5', 'Process timezone for existing activities', false);
|
(2, 'v0.6.5', 'Process timezone for existing activities', false);
|
||||||
@@ -79,56 +75,52 @@ def upgrade() -> None:
|
|||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
op.drop_column('gear', 'initial_kms')
|
op.add_column('health_data', sa.Column('created_at', sa.DATE(), nullable=True, comment='Health data creation date (datetime)'))
|
||||||
op.alter_column('health_data', 'created_at',
|
# Copy data back from `date` to `created_at`
|
||||||
existing_type=sa.DATE(),
|
op.execute("""
|
||||||
comment='Health data creation date (datetime)',
|
UPDATE health_data
|
||||||
existing_comment='Health data creation date (date)',
|
SET created_at = date
|
||||||
existing_nullable=False)
|
""")
|
||||||
|
# Make `created_at` column non-nullable
|
||||||
|
op.alter_column('health_data', 'created_at', nullable=False, existing_type=sa.Date())
|
||||||
|
op.drop_constraint(None, 'health_data', type_='unique')
|
||||||
|
op.create_index('created_at', 'health_data', ['created_at'], unique=True)
|
||||||
op.drop_column('health_data', 'garminconnect_body_composition_id')
|
op.drop_column('health_data', 'garminconnect_body_composition_id')
|
||||||
op.drop_column('health_data', 'metabolic_age')
|
|
||||||
op.drop_column('health_data', 'visceral_fat')
|
|
||||||
op.drop_column('health_data', 'physique_rating')
|
|
||||||
op.drop_column('health_data', 'muscle_mass')
|
|
||||||
op.drop_column('health_data', 'bone_mass')
|
|
||||||
op.drop_column('health_data', 'body_water')
|
|
||||||
op.drop_column('health_data', 'body_fat')
|
|
||||||
op.drop_column('health_data', 'bmi')
|
op.drop_column('health_data', 'bmi')
|
||||||
|
op.drop_column('health_data', 'date')
|
||||||
op.alter_column('gear', 'created_at',
|
op.alter_column('gear', 'created_at',
|
||||||
existing_type=sa.TIMESTAMP(),
|
existing_type=mysql.DATETIME(),
|
||||||
type_=sa.DATETIME(),
|
|
||||||
comment='Gear creation date (date)',
|
comment='Gear creation date (date)',
|
||||||
existing_comment='Gear creation date (TIMESTAMP)',
|
existing_comment='Gear creation date (DATETIME)',
|
||||||
existing_nullable=False)
|
existing_nullable=False)
|
||||||
|
op.drop_column('gear', 'initial_kms')
|
||||||
op.alter_column('activities', 'created_at',
|
op.alter_column('activities', 'created_at',
|
||||||
existing_type=sa.TIMESTAMP(),
|
existing_type=mysql.DATETIME(),
|
||||||
type_=sa.DATETIME(),
|
|
||||||
comment='Activity creation date (datetime)',
|
comment='Activity creation date (datetime)',
|
||||||
existing_comment='Activity creation date (TIMESTAMP)',
|
existing_comment='Activity creation date (DATETIME)',
|
||||||
existing_nullable=False)
|
existing_nullable=False)
|
||||||
op.alter_column('activities', 'total_timer_time',
|
op.alter_column('activities', 'total_timer_time',
|
||||||
existing_type=sa.DECIMAL(precision=20, scale=10),
|
existing_type=mysql.DECIMAL(precision=20, scale=10),
|
||||||
comment='Activity total timer time (datetime)',
|
comment='Activity total timer time (datetime)',
|
||||||
existing_comment='Activity total timer time (s)',
|
existing_comment='Activity total timer time (s)',
|
||||||
existing_nullable=False)
|
existing_nullable=False)
|
||||||
op.alter_column('activities', 'total_elapsed_time',
|
op.alter_column('activities', 'total_elapsed_time',
|
||||||
existing_type=sa.DECIMAL(precision=20, scale=10),
|
existing_type=mysql.DECIMAL(precision=20, scale=10),
|
||||||
comment='Activity total elapsed time (datetime)',
|
comment='Activity total elapsed time (datetime)',
|
||||||
existing_comment='Activity total elapsed time (s)',
|
existing_comment='Activity total elapsed time (s)',
|
||||||
existing_nullable=False)
|
existing_nullable=False)
|
||||||
op.alter_column('activities', 'end_time',
|
op.alter_column('activities', 'end_time',
|
||||||
existing_type=sa.TIMESTAMP(),
|
existing_type=mysql.DATETIME(),
|
||||||
type_=sa.DATETIME(),
|
|
||||||
comment='Activity end date (datetime)',
|
comment='Activity end date (datetime)',
|
||||||
existing_comment='Activity end date (TIMESTAMP)',
|
existing_comment='Activity end date (DATETIME)',
|
||||||
existing_nullable=False)
|
existing_nullable=False)
|
||||||
op.alter_column('activities', 'start_time',
|
op.alter_column('activities', 'start_time',
|
||||||
existing_type=sa.TIMESTAMP(),
|
existing_type=mysql.DATETIME(),
|
||||||
type_=sa.DATETIME(),
|
|
||||||
comment='Activity start date (datetime)',
|
comment='Activity start date (datetime)',
|
||||||
existing_comment='Activity start date (TIMESTAMP)',
|
existing_comment='Activity start date (DATETIME)',
|
||||||
existing_nullable=False)
|
existing_nullable=False)
|
||||||
op.drop_column('activities', 'timezone')
|
op.drop_column('activities', 'timezone')
|
||||||
|
# Remove the entry from the migrations table
|
||||||
op.execute("""
|
op.execute("""
|
||||||
DELETE FROM migrations
|
DELETE FROM migrations
|
||||||
WHERE id = 2;
|
WHERE id = 2;
|
||||||
@@ -32,12 +32,12 @@ def get_main_logger():
|
|||||||
return logging.getLogger("main_logger")
|
return logging.getLogger("main_logger")
|
||||||
|
|
||||||
|
|
||||||
def print_to_log(message: str, type: str = "info"):
|
def print_to_log(message: str, type: str = "info", exc: Exception = None):
|
||||||
main_logger = get_main_logger()
|
main_logger = get_main_logger()
|
||||||
if type == "info":
|
if type == "info":
|
||||||
main_logger.info(message)
|
main_logger.info(message)
|
||||||
elif type == "error":
|
elif type == "error":
|
||||||
main_logger.error(message)
|
main_logger.error(message, exc_info=exc is not None)
|
||||||
elif type == "warning":
|
elif type == "warning":
|
||||||
main_logger.warning(message)
|
main_logger.warning(message)
|
||||||
elif type == "debug":
|
elif type == "debug":
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
#from apscheduler.schedulers.background import BackgroundScheduler
|
# from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||||
|
|
||||||
import strava.utils as strava_utils
|
import strava.utils as strava_utils
|
||||||
import strava.activity_utils as strava_activity_utils
|
import strava.activity_utils as strava_activity_utils
|
||||||
|
|
||||||
import garmin.activity_utils as garmin_activity_utils
|
import garmin.activity_utils as garmin_activity_utils
|
||||||
|
import garmin.health_utils as garmin_health_utils
|
||||||
|
|
||||||
import core.logger as core_logger
|
import core.logger as core_logger
|
||||||
|
|
||||||
#scheduler = BackgroundScheduler()
|
# scheduler = BackgroundScheduler()
|
||||||
scheduler = AsyncIOScheduler()
|
scheduler = AsyncIOScheduler()
|
||||||
|
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ def start_scheduler():
|
|||||||
core_logger.print_to_log(
|
core_logger.print_to_log(
|
||||||
"Added scheduler job to retrieve last day Garmin Connect users activities every 60 minutes"
|
"Added scheduler job to retrieve last day Garmin Connect users activities every 60 minutes"
|
||||||
)
|
)
|
||||||
# Add scheduler jobs to retrieve last day activities from Garmin Connect
|
# Add scheduler job to retrieve last day activities from Garmin Connect
|
||||||
scheduler.add_job(
|
scheduler.add_job(
|
||||||
garmin_activity_utils.retrieve_garminconnect_users_activities_for_days,
|
garmin_activity_utils.retrieve_garminconnect_users_activities_for_days,
|
||||||
"interval",
|
"interval",
|
||||||
@@ -46,6 +47,18 @@ def start_scheduler():
|
|||||||
args=[1],
|
args=[1],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Log the addition of the job to retrieve last day Garmin Connect users body composition
|
||||||
|
core_logger.print_to_log(
|
||||||
|
"Added scheduler job to retrieve last day Garmin Connect users body composition every 4 hours (240 minutes)"
|
||||||
|
)
|
||||||
|
# Add scheduler job to retrieve last day body composition from Garmin Connect
|
||||||
|
scheduler.add_job(
|
||||||
|
garmin_health_utils.retrieve_garminconnect_users_bc_for_days,
|
||||||
|
"interval",
|
||||||
|
minutes=240,
|
||||||
|
args=[1],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def stop_scheduler():
|
def stop_scheduler():
|
||||||
scheduler.shutdown()
|
scheduler.shutdown()
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ def create_activity_objects(
|
|||||||
pace = 0
|
pace = 0
|
||||||
|
|
||||||
if session_record["session"]["activity_type"]:
|
if session_record["session"]["activity_type"]:
|
||||||
activity_type=activities_utils.define_activity_type(
|
activity_type = activities_utils.define_activity_type(
|
||||||
session_record["session"]["activity_type"]
|
session_record["session"]["activity_type"]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -82,7 +82,8 @@ def create_activity_objects(
|
|||||||
else:
|
else:
|
||||||
if session_record["time_offset"]:
|
if session_record["time_offset"]:
|
||||||
timezone = find_timezone_name(
|
timezone = find_timezone_name(
|
||||||
session_record["time_offset"], session_record["session"]["first_waypoint_time"]
|
session_record["time_offset"],
|
||||||
|
session_record["session"]["first_waypoint_time"],
|
||||||
)
|
)
|
||||||
|
|
||||||
parsed_activity = {
|
parsed_activity = {
|
||||||
@@ -611,7 +612,7 @@ def parse_fit_file(file: str) -> dict:
|
|||||||
raise http_err
|
raise http_err
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in parse_fit_file: {err}", "error")
|
core_logger.print_to_log(f"Error in parse_fit_file: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -822,9 +823,9 @@ def find_timezone_name(offset_seconds, reference_date):
|
|||||||
tz = ZoneInfo(tz_name)
|
tz = ZoneInfo(tz_name)
|
||||||
if reference_date.utcoffset() is None: # Skip invalid timezones
|
if reference_date.utcoffset() is None: # Skip invalid timezones
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Get the UTC offset for the reference date
|
# Get the UTC offset for the reference date
|
||||||
utc_offset = reference_date.astimezone(tz).utcoffset()
|
utc_offset = reference_date.astimezone(tz).utcoffset()
|
||||||
|
|
||||||
if utc_offset.total_seconds() == offset_seconds:
|
if utc_offset.total_seconds() == offset_seconds:
|
||||||
return tz_name
|
return tz_name
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ def get_all_followers_by_user_id(user_id: int, db: Session):
|
|||||||
return followers
|
return followers
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_all_followers_by_user_id: {err}", "error")
|
core_logger.print_to_log(f"Error in get_all_followers_by_user_id: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -53,7 +53,7 @@ def get_accepted_followers_by_user_id(user_id: int, db: Session):
|
|||||||
return followers
|
return followers
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_accepted_followers_by_user_id: {err}", "error")
|
core_logger.print_to_log(f"Error in get_accepted_followers_by_user_id: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -78,7 +78,7 @@ def get_all_following_by_user_id(user_id: int, db: Session):
|
|||||||
return followings
|
return followings
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_all_following_by_user_id: {err}", "error")
|
core_logger.print_to_log(f"Error in get_all_following_by_user_id: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -106,7 +106,7 @@ def get_accepted_following_by_user_id(user_id: int, db: Session):
|
|||||||
return followings
|
return followings
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_accepted_following_by_user_id: {err}", "error")
|
core_logger.print_to_log(f"Error in get_accepted_following_by_user_id: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -136,7 +136,7 @@ def get_follower_for_user_id_and_target_user_id(
|
|||||||
return follower
|
return follower
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_follower_for_user_id_and_target_user_id: {err}", "error")
|
core_logger.print_to_log(f"Error in get_follower_for_user_id_and_target_user_id: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -162,7 +162,7 @@ def create_follower(user_id: int, target_user_id: int, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in create_follower: {err}", "error")
|
core_logger.print_to_log(f"Error in create_follower: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -201,7 +201,7 @@ def accept_follower(user_id: int, target_user_id: int, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in accept_follower: {err}", "error")
|
core_logger.print_to_log(f"Error in accept_follower: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -236,7 +236,7 @@ def delete_follower(user_id: int, target_user_id: int, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in delete_follower: {err}", "error")
|
core_logger.print_to_log(f"Error in delete_follower: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
@@ -53,9 +53,7 @@ def fetch_and_process_activities(
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
garmin_logger.print_to_log(
|
garmin_logger.print_to_log(f"User {user_id}: Processing activity {activity_id}")
|
||||||
f"User {user_id}: Processing activity {activity_id}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get activity gear
|
# Get activity gear
|
||||||
activity_gear = garminconnect_client.get_activity_gear(activity_id)
|
activity_gear = garminconnect_client.get_activity_gear(activity_id)
|
||||||
|
|||||||
133
backend/app/garmin/health_utils.py
Normal file
133
backend/app/garmin/health_utils.py
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import os
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta, date
|
||||||
|
import garminconnect
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
import garmin.utils as garmin_utils
|
||||||
|
import garmin.logger as garmin_logger
|
||||||
|
|
||||||
|
import activities.utils as activities_utils
|
||||||
|
import activities.crud as activities_crud
|
||||||
|
|
||||||
|
import health_data.crud as health_data_crud
|
||||||
|
import health_data.schema as health_data_schema
|
||||||
|
|
||||||
|
import users.crud as users_crud
|
||||||
|
|
||||||
|
from core.database import SessionLocal
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_and_process_bc(
|
||||||
|
garminconnect_client: garminconnect.Garmin,
|
||||||
|
start_date: datetime,
|
||||||
|
user_id: int,
|
||||||
|
db: Session,
|
||||||
|
) -> int:
|
||||||
|
# Fetch Garmin Connect body composition after the specified start date
|
||||||
|
garmin_bc = garminconnect_client.get_body_composition(start_date, date.today())
|
||||||
|
|
||||||
|
if garmin_bc is None:
|
||||||
|
# Log an informational event if no body composition were found
|
||||||
|
garmin_logger.print_to_log_and_console(
|
||||||
|
f"User {user_id}: No new Garmin Connect body composition found after {start_date}: garmin_bc is None"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Return 0 to indicate no body composition were processed
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Process body composition
|
||||||
|
for bc in garmin_bc["dateWeightList"]:
|
||||||
|
health_data = health_data_schema.HealthData(
|
||||||
|
user_id=user_id,
|
||||||
|
date=bc["calendarDate"],
|
||||||
|
weight=bc["weight"] / 1000,
|
||||||
|
bmi=bc["bmi"],
|
||||||
|
#body_fat=bc["bodyFat"],
|
||||||
|
#body_water=bc["bodyWater"],
|
||||||
|
#bone_mass=bc["boneMass"],
|
||||||
|
#muscle_mass=bc["muscleMass"],
|
||||||
|
#physique_rating=bc["physiqueRating"],
|
||||||
|
#visceral_fat=bc["visceralFat"],
|
||||||
|
#metabolic_age=bc["metabolicAge"],
|
||||||
|
garminconnect_body_composition_id=str(bc["samplePk"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if the body composition is already stored in the database
|
||||||
|
health_data_db = health_data_crud.get_health_data_by_date(
|
||||||
|
user_id, health_data.date, db
|
||||||
|
)
|
||||||
|
|
||||||
|
if health_data_db:
|
||||||
|
health_data.id = health_data_db.id
|
||||||
|
health_data_crud.edit_health_data(user_id, health_data, db)
|
||||||
|
garmin_logger.print_to_log(
|
||||||
|
f"User {user_id}: Body composition edited for date {health_data.date}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
health_data_crud.create_health_data(health_data, user_id, db)
|
||||||
|
garmin_logger.print_to_log(
|
||||||
|
f"User {user_id}: Body composition created for date {health_data.date}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Return the number of body compositions processed
|
||||||
|
return len(garmin_bc)
|
||||||
|
|
||||||
|
|
||||||
|
def retrieve_garminconnect_users_bc_for_days(days: int):
|
||||||
|
# Create a new database session
|
||||||
|
db = SessionLocal()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Get all users
|
||||||
|
users = users_crud.get_all_users(db)
|
||||||
|
finally:
|
||||||
|
# Ensure the session is closed after use
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
# Process the body composition for each user
|
||||||
|
for user in users:
|
||||||
|
get_user_garminconnect_bc_by_days(
|
||||||
|
(datetime.utcnow() - timedelta(days=days)).strftime("%Y-%m-%dT%H:%M:%S"),
|
||||||
|
user.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_garminconnect_bc_by_days(start_date: datetime, user_id: int):
|
||||||
|
# Create a new database session
|
||||||
|
db = SessionLocal()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Get the user integrations by user ID
|
||||||
|
user_integrations = garmin_utils.fetch_user_integrations_and_validate_token(
|
||||||
|
user_id, db
|
||||||
|
)
|
||||||
|
|
||||||
|
if user_integrations is None:
|
||||||
|
garmin_logger.print_to_log(f"User {user_id}: Garmin Connect not linked")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Log the start of the body composition processing
|
||||||
|
garmin_logger.print_to_log(
|
||||||
|
f"User {user_id}: Started Garmin Connect body composition processing"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a Garmin Connect client with the user's access token
|
||||||
|
garminconnect_client = garmin_utils.login_garminconnect_using_tokens(
|
||||||
|
user_integrations.garminconnect_oauth1,
|
||||||
|
user_integrations.garminconnect_oauth2,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fetch Garmin Connect body composition after the specified start date
|
||||||
|
num_garminconnect_bc_processed = fetch_and_process_bc(
|
||||||
|
garminconnect_client, start_date, user_id, db
|
||||||
|
)
|
||||||
|
|
||||||
|
# Log an informational event for tracing
|
||||||
|
garmin_logger.print_to_log(
|
||||||
|
f"User {user_id}: {num_garminconnect_bc_processed} Garmin Connect body composition processed"
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
# Ensure the session is closed after use
|
||||||
|
db.close()
|
||||||
@@ -22,12 +22,12 @@ def get_garminconnect_logger():
|
|||||||
return logging.getLogger("garminconnect_logger")
|
return logging.getLogger("garminconnect_logger")
|
||||||
|
|
||||||
|
|
||||||
def print_to_log(message: str, type: str = "info"):
|
def print_to_log(message: str, type: str = "info", exc: Exception = None):
|
||||||
garminconnect_logger = get_garminconnect_logger()
|
garminconnect_logger = get_garminconnect_logger()
|
||||||
if type == "info":
|
if type == "info":
|
||||||
garminconnect_logger.info(message)
|
garminconnect_logger.info(message)
|
||||||
elif type == "error":
|
elif type == "error":
|
||||||
garminconnect_logger.error(message)
|
garminconnect_logger.error(message, exc_info=exc is not None)
|
||||||
elif type == "warning":
|
elif type == "warning":
|
||||||
garminconnect_logger.warning(message)
|
garminconnect_logger.warning(message)
|
||||||
elif type == "debug":
|
elif type == "debug":
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ def get_gear_user_by_id(gear_id: int, db: Session) -> gears_schema.Gear | None:
|
|||||||
return gear
|
return gear
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_gear_user_by_id: {err}", "error")
|
core_logger.print_to_log(f"Error in get_gear_user_by_id: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -61,7 +61,7 @@ def get_gear_users_with_pagination(
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(
|
core_logger.print_to_log(
|
||||||
f"Error in get_gear_users_with_pagination: {err}", "error"
|
f"Error in get_gear_users_with_pagination: {err}", "error", exc=err
|
||||||
)
|
)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -87,7 +87,7 @@ def get_gear_user(user_id: int, db: Session) -> list[gears_schema.Gear] | None:
|
|||||||
return gears
|
return gears
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_gear_user: {err}", "error")
|
core_logger.print_to_log(f"Error in get_gear_user: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -124,7 +124,7 @@ def get_gear_user_by_nickname(
|
|||||||
return gears
|
return gears
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_gear_user_by_nickname: {err}", "error")
|
core_logger.print_to_log(f"Error in get_gear_user_by_nickname: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -155,7 +155,7 @@ def get_gear_by_type_and_user(gear_type: int, user_id: int, db: Session):
|
|||||||
return gear
|
return gear
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_gear_by_type_and_user: {err}", "error")
|
core_logger.print_to_log(f"Error in get_gear_by_type_and_user: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -189,7 +189,7 @@ def get_gear_by_strava_id_from_user_id(
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(
|
core_logger.print_to_log(
|
||||||
f"Error in get_gear_by_strava_id_from_user_id: {err}", "error"
|
f"Error in get_gear_by_strava_id_from_user_id: {err}", "error", exc=err
|
||||||
)
|
)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -224,7 +224,7 @@ def get_gear_by_garminconnect_id_from_user_id(
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(
|
core_logger.print_to_log(
|
||||||
f"Error in get_gear_by_garminconnect_id_from_user_id: {err}", "error"
|
f"Error in get_gear_by_garminconnect_id_from_user_id: {err}", "error", exc=err
|
||||||
)
|
)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -263,7 +263,7 @@ def create_multiple_gears(gears: list[gears_schema.Gear], user_id: int, db: Sess
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in create_multiple_gears: {err}", "error")
|
core_logger.print_to_log(f"Error in create_multiple_gears: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -298,7 +298,7 @@ def create_gear(gear: gears_schema.Gear, user_id: int, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in create_gear: {err}", "error")
|
core_logger.print_to_log(f"Error in create_gear: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -339,7 +339,7 @@ def edit_gear(gear_id: int, gear: gears_schema.Gear, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in edit_gear: {err}", "error")
|
core_logger.print_to_log(f"Error in edit_gear: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -367,7 +367,7 @@ def delete_gear(gear_id: int, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in delete_gear: {err}", "error")
|
core_logger.print_to_log(f"Error in delete_gear: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -397,7 +397,7 @@ def delete_all_strava_gear_for_user(user_id: int, db: Session):
|
|||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(
|
core_logger.print_to_log(
|
||||||
f"Error in delete_all_strava_gear_for_user: {err}", "error"
|
f"Error in delete_all_strava_gear_for_user: {err}", "error", exc=err
|
||||||
)
|
)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
@@ -429,7 +429,7 @@ def delete_all_garminconnect_gear_for_user(user_id: int, db: Session):
|
|||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(
|
core_logger.print_to_log(
|
||||||
f"Error in delete_all_garminconnect_gear_for_user: {err}", "error"
|
f"Error in delete_all_garminconnect_gear_for_user: {err}", "error", exc=err
|
||||||
)
|
)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from sqlalchemy import (
|
|||||||
Column,
|
Column,
|
||||||
Integer,
|
Integer,
|
||||||
String,
|
String,
|
||||||
TIMESTAMP,
|
DATETIME,
|
||||||
ForeignKey,
|
ForeignKey,
|
||||||
DECIMAL,
|
DECIMAL,
|
||||||
)
|
)
|
||||||
@@ -37,7 +37,7 @@ class Gear(Base):
|
|||||||
nullable=False,
|
nullable=False,
|
||||||
comment="User ID that the gear belongs to",
|
comment="User ID that the gear belongs to",
|
||||||
)
|
)
|
||||||
created_at = Column(TIMESTAMP, nullable=False, comment="Gear creation date (TIMESTAMP)")
|
created_at = Column(DATETIME, nullable=False, comment="Gear creation date (DATETIME)")
|
||||||
is_active = Column(
|
is_active = Column(
|
||||||
Integer, nullable=False, comment="Is gear active (0 - not active, 1 - active)"
|
Integer, nullable=False, comment="Is gear active (0 - not active, 1 - active)"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -68,137 +68,150 @@ def parse_gpx_file(file: str, user_id: int) -> dict:
|
|||||||
with open(file, "r") as gpx_file:
|
with open(file, "r") as gpx_file:
|
||||||
gpx = gpxpy.parse(gpx_file)
|
gpx = gpxpy.parse(gpx_file)
|
||||||
|
|
||||||
# Iterate over tracks in the GPX file
|
if gpx.tracks:
|
||||||
for track in gpx.tracks:
|
# Iterate over tracks in the GPX file
|
||||||
# Set activity name and type if available
|
for track in gpx.tracks:
|
||||||
activity_name = track.name if track.name else "Workout"
|
# Set activity name and type if available
|
||||||
activity_type = track.type if track.type else "Workout"
|
activity_name = track.name if track.name else "Workout"
|
||||||
|
activity_type = track.type if track.type else "Workout"
|
||||||
|
|
||||||
# Iterate over segments in each track
|
if track.segments:
|
||||||
for segment in track.segments:
|
# Iterate over segments in each track
|
||||||
# Iterate over points in each segment
|
for segment in track.segments:
|
||||||
for point in segment.points:
|
# Iterate over points in each segment
|
||||||
# Extract latitude and longitude from the point
|
for point in segment.points:
|
||||||
latitude, longitude = point.latitude, point.longitude
|
# Extract latitude and longitude from the point
|
||||||
|
latitude, longitude = point.latitude, point.longitude
|
||||||
|
|
||||||
# Calculate distance between waypoints
|
# Calculate distance between waypoints
|
||||||
if prev_latitude is not None and prev_longitude is not None:
|
if prev_latitude is not None and prev_longitude is not None:
|
||||||
distance += geodesic(
|
distance += geodesic(
|
||||||
(prev_latitude, prev_longitude), (latitude, longitude)
|
(prev_latitude, prev_longitude),
|
||||||
).meters
|
(latitude, longitude),
|
||||||
|
).meters
|
||||||
|
|
||||||
# Extract elevation, time, and location details
|
# Extract elevation, time, and location details
|
||||||
elevation, time = point.elevation, point.time
|
elevation, time = point.elevation, point.time
|
||||||
|
|
||||||
if elevation != 0:
|
if elevation != 0:
|
||||||
is_elevation_set = True
|
is_elevation_set = True
|
||||||
|
|
||||||
if first_waypoint_time is None:
|
if first_waypoint_time is None:
|
||||||
first_waypoint_time = point.time
|
first_waypoint_time = point.time
|
||||||
|
|
||||||
if process_one_time_fields == 0:
|
if process_one_time_fields == 0:
|
||||||
# Use geocoding API to get city, town, and country based on coordinates
|
# Use geocoding API to get city, town, and country based on coordinates
|
||||||
location_data = (
|
location_data = (
|
||||||
activities_utils.location_based_on_coordinates(
|
activities_utils.location_based_on_coordinates(
|
||||||
latitude, longitude
|
latitude, longitude
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Extract city, town, and country from location data
|
||||||
|
if location_data:
|
||||||
|
city = location_data["city"]
|
||||||
|
town = location_data["town"]
|
||||||
|
country = location_data["country"]
|
||||||
|
|
||||||
|
process_one_time_fields = 1
|
||||||
|
|
||||||
|
# Extract heart rate, cadence, and power data from point extensions
|
||||||
|
heart_rate, cadence, power = 0, 0, 0
|
||||||
|
|
||||||
|
if point.extensions:
|
||||||
|
# Iterate through each extension element
|
||||||
|
for extension in point.extensions:
|
||||||
|
if extension.tag.endswith("TrackPointExtension"):
|
||||||
|
hr_element = extension.find(
|
||||||
|
".//{http://www.garmin.com/xmlschemas/TrackPointExtension/v1}hr"
|
||||||
|
)
|
||||||
|
if hr_element is not None:
|
||||||
|
heart_rate = hr_element.text
|
||||||
|
cad_element = extension.find(
|
||||||
|
".//{http://www.garmin.com/xmlschemas/TrackPointExtension/v1}cad"
|
||||||
|
)
|
||||||
|
if cad_element is not None:
|
||||||
|
cadence = cad_element.text
|
||||||
|
elif extension.tag.endswith("power"):
|
||||||
|
# Extract 'power' value
|
||||||
|
power = extension.text
|
||||||
|
|
||||||
|
# Check if heart rate, cadence, power are set
|
||||||
|
if heart_rate != 0:
|
||||||
|
is_heart_rate_set = True
|
||||||
|
|
||||||
|
if cadence != 0:
|
||||||
|
is_cadence_set = True
|
||||||
|
|
||||||
|
if power != 0:
|
||||||
|
is_power_set = True
|
||||||
|
else:
|
||||||
|
power = None
|
||||||
|
|
||||||
|
# Calculate instant speed, pace, and update waypoint arrays
|
||||||
|
instant_speed = activities_utils.calculate_instant_speed(
|
||||||
|
last_waypoint_time,
|
||||||
|
time,
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
prev_latitude,
|
||||||
|
prev_longitude,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
# Extract city, town, and country from location data
|
# Calculate instance pace
|
||||||
if location_data:
|
instant_pace = 0
|
||||||
city = location_data["city"]
|
if instant_speed > 0:
|
||||||
town = location_data["town"]
|
instant_pace = 1 / instant_speed
|
||||||
country = location_data["country"]
|
is_velocity_set = True
|
||||||
|
|
||||||
process_one_time_fields = 1
|
timestamp = time.strftime("%Y-%m-%dT%H:%M:%S")
|
||||||
|
|
||||||
# Extract heart rate, cadence, and power data from point extensions
|
# Append waypoint data to respective arrays
|
||||||
heart_rate, cadence, power = 0, 0, 0
|
if latitude is not None and longitude is not None:
|
||||||
|
lat_lon_waypoints.append(
|
||||||
if point.extensions:
|
{
|
||||||
# Iterate through each extension element
|
"time": timestamp,
|
||||||
for extension in point.extensions:
|
"lat": latitude,
|
||||||
if extension.tag.endswith("TrackPointExtension"):
|
"lon": longitude,
|
||||||
hr_element = extension.find(
|
}
|
||||||
".//{http://www.garmin.com/xmlschemas/TrackPointExtension/v1}hr"
|
|
||||||
)
|
)
|
||||||
if hr_element is not None:
|
is_lat_lon_set = True
|
||||||
heart_rate = hr_element.text
|
|
||||||
cad_element = extension.find(
|
|
||||||
".//{http://www.garmin.com/xmlschemas/TrackPointExtension/v1}cad"
|
|
||||||
)
|
|
||||||
if cad_element is not None:
|
|
||||||
cadence = cad_element.text
|
|
||||||
elif extension.tag.endswith("power"):
|
|
||||||
# Extract 'power' value
|
|
||||||
power = extension.text
|
|
||||||
|
|
||||||
# Check if heart rate, cadence, power are set
|
activities_utils.append_if_not_none(
|
||||||
if heart_rate != 0:
|
ele_waypoints, timestamp, elevation, "ele"
|
||||||
is_heart_rate_set = True
|
)
|
||||||
|
activities_utils.append_if_not_none(
|
||||||
|
hr_waypoints, timestamp, heart_rate, "hr"
|
||||||
|
)
|
||||||
|
activities_utils.append_if_not_none(
|
||||||
|
cad_waypoints, timestamp, cadence, "cad"
|
||||||
|
)
|
||||||
|
activities_utils.append_if_not_none(
|
||||||
|
power_waypoints, timestamp, power, "power"
|
||||||
|
)
|
||||||
|
activities_utils.append_if_not_none(
|
||||||
|
vel_waypoints, timestamp, instant_speed, "vel"
|
||||||
|
)
|
||||||
|
activities_utils.append_if_not_none(
|
||||||
|
pace_waypoints, timestamp, instant_pace, "pace"
|
||||||
|
)
|
||||||
|
|
||||||
if cadence != 0:
|
# Update previous latitude, longitude, and last waypoint time
|
||||||
is_cadence_set = True
|
prev_latitude, prev_longitude, last_waypoint_time = (
|
||||||
|
latitude,
|
||||||
if power != 0:
|
longitude,
|
||||||
is_power_set = True
|
time,
|
||||||
else:
|
)
|
||||||
power = None
|
else:
|
||||||
|
raise HTTPException(
|
||||||
# Calculate instant speed, pace, and update waypoint arrays
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
instant_speed = activities_utils.calculate_instant_speed(
|
detail="Invalid GPX file - no segments found in the GPX file",
|
||||||
last_waypoint_time,
|
|
||||||
time,
|
|
||||||
latitude,
|
|
||||||
longitude,
|
|
||||||
prev_latitude,
|
|
||||||
prev_longitude,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Calculate instance pace
|
|
||||||
instant_pace = 0
|
|
||||||
if instant_speed > 0:
|
|
||||||
instant_pace = 1 / instant_speed
|
|
||||||
is_velocity_set = True
|
|
||||||
|
|
||||||
timestamp = time.strftime("%Y-%m-%dT%H:%M:%S")
|
|
||||||
|
|
||||||
# Append waypoint data to respective arrays
|
|
||||||
if latitude is not None and longitude is not None:
|
|
||||||
lat_lon_waypoints.append(
|
|
||||||
{
|
|
||||||
"time": timestamp,
|
|
||||||
"lat": latitude,
|
|
||||||
"lon": longitude,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
is_lat_lon_set = True
|
|
||||||
|
|
||||||
activities_utils.append_if_not_none(
|
|
||||||
ele_waypoints, timestamp, elevation, "ele"
|
|
||||||
)
|
|
||||||
activities_utils.append_if_not_none(
|
|
||||||
hr_waypoints, timestamp, heart_rate, "hr"
|
|
||||||
)
|
|
||||||
activities_utils.append_if_not_none(
|
|
||||||
cad_waypoints, timestamp, cadence, "cad"
|
|
||||||
)
|
|
||||||
activities_utils.append_if_not_none(
|
|
||||||
power_waypoints, timestamp, power, "power"
|
|
||||||
)
|
|
||||||
activities_utils.append_if_not_none(
|
|
||||||
vel_waypoints, timestamp, instant_speed, "vel"
|
|
||||||
)
|
|
||||||
activities_utils.append_if_not_none(
|
|
||||||
pace_waypoints, timestamp, instant_pace, "pace"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Update previous latitude, longitude, and last waypoint time
|
|
||||||
prev_latitude, prev_longitude, last_waypoint_time = (
|
|
||||||
latitude,
|
|
||||||
longitude,
|
|
||||||
time,
|
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="Invalid GPX file - no tracks found in the GPX file",
|
||||||
|
)
|
||||||
|
|
||||||
# Calculate elevation gain/loss, pace, average speed, and average power
|
# Calculate elevation gain/loss, pace, average speed, and average power
|
||||||
if ele_waypoints:
|
if ele_waypoints:
|
||||||
@@ -244,9 +257,9 @@ def parse_gpx_file(file: str, user_id: int) -> dict:
|
|||||||
if activity_type != 3 and activity_type != 7:
|
if activity_type != 3 and activity_type != 7:
|
||||||
if is_lat_lon_set:
|
if is_lat_lon_set:
|
||||||
timezone = tf.timezone_at(
|
timezone = tf.timezone_at(
|
||||||
lat=lat_lon_waypoints[0]["lat"],
|
lat=lat_lon_waypoints[0]["lat"],
|
||||||
lng=lat_lon_waypoints[0]["lon"],
|
lng=lat_lon_waypoints[0]["lon"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create an Activity object with parsed data
|
# Create an Activity object with parsed data
|
||||||
activity = activities_schema.Activity(
|
activity = activities_schema.Activity(
|
||||||
@@ -268,12 +281,12 @@ def parse_gpx_file(file: str, user_id: int) -> dict:
|
|||||||
average_speed=avg_speed,
|
average_speed=avg_speed,
|
||||||
max_speed=max_speed,
|
max_speed=max_speed,
|
||||||
average_power=round(avg_power) if avg_power else None,
|
average_power=round(avg_power) if avg_power else None,
|
||||||
max_power=max_power,
|
max_power=round(max_power) if max_power else None,
|
||||||
normalized_power=round(np) if np else None,
|
normalized_power=round(np) if np else None,
|
||||||
average_hr=round(avg_hr) if avg_hr else None,
|
average_hr=round(avg_hr) if avg_hr else None,
|
||||||
max_hr=max_hr,
|
max_hr=round(max_hr) if max_hr else None,
|
||||||
average_cad=round(avg_cadence) if avg_cadence else None,
|
average_cad=round(avg_cadence) if avg_cadence else None,
|
||||||
max_cad=max_cadence,
|
max_cad=round(max_cadence) if max_cadence else None,
|
||||||
calories=calories,
|
calories=calories,
|
||||||
visibility=visibility,
|
visibility=visibility,
|
||||||
strava_gear_id=None,
|
strava_gear_id=None,
|
||||||
@@ -302,7 +315,9 @@ def parse_gpx_file(file: str, user_id: int) -> dict:
|
|||||||
raise http_err
|
raise http_err
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in parse_gpx_file - {str(err)}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in parse_gpx_file - {str(err)}", "error", exc=err
|
||||||
|
)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
|||||||
@@ -7,10 +7,38 @@ import users.crud as users_crud
|
|||||||
|
|
||||||
import health_data.schema as health_data_schema
|
import health_data.schema as health_data_schema
|
||||||
import health_data.models as health_data_models
|
import health_data.models as health_data_models
|
||||||
|
import health_data.utils as health_data_utils
|
||||||
|
|
||||||
import core.logger as core_logger
|
import core.logger as core_logger
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_health_data(db: Session):
|
||||||
|
try:
|
||||||
|
# Get the health_data from the database
|
||||||
|
health_data = (
|
||||||
|
db.query(health_data_models.HealthData)
|
||||||
|
.order_by(desc(health_data_models.HealthData.date))
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if there are health_data if not return None
|
||||||
|
if not health_data:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Return the health_data
|
||||||
|
return health_data
|
||||||
|
except Exception as err:
|
||||||
|
# Log the exception
|
||||||
|
core_logger.print_to_log(
|
||||||
|
f"Error in get_all_health_data: {err}", "error", exc=err
|
||||||
|
)
|
||||||
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Internal Server Error",
|
||||||
|
) from err
|
||||||
|
|
||||||
|
|
||||||
def get_health_data_number(user_id: int, db: Session):
|
def get_health_data_number(user_id: int, db: Session):
|
||||||
try:
|
try:
|
||||||
# Get the number of health_data from the database
|
# Get the number of health_data from the database
|
||||||
@@ -21,7 +49,9 @@ def get_health_data_number(user_id: int, db: Session):
|
|||||||
)
|
)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_health_data_number: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in get_health_data_number: {err}", "error", exc=err
|
||||||
|
)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -35,7 +65,7 @@ def get_health_data(user_id: int, db: Session):
|
|||||||
health_data = (
|
health_data = (
|
||||||
db.query(health_data_models.HealthData)
|
db.query(health_data_models.HealthData)
|
||||||
.filter(health_data_models.HealthData.user_id == user_id)
|
.filter(health_data_models.HealthData.user_id == user_id)
|
||||||
.order_by(desc(health_data_models.HealthData.created_at))
|
.order_by(desc(health_data_models.HealthData.date))
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,7 +77,7 @@ def get_health_data(user_id: int, db: Session):
|
|||||||
return health_data
|
return health_data
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_health_data: {err}", "error")
|
core_logger.print_to_log(f"Error in get_health_data: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -63,7 +93,7 @@ def get_health_data_with_pagination(
|
|||||||
health_data = (
|
health_data = (
|
||||||
db.query(health_data_models.HealthData)
|
db.query(health_data_models.HealthData)
|
||||||
.filter(health_data_models.HealthData.user_id == user_id)
|
.filter(health_data_models.HealthData.user_id == user_id)
|
||||||
.order_by(desc(health_data_models.HealthData.created_at))
|
.order_by(desc(health_data_models.HealthData.date))
|
||||||
.offset((page_number - 1) * num_records)
|
.offset((page_number - 1) * num_records)
|
||||||
.limit(num_records)
|
.limit(num_records)
|
||||||
.all()
|
.all()
|
||||||
@@ -78,7 +108,7 @@ def get_health_data_with_pagination(
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(
|
core_logger.print_to_log(
|
||||||
f"Error in get_health_data_with_pagination: {err}", "error"
|
f"Error in get_health_data_with_pagination: {err}", "error", exc=err
|
||||||
)
|
)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -87,13 +117,13 @@ def get_health_data_with_pagination(
|
|||||||
) from err
|
) from err
|
||||||
|
|
||||||
|
|
||||||
def get_health_data_by_created_at(user_id: int, created_at: str, db: Session):
|
def get_health_data_by_date(user_id: int, date: str, db: Session):
|
||||||
try:
|
try:
|
||||||
# Get the health_data from the database
|
# Get the health_data from the database
|
||||||
health_data = (
|
health_data = (
|
||||||
db.query(health_data_models.HealthData)
|
db.query(health_data_models.HealthData)
|
||||||
.filter(
|
.filter(
|
||||||
health_data_models.HealthData.created_at == created_at,
|
health_data_models.HealthData.date == date,
|
||||||
health_data_models.HealthData.user_id == user_id,
|
health_data_models.HealthData.user_id == user_id,
|
||||||
)
|
)
|
||||||
.first()
|
.first()
|
||||||
@@ -108,7 +138,7 @@ def get_health_data_by_created_at(user_id: int, created_at: str, db: Session):
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(
|
core_logger.print_to_log(
|
||||||
f"Error in get_health_data_by_created_at: {err}", "error"
|
f"Error in get_health_data_by_date: {err}", "error", exc=err
|
||||||
)
|
)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -121,29 +151,29 @@ def create_health_data(
|
|||||||
health_data: health_data_schema.HealthData, user_id: int, db: Session
|
health_data: health_data_schema.HealthData, user_id: int, db: Session
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
|
# Check if date is None
|
||||||
|
if health_data.date is None:
|
||||||
|
# Set the date to the current date
|
||||||
|
health_data.date = func.now()
|
||||||
|
|
||||||
# Check if bmi is None
|
# Check if bmi is None
|
||||||
if health_data.bmi is None:
|
if health_data.bmi is None:
|
||||||
# Get the user from the database
|
health_data = health_data_utils.calculate_bmi(health_data, user_id, db)
|
||||||
user = users_crud.get_user_by_id(user_id, db)
|
|
||||||
|
|
||||||
# Check if user is not None
|
|
||||||
if user is not None:
|
|
||||||
# Calculate the bmi
|
|
||||||
health_data.bmi = health_data.weight / ((user.height / 100) ** 2)
|
|
||||||
|
|
||||||
# Create a new health_data
|
# Create a new health_data
|
||||||
db_health_data = health_data_models.HealthData(
|
db_health_data = health_data_models.HealthData(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
created_at=func.now(),
|
date=health_data.date,
|
||||||
weight=health_data.weight,
|
weight=health_data.weight,
|
||||||
bmi=health_data.bmi,
|
bmi=health_data.bmi,
|
||||||
body_fat=health_data.body_fat,
|
# body_fat=health_data.body_fat,
|
||||||
body_water=health_data.body_water,
|
# body_water=health_data.body_water,
|
||||||
bone_mass=health_data.bone_mass,
|
# bone_mass=health_data.bone_mass,
|
||||||
muscle_mass=health_data.muscle_mass,
|
# muscle_mass=health_data.muscle_mass,
|
||||||
physique_rating=health_data.physique_rating,
|
# physique_rating=health_data.physique_rating,
|
||||||
visceral_fat=health_data.visceral_fat,
|
# visceral_fat=health_data.visceral_fat,
|
||||||
metabolic_age=health_data.metabolic_age,
|
# metabolic_age=health_data.metabolic_age,
|
||||||
|
garminconnect_body_composition_id=health_data.garminconnect_body_composition_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add the health_data to the database
|
# Add the health_data to the database
|
||||||
@@ -170,7 +200,63 @@ def create_health_data(
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in create_health_data: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in create_health_data: {err}", "error", exc=err
|
||||||
|
)
|
||||||
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Internal Server Error",
|
||||||
|
) from err
|
||||||
|
|
||||||
|
|
||||||
|
def edit_health_data(user_id, health_data: health_data_schema.HealthData, db: Session):
|
||||||
|
try:
|
||||||
|
# Get the health_data from the database
|
||||||
|
db_health_data = (
|
||||||
|
db.query(health_data_models.HealthData)
|
||||||
|
.filter(
|
||||||
|
health_data_models.HealthData.id == health_data.id,
|
||||||
|
health_data_models.HealthData.user_id == user_id,
|
||||||
|
)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
|
||||||
|
if db_health_data is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail="Health data not found",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if bmi is None
|
||||||
|
if health_data.bmi is None:
|
||||||
|
health_data = health_data_utils.calculate_bmi(health_data, user_id, db)
|
||||||
|
|
||||||
|
# Dictionary of the fields to update if they are not None
|
||||||
|
health_data_data = health_data.dict(exclude_unset=True)
|
||||||
|
# Iterate over the fields and update the db_health_data dynamically
|
||||||
|
for key, value in health_data_data.items():
|
||||||
|
setattr(db_health_data, key, value)
|
||||||
|
|
||||||
|
# Commit the transaction
|
||||||
|
db.commit()
|
||||||
|
except IntegrityError as integrity_error:
|
||||||
|
# Rollback the transaction
|
||||||
|
db.rollback()
|
||||||
|
|
||||||
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_409_CONFLICT,
|
||||||
|
detail="Duplicate entry error. Check if date selected is not already added",
|
||||||
|
) from integrity_error
|
||||||
|
except Exception as err:
|
||||||
|
# Rollback the transaction
|
||||||
|
db.rollback()
|
||||||
|
|
||||||
|
# Log the exception
|
||||||
|
core_logger.print_to_log(f"Error in edit_health_data: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -185,7 +271,7 @@ def create_health_weight_data(
|
|||||||
# Create a new health_data
|
# Create a new health_data
|
||||||
db_health_data = health_data_models.HealthData(
|
db_health_data = health_data_models.HealthData(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
created_at=health_data.created_at,
|
date=health_data.date,
|
||||||
weight=health_data.weight,
|
weight=health_data.weight,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -212,7 +298,9 @@ def create_health_weight_data(
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in create_health_weight_data: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in create_health_weight_data: {err}", "error", exc=err
|
||||||
|
)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -236,9 +324,9 @@ def edit_health_weight_data(health_data: health_data_schema.HealthData, db: Sess
|
|||||||
headers={"WWW-Authenticate": "Bearer"},
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update the user
|
# Update the health_data
|
||||||
if health_data.created_at is not None:
|
if health_data.date is not None:
|
||||||
db_health_data.created_at = health_data.created_at
|
db_health_data.date = health_data.date
|
||||||
if health_data.weight is not None:
|
if health_data.weight is not None:
|
||||||
db_health_data.weight = health_data.weight
|
db_health_data.weight = health_data.weight
|
||||||
|
|
||||||
@@ -258,7 +346,9 @@ def edit_health_weight_data(health_data: health_data_schema.HealthData, db: Sess
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in edit_health_weight_data: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in edit_health_weight_data: {err}", "error", exc=err
|
||||||
|
)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -279,7 +369,7 @@ def delete_health_weight_data(health_data_id: int, user_id: int, db: Session):
|
|||||||
.delete()
|
.delete()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check if the gear was found and deleted
|
# Check if the health_data was found and deleted
|
||||||
if num_deleted == 0:
|
if num_deleted == 0:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
@@ -293,7 +383,9 @@ def delete_health_weight_data(health_data_id: int, user_id: int, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in delete_health_weight_data: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in delete_health_weight_data: {err}", "error", exc=err
|
||||||
|
)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class HealthData(Base):
|
|||||||
index=True,
|
index=True,
|
||||||
comment="User ID that the health_data belongs",
|
comment="User ID that the health_data belongs",
|
||||||
)
|
)
|
||||||
created_at = Column(
|
date = Column(
|
||||||
Date,
|
Date,
|
||||||
nullable=False,
|
nullable=False,
|
||||||
unique=True,
|
unique=True,
|
||||||
@@ -37,41 +37,41 @@ class HealthData(Base):
|
|||||||
nullable=True,
|
nullable=True,
|
||||||
comment="Body mass index (BMI)",
|
comment="Body mass index (BMI)",
|
||||||
)
|
)
|
||||||
body_fat = Column(
|
# body_fat = Column(
|
||||||
DECIMAL(precision=10, scale=2),
|
# DECIMAL(precision=10, scale=2),
|
||||||
nullable=True,
|
# nullable=True,
|
||||||
comment="Body fat percentage",
|
# comment="Body fat percentage",
|
||||||
)
|
# )
|
||||||
body_water = Column(
|
# body_water = Column(
|
||||||
DECIMAL(precision=10, scale=2),
|
# DECIMAL(precision=10, scale=2),
|
||||||
nullable=True,
|
# nullable=True,
|
||||||
comment="Body hydration percentage",
|
# comment="Body hydration percentage",
|
||||||
)
|
# )
|
||||||
bone_mass = Column(
|
# bone_mass = Column(
|
||||||
DECIMAL(precision=10, scale=2),
|
# DECIMAL(precision=10, scale=2),
|
||||||
nullable=True,
|
# nullable=True,
|
||||||
comment="Bone mass percentage",
|
# comment="Bone mass percentage",
|
||||||
)
|
# )
|
||||||
muscle_mass = Column(
|
# muscle_mass = Column(
|
||||||
DECIMAL(precision=10, scale=2),
|
# DECIMAL(precision=10, scale=2),
|
||||||
nullable=True,
|
# nullable=True,
|
||||||
comment="Muscle mass percentage",
|
# comment="Muscle mass percentage",
|
||||||
)
|
# )
|
||||||
physique_rating = Column(
|
# physique_rating = Column(
|
||||||
DECIMAL(precision=10, scale=2),
|
# DECIMAL(precision=10, scale=2),
|
||||||
nullable=True,
|
# nullable=True,
|
||||||
comment="Physique rating",
|
# comment="Physique rating",
|
||||||
)
|
# )
|
||||||
visceral_fat = Column(
|
# visceral_fat = Column(
|
||||||
DECIMAL(precision=10, scale=2),
|
# DECIMAL(precision=10, scale=2),
|
||||||
nullable=True,
|
# nullable=True,
|
||||||
comment="Visceral fat rating",
|
# comment="Visceral fat rating",
|
||||||
)
|
# )
|
||||||
metabolic_age = Column(
|
# metabolic_age = Column(
|
||||||
DECIMAL(precision=10, scale=2),
|
# DECIMAL(precision=10, scale=2),
|
||||||
nullable=True,
|
# nullable=True,
|
||||||
comment="Metabolic age",
|
# comment="Metabolic age",
|
||||||
)
|
# )
|
||||||
garminconnect_body_composition_id = Column(
|
garminconnect_body_composition_id = Column(
|
||||||
String(length=45), nullable=True, comment="Garmin Connect body composition ID"
|
String(length=45), nullable=True, comment="Garmin Connect body composition ID"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ async def create_health_weight_data(
|
|||||||
Depends(core_database.get_db),
|
Depends(core_database.get_db),
|
||||||
],
|
],
|
||||||
):
|
):
|
||||||
health_for_date = health_data_crud.get_health_data_by_created_at(
|
health_for_date = health_data_crud.get_health_data_by_date(
|
||||||
token_user_id, health_data.created_at, db
|
token_user_id, health_data.date, db
|
||||||
)
|
)
|
||||||
if health_for_date:
|
if health_for_date:
|
||||||
if health_for_date.weight is None:
|
if health_for_date.weight is None:
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from datetime import date
|
from datetime import date as datetime_date
|
||||||
|
|
||||||
class HealthData(BaseModel):
|
class HealthData(BaseModel):
|
||||||
id: int | None = None
|
id: int | None = None
|
||||||
user_id: int | None = None
|
user_id: int | None = None
|
||||||
created_at: date | None = None
|
date: datetime_date | None = None
|
||||||
weight: float | None = None
|
weight: float | None = None
|
||||||
bmi: float | None = None
|
bmi: float | None = None
|
||||||
body_fat: float | None = None
|
#body_fat: float | None = None
|
||||||
body_water: float | None = None
|
#body_water: float | None = None
|
||||||
bone_mass: float | None = None
|
#bone_mass: float | None = None
|
||||||
muscle_mass: float | None = None
|
#muscle_mass: float | None = None
|
||||||
physique_rating: float | None = None
|
#physique_rating: float | None = None
|
||||||
visceral_fat: float | None = None
|
#visceral_fat: float | None = None
|
||||||
metabolic_age: float | None = None
|
#metabolic_age: float | None = None
|
||||||
|
garminconnect_body_composition_id: str | None = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
orm_mode = True
|
||||||
17
backend/app/health_data/utils.py
Normal file
17
backend/app/health_data/utils.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
import users.crud as users_crud
|
||||||
|
|
||||||
|
import health_data.schema as health_data_schema
|
||||||
|
|
||||||
|
def calculate_bmi(health_data: health_data_schema.HealthData, user_id: int, db: Session):
|
||||||
|
# Get the user from the database
|
||||||
|
user = users_crud.get_user_by_id(user_id, db)
|
||||||
|
|
||||||
|
# Check if user is not None and user height is not None
|
||||||
|
if user is not None and user.height is not None:
|
||||||
|
# Calculate the bmi
|
||||||
|
health_data.bmi = health_data.weight / ((user.height / 100) ** 2)
|
||||||
|
|
||||||
|
# return the health data
|
||||||
|
return health_data
|
||||||
@@ -26,7 +26,7 @@ def get_user_health_targets(user_id: int, db: Session):
|
|||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_user_health_targets: {err}", "error")
|
core_logger.print_to_log(f"Error in get_user_health_targets: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -68,7 +68,7 @@ def create_health_targets(user_id: int, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in create_health_targets: {err}", "error")
|
core_logger.print_to_log(f"Error in create_health_targets: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
|||||||
@@ -15,12 +15,13 @@ import core.migrations as core_migrations
|
|||||||
|
|
||||||
import garmin.logger as garmin_logger
|
import garmin.logger as garmin_logger
|
||||||
import garmin.activity_utils as garmin_activity_utils
|
import garmin.activity_utils as garmin_activity_utils
|
||||||
|
import garmin.health_utils as garmin_health_utils
|
||||||
|
|
||||||
import strava.activity_utils as strava_activity_utils
|
import strava.activity_utils as strava_activity_utils
|
||||||
|
import strava.logger as strava_logger
|
||||||
|
|
||||||
import migrations.logger as migrations_logger
|
import migrations.logger as migrations_logger
|
||||||
|
|
||||||
import strava.logger as strava_logger
|
|
||||||
|
|
||||||
from core.routes import router as api_router
|
from core.routes import router as api_router
|
||||||
|
|
||||||
|
|
||||||
@@ -41,13 +42,19 @@ def startup_event():
|
|||||||
# Create a scheduler to run background jobs
|
# Create a scheduler to run background jobs
|
||||||
core_scheduler.start_scheduler()
|
core_scheduler.start_scheduler()
|
||||||
|
|
||||||
# Retrieve last day activities from Garmin Connect
|
# Retrieve last day activities from Garmin Connect and Strava
|
||||||
core_logger.print_to_log_and_console(
|
core_logger.print_to_log_and_console(
|
||||||
"Retrieving last day activities from Garmin Connect and Strava on startup"
|
"Retrieving last day activities from Garmin Connect and Strava on startup"
|
||||||
)
|
)
|
||||||
garmin_activity_utils.retrieve_garminconnect_users_activities_for_days(1)
|
garmin_activity_utils.retrieve_garminconnect_users_activities_for_days(1)
|
||||||
strava_activity_utils.retrieve_strava_users_activities_for_days(1)
|
strava_activity_utils.retrieve_strava_users_activities_for_days(1)
|
||||||
|
|
||||||
|
# Retrieve last day body composition from Garmin Connect
|
||||||
|
core_logger.print_to_log_and_console(
|
||||||
|
"Retrieving last day body composition from Garmin Connect on startup"
|
||||||
|
)
|
||||||
|
garmin_health_utils.retrieve_garminconnect_users_bc_for_days(1)
|
||||||
|
|
||||||
|
|
||||||
def shutdown_event():
|
def shutdown_event():
|
||||||
# Log the shutdown event
|
# Log the shutdown event
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ def get_migrations_not_executed(db: Session):
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log_and_console(f"Error in get_migrations_not_executed. See migrations log for more information", "error")
|
core_logger.print_to_log_and_console(f"Error in get_migrations_not_executed. See migrations log for more information", "error")
|
||||||
migrations_logger.print_to_log(f"Error in get_migrations_not_executed: {err}", "error")
|
migrations_logger.print_to_log(f"Error in get_migrations_not_executed: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -58,7 +58,7 @@ def set_migration_as_executed(migration_id: int, db: Session):
|
|||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log_and_console(f"Error in set_migration_as_executed. See migrations log for more information", "error")
|
core_logger.print_to_log_and_console(f"Error in set_migration_as_executed. See migrations log for more information", "error")
|
||||||
migrations_logger.print_to_log(f"Error in set_migration_as_executed: {err}", "error")
|
migrations_logger.print_to_log(f"Error in set_migration_as_executed: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ import activity_streams.crud as activity_streams_crud
|
|||||||
import migrations.crud as migrations_crud
|
import migrations.crud as migrations_crud
|
||||||
import migrations.logger as migrations_logger
|
import migrations.logger as migrations_logger
|
||||||
|
|
||||||
|
import health_data.crud as health_data_crud
|
||||||
|
import health_data.utils as health_data_utils
|
||||||
|
|
||||||
import core.logger as core_logger
|
import core.logger as core_logger
|
||||||
|
|
||||||
|
|
||||||
@@ -56,7 +59,7 @@ def process_migration_1(db: Session):
|
|||||||
activities = activities_crud.get_all_activities(db)
|
activities = activities_crud.get_all_activities(db)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
migrations_logger.print_to_log(
|
migrations_logger.print_to_log(
|
||||||
f"Migration 1 - Error fetching activities: {err}", "error"
|
f"Migration 1 - Error fetching activities: {err}", "error", exc=err
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -95,6 +98,7 @@ def process_migration_1(db: Session):
|
|||||||
migrations_logger.print_to_log(
|
migrations_logger.print_to_log(
|
||||||
f"Migration 1 - Failed to fetch streams for activity {activity.id}: {err}",
|
f"Migration 1 - Failed to fetch streams for activity {activity.id}: {err}",
|
||||||
"warning",
|
"warning",
|
||||||
|
exc=err,
|
||||||
)
|
)
|
||||||
activities_processed_with_no_errors = False
|
activities_processed_with_no_errors = False
|
||||||
continue
|
continue
|
||||||
@@ -160,7 +164,9 @@ def process_migration_1(db: Session):
|
|||||||
)
|
)
|
||||||
|
|
||||||
migrations_logger.print_to_log(
|
migrations_logger.print_to_log(
|
||||||
f"Migration 1 - Failed to process activity {activity.id}: {err}", "error"
|
f"Migration 1 - Failed to process activity {activity.id}: {err}",
|
||||||
|
"error",
|
||||||
|
exc=err,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Mark migration as executed
|
# Mark migration as executed
|
||||||
@@ -174,7 +180,9 @@ def process_migration_1(db: Session):
|
|||||||
)
|
)
|
||||||
|
|
||||||
migrations_logger.print_to_log(
|
migrations_logger.print_to_log(
|
||||||
f"Migration 1 - Failed to set migration as executed: {err}", "error"
|
f"Migration 1 - Failed to set migration as executed: {err}",
|
||||||
|
"error",
|
||||||
|
exc=err,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
@@ -192,19 +200,27 @@ def process_migration_2(db: Session):
|
|||||||
# Create an instance of TimezoneFinder
|
# Create an instance of TimezoneFinder
|
||||||
tf = TimezoneFinder()
|
tf = TimezoneFinder()
|
||||||
|
|
||||||
|
# Initialize flag to track if all activities and health_data were processed without errors
|
||||||
activities_processed_with_no_errors = True
|
activities_processed_with_no_errors = True
|
||||||
|
health_data_processed_with_no_errors = True
|
||||||
|
|
||||||
|
# Fetch all activities and health_data
|
||||||
try:
|
try:
|
||||||
activities = activities_crud.get_all_activities(db)
|
activities = activities_crud.get_all_activities(db)
|
||||||
|
health_data = health_data_crud.get_all_health_data(db)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
migrations_logger.print_to_log(
|
migrations_logger.print_to_log(
|
||||||
f"Migration 2 - Error fetching activities: {err}", "error"
|
f"Migration 2 - Error fetching activities and/or health_data: {err}",
|
||||||
|
"error",
|
||||||
|
exc=err,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if activities:
|
if activities:
|
||||||
|
# Process each activity and add timezone
|
||||||
for activity in activities:
|
for activity in activities:
|
||||||
try:
|
try:
|
||||||
|
# Skip if activity already has timezone
|
||||||
if activity.timezone:
|
if activity.timezone:
|
||||||
migrations_logger.print_to_log(
|
migrations_logger.print_to_log(
|
||||||
f"Migration 2 - {activity.id} already has timezone defined. Skipping.",
|
f"Migration 2 - {activity.id} already has timezone defined. Skipping.",
|
||||||
@@ -225,6 +241,7 @@ def process_migration_2(db: Session):
|
|||||||
migrations_logger.print_to_log(
|
migrations_logger.print_to_log(
|
||||||
f"Migration 2 - Failed to fetch streams for activity {activity.id}: {err}",
|
f"Migration 2 - Failed to fetch streams for activity {activity.id}: {err}",
|
||||||
"warning",
|
"warning",
|
||||||
|
exc=err,
|
||||||
)
|
)
|
||||||
activities_processed_with_no_errors = False
|
activities_processed_with_no_errors = False
|
||||||
continue
|
continue
|
||||||
@@ -239,7 +256,7 @@ def process_migration_2(db: Session):
|
|||||||
|
|
||||||
# Update the activity in the database
|
# Update the activity in the database
|
||||||
activities_crud.edit_activity(activity.user_id, activity, db)
|
activities_crud.edit_activity(activity.user_id, activity, db)
|
||||||
|
|
||||||
migrations_logger.print_to_log(
|
migrations_logger.print_to_log(
|
||||||
f"Migration 2 - Processed activity: {activity.id} - {activity.name}"
|
f"Migration 2 - Processed activity: {activity.id} - {activity.name}"
|
||||||
)
|
)
|
||||||
@@ -252,11 +269,45 @@ def process_migration_2(db: Session):
|
|||||||
)
|
)
|
||||||
|
|
||||||
migrations_logger.print_to_log(
|
migrations_logger.print_to_log(
|
||||||
f"Migration 2 - Failed to process activity {activity.id}: {err}", "error"
|
f"Migration 2 - Failed to process activity {activity.id}: {err}",
|
||||||
|
"error",
|
||||||
|
exc=err,
|
||||||
|
)
|
||||||
|
|
||||||
|
if health_data:
|
||||||
|
# Process each weight and add timezone
|
||||||
|
for data in health_data:
|
||||||
|
try:
|
||||||
|
# Skip if weight already has timezone
|
||||||
|
if data.bmi:
|
||||||
|
migrations_logger.print_to_log(
|
||||||
|
f"Migration 2 - {data.id} already has BMI defined. Skipping.",
|
||||||
|
"info",
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Update the weight in the database
|
||||||
|
health_data_crud.edit_health_data(data.user_id, data, db)
|
||||||
|
|
||||||
|
migrations_logger.print_to_log(
|
||||||
|
f"Migration 2 - Processed BMI: {data.id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as err:
|
||||||
|
health_data_processed_with_no_errors = False
|
||||||
|
core_logger.print_to_log_and_console(
|
||||||
|
f"Migration 2 - Failed to process BMI {data.id}. Please check migrations log for more details.",
|
||||||
|
"error",
|
||||||
|
)
|
||||||
|
|
||||||
|
migrations_logger.print_to_log(
|
||||||
|
f"Migration 2 - Failed to process BMI {data.id}: {err}",
|
||||||
|
"error",
|
||||||
|
exc=err,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Mark migration as executed
|
# Mark migration as executed
|
||||||
if activities_processed_with_no_errors:
|
if activities_processed_with_no_errors and health_data_processed_with_no_errors:
|
||||||
try:
|
try:
|
||||||
migrations_crud.set_migration_as_executed(2, db)
|
migrations_crud.set_migration_as_executed(2, db)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
@@ -266,7 +317,9 @@ def process_migration_2(db: Session):
|
|||||||
)
|
)
|
||||||
|
|
||||||
migrations_logger.print_to_log(
|
migrations_logger.print_to_log(
|
||||||
f"Migration 2 - Failed to set migration as executed: {err}", "error"
|
f"Migration 2 - Failed to set migration as executed: {err}",
|
||||||
|
"error",
|
||||||
|
exc=err,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from stravalib.client import Client
|
from stravalib.client import Client
|
||||||
|
from timezonefinder import TimezoneFinder
|
||||||
|
|
||||||
import activities.schema as activities_schema
|
import activities.schema as activities_schema
|
||||||
import activities.crud as activities_crud
|
import activities.crud as activities_crud
|
||||||
@@ -55,6 +58,10 @@ def parse_activity(
|
|||||||
user_integrations: user_integrations_schema.UserIntegrations,
|
user_integrations: user_integrations_schema.UserIntegrations,
|
||||||
db: Session,
|
db: Session,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
|
# Create an instance of TimezoneFinder
|
||||||
|
tf = TimezoneFinder()
|
||||||
|
timezone = os.environ.get("TZ")
|
||||||
|
|
||||||
# Get the detailed activity
|
# Get the detailed activity
|
||||||
detailedActivity = strava_client.get_activity(activity.id)
|
detailedActivity = strava_client.get_activity(activity.id)
|
||||||
|
|
||||||
@@ -102,6 +109,7 @@ def parse_activity(
|
|||||||
# Extract data from streams
|
# Extract data from streams
|
||||||
lat_lon = streams["latlng"].data if "latlng" in streams else []
|
lat_lon = streams["latlng"].data if "latlng" in streams else []
|
||||||
lat_lon_waypoints = []
|
lat_lon_waypoints = []
|
||||||
|
is_lat_lon_set = False
|
||||||
ele = streams["altitude"].data if "altitude" in streams else []
|
ele = streams["altitude"].data if "altitude" in streams else []
|
||||||
ele_waypoints = []
|
ele_waypoints = []
|
||||||
is_elevation_set = False
|
is_elevation_set = False
|
||||||
@@ -128,6 +136,7 @@ def parse_activity(
|
|||||||
"lon": lat_lon[i][1],
|
"lon": lat_lon[i][1],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
is_lat_lon_set = True
|
||||||
|
|
||||||
for i in range(len(ele)):
|
for i in range(len(ele)):
|
||||||
ele_waypoints.append({"time": time[i], "ele": ele[i]})
|
ele_waypoints.append({"time": time[i], "ele": ele[i]})
|
||||||
@@ -235,17 +244,26 @@ def parse_activity(
|
|||||||
if gear is not None:
|
if gear is not None:
|
||||||
gear_id = gear.id
|
gear_id = gear.id
|
||||||
|
|
||||||
|
# Activity type
|
||||||
|
activity_type = activities_utils.define_activity_type(detailedActivity.sport_type.root)
|
||||||
|
|
||||||
|
if activity_type != 3 and activity_type != 7:
|
||||||
|
if is_lat_lon_set:
|
||||||
|
timezone = tf.timezone_at(
|
||||||
|
lat=lat_lon_waypoints[0]["lat"],
|
||||||
|
lng=lat_lon_waypoints[0]["lon"],
|
||||||
|
)
|
||||||
|
|
||||||
# Create the activity object
|
# Create the activity object
|
||||||
activity_to_store = activities_schema.Activity(
|
activity_to_store = activities_schema.Activity(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
name=detailedActivity.name,
|
name=detailedActivity.name,
|
||||||
distance=round(detailedActivity.distance) if detailedActivity.distance else 0,
|
distance=round(detailedActivity.distance) if detailedActivity.distance else 0,
|
||||||
description=detailedActivity.description,
|
description=detailedActivity.description,
|
||||||
activity_type=activities_utils.define_activity_type(
|
activity_type=activity_type,
|
||||||
detailedActivity.sport_type.root
|
|
||||||
),
|
|
||||||
start_time=start_date_parsed.strftime("%Y-%m-%dT%H:%M:%S"),
|
start_time=start_date_parsed.strftime("%Y-%m-%dT%H:%M:%S"),
|
||||||
end_time=end_date_parsed.strftime("%Y-%m-%dT%H:%M:%S"),
|
end_time=end_date_parsed.strftime("%Y-%m-%dT%H:%M:%S"),
|
||||||
|
timezone=timezone,
|
||||||
total_elapsed_time=total_elapsed_time,
|
total_elapsed_time=total_elapsed_time,
|
||||||
total_timer_time=total_timer_time,
|
total_timer_time=total_timer_time,
|
||||||
city=city,
|
city=city,
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ def get_strava_logger():
|
|||||||
return logging.getLogger("strava_logger")
|
return logging.getLogger("strava_logger")
|
||||||
|
|
||||||
|
|
||||||
def print_to_log(message: str, type: str = "info"):
|
def print_to_log(message: str, type: str = "info", exc: Exception = None):
|
||||||
garminconnect_logger = get_strava_logger()
|
garminconnect_logger = get_strava_logger()
|
||||||
if type == "info":
|
if type == "info":
|
||||||
garminconnect_logger.info(message)
|
garminconnect_logger.info(message)
|
||||||
elif type == "error":
|
elif type == "error":
|
||||||
garminconnect_logger.error(message)
|
garminconnect_logger.error(message, exc_info=exc is not None)
|
||||||
elif type == "warning":
|
elif type == "warning":
|
||||||
garminconnect_logger.warning(message)
|
garminconnect_logger.warning(message)
|
||||||
elif type == "debug":
|
elif type == "debug":
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ async def strava_link(
|
|||||||
core_logger.print_to_log(
|
core_logger.print_to_log(
|
||||||
f"Error in strava_link. For more information check Strava log.", "error"
|
f"Error in strava_link. For more information check Strava log.", "error"
|
||||||
)
|
)
|
||||||
strava_logger.print_to_log(f"Error in strava_link: {err}", "error")
|
strava_logger.print_to_log(f"Error in strava_link: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ def get_user_integrations_by_user_id(user_id: int, db: Session):
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(
|
core_logger.print_to_log(
|
||||||
f"Error in get_user_integrations_by_user_id: {err}", "error"
|
f"Error in get_user_integrations_by_user_id: {err}", "error", exc=err
|
||||||
)
|
)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -43,7 +43,9 @@ def get_user_integrations_by_strava_state(strava_state: str, db: Session):
|
|||||||
try:
|
try:
|
||||||
user_integrations = (
|
user_integrations = (
|
||||||
db.query(user_integrations_models.UserIntegrations)
|
db.query(user_integrations_models.UserIntegrations)
|
||||||
.filter(user_integrations_models.UserIntegrations.strava_state == strava_state)
|
.filter(
|
||||||
|
user_integrations_models.UserIntegrations.strava_state == strava_state
|
||||||
|
)
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -56,7 +58,7 @@ def get_user_integrations_by_strava_state(strava_state: str, db: Session):
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(
|
core_logger.print_to_log(
|
||||||
f"Error in get_user_integrations_by_user_id: {err}", "error"
|
f"Error in get_user_integrations_by_user_id: {err}", "error", exc=err
|
||||||
)
|
)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -86,7 +88,9 @@ def create_user_integrations(user_id: int, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in create_user_integrations: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in create_user_integrations: {err}", "error", exc=err
|
||||||
|
)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -118,7 +122,9 @@ def link_strava_account(
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in link_strava_account: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in link_strava_account: {err}", "error", exc=err
|
||||||
|
)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -153,7 +159,9 @@ def unlink_strava_account(user_id: int, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in unlink_strava_account: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in unlink_strava_account: {err}", "error", exc=err
|
||||||
|
)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -185,7 +193,9 @@ def set_user_strava_state(user_id: int, state: str, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in set_user_strava_state: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in set_user_strava_state: {err}", "error", exc=err
|
||||||
|
)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -209,7 +219,9 @@ def set_user_strava_sync_gear(user_id: int, strava_sync_gear: bool, db: Session)
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in set_user_strava_state: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in set_user_strava_state: {err}", "error", exc=err
|
||||||
|
)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -247,7 +259,9 @@ def link_garminconnect_account(
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in link_garminconnect_account: {err}", "error")
|
core_logger.print_to_log(
|
||||||
|
f"Error in link_garminconnect_account: {err}", "error", exc=err
|
||||||
|
)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -274,7 +288,7 @@ def set_user_garminconnect_sync_gear(
|
|||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(
|
core_logger.print_to_log(
|
||||||
f"Error in set_user_garminconnect_sync_gear: {err}", "error"
|
f"Error in set_user_garminconnect_sync_gear: {err}", "error", exc=err
|
||||||
)
|
)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
@@ -310,7 +324,7 @@ def unlink_garminconnect_account(user_id: int, db: Session):
|
|||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(
|
core_logger.print_to_log(
|
||||||
f"Error in unlink_garminconnect_account: {err}", "error"
|
f"Error in unlink_garminconnect_account: {err}", "error", exc=err
|
||||||
)
|
)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ def authenticate_user(username: str, db: Session):
|
|||||||
return user
|
return user
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in authenticate_user: {err}", "error")
|
core_logger.print_to_log(f"Error in authenticate_user: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -39,7 +39,7 @@ def get_all_users(db: Session):
|
|||||||
return db.query(users_models.User).all()
|
return db.query(users_models.User).all()
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_all_number: {err}", "error")
|
core_logger.print_to_log(f"Error in get_all_number: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -55,7 +55,7 @@ def get_users_number(db: Session):
|
|||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_users_number: {err}", "error")
|
core_logger.print_to_log(f"Error in get_users_number: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -84,7 +84,7 @@ def get_users_with_pagination(db: Session, page_number: int = 1, num_records: in
|
|||||||
|
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_users_with_pagination: {err}", "error")
|
core_logger.print_to_log(f"Error in get_users_with_pagination: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -115,7 +115,7 @@ def get_user_if_contains_username(username: str, db: Session):
|
|||||||
return users
|
return users
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_user_if_contains_username: {err}", "error")
|
core_logger.print_to_log(f"Error in get_user_if_contains_username: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -139,7 +139,7 @@ def get_user_by_username(username: str, db: Session):
|
|||||||
return user
|
return user
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_user_by_username: {err}", "error")
|
core_logger.print_to_log(f"Error in get_user_by_username: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -163,7 +163,7 @@ def get_user_by_id(user_id: int, db: Session):
|
|||||||
return user
|
return user
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_user_by_id: {err}", "error")
|
core_logger.print_to_log(f"Error in get_user_by_id: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -188,7 +188,7 @@ def get_user_id_by_username(username: str, db: Session):
|
|||||||
return user_id
|
return user_id
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_user_id_by_username: {err}", "error")
|
core_logger.print_to_log(f"Error in get_user_id_by_username: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -211,7 +211,7 @@ def get_user_photo_path_by_id(user_id: int, db: Session):
|
|||||||
return user_db.photo_path
|
return user_db.photo_path
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in get_user_photo_path_by_id: {err}", "error")
|
core_logger.print_to_log(f"Error in get_user_photo_path_by_id: {err}", "error", exc=err)
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@@ -257,7 +257,7 @@ def create_user(user: users_schema.UserCreate, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in create_user: {err}", "error")
|
core_logger.print_to_log(f"Error in create_user: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -304,7 +304,7 @@ def edit_user(user_id: int, user: users_schema.User, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in edit_user: {err}", "error")
|
core_logger.print_to_log(f"Error in edit_user: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -328,7 +328,7 @@ def edit_user_password(user_id: int, password: str, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in edit_user_password: {err}", "error")
|
core_logger.print_to_log(f"Error in edit_user_password: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -355,7 +355,7 @@ def edit_user_photo_path(user_id: int, photo_path: str, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in edit_user_photo_path: {err}", "error")
|
core_logger.print_to_log(f"Error in edit_user_photo_path: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -382,7 +382,7 @@ def delete_user_photo(user_id: int, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in delete_user_photo: {err}", "error")
|
core_logger.print_to_log(f"Error in delete_user_photo: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -413,7 +413,7 @@ def delete_user(user_id: int, db: Session):
|
|||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in delete_user: {err}", "error")
|
core_logger.print_to_log(f"Error in delete_user: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Raise an HTTPException with a 500 Internal Server Error status code
|
# Raise an HTTPException with a 500 Internal Server Error status code
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ async def save_user_image(user_id: int, file: UploadFile, db: Session):
|
|||||||
return users_crud.edit_user_photo_path(user_id, file_path_to_save, db)
|
return users_crud.edit_user_photo_path(user_id, file_path_to_save, db)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# Log the exception
|
# Log the exception
|
||||||
core_logger.print_to_log(f"Error in save_user_image: {err}", "error")
|
core_logger.print_to_log(f"Error in save_user_image: {err}", "error", exc=err)
|
||||||
|
|
||||||
# Remove the file after processing
|
# Remove the file after processing
|
||||||
if os.path.exists(file_path_to_save):
|
if os.path.exists(file_path_to_save):
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ services:
|
|||||||
container_name: backend
|
container_name: backend
|
||||||
image: ghcr.io/joaovitoriasilva/endurain/backend:latest
|
image: ghcr.io/joaovitoriasilva/endurain/backend:latest
|
||||||
environment:
|
environment:
|
||||||
|
- TZ=Europe/Lisbon
|
||||||
- DB_PASSWORD=changeme
|
- DB_PASSWORD=changeme
|
||||||
- SECRET_KEY=changeme # openssl rand -hex 32
|
- SECRET_KEY=changeme # openssl rand -hex 32
|
||||||
- STRAVA_CLIENT_ID=changeme
|
- STRAVA_CLIENT_ID=changeme
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { RouterView } from 'vue-router'
|
import { RouterView } from "vue-router";
|
||||||
import NavbarComponent from './components/Navbar/NavbarComponent.vue'
|
import NavbarComponent from "./components/Navbar/NavbarComponent.vue";
|
||||||
import FooterComponent from './components/FooterComponent.vue'
|
import FooterComponent from "./components/FooterComponent.vue";
|
||||||
import { Notivue, Notification, NotificationProgress, pastelTheme } from 'notivue'
|
import {
|
||||||
|
Notivue,
|
||||||
|
Notification,
|
||||||
|
NotificationProgress,
|
||||||
|
pastelTheme,
|
||||||
|
} from "notivue";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -93,7 +93,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" :class="{ disabled: activity.strava_activity_id || activity.garminconnect_activity_id }" href="#" data-bs-toggle="modal" data-bs-target="#deleteActivityModal">
|
<a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#deleteActivityModal">
|
||||||
{{ $t("activitySummary.buttonDeleteActivity") }}
|
{{ $t("activitySummary.buttonDeleteActivity") }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
<div class="fw-bold">
|
<div class="fw-bold">
|
||||||
{{ data.weight }}
|
{{ data.weight }}
|
||||||
</div>
|
</div>
|
||||||
{{ formatDate(data.created_at) }}
|
{{ data.date }}
|
||||||
|
{{ formatDate(data.date) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modal fade" :id="`${modalId}`" tabindex="-1" :aria-labelledby="`${modalId}`" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5" :id="`${modalId}`">{{ title }}</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<!-- number field -->
|
||||||
|
<label for="numberToEmit"><b>* {{ numberFieldLabel }}</b></label>
|
||||||
|
<input class="form-control" type="number" name="numberToEmit" :placeholder="`${numberFieldLabel}`" v-model="numberToEmit" required>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ $t("generalItems.buttonClose") }}</button>
|
||||||
|
<a type="button" @click="submitAction()" class="btn" :class="{ 'btn-success': actionButtonType === 'success', 'btn-danger': actionButtonType === 'danger', 'btn-warning': actionButtonType === 'warning', 'btn-primary': actionButtonType === 'loading' }" data-bs-dismiss="modal">{{ actionButtonText }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref} from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
modalId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
numberFieldLabel: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
actionButtonType: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
actionButtonText: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['numberToEmitAction'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const numberToEmit = ref(7);
|
||||||
|
|
||||||
|
function submitAction() {
|
||||||
|
emit('numberToEmitAction', numberToEmit.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
numberToEmit,
|
||||||
|
submitAction,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -95,57 +95,19 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<!-- modal retrieve Strava activities by days -->
|
||||||
|
<ModalComponentNumberInput modalId="retrieveStravaActivitiesByDaysModal" :title="t('settingsIntegrationsZone.modalRetrieveActivitiesByDaysTitle')" :numberFieldLabel="`${t('settingsIntegrationsZone.modalRetrieveActivitiesByDaysLabel')}`" :actionButtonType="`success`" :actionButtonText="t('settingsIntegrationsZone.modalRetrieveButton')" @numberToEmitAction="submitRetrieveStravaActivities"/>
|
||||||
|
|
||||||
|
<!-- modal unlink Strava -->
|
||||||
|
<ModalComponent modalId="unlinkStravaModal" :title="t('settingsIntegrationsZone.modalUnlinkStravaTitle')" :body="`${t('settingsIntegrationsZone.modalUnlinkStravaBody')}`" :actionButtonType="`danger`" :actionButtonText="t('settingsIntegrationsZone.modalUnlinkStravaTitle')" @submitAction="buttonStravaUnlink"/>
|
||||||
|
|
||||||
<!-- modal garmin connect auth -->
|
<!-- modal garmin connect auth -->
|
||||||
<GarminConnectLoginModalComponent />
|
<GarminConnectLoginModalComponent />
|
||||||
|
|
||||||
<!-- modal retrieve strava activities by days -->
|
<!-- modal retrieve Garmin Connect activities by days -->
|
||||||
<div class="modal fade" id="retrieveStravaActivitiesByDaysModal" tabindex="-1" aria-labelledby="retrieveStravaActivitiesByDaysModalLabel" aria-hidden="true">
|
<ModalComponentNumberInput modalId="retrieveGarminConnectActivitiesByDaysModal" :title="t('settingsIntegrationsZone.modalRetrieveActivitiesByDaysTitle')" :numberFieldLabel="`${t('settingsIntegrationsZone.modalRetrieveActivitiesByDaysLabel')}`" :actionButtonType="`success`" :actionButtonText="t('settingsIntegrationsZone.modalRetrieveButton')" @numberToEmitAction="submitRetrieveGarminConnectActivities"/>
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h1 class="modal-title fs-5" id="retrieveStravaActivitiesByDaysModalLabel">{{ $t("settingsIntegrationsZone.modalRetrieveActivitiesByDaysTitle") }}</h1>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<form @submit.prevent="submitRetrieveStravaActivities">
|
|
||||||
<div class="modal-body">
|
|
||||||
<!-- number of days fields -->
|
|
||||||
<label for="daysToRetrieve"><b>* {{ $t("settingsIntegrationsZone.modalRetrieveActivitiesByDaysLabel") }}</b></label>
|
|
||||||
<input class="form-control" type="number" name="daysToRetrieve" :placeholder='$t("settingsIntegrationsZone.modalRetrieveActivitiesByDaysPlaceholder")' v-model="daysToRetrieveStrava" required>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" name="retrieveStravaActivities" data-bs-dismiss="modal">{{ $t("generalItems.buttonClose") }}</button>
|
|
||||||
<button type="submit" class="btn btn-success" data-bs-dismiss="modal">{{ $t("settingsIntegrationsZone.modalRetrieveButton") }}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- modal retrieve garmin connect activities by days -->
|
|
||||||
<div class="modal fade" id="retrieveGarminConnectActivitiesByDaysModal" tabindex="-1" aria-labelledby="retrieveGarminConnectActivitiesByDaysModal" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h1 class="modal-title fs-5" id="retrieveGarminConnectActivitiesByDaysModal">{{ $t("settingsIntegrationsZone.modalRetrieveActivitiesByDaysTitle") }}</h1>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<form @submit.prevent="submitRetrieveGarminConnectActivities">
|
|
||||||
<div class="modal-body">
|
|
||||||
<!-- number of days fields -->
|
|
||||||
<label for="daysToRetrieve"><b>* {{ $t("settingsIntegrationsZone.modalRetrieveActivitiesByDaysLabel") }}</b></label>
|
|
||||||
<input class="form-control" type="number" name="daysToRetrieve" :placeholder='$t("settingsIntegrationsZone.modalRetrieveActivitiesByDaysPlaceholder")' v-model="daysToRetrieveGarmin" required>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" name="retrieveStravaActivities" data-bs-dismiss="modal">{{ $t("generalItems.buttonClose") }}</button>
|
|
||||||
<button type="submit" class="btn btn-success" data-bs-dismiss="modal">{{ $t("settingsIntegrationsZone.modalRetrieveButton") }}</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ModalComponent modalId="unlinkStravaModal" :title="t('settingsIntegrationsZone.modalUnlinkStravaTitle')" :body="`${t('settingsIntegrationsZone.modalUnlinkStravaBody')}`" :actionButtonType="`danger`" :actionButtonText="t('settingsIntegrationsZone.modalUnlinkStravaTitle')" @submitAction="buttonStravaUnlink"/>
|
|
||||||
|
|
||||||
|
<!-- modal unlink Garmin Connect -->
|
||||||
<ModalComponent modalId="unlinkGarminConnectModal" :title="t('settingsIntegrationsZone.modalUnlinkGarminConnectTitle')" :body="`${t('settingsIntegrationsZone.modalUnlinkGarminConnectBody')}`" :actionButtonType="`danger`" :actionButtonText="t('settingsIntegrationsZone.modalUnlinkGarminConnectTitle')" @submitAction="buttonGarminConnectUnlink"/>
|
<ModalComponent modalId="unlinkGarminConnectModal" :title="t('settingsIntegrationsZone.modalUnlinkGarminConnectTitle')" :body="`${t('settingsIntegrationsZone.modalUnlinkGarminConnectBody')}`" :actionButtonType="`danger`" :actionButtonText="t('settingsIntegrationsZone.modalUnlinkGarminConnectTitle')" @submitAction="buttonGarminConnectUnlink"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -163,6 +125,7 @@ import { activities } from "@/services/activitiesService";
|
|||||||
import { garminConnect } from "@/services/garminConnectService";
|
import { garminConnect } from "@/services/garminConnectService";
|
||||||
// Import the components
|
// Import the components
|
||||||
import ModalComponent from "@/components/Modals/ModalComponent.vue";
|
import ModalComponent from "@/components/Modals/ModalComponent.vue";
|
||||||
|
import ModalComponentNumberInput from "@/components/Modals/ModalComponentNumberInput.vue";
|
||||||
import GarminConnectLoginModalComponent from "./SettingsIntegrations/GarminConnectLoginModalComponent.vue";
|
import GarminConnectLoginModalComponent from "./SettingsIntegrations/GarminConnectLoginModalComponent.vue";
|
||||||
|
|
||||||
//import Modal from 'bootstrap/js/dist/modal';
|
//import Modal from 'bootstrap/js/dist/modal';
|
||||||
@@ -170,15 +133,12 @@ import GarminConnectLoginModalComponent from "./SettingsIntegrations/GarminConne
|
|||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ModalComponent,
|
ModalComponent,
|
||||||
|
ModalComponentNumberInput,
|
||||||
GarminConnectLoginModalComponent,
|
GarminConnectLoginModalComponent,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const { locale, t } = useI18n();
|
const { locale, t } = useI18n();
|
||||||
const daysToRetrieveStrava = ref(7);
|
|
||||||
const daysToRetrieveGarmin = ref(7);
|
|
||||||
const garminConnectUsername = ref("");
|
|
||||||
const garminConnectPassword = ref("");
|
|
||||||
|
|
||||||
async function submitConnectStrava() {
|
async function submitConnectStrava() {
|
||||||
const array = new Uint8Array(16);
|
const array = new Uint8Array(16);
|
||||||
@@ -199,9 +159,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitRetrieveStravaActivities() {
|
async function submitRetrieveStravaActivities(daysToRetrieveStrava) {
|
||||||
try {
|
try {
|
||||||
await strava.getStravaActivitiesLastDays(daysToRetrieveStrava.value);
|
await strava.getStravaActivitiesLastDays(daysToRetrieveStrava);
|
||||||
|
|
||||||
// Show the loading alert.
|
// Show the loading alert.
|
||||||
push.info(
|
push.info(
|
||||||
@@ -268,9 +228,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitRetrieveGarminConnectActivities() {
|
async function submitRetrieveGarminConnectActivities(daysToRetrieveGarmin) {
|
||||||
try {
|
try {
|
||||||
await garminConnect.getGarminConnectActivitiesLastDays(daysToRetrieveGarmin.value);
|
await garminConnect.getGarminConnectActivitiesLastDays(daysToRetrieveGarmin);
|
||||||
|
|
||||||
// Show the loading alert.
|
// Show the loading alert.
|
||||||
push.info(
|
push.info(
|
||||||
@@ -328,14 +288,10 @@ export default {
|
|||||||
t,
|
t,
|
||||||
submitConnectStrava,
|
submitConnectStrava,
|
||||||
submitRetrieveStravaActivities,
|
submitRetrieveStravaActivities,
|
||||||
daysToRetrieveStrava,
|
|
||||||
submitRetrieveStravaGear,
|
submitRetrieveStravaGear,
|
||||||
buttonStravaUnlink,
|
buttonStravaUnlink,
|
||||||
submitBulkImport,
|
submitBulkImport,
|
||||||
garminConnectUsername,
|
|
||||||
garminConnectPassword,
|
|
||||||
submitRetrieveGarminConnectActivities,
|
submitRetrieveGarminConnectActivities,
|
||||||
daysToRetrieveGarmin,
|
|
||||||
submitRetrieveGarminConnectGear,
|
submitRetrieveGarminConnectGear,
|
||||||
buttonGarminConnectUnlink,
|
buttonGarminConnectUnlink,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ export default {
|
|||||||
gearId.value = activity.value.gear_id;
|
gearId.value = activity.value.gear_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activity.value.activity_type === 1 || activity.value.activity_type === 2 || activity.value.activity_type === 3) {
|
if (activity.value.activity_type === 1 || activity.value.activity_type === 2 || activity.value.activity_type === 3 || activity.value.activity_type === 11 || activity.value.activity_type === 12) {
|
||||||
gearsByType.value = await gears.getGearFromType(2);
|
gearsByType.value = await gears.getGearFromType(2);
|
||||||
} else {
|
} else {
|
||||||
if (activity.value.activity_type === 4 || activity.value.activity_type === 5 || activity.value.activity_type === 6 || activity.value.activity_type === 7) {
|
if (activity.value.activity_type === 4 || activity.value.activity_type === 5 || activity.value.activity_type === 6 || activity.value.activity_type === 7) {
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ export default {
|
|||||||
userNumberOfActivities.value ++;
|
userNumberOfActivities.value ++;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Set the error message
|
// Set the error message
|
||||||
notification.reject(`${t('generalItems.errorFetchingInfo')} - ${error}`)
|
notification.reject(`${error}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user