Added more GPX import parameters and added database columns and logic. Added to index.php the activities summary. Fixed also some bugs

This commit is contained in:
João Silva
2023-10-30 21:32:21 +00:00
parent 6387c592c0
commit 6e1ca5f370
3 changed files with 91 additions and 52 deletions

View File

@@ -3,7 +3,7 @@ import logging
from fastapi import APIRouter, Depends, HTTPException, Form, Response, File, UploadFile, Request
from fastapi.security import OAuth2PasswordBearer
from typing import List, Optional
from sqlalchemy import func, DECIMAL, DateTime
from sqlalchemy import func, DECIMAL, DateTime, desc
from db.db import get_db_session, Activity
from jose import jwt, JWTError
from dotenv import load_dotenv
@@ -31,7 +31,7 @@ async def read_activities_all(token: str = Depends(oauth2_scheme)):
#user_id = payload.get("id")
# Query the activities records using SQLAlchemy
activity_records = db_session.query(Activity).all()
activity_records = db_session.query(Activity).order_by(desc(Activity.start_time)).all()
# Convert the SQLAlchemy objects to dictionaries
results = [activity.to_dict() for activity in activity_records]
@@ -78,6 +78,7 @@ async def read_activities_all_pagination(
# Use SQLAlchemy to query the gear records with pagination
activity_records = (
db_session.query(Activity)
.order_by(desc(Activity.start_time))
.offset((pageNumber - 1) * numRecords)
.limit(numRecords)
.all()
@@ -99,10 +100,13 @@ class CreateActivityRequest(BaseModel):
type: str
starttime: str
endtime: str
city: str
town: str
country: str
city: Optional[str]
town: Optional[str]
country: Optional[str]
waypoints: List[dict]
elevationGain: int
elevationLoss: int
pace: float
@router.post("/activities/create")
async def create_activity(
@@ -119,19 +123,25 @@ async def create_activity(
user_id = payload.get("id")
# Convert the 'starttime' string to a datetime
starttime = datetime.strptime(activity_data.starttime, "%Y-%m-%dT%H:%M:%SZ")
#starttime = datetime.strptime(activity_data.starttime, "%Y-%m-%dT%H:%M:%SZ")
starttime = parse_timestamp(activity_data.starttime)
# Convert the 'endtime' string to a datetime
endtime = datetime.strptime(activity_data.endtime, "%Y-%m-%dT%H:%M:%SZ")
#endtime = datetime.strptime(activity_data.endtime, "%Y-%m-%dT%H:%M:%SZ")
endtime = parse_timestamp(activity_data.endtime)
auxType = 10 # Default value
type_mapping = {
"running": 1,
"trail running": 2,
"VirtualRun": 3,
"cycling": 4,
"Ride": 4,
"GravelRide": 5,
"EBikeRide": 6,
"VirtualRide": 7
"VirtualRide": 7,
"virtual_ride": 7,
"swimming": 8,
"open_water_swimming": 8
}
auxType = type_mapping.get(activity_data.type, 10)
@@ -147,7 +157,10 @@ async def create_activity(
town=activity_data.town,
country=activity_data.country,
created_at=func.now(), # Use func.now() to set 'created_at' to the current timestamp
waypoints=activity_data.waypoints
waypoints=activity_data.waypoints,
elevation_gain=activity_data.elevationGain,
elevation_loss=activity_data.elevationLoss,
pace=activity_data.pace
)
# Store the Activity record in the database
@@ -156,7 +169,8 @@ async def create_activity(
db_session.commit()
db_session.refresh(activity) # This will ensure that the activity object is updated with the ID from the database
return {"message": "Activity stored successfully", "id": activity.id}
#return {"message": "Activity stored successfully", "id": activity.id}
return {"message": "Activity stored successfully"}
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
@@ -165,4 +179,12 @@ async def create_activity(
logger.error(err)
raise HTTPException(status_code=500, detail="Failed to store activity")
#return {"message": "Activity stored successfully"}
#return {"message": "Activity stored successfully"}
def parse_timestamp(timestamp_string):
try:
# Try to parse with milliseconds
return datetime.strptime(timestamp_string, "%Y-%m-%dT%H:%M:%S.%fZ")
except ValueError:
# If milliseconds are not present, use a default value of 0
return datetime.strptime(timestamp_string, "%Y-%m-%dT%H:%M:%SZ")

View File

@@ -72,6 +72,29 @@ CREATE TABLE IF NOT EXISTS `gearguardian`.`gear` (
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `gearguardian`.`user_settings`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `gearguardian`.`user_settings` (
`id` INT(10) NOT NULL AUTO_INCREMENT ,
`user_id` INT(10) NOT NULL COMMENT 'User ID that the activity belongs' ,
`activity_type` INT(2) NULL COMMENT 'Gear type' ,
`gear_id` INT(10) NULL COMMENT 'Gear ID associated with this activity' ,
PRIMARY KEY (`id`) ,
INDEX `FK_user_id_idx` (`user_id` ASC) ,
CONSTRAINT `FK_user_settings_user`
FOREIGN KEY (`user_id` )
REFERENCES `gearguardian`.`users` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
INDEX `FK_gear_id_idx` (`gear_id` ASC) ,
CONSTRAINT `FK_user_settings_gear`
FOREIGN KEY (`gear_id` )
REFERENCES `gearguardian`.`gear` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `gearguardian`.`activities`
-- -----------------------------------------------------
@@ -80,7 +103,7 @@ CREATE TABLE IF NOT EXISTS `gearguardian`.`activities` (
`user_id` INT(10) NOT NULL COMMENT 'User ID that the activity belongs' ,
`name` VARCHAR(45) NULL COMMENT 'Activity name (May include spaces)' ,
`distance` INT(9) NOT NULL COMMENT 'Distance in meters' ,
`activity_type` INT(2) NOT NULL COMMENT 'Gear type (1 - mountain bike, 2 - gravel bike, 3 - road bike, 4 - indoor bike, 5 - road run, 6 - trail run, 7 - indoor run, 8 - indoor swim, 9 - openwater swim, 10 - other)' ,
`activity_type` INT(2) NOT NULL COMMENT 'Gear type' ,
`start_time` DATETIME NOT NULL COMMENT 'Actvitiy start date (datetime)' ,
`end_time` DATETIME NOT NULL COMMENT 'Actvitiy end date (datetime)' ,
`city` VARCHAR(45) NULL COMMENT 'Activity city (May include spaces)' ,
@@ -88,34 +111,23 @@ CREATE TABLE IF NOT EXISTS `gearguardian`.`activities` (
`country` VARCHAR(45) NULL COMMENT 'Activity country (May include spaces)' ,
`created_at` DATETIME NOT NULL COMMENT 'Actvitiy creation date (datetime)' ,
`waypoints` LONGTEXT NULL COMMENT 'Store waypoints data',
`elevation_gain` INT(5) NOT NULL COMMENT 'Elevation gain in meters' ,
`elevation_loss` INT(5) NOT NULL COMMENT 'Elevation loss in meters' ,
`pace` DECIMAL(20, 10) NOT NULL COMMENT 'Pace seconds per meter (s/m)' ,
`gear_id` INT(10) NULL COMMENT 'Gear ID associated with this activity' ,
PRIMARY KEY (`id`) ,
INDEX `FK_user_id_idx` (`user_id` ASC) ,
CONSTRAINT `FK_activity_user`
FOREIGN KEY (`user_id` )
REFERENCES `gearguardian`.`users` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `gearguardian`.`waypoints`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `gearguardian`.`waypoints` (
`id` INT(10) NOT NULL AUTO_INCREMENT ,
`activity_id` INT(10) NOT NULL COMMENT 'Activity ID that the waypoint belongs' ,
`latitude` DECIMAL(10, 6) NULL COMMENT 'Latitude with 6 decimal places' ,
`longitude` DECIMAL(10, 6) NULL COMMENT 'Longitude with 6 decimal places' ,
`elevation` DECIMAL(8, 2) NULL COMMENT 'Elevation with 2 decimal places' ,
`time` DATETIME NULL COMMENT 'Timestamp of the waypoint' ,
`heart_rate` INT NULL COMMENT 'Heart rate data' ,
`cadence` INT NULL COMMENT 'Cadence data' ,
PRIMARY KEY (`id`) ,
INDEX `FK_activity_id_idx` (`activity_id` ASC) ,
CONSTRAINT `FK_waypoint_activity`
FOREIGN KEY (`activity_id` )
REFERENCES `gearguardian`.`activities` (`id` )
ON UPDATE NO ACTION,
INDEX `FK_gear_id_idx` (`gear_id` ASC) ,
CONSTRAINT `FK_activity_gear`
FOREIGN KEY (`gear_id` )
REFERENCES `gearguardian`.`gear` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ON UPDATE NO ACTION,)
ENGINE = InnoDB;
-- -----------------------------------------------------

View File

@@ -54,6 +54,8 @@ class User(Base):
gear = relationship('Gear', back_populates='user')
# Establish a one-to-many relationship with 'activities'
activities = relationship('Activity', back_populates='user')
# Establish a one-to-many relationship between User and UserSettings
user_settings = relationship("UserSettings", back_populates="user")
# Data model for access_tokens table using SQLAlchemy's ORM
class AccessToken(Base):
@@ -83,6 +85,22 @@ class Gear(Base):
# Define a relationship to the User model
user = relationship('User', back_populates='gear')
# Establish a one-to-many relationship with 'activities'
activities = relationship('Activity', back_populates='gear')
# Establish a one-to-many relationship between Gear and UserSettings
user_settings = relationship("UserSettings", back_populates="gear")
class UserSettings(Base):
__tablename__ = 'user_settings'
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, nullable=False, doc='User ID that the activity belongs')
activity_type = Column(Integer, nullable=True, doc='Gear type')
gear_id = Column(Integer, nullable=True, doc='Gear ID associated with this activity')
# Define the foreign key relationships
user = relationship("User", back_populates="user_settings")
gear = relationship("Gear", back_populates="user_settings")
# Data model for activities table using SQLAlchemy's ORM
class Activity(Base):
@@ -100,29 +118,16 @@ class Activity(Base):
country = Column(String(length=45), nullable=True, comment='Activity country (May include spaces)')
created_at = Column(DateTime, nullable=False, comment='Activity creation date (datetime)')
waypoints = Column(JSON, nullable=True, doc='Store waypoints data')
elevation_gain = Column(Integer, nullable=False, comment='Elevation gain in meters')
elevation_loss = Column(Integer, nullable=False, comment='Elevation loss in meters')
pace = Column(DECIMAL(precision=20, scale=10), nullable=False, comment='Pace seconds per meter (s/m)')
gear_id = Column(Integer, ForeignKey('gear.id'), nullable=True, comment='Gear ID associated with this activity')
# Define a relationship to the User model
user = relationship('User', back_populates='activities')
# Establish a one-to-many relationship with 'waypoints'
#waypoints = relationship('Waypoint', back_populates='activity')
# Data model for waypoints table using SQLAlchemy's ORM
# class Waypoint(Base):
# __tablename__ = 'waypoints'
# id = Column(Integer, primary_key=True, autoincrement=True)
# activity_id = Column(Integer, ForeignKey('activities.id'), nullable=False, comment='Activity ID that the waypoint belongs')
# latitude = Column(DECIMAL(precision=10, scale=6), nullable=True, comment='Latitude with 6 decimal places')
# longitude = Column(DECIMAL(precision=10, scale=6), nullable=True, comment='Longitude with 6 decimal places')
# elevation = Column(DECIMAL(precision=8, scale=2), nullable=True, comment='Elevation with 2 decimal places')
# time = Column(DateTime, nullable=True, comment='Timestamp of the waypoint')
# heart_rate = Column(Integer, nullable=True, comment='Heart rate data')
# cadence = Column(Integer, nullable=True, comment='Cadence data')
# power = Column(Integer, nullable=True, comment='Power data')
# # Define a relationship to the Activity model
# activity = relationship('Activity', back_populates='waypoints')
# Define a relationship to the Gear model
gear = relationship('Gear', back_populates='activities')
# Context manager to get a database session
from contextlib import contextmanager