From a91c02801d237221b8f721572d70073ad1b3f7ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Silva?= Date: Fri, 3 Nov 2023 13:25:15 +0000 Subject: [PATCH] Added ability to add/edit and delete gear from activity --- controllers/activitiesController.py | 220 ++++++++++++++++++++++++++-- controllers/gearController.py | 86 ++++++++++- db/createdb.sql | 2 + db/db.py | 2 + 4 files changed, 293 insertions(+), 17 deletions(-) diff --git a/controllers/activitiesController.py b/controllers/activitiesController.py index 012ce655b..247ff94c8 100644 --- a/controllers/activitiesController.py +++ b/controllers/activitiesController.py @@ -27,8 +27,6 @@ async def read_activities_all(token: str = Depends(oauth2_scheme)): try: sessionController.validate_token(token) with get_db_session() as db_session: - #payload = jwt.decode(token, os.getenv("SECRET_KEY"), algorithms=[os.getenv("ALGORITHM")]) - #user_id = payload.get("id") # Query the activities records using SQLAlchemy activity_records = db_session.query(Activity).order_by(desc(Activity.start_time)).all() @@ -43,14 +41,34 @@ async def read_activities_all(token: str = Depends(oauth2_scheme)): return results -@router.get("/activities/number") -async def read_activities_number(token: str = Depends(oauth2_scheme)): +@router.get("/activities/useractivities", response_model=List[dict]) +async def read_activities_useractivities(token: str = Depends(oauth2_scheme)): + from . import sessionController + try: + sessionController.validate_token(token) + with get_db_session() as db_session: + payload = jwt.decode(token, os.getenv("SECRET_KEY"), algorithms=[os.getenv("ALGORITHM")]) + user_id = payload.get("id") + + # Query the activities records using SQLAlchemy + activity_records = db_session.query(Activity).filter(Activity.user_id == user_id).order_by(desc(Activity.start_time)).all() + + # Convert the SQLAlchemy objects to dictionaries + results = [activity.to_dict() for activity in activity_records] + + except JWTError: + raise HTTPException(status_code=401, detail="Unauthorized") + except Error as err: + print(err) + + return results + +@router.get("/activities/all/number") +async def read_activities_all_number(token: str = Depends(oauth2_scheme)): from . import sessionController try: sessionController.validate_token(token) with get_db_session() as db_session: - #payload = jwt.decode(token, os.getenv("SECRET_KEY"), algorithms=[os.getenv("ALGORITHM")]) - #user_id = payload.get("id") # Query the number of activities records for the user using SQLAlchemy activities_count = db_session.query(func.count(Activity.id)).scalar() @@ -62,6 +80,25 @@ async def read_activities_number(token: str = Depends(oauth2_scheme)): return {0: activities_count} +@router.get("/activities/useractivities/number") +async def read_activities_useractivities_number(token: str = Depends(oauth2_scheme)): + from . import sessionController + try: + sessionController.validate_token(token) + with get_db_session() as db_session: + payload = jwt.decode(token, os.getenv("SECRET_KEY"), algorithms=[os.getenv("ALGORITHM")]) + user_id = payload.get("id") + + # Query the number of activities records for the user using SQLAlchemy + activities_count = db_session.query(func.count(Activity.id)).filter(Activity.user_id == user_id).scalar() + + except JWTError: + raise HTTPException(status_code=401, detail="Unauthorized") + except Error as err: + print(err) + + return {0: activities_count} + @router.get("/activities/all/pagenumber/{pageNumber}/numRecords/{numRecords}", response_model=List[dict]) async def read_activities_all_pagination( pageNumber: int, @@ -72,8 +109,6 @@ async def read_activities_all_pagination( try: sessionController.validate_token(token) with get_db_session() as db_session: - #payload = jwt.decode(token, os.getenv("SECRET_KEY"), algorithms=[os.getenv("ALGORITHM")]) - #user_id = payload.get("id") # Use SQLAlchemy to query the gear records with pagination activity_records = ( @@ -94,6 +129,95 @@ async def read_activities_all_pagination( return results +@router.get("/activities/useractivities/pagenumber/{pageNumber}/numRecords/{numRecords}", response_model=List[dict]) +async def read_activities_useractivities_pagination( + pageNumber: int, + numRecords: int, + token: str = Depends(oauth2_scheme) +): + from . import sessionController + try: + sessionController.validate_token(token) + with get_db_session() as db_session: + payload = jwt.decode(token, os.getenv("SECRET_KEY"), algorithms=[os.getenv("ALGORITHM")]) + user_id = payload.get("id") + + # Use SQLAlchemy to query the gear records with pagination + activity_records = ( + db_session.query(Activity) + .filter(Activity.user_id == user_id) + .order_by(desc(Activity.start_time)) + .offset((pageNumber - 1) * numRecords) + .limit(numRecords) + .all() + ) + + # Convert the SQLAlchemy results to a list of dictionaries + results = [record.__dict__ for record in activity_records] + + except JWTError: + raise HTTPException(status_code=401, detail="Unauthorized") + except Error as err: + print(err) + + return results + +# Get gear from id +@router.get("/activities/{id}/activityfromid", response_model=List[dict]) +async def read_activities_activityFromId(id: int, token: str = Depends(oauth2_scheme)): + from . import sessionController + try: + sessionController.validate_token(token) + with get_db_session() as db_session: + payload = jwt.decode(token, os.getenv("SECRET_KEY"), algorithms=[os.getenv("ALGORITHM")]) + user_id = payload.get("id") + + # Use SQLAlchemy to query the gear record by ID + activity_record = ( + db_session.query(Activity) + .filter(Activity.id == id, Activity.user_id == user_id) + .first() + ) + + # Convert the SQLAlchemy result to a list of dictionaries + if activity_record: + results = [activity_record.__dict__] + else: + results = [] + + except JWTError: + raise HTTPException(status_code=401, detail="Unauthorized") + except Error as err: + print(err) + + return results + +@router.put("/activities/{activity_id}/addgear/{gear_id}") +async def activity_add_gear(activity_id: int, gear_id: int, token: str = Depends(oauth2_scheme)): + from . import sessionController + try: + sessionController.validate_token(token) + + # Use SQLAlchemy to query and delete the gear record + with get_db_session() as db_session: + activity_record = db_session.query(Activity).filter(Activity.id == activity_id).first() + + if activity_record: + activity_record.gear_id = gear_id + + # Commit the transaction + db_session.commit() + return {"message": "Gear added to activity successfully"} + else: + raise HTTPException(status_code=404, detail="Activity not found") + + except JWTError: + raise HTTPException(status_code=401, detail="Unauthorized") + except Exception as err: + raise HTTPException(status_code=500, detail="Failed to add gear to activity") + + return {"message": f"Gear added to {activity_id}"} + class CreateActivityRequest(BaseModel): distance: int name: str @@ -107,6 +231,8 @@ class CreateActivityRequest(BaseModel): elevationGain: int elevationLoss: int pace: float + averageSpeed: float + averagePower: int @router.post("/activities/create") async def create_activity( @@ -160,7 +286,9 @@ async def create_activity( waypoints=activity_data.waypoints, elevation_gain=activity_data.elevationGain, elevation_loss=activity_data.elevationLoss, - pace=activity_data.pace + pace=activity_data.pace, + average_speed=activity_data.averageSpeed, + average_power=activity_data.averagePower ) # Store the Activity record in the database @@ -187,4 +315,76 @@ def parse_timestamp(timestamp_string): 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") \ No newline at end of file + return datetime.strptime(timestamp_string, "%Y-%m-%dT%H:%M:%SZ") + +# Define an HTTP PUT route to delete an activity gear +@router.put("/activities/{activity_id}/deletegear") +async def delete_activity_gear( + activity_id: int, + token: str = Depends(oauth2_scheme) +): + from . import sessionController + try: + # Validate the user's access token using the oauth2_scheme + sessionController.validate_token(token) + + with get_db_session() as db_session: + # get user_id + payload = jwt.decode(token, os.getenv("SECRET_KEY"), algorithms=[os.getenv("ALGORITHM")]) + user_id = payload.get("id") + + # Query the database to find the user by their ID + activity = db_session.query(Activity).filter(Activity.id == activity_id,Activity.user_id == user_id).first() + + # Check if the user with the given ID exists + if not activity: + raise HTTPException(status_code=404, detail="Activity not found") + + # Set the user's photo paths to None to delete the photo + activity.gear_id = None + + # Commit the changes to the database + db_session.commit() + except JWTError: + # Handle JWT (JSON Web Token) authentication error + raise HTTPException(status_code=401, detail="Unauthorized") + except Exception as err: + # Handle any other unexpected exceptions + print(err) + raise HTTPException(status_code=500, detail="Failed to update activity gear") + + # Return a success message + return {"message": f"Activity gear {activity_id} has been deleted"} + +@router.delete("/activities/{activity_id}/delete") +async def delete_activity(activity_id: int, token: str = Depends(oauth2_scheme)): + from . import sessionController + try: + sessionController.validate_token(token) + + # Use SQLAlchemy to query and delete the gear record + with get_db_session() as db_session: + activity_record = db_session.query(Activity).filter(Activity.id == activity_id).first() + + if gear_record: + # Check for existing dependencies (uncomment if needed) + # Example: Check if there are dependencies in another table + # if db_session.query(OtherModel).filter(OtherModel.gear_id == gear_id).count() > 0: + # response.status_code = 409 + # return {"detail": "Cannot delete gear due to existing dependencies"} + + # Delete the gear record + db_session.delete(activity_record) + + # Commit the transaction + db_session.commit() + return {"message": f"Activity {activity_id} has been deleted"} + else: + raise HTTPException(status_code=404, detail="Gear not found") + + except JWTError: + raise HTTPException(status_code=401, detail="Unauthorized") + except Exception as err: + raise HTTPException(status_code=500, detail="Failed to delete activity") + + return {"message": f"Activity {activity_id} has been deleted"} \ No newline at end of file diff --git a/controllers/gearController.py b/controllers/gearController.py index e34f79a3c..829e96119 100644 --- a/controllers/gearController.py +++ b/controllers/gearController.py @@ -33,7 +33,82 @@ async def read_gear_all(token: str = Depends(oauth2_scheme)): gear_records = db_session.query(Gear).filter(Gear.user_id == user_id).order_by(Gear.nickname).all() # Convert the SQLAlchemy objects to dictionaries - results = [gear.to_dict() for gear in gear_records] + results = [gear.__dict__ for gear in gear_records] + + except JWTError: + raise HTTPException(status_code=401, detail="Unauthorized") + except Error as err: + print(err) + + return results + +@router.get("/gear/all/running", response_model=List[dict]) +async def read_gear_all_running(token: str = Depends(oauth2_scheme)): + from . import sessionController + try: + sessionController.validate_token(token) + with get_db_session() as db_session: + payload = jwt.decode(token, os.getenv("SECRET_KEY"), algorithms=[os.getenv("ALGORITHM")]) + user_id = payload.get("id") + + # Query the gear records using SQLAlchemy + gear_records = db_session.query(Gear).filter( + Gear.gear_type == 2, + Gear.user_id == user_id + ).order_by(Gear.nickname).all() + + # Convert the SQLAlchemy objects to dictionaries + results = [gear.__dict__ for gear in gear_records] + + except JWTError: + raise HTTPException(status_code=401, detail="Unauthorized") + except Error as err: + print(err) + + return results + +@router.get("/gear/all/cycling", response_model=List[dict]) +async def read_gear_all_cycling(token: str = Depends(oauth2_scheme)): + from . import sessionController + try: + sessionController.validate_token(token) + with get_db_session() as db_session: + payload = jwt.decode(token, os.getenv("SECRET_KEY"), algorithms=[os.getenv("ALGORITHM")]) + user_id = payload.get("id") + + # Query the gear records using SQLAlchemy + gear_records = db_session.query(Gear).filter( + Gear.gear_type == 1, + Gear.user_id == user_id + ).order_by(Gear.nickname).all() + + # Convert the SQLAlchemy objects to dictionaries + results = [gear.__dict__ for gear in gear_records] + + except JWTError: + raise HTTPException(status_code=401, detail="Unauthorized") + except Error as err: + print(err) + + return results + +@router.get("/gear/all/swimming", response_model=List[dict]) +async def read_gear_all_swimming(token: str = Depends(oauth2_scheme)): + from . import sessionController + try: + sessionController.validate_token(token) + with get_db_session() as db_session: + payload = jwt.decode(token, os.getenv("SECRET_KEY"), algorithms=[os.getenv("ALGORITHM")]) + user_id = payload.get("id") + + # Query the gear records using SQLAlchemy + gear_records = db_session.query(Gear).filter( + Gear.gear_type == 3, + Gear.user_id == user_id + ).order_by(Gear.nickname).all() + + # Convert the SQLAlchemy objects to dictionaries + results = [gear.__dict__ for gear in gear_records] except JWTError: raise HTTPException(status_code=401, detail="Unauthorized") @@ -268,14 +343,11 @@ async def delete_gear(gear_id: int, response: Response, token: str = Depends(oau db_session.commit() return {"message": f"Gear {gear_id} has been deleted"} else: - response.status_code = 404 - return {"detail": "Gear not found"} + raise HTTPException(status_code=404, detail="Gear not found") except JWTError: - response.status_code = 401 - return {"detail": "Unauthorized"} + raise HTTPException(status_code=401, detail="Unauthorized") except Exception as err: - response.status_code = 500 - return {"detail": "Failed to delete gear"} + raise HTTPException(status_code=500, detail="Failed to delete gear") return {"message": f"Gear {gear_id} has been deleted"} diff --git a/db/createdb.sql b/db/createdb.sql index 995e40f99..b7e83ed69 100644 --- a/db/createdb.sql +++ b/db/createdb.sql @@ -114,6 +114,8 @@ CREATE TABLE IF NOT EXISTS `gearguardian`.`activities` ( `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)' , + `average_speed` DECIMAL(20, 10) NOT NULL COMMENT 'Average speed seconds per meter (s/m)' , + `average_power` INT(4) NOT NULL COMMENT 'Average power (watts)' , `gear_id` INT(10) NULL COMMENT 'Gear ID associated with this activity' , PRIMARY KEY (`id`) , INDEX `FK_user_id_idx` (`user_id` ASC) , diff --git a/db/db.py b/db/db.py index 09860695e..b3d1a7b90 100644 --- a/db/db.py +++ b/db/db.py @@ -121,6 +121,8 @@ class Activity(Base): 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)') + average_speed = Column(DECIMAL(precision=20, scale=10), nullable=False, comment='Average speed seconds per meter (s/m)') + average_power = Column(Integer, nullable=False, comment='Average power (watts)') gear_id = Column(Integer, ForeignKey('gear.id'), nullable=True, comment='Gear ID associated with this activity') # Define a relationship to the User model