mirror of
https://github.com/joaovitoriasilva/endurain.git
synced 2026-01-09 15:57:59 -05:00
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:
@@ -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")
|
||||
@@ -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;
|
||||
|
||||
-- -----------------------------------------------------
|
||||
|
||||
43
db/db.py
43
db/db.py
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user