Backend optimziation

Activity controller
Gear controller
This commit is contained in:
João Vitória Silva
2024-01-03 08:47:12 +00:00
parent 22d36dd16e
commit 3287e82e88
13 changed files with 2662 additions and 1603 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -4,268 +4,339 @@ from . import sessionController
from jose import JWTError
from fastapi.responses import JSONResponse
from db.db import (
get_db_session,
Follower,
)
from dependencies import get_db_session
from sqlalchemy.orm import Session
router = APIRouter()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# Define an HTTP GET route to retrieve the number of users
@router.get("/followers/user/{user_id}/targetUser/{target_user_id}")
async def read_followers_user_specific_user(user_id: int, target_user_id: int, token: str = Depends(oauth2_scheme)):
async def read_followers_user_specific_user(
user_id: int,
target_user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Query the specific follower record
follower = db_session.query(Follower).filter(
(Follower.follower_id == user_id) & (Follower.following_id == target_user_id)
).first()
# Query the specific follower record
follower = (
db_session.query(Follower)
.filter(
(Follower.follower_id == user_id)
& (Follower.following_id == target_user_id)
)
.first()
)
if follower:
# User follows target_user_id or vice versa
response_data = {
"follower_id": user_id,
"following_id": target_user_id,
"is_accepted": follower.is_accepted
}
return JSONResponse(content=response_data, status_code=200)
if follower:
# User follows target_user_id or vice versa
response_data = {
"follower_id": user_id,
"following_id": target_user_id,
"is_accepted": follower.is_accepted,
}
return JSONResponse(content=response_data, status_code=200)
# Users are not following each other
return JSONResponse(content={"detail": "Users are not following each other."}, status_code=404)
# Users are not following each other
return JSONResponse(
content={"detail": "Users are not following each other."}, status_code=404
)
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")
@router.get("/followers/user/{user_id}/followers/count/all")
async def get_user_follower_count_all(user_id: int, token: str = Depends(oauth2_scheme)):
async def get_user_follower_count_all(
user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
follower_count = db_session.query(Follower).filter(
Follower.follower_id == user_id
).count()
follower_count = (
db_session.query(Follower).filter(Follower.follower_id == user_id).count()
)
# Respond with the count
return {"follower_count": follower_count}
# Respond with the count
return {"follower_count": follower_count}
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")
@router.get("/followers/user/{user_id}/followers/count")
async def get_user_follower_count(user_id: int, token: str = Depends(oauth2_scheme)):
async def get_user_follower_count(
user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Query for the count of entries where user_id is equal to Follower.follower_id
follower_count = db_session.query(Follower).filter(
(Follower.follower_id == user_id) & (Follower.is_accepted == True)
).count()
# Query for the count of entries where user_id is equal to Follower.follower_id
follower_count = (
db_session.query(Follower)
.filter((Follower.follower_id == user_id) & (Follower.is_accepted == True))
.count()
)
# Respond with the count
return {"follower_count": follower_count}
# Respond with the count
return {"follower_count": follower_count}
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")
@router.get("/followers/user/{user_id}/followers/all")
async def get_user_follower_all(user_id: int, token: str = Depends(oauth2_scheme)):
async def get_user_follower_all(
user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Query for the entries where both conditions are met
followers = db_session.query(Follower).filter(
Follower.following_id == user_id
).all()
# Query for the entries where both conditions are met
followers = (
db_session.query(Follower).filter(Follower.following_id == user_id).all()
)
# Convert the query result to a list of dictionaries
followers_list = [{"follower_id": follower.follower_id, "is_accepted": follower.is_accepted}
for follower in followers]
# Convert the query result to a list of dictionaries
followers_list = [
{"follower_id": follower.follower_id, "is_accepted": follower.is_accepted}
for follower in followers
]
# Respond with the list of followers
return {"followers": followers_list}
# Respond with the list of followers
return {"followers": followers_list}
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")
@router.get("/followers/user/{user_id}/following/count/all")
async def get_user_follower_count_all(user_id: int, token: str = Depends(oauth2_scheme)):
async def get_user_following_count_all(
user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
following_count = db_session.query(Follower).filter(
Follower.following_id == user_id
).count()
following_count = (
db_session.query(Follower).filter(Follower.following_id == user_id).count()
)
# Respond with the count
return {"following_count": following_count}
# Respond with the count
return {"following_count": following_count}
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")
@router.get("/followers/user/{user_id}/following/count")
async def get_user_follower_count(user_id: int, token: str = Depends(oauth2_scheme)):
async def get_user_following_count(
user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Query for the count of entries where user_id is equal to Follower.follower_id
following_count = db_session.query(Follower).filter(
(Follower.following_id == user_id) & (Follower.is_accepted == True)
).count()
# Query for the count of entries where user_id is equal to Follower.follower_id
following_count = (
db_session.query(Follower)
.filter((Follower.following_id == user_id) & (Follower.is_accepted == True))
.count()
)
# Respond with the count
return {"following_count": following_count}
# Respond with the count
return {"following_count": following_count}
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")
@router.get("/followers/user/{user_id}/following/all")
async def get_user_following_all(user_id: int, token: str = Depends(oauth2_scheme)):
async def get_user_following_all(
user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Query for the entries where both conditions are met
followings = db_session.query(Follower).filter(
Follower.follower_id == user_id
).all()
# Query for the entries where both conditions are met
followings = (
db_session.query(Follower).filter(Follower.follower_id == user_id).all()
)
# Convert the query result to a list of dictionaries
following_list = [{"following_id": following.following_id, "is_accepted": following.is_accepted}
for following in followings]
# Convert the query result to a list of dictionaries
following_list = [
{
"following_id": following.following_id,
"is_accepted": following.is_accepted,
}
for following in followings
]
# Respond with the list of following
return {"followings": following_list}
# Respond with the list of following
return {"followings": following_list}
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")
@router.put("/followers/accept/user/{user_id}/targetUser/{target_user_id}")
async def accept_follow(user_id: int, target_user_id: int, token: str = Depends(oauth2_scheme)):
async def accept_follow(
user_id: int,
target_user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Check if the follow relationship exists and is not accepted yet
follow_request = db_session.query(Follower).filter(
(Follower.follower_id == target_user_id) & (Follower.following_id == user_id) &
(Follower.is_accepted == False)
).first()
if follow_request:
# Accept the follow request by changing the "is_accepted" column to True
follow_request.is_accepted = True
db_session.commit()
# Return success response
response_data = {
"follower_id": target_user_id,
"following_id": user_id,
"is_accepted": True
}
return JSONResponse(content=response_data, status_code=200)
else:
# Follow request does not exist or has already been accepted
raise HTTPException(status_code=400, detail="Invalid follow request.")
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")
@router.post("/followers/create/user/{user_id}/targetUser/{target_user_id}")
async def create_follow(user_id: int, target_user_id: int, token: str = Depends(oauth2_scheme)):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Check if the follow relationship already exists
existing_follow = db_session.query(Follower).filter(
(Follower.follower_id == user_id) & (Follower.following_id == target_user_id)
).first()
if existing_follow:
# Follow relationship already exists
raise HTTPException(status_code=400, detail="Follow relationship already exists.")
# Create a new follow relationship
new_follow = Follower(
follower_id=user_id,
following_id=target_user_id,
is_accepted=False
# Check if the follow relationship exists and is not accepted yet
follow_request = (
db_session.query(Follower)
.filter(
(Follower.follower_id == target_user_id)
& (Follower.following_id == user_id)
& (Follower.is_accepted == False)
)
.first()
)
# Add the new follow relationship to the database
db_session.add(new_follow)
if follow_request:
# Accept the follow request by changing the "is_accepted" column to True
follow_request.is_accepted = True
db_session.commit()
# Return success response
response_data = {
"follower_id": user_id,
"following_id": target_user_id,
"is_accepted": False
"follower_id": target_user_id,
"following_id": user_id,
"is_accepted": True,
}
return JSONResponse(content=response_data, status_code=201)
return JSONResponse(content=response_data, status_code=200)
else:
# Follow request does not exist or has already been accepted
raise HTTPException(status_code=400, detail="Invalid follow request.")
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")
@router.delete("/followers/delete/user/{user_id}/targetUser/{target_user_id}")
async def delete_follow(user_id: int, target_user_id: int, token: str = Depends(oauth2_scheme)):
@router.post("/followers/create/user/{user_id}/targetUser/{target_user_id}")
async def create_follow(
user_id: int,
target_user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Query and delete the specific follower record
follower = db_session.query(Follower).filter(
(Follower.follower_id == user_id) & (Follower.following_id == target_user_id)
).first()
# Check if the follow relationship already exists
existing_follow = (
db_session.query(Follower)
.filter(
(Follower.follower_id == user_id)
& (Follower.following_id == target_user_id)
)
.first()
)
if follower:
# Delete the follower record
db_session.delete(follower)
db_session.commit()
if existing_follow:
# Follow relationship already exists
raise HTTPException(
status_code=400, detail="Follow relationship already exists."
)
# Respond with a success message
return JSONResponse(content={"detail": "Follower record deleted successfully."}, status_code=200)
# Create a new follow relationship
new_follow = Follower(
follower_id=user_id, following_id=target_user_id, is_accepted=False
)
# Follower record not found
raise HTTPException(status_code=404, detail="Follower record not found.")
# Add the new follow relationship to the database
db_session.add(new_follow)
db_session.commit()
# Return success response
response_data = {
"follower_id": user_id,
"following_id": target_user_id,
"is_accepted": False,
}
return JSONResponse(content=response_data, status_code=201)
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")
raise HTTPException(status_code=401, detail="Unauthorized")
@router.delete("/followers/delete/user/{user_id}/targetUser/{target_user_id}")
async def delete_follow(
user_id: int,
target_user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(db_session, token)
# Query and delete the specific follower record
follower = (
db_session.query(Follower)
.filter(
(Follower.follower_id == user_id)
& (Follower.following_id == target_user_id)
)
.first()
)
if follower:
# Delete the follower record
db_session.delete(follower)
db_session.commit()
# Respond with a success message
return JSONResponse(
content={"detail": "Follower record deleted successfully."},
status_code=200,
)
# Follower record not found
raise HTTPException(status_code=404, detail="Follower record not found.")
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")

View File

@@ -1,330 +1,581 @@
import os
"""
API Router for managing user gear information.
This module defines FastAPI routes for performing CRUD operations on user gear records.
It includes endpoints for retrieving, creating, updating, and deleting gear records.
The routes handle user authentication, database interactions using SQLAlchemy,
and provide JSON responses with appropriate metadata.
Endpoints:
- GET /gear/all: Retrieve all user gear records.
- GET /gear/all/{gear_type}: Retrieve user gear records filtered by gear type.
- GET /gear/number: Retrieve the total number of user gear records.
- GET /gear/all/pagenumber/{pageNumber}/numRecords/{numRecords}: Retrieve user gear records with pagination.
- GET /gear/{nickname}/gearfromnickname: Retrieve user gear records by nickname.
- GET /gear/{id}/gearfromid: Retrieve user gear records by ID.
- POST /gear/create: Create a new user gear record.
- PUT /gear/{gear_id}/edit: Edit an existing user gear record.
- DELETE /gear/{gear_id}/delete: Delete an existing user gear record.
Dependencies:
- OAuth2PasswordBearer: FastAPI security scheme for handling OAuth2 password bearer tokens.
- get_db_session: Dependency function to get a database session.
- create_error_response: Function to create a standardized error response.
- get_current_user: Dependency function to get the current authenticated user.
Models:
- GearBase: Base Pydantic model for gear attributes.
- GearCreateRequest: Pydantic model for creating gear records.
- GearEditRequest: Pydantic model for editing gear records.
Functions:
- gear_record_to_dict: Convert Gear SQLAlchemy objects to dictionaries.
Logger:
- Logger named "myLogger" for logging errors and exceptions.
"""
import logging
from fastapi import APIRouter, Depends, HTTPException, Response
from fastapi import APIRouter, Depends
from fastapi.security import OAuth2PasswordBearer
from typing import List, Optional
from sqlalchemy import func
from db.db import get_db_session, Gear
from jose import jwt, JWTError
from sqlalchemy.orm import Session
from db.db import Gear
from jose import JWTError
from urllib.parse import unquote
from pydantic import BaseModel
from . import sessionController
from dependencies import get_db_session, create_error_response, get_current_user
from fastapi.responses import JSONResponse
# Define the API router
router = APIRouter()
# Define a loggger created on main.py
logger = logging.getLogger("myLogger")
# Define the OAuth2 scheme for handling bearer tokens
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
class GearBase(BaseModel):
"""
Base Pydantic model for representing gear attributes.
Attributes:
- brand (str, optional): The brand of the gear.
- model (str, optional): The model of the gear.
- nickname (str): The nickname of the gear.
- gear_type (int): The type of gear.
- date (str): The creation date of the gear.
"""
brand: Optional[str]
model: Optional[str]
nickname: str
gear_type: int
date: str
class GearCreateRequest(GearBase):
"""
Pydantic model for creating gear records.
Inherits from GearBase, which defines the base attributes for gear.
This class extends the GearBase Pydantic model and is specifically tailored for
creating new gear records.
"""
pass
class GearEditRequest(GearBase):
"""
Pydantic model for editing gear records.
Inherits from GearBase, which defines the base attributes for gear.
This class extends the GearBase Pydantic model and is designed for editing existing
gear records. Includes an additional attribute 'is_active'
to indicate whether the gear is active or not.
"""
is_active: int
# Define a function to convert Gear SQLAlchemy objects to dictionaries
def gear_record_to_dict(record: Gear) -> dict:
"""
Convert Gear SQLAlchemy objects to dictionaries.
Parameters:
- record (Gear): The Gear SQLAlchemy object to convert.
Returns:
- dict: A dictionary representation of the Gear object.
This function is used to convert an SQLAlchemy Gear object into a dictionary format for easier serialization and response handling.
"""
return {
"id": record.id,
"brand": record.brand,
"model": record.model,
"nickname": record.nickname,
"gear_type": record.gear_type,
"user_id": record.user_id,
"created_at": record.created_at.strftime("%Y-%m-%dT%H:%M:%S"),
"is_active": record.is_active,
}
@router.get("/gear/all", response_model=List[dict])
async def read_gear_all(token: str = Depends(oauth2_scheme)):
async def read_gear_all(
user_id: int = Depends(get_current_user),
db_session: Session = Depends(get_db_session),
):
"""
Retrieve all user gear records.
Parameters:
- user_id (int): The ID of the authenticated user.
- db_session (Session): SQLAlchemy database session.
Returns:
- JSONResponse: JSON response containing metadata and user gear records.
Raises:
- JWTError: If the user is not authenticated.
- Exception: For other unexpected errors.
"""
try:
sessionController.validate_token(token)
with get_db_session() as db_session:
payload = jwt.decode(
token, os.environ.get("SECRET_KEY"), algorithms=[os.environ.get("ALGORITHM")]
)
user_id = payload.get("id")
# Query the gear records using SQLAlchemy
gear_records = (
db_session.query(Gear)
.filter(Gear.user_id == user_id)
.order_by(Gear.nickname)
.all()
)
# Query the gear records using SQLAlchemy
gear_records = (
db_session.query(Gear)
.filter(Gear.user_id == user_id)
.order_by(Gear.nickname)
.all()
)
# Use the gear_record_to_dict function to convert SQLAlchemy objects to dictionaries
gear_records_dict = [gear_record_to_dict(record) for record in gear_records]
# Convert the SQLAlchemy objects to dictionaries
results = [gear.__dict__ for gear in gear_records]
# Include metadata in the response
metadata = {"total_records": len(gear_records)}
# Return the queried values using JSONResponse
return JSONResponse(
content={"metadata": metadata, "gear_records": gear_records_dict}
)
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
except NameError as err:
print(err)
return results
# Return an error response if the user is not authenticated
return create_error_response("UNAUTHORIZED", "Unauthorized", 401)
except Exception as err:
# Log the error and return an error response
logger.error(f"Error in read_gear_all: {err}", exc_info=True)
return create_error_response(
"INTERNAL_SERVER_ERROR", "Internal Server Error", 500
)
@router.get("/gear/all/running", response_model=List[dict])
async def read_gear_all_running(token: str = Depends(oauth2_scheme)):
@router.get("/gear/all/{gear_type}", response_model=List[dict])
async def read_gear_all_by_type(
gear_type: int,
user_id: int = Depends(get_current_user),
db_session: Session = Depends(get_db_session),
):
"""
Retrieve user gear records filtered by gear type.
Parameters:
- gear_type (int): The type of gear to filter by.
- user_id (int, optional): The ID of the authenticated user (default: extracted from token).
- db_session (Session, optional): SQLAlchemy database session (default: obtained from dependency).
Returns:
- JSONResponse: JSON response containing metadata and user gear records filtered by gear type.
Raises:
- ValueError: If the gear type is invalid.
- JWTError: If the user is not authenticated.
- Exception: For other unexpected errors.
This function queries user gear records from the database based on the specified gear type.
It filters records by both gear type and user ID, includes metadata in the response,
and returns a JSONResponse with the filtered gear records.
"""
try:
sessionController.validate_token(token)
with get_db_session() as db_session:
payload = jwt.decode(
token, os.environ.get("SECRET_KEY"), algorithms=[os.environ.get("ALGORITHM")]
)
user_id = payload.get("id")
# Validate the gear type (example validation)
if not (1 <= gear_type <= 3):
# Return an error response if the gear type in invalid
return create_error_response("UNPROCESSABLE COMTENT", "Invalid gear type. Must be between 1 and 3", 422)
# 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()
)
# Query the gear records using SQLAlchemy and filter by gear type and user ID
gear_records = (
db_session.query(Gear)
.filter(Gear.gear_type == gear_type, Gear.user_id == user_id)
.order_by(Gear.nickname)
.all()
)
# Convert the SQLAlchemy objects to dictionaries
results = [gear.__dict__ for gear in gear_records]
# Use the gear_record_to_dict function to convert SQLAlchemy objects to dictionaries
gear_records_dict = [gear_record_to_dict(record) for record in gear_records]
# Include metadata in the response
metadata = {
"total_records": len(gear_records),
"gear_type": gear_type,
}
# Return the queried values using JSONResponse
return JSONResponse(
content={"metadata": metadata, "gear_records": gear_records_dict}
)
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
except NameError 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)):
try:
sessionController.validate_token(token)
with get_db_session() as db_session:
payload = jwt.decode(
token, os.environ.get("SECRET_KEY"), algorithms=[os.environ.get("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 NameError 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)):
try:
sessionController.validate_token(token)
with get_db_session() as db_session:
payload = jwt.decode(
token, os.environ.get("SECRET_KEY"), algorithms=[os.environ.get("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")
except NameError as err:
print(err)
return results
# Return an error response if the user is not authenticated
return create_error_response("UNAUTHORIZED", "Unauthorized", 401)
except Exception as err:
# Log the error and return an error response
logger.error(f"Error in read_gear_all_by_type: {err}", exc_info=True)
return create_error_response(
"INTERNAL_SERVER_ERROR", "Internal Server Error", 500
)
@router.get("/gear/number")
async def read_gear_number(token: str = Depends(oauth2_scheme)):
try:
sessionController.validate_token(token)
with get_db_session() as db_session:
payload = jwt.decode(
token, os.environ.get("SECRET_KEY"), algorithms=[os.environ.get("ALGORITHM")]
)
user_id = payload.get("id")
async def read_gear_number(
user_id: int = Depends(get_current_user),
db_session: Session = Depends(get_db_session),
):
"""
Retrieve the total number of user gear records.
# Query the number of gear records for the user using SQLAlchemy
gear_count = (
db_session.query(func.count(Gear.id))
.filter(Gear.user_id == user_id)
.scalar()
)
Parameters:
- user_id (int, optional): The ID of the authenticated user (default: extracted from token).
- db_session (Session, optional): SQLAlchemy database session (default: obtained from dependency).
Returns:
- JSONResponse: JSON response containing metadata and the total number of user gear records.
Raises:
- JWTError: If the user is not authenticated.
- Exception: For other unexpected errors.
This function queries the total number of user gear records from the database,
includes metadata in the response, and returns a JSONResponse with the total gear count.
"""
try:
# Query the number of gear records for the user using SQLAlchemy, filter by user ID and return the count
gear_count = (
db_session.query(func.count(Gear.id))
.filter(Gear.user_id == user_id)
.scalar()
)
# Include metadata in the response
metadata = {"total_records": 1}
# Return the queried values using JSONResponse
return JSONResponse(content={"metadata": metadata, "gear_count": gear_count})
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
except NameError as err:
print(err)
return {0: gear_count}
# Return an error response if the user is not authenticated
return create_error_response("UNAUTHORIZED", "Unauthorized", 401)
except Exception as err:
# Log the error and return an error response
logger.error(f"Error in read_gear_number: {err}", exc_info=True)
return create_error_response(
"INTERNAL_SERVER_ERROR", "Internal Server Error", 500
)
@router.get(
"/gear/all/pagenumber/{pageNumber}/numRecords/{numRecords}",
response_model=List[dict],
#tags=["Pagination"],
)
async def read_gear_all_pagination(
pageNumber: int, numRecords: int, token: str = Depends(oauth2_scheme)
pageNumber: int,
numRecords: int,
user_id: int = Depends(get_current_user),
db_session: Session = Depends(get_db_session),
):
"""
Retrieve user gear records with pagination.
Parameters:
- pageNumber (int): The page number to retrieve.
- numRecords (int): The number of records to display per page.
- user_id (int, optional): The ID of the authenticated user (default: extracted from token).
- db_session (Session, optional): SQLAlchemy database session (default: obtained from dependency).
Returns:
- JSONResponse: JSON response containing metadata and user gear records for the specified page.
Raises:
- JWTError: If the user is not authenticated.
- Exception: For other unexpected errors.
This function queries user gear records from the database with pagination,
includes metadata in the response, and returns a JSONResponse with the gear records for the specified page.
"""
try:
sessionController.validate_token(token)
with get_db_session() as db_session:
payload = jwt.decode(
token, os.environ.get("SECRET_KEY"), algorithms=[os.environ.get("ALGORITHM")]
)
user_id = payload.get("id")
# Use SQLAlchemy to query the gear records with pagination, filter by user ID, order by nickname ascending and return the records
gear_records = (
db_session.query(Gear)
.filter(Gear.user_id == user_id)
.order_by(Gear.nickname.asc())
.offset((pageNumber - 1) * numRecords)
.limit(numRecords)
.all()
)
# Use SQLAlchemy to query the gear records with pagination
gear_records = (
db_session.query(Gear)
.filter(Gear.user_id == user_id)
.order_by(Gear.nickname.asc())
.offset((pageNumber - 1) * numRecords)
.limit(numRecords)
.all()
)
# Use the gear_record_to_dict function to convert SQLAlchemy objects to dictionaries
gear_records_dict = [gear_record_to_dict(record) for record in gear_records]
# Convert the SQLAlchemy results to a list of dictionaries
results = [record.__dict__ for record in gear_records]
# Include metadata in the response
metadata = {
"total_records": len(gear_records),
"page_number": pageNumber,
"num_records": numRecords,
}
# Return the queried values using JSONResponse
return JSONResponse(
content={"metadata": metadata, "gear_records": gear_records_dict}
)
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
except NameError as err:
print(err)
return results
# Return an error response if the user is not authenticated
return create_error_response("UNAUTHORIZED", "Unauthorized", 401)
except Exception as err:
# Log the error and return an error response
logger.error(f"Error in read_gear_all_pagination: {err}", exc_info=True)
return create_error_response(
"INTERNAL_SERVER_ERROR", "Internal Server Error", 500
)
@router.get("/gear/{nickname}/gearfromnickname", response_model=List[dict])
async def read_gear_gearFromNickname(
nickname: str, token: str = Depends(oauth2_scheme)
nickname: str,
user_id: int = Depends(get_current_user),
db_session: Session = Depends(get_db_session),
):
"""
Retrieve user gear records by nickname.
Parameters:
- nickname (str): The nickname to search for in user gear records.
- user_id (int, optional): The ID of the authenticated user (default: extracted from token).
- db_session (Session, optional): SQLAlchemy database session (default: obtained from dependency).
Returns:
- JSONResponse: JSON response containing metadata and user gear records matching the provided nickname.
Raises:
- JWTError: If the user is not authenticated.
- Exception: For other unexpected errors.
This function queries user gear records from the database by nickname,
includes metadata in the response, and returns a JSONResponse with the matching gear records.
"""
try:
sessionController.validate_token(token)
with get_db_session() as db_session:
payload = jwt.decode(
token, os.environ.get("SECRET_KEY"), algorithms=[os.environ.get("ALGORITHM")]
# Define a search term
partial_nickname = unquote(nickname).replace("+", " ")
# Use SQLAlchemy to query the gear records by nickname, filter by user ID and nickname, and return the records
gear_records = (
db_session.query(Gear)
.filter(
Gear.nickname.like(f"%{partial_nickname}%"), Gear.user_id == user_id
)
user_id = payload.get("id")
.all()
)
# Define a search term
partial_nickname = unquote(nickname).replace("+", " ")
# Use the gear_record_to_dict function to convert SQLAlchemy objects to dictionaries
gear_records_dict = [gear_record_to_dict(record) for record in gear_records]
# Use SQLAlchemy to query the gear records by nickname
gear_records = (
db_session.query(Gear)
.filter(
Gear.nickname.like(f"%{partial_nickname}%"), Gear.user_id == user_id
)
.all()
)
# Include metadata in the response
metadata = {
"total_records": len(gear_records),
"nickname": nickname,
}
# Convert the SQLAlchemy results to a list of dictionaries
results = [record.__dict__ for record in gear_records]
# Return the queried values using JSONResponse
return JSONResponse(
content={"metadata": metadata, "gear_records": gear_records_dict}
)
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
except NameError as err:
print(err)
return results
# Return an error response if the user is not authenticated
return create_error_response("UNAUTHORIZED", "Unauthorized", 401)
except Exception as err:
# Log the error and return an error response
logger.error(f"Error in read_gear_gearFromNickname: {err}", exc_info=True)
return create_error_response(
"INTERNAL_SERVER_ERROR", "Internal Server Error", 500
)
# Get gear from id
@router.get("/gear/{id}/gearfromid", response_model=List[dict])
async def read_gear_gearFromId(id: int, token: str = Depends(oauth2_scheme)):
async def read_gear_gearFromId(
id: int,
user_id: int = Depends(get_current_user),
db_session: Session = Depends(get_db_session),
):
"""
Retrieve user gear records by ID.
Parameters:
- id (int): The ID of the gear record to retrieve.
- user_id (int, optional): The ID of the authenticated user (default: extracted from token).
- db_session (Session, optional): SQLAlchemy database session (default: obtained from dependency).
Returns:
- JSONResponse: JSON response containing metadata and user gear records matching the provided ID.
Raises:
- JWTError: If the user is not authenticated.
- Exception: For other unexpected errors.
This function queries user gear records from the database by ID,
includes metadata in the response, and returns a JSONResponse with the matching gear records.
"""
try:
sessionController.validate_token(token)
with get_db_session() as db_session:
payload = jwt.decode(
token, os.environ.get("SECRET_KEY"), algorithms=[os.environ.get("ALGORITHM")]
)
user_id = payload.get("id")
# Use SQLAlchemy to query the gear record by ID and filter by user ID and gear ID
gear_records = (
db_session.query(Gear).filter(Gear.id == id, Gear.user_id == user_id).all()
)
# Use SQLAlchemy to query the gear record by ID
gear_record = (
db_session.query(Gear)
.filter(Gear.id == id, Gear.user_id == user_id)
.first()
)
# Use the gear_record_to_dict function to convert SQLAlchemy objects to dictionaries
gear_records_dict = [gear_record_to_dict(record) for record in gear_records]
# Convert the SQLAlchemy result to a list of dictionaries
if gear_record:
results = [gear_record.__dict__]
else:
results = []
# Include metadata in the response
metadata = {
"total_records": len(gear_records),
"id": id,
}
# Return the queried values using JSONResponse
return JSONResponse(
content={"metadata": metadata, "gear_records": gear_records_dict}
)
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
except NameError as err:
print(err)
return results
class CreateGearRequest(BaseModel):
brand: Optional[str]
model: Optional[str]
nickname: str
gear_type: int
date: str
# Return an error response if the user is not authenticated
return create_error_response("UNAUTHORIZED", "Unauthorized", 401)
except Exception as err:
# Log the error and return an error response
logger.error(f"Error in read_gear_gearFromId: {err}", exc_info=True)
return create_error_response(
"INTERNAL_SERVER_ERROR", "Internal Server Error", 500
)
@router.post("/gear/create")
async def create_gear(gear: CreateGearRequest, token: str = Depends(oauth2_scheme)):
async def create_gear(
gear: GearCreateRequest,
user_id: int = Depends(get_current_user),
db_session: Session = Depends(get_db_session),
):
"""
Create a new user gear record.
Parameters:
- gear (GearCreateRequest): Pydantic model containing information for creating a new gear record.
- user_id (int, optional): The ID of the authenticated user (default: extracted from token).
- db_session (Session, optional): SQLAlchemy database session (default: obtained from dependency).
Returns:
- JSONResponse: JSON response indicating the success of the gear creation.
Raises:
- JWTError: If the user is not authenticated.
- Exception: For other unexpected errors.
This function uses the provided GearCreateRequest model to create a new gear record in the database,
associates it with the authenticated user, and returns a JSONResponse indicating the success of the creation.
"""
try:
sessionController.validate_token(token)
payload = jwt.decode(
token, os.environ.get("SECRET_KEY"), algorithms=[os.environ.get("ALGORITHM")]
# Use SQLAlchemy to create a new gear record
gear_record = Gear(
brand=unquote(gear.brand).replace("+", " "),
model=unquote(gear.model).replace("+", " "),
nickname=unquote(gear.nickname).replace("+", " "),
gear_type=gear.gear_type,
user_id=user_id,
created_at=gear.date,
is_active=True,
)
user_id = payload.get("id")
with get_db_session() as db_session:
# Use SQLAlchemy to create a new gear record
gear_record = Gear(
brand=unquote(gear.brand).replace("+", " "),
model=unquote(gear.model).replace("+", " "),
nickname=unquote(gear.nickname).replace("+", " "),
gear_type=gear.gear_type,
user_id=user_id,
created_at=gear.date,
is_active=True,
)
# Add the gear record to the database using SQLAlchemy
db_session.add(gear_record)
db_session.commit()
# Add the gear record to the database using SQLAlchemy
db_session.add(gear_record)
db_session.commit()
# Return a JSONResponse indicating the success of the gear creation
return JSONResponse(
content={"message": "Gear created successfully"}, status_code=201
)
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
except NameError as err:
print(err)
raise HTTPException(status_code=500, detail="Failed to create gear")
return {"message": "Gear added successfully"}
class EditGearRequest(BaseModel):
brand: Optional[str]
model: Optional[str]
nickname: str
gear_type: int
date: str
is_active: int
# Return an error response if the user is not authenticated
return create_error_response("UNAUTHORIZED", "Unauthorized", 401)
except Exception as err:
# Log the error, rollback the transaction, and return an error response
db_session.rollback()
logger.error(f"Error in create_gear: {err}", exc_info=True)
return create_error_response(
"INTERNAL_SERVER_ERROR", "Internal Server Error", 500
)
@router.put("/gear/{gear_id}/edit")
async def edit_gear(
gear_id: int, gear: EditGearRequest, token: str = Depends(oauth2_scheme)
gear_id: int,
gear: GearEditRequest,
user_id: int = Depends(get_current_user),
db_session: Session = Depends(get_db_session),
):
"""
Edit an existing user gear record.
Parameters:
- gear_id (int): The ID of the gear record to edit.
- gear (GearEditRequest): Pydantic model containing information for editing the gear record.
- user_id (int, optional): The ID of the authenticated user (default: extracted from token).
- db_session (Session, optional): SQLAlchemy database session (default: obtained from dependency).
Returns:
- JSONResponse: JSON response indicating the success of the gear edit.
Raises:
- JWTError: If the user is not authenticated.
- Exception: For other unexpected errors.
This function uses the provided GearEditRequest model to edit an existing gear record in the database,
verifies ownership, and returns a JSONResponse indicating the success of the edit.
"""
try:
sessionController.validate_token(token)
# Use SQLAlchemy to query and update the gear record
with get_db_session() as db_session:
gear_record = db_session.query(Gear).filter(Gear.id == gear_id).first()
gear_record = db_session.query(Gear).filter(Gear.id == gear_id).first()
if gear_record:
# Check if the gear record exists
if gear_record:
# Check if the gear record belongs to the user
if gear_record.user_id == user_id:
# Update the gear record
if gear.brand is not None:
gear_record.brand = unquote(gear.brand).replace("+", " ")
if gear.model is not None:
@@ -336,50 +587,98 @@ async def edit_gear(
# Commit the transaction
db_session.commit()
return {"message": "Gear edited successfully"}
# Return a JSONResponse indicating the success of the gear edit
return JSONResponse(
content={"message": "Gear edited successfully"}, status_code=200
)
else:
raise HTTPException(status_code=404, detail="Gear not found")
# Return an error response if the gear record does not belong to the user
return JSONResponse(
content={
"message": f"Gear does not belong to user {user_id}. Will not edit"
},
status_code=404,
)
else:
# Return an error response if the gear record is not found
return JSONResponse(content={"message": "Gear not found"}, status_code=404)
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
# Return an error response if the user is not authenticated
return create_error_response("UNAUTHORIZED", "Unauthorized", 401)
except Exception as err:
print(err)
raise HTTPException(status_code=500, detail="Failed to edit gear")
return {"message": "Gear edited successfully"}
# Log the error, rollback the transaction, and return an error response
db_session.rollback()
logger.error(f"Error in edit_gear: {err}", exc_info=True)
return create_error_response(
"INTERNAL_SERVER_ERROR", "Internal Server Error", 500
)
@router.delete("/gear/{gear_id}/delete")
async def delete_gear(
gear_id: int, response: Response, token: str = Depends(oauth2_scheme)
gear_id: int,
user_id: int = Depends(get_current_user),
db_session: Session = Depends(get_db_session),
):
"""
Delete an existing user gear record.
Parameters:
- gear_id (int): The ID of the gear record to delete.
- user_id (int, optional): The ID of the authenticated user (default: extracted from token).
- db_session (Session, optional): SQLAlchemy database session (default: obtained from dependency).
Returns:
- JSONResponse: JSON response indicating the success of the gear deletion.
Raises:
- JWTError: If the user is not authenticated.
- Exception: For other unexpected errors.
This function deletes an existing gear record from the database,
verifies ownership, and returns a JSONResponse indicating the success of the deletion.
"""
try:
sessionController.validate_token(token)
# Use SQLAlchemy to query and delete the gear record
with get_db_session() as db_session:
gear_record = db_session.query(Gear).filter(Gear.id == gear_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"}
gear_record = db_session.query(Gear).filter(Gear.id == gear_id).first()
# Check if the gear record exists
if gear_record:
# Check if the gear record belongs to the user
if gear_record.user_id == user_id:
# Delete the gear record
db_session.delete(gear_record)
# Commit the transaction
db_session.commit()
return {"message": f"Gear {gear_id} has been deleted"}
# Return a JSONResponse indicating the success of the gear deletion
return JSONResponse(
content={"message": f"Gear {gear_id} has been deleted"},
status_code=200,
)
else:
raise HTTPException(status_code=404, detail="Gear not found")
# Return an error response if the gear record does not belong to the user
return JSONResponse(
content={
"message": f"Gear does not belong to user {user_id}. Will not delete"
},
status_code=404,
)
else:
# Return an error response if the gear record is not found
return JSONResponse(content={"message": "Gear not found"}, status_code=404)
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
# Return an error response if the user is not authenticated
return create_error_response("UNAUTHORIZED", "Unauthorized", 401)
except Exception as err:
print(err)
raise HTTPException(status_code=500, detail="Failed to delete gear")
return {"message": f"Gear {gear_id} has been deleted"}
# Log the error, rollback the transaction, and return an error response
db_session.rollback()
logger.error(f"Error in delete_gear: {err}", exc_info=True)
return create_error_response(
"INTERNAL_SERVER_ERROR", "Internal Server Error", 500
)

View File

@@ -4,14 +4,14 @@ from fastapi import APIRouter, Depends, HTTPException
from datetime import datetime, timedelta
from jose import jwt, JWTError
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.orm import Session
from db.db import (
get_db_session,
User,
AccessToken,
) # Import your SQLAlchemy session management from db.db and models
from controllers.userController import UserResponse
from pydantic import BaseModel
from opentelemetry import trace
from dependencies import get_db_session
router = APIRouter()
@@ -20,43 +20,78 @@ logger = logging.getLogger("myLogger")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def get_user_id_from_token(token: str):
try:
payload = jwt.decode(
token,
os.environ.get("SECRET_KEY"),
algorithms=[os.environ.get("ALGORITHM")],
)
return payload.get("id")
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
def get_exp_from_token(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(
token,
os.environ.get("SECRET_KEY"),
algorithms=[os.environ.get("ALGORITHM")],
)
return payload.get("exp")
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
def get_access_type_from_token(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(
token,
os.environ.get("SECRET_KEY"),
algorithms=[os.environ.get("ALGORITHM")],
)
return payload.get("access_type")
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
async def authenticate_user(
username: str, password: str, neverExpires: bool, db_session=Depends(get_db_session)
username: str,
password: str,
neverExpires: bool,
db_session: Session,
):
try:
with get_db_session() as db_session:
# Use SQLAlchemy ORM to query the database
user = (
db_session.query(User)
.filter(User.username == username, User.password == password)
.first()
)
if not user:
raise HTTPException(
status_code=400, detail="Incorrect username or password"
)
# Check if there is an existing access token for the user
access_token = (
db_session.query(AccessToken)
.filter(AccessToken.user_id == user.id)
.first()
)
if access_token:
return access_token.token
# If there is no existing access token, create a new one
access_token_expires = timedelta(
minutes=int(os.environ.get("ACCESS_TOKEN_EXPIRE_MINUTES"))
)
access_token = await create_access_token(
data={"id": user.id, "access_type": user.access_type},
never_expire=neverExpires,
expires_delta=access_token_expires,
db_session=db_session,
# Use SQLAlchemy ORM to query the database
user = (
db_session.query(User)
.filter(User.username == username, User.password == password)
.first()
)
if not user:
raise HTTPException(
status_code=400, detail="Incorrect username or password"
)
return access_token
# Check if there is an existing access token for the user
access_token = (
db_session.query(AccessToken).filter(AccessToken.user_id == user.id).first()
)
if access_token:
return access_token.token
# If there is no existing access token, create a new one
access_token_expires = timedelta(
minutes=int(os.environ.get("ACCESS_TOKEN_EXPIRE_MINUTES"))
)
access_token = await create_access_token(
data={"id": user.id, "access_type": user.access_type},
never_expire=neverExpires,
db_session=db_session,
expires_delta=access_token_expires,
)
return access_token
except Exception as e:
logger.error(e)
@@ -66,8 +101,8 @@ async def authenticate_user(
async def create_access_token(
data: dict,
never_expire: bool,
db_session: Session,
expires_delta: timedelta = None,
db_session=Depends(get_db_session),
):
to_encode = data.copy()
if never_expire:
@@ -89,135 +124,97 @@ async def create_access_token(
created_at=datetime.utcnow(),
expires_at=expire,
)
with get_db_session() as db_session:
db_session.add(access_token)
db_session.commit()
db_session.add(access_token)
db_session.commit()
return encoded_jwt
def remove_expired_tokens():
# Get the tracer from the main module
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("remove_expired_tokens"):
try:
# Calculate the expiration time
expiration_time = datetime.utcnow() - timedelta(
minutes=int(os.environ.get("ACCESS_TOKEN_EXPIRE_MINUTES"))
)
# Add tags related to the expiration check
trace.get_current_span().set_attribute(
"expiration_time", expiration_time.isoformat()
)
trace.get_current_span().set_attribute(
"token_expire_minutes", os.environ.get("ACCESS_TOKEN_EXPIRE_MINUTES")
)
# Delete expired access tokens using SQLAlchemy ORM
with get_db_session() as db_session:
rows_deleted = (
db_session.query(AccessToken)
.filter(AccessToken.created_at < expiration_time)
.delete()
)
db_session.commit()
# Add tags related to the database operation
trace.get_current_span().set_attribute("tokens_deleted", rows_deleted)
trace.get_current_span().set_attribute("database_commit", "success")
logger.info(f"{rows_deleted} access tokens deleted from the database")
# Log a success event
trace.get_current_span().add_event(
"SuccessEvent",
{"message": f"{rows_deleted} access tokens deleted from the database"},
)
except Exception as e:
logger.error(e)
# Add tags related to the error
trace.get_current_span().set_attribute("error_message", str(e))
trace.get_current_span().set_attribute("database_commit", "failure")
trace.get_current_span().set_status(
trace.status.Status(trace.StatusCode.ERROR, str(e))
)
def get_user_data(token: str):
def remove_expired_tokens(db_session: Session):
try:
validate_token(token)
payload = jwt.decode(
token,
os.environ.get("SECRET_KEY"),
algorithms=[os.environ.get("ALGORITHM")],
# Calculate the expiration time
expiration_time = datetime.utcnow() - timedelta(
minutes=int(os.environ.get("ACCESS_TOKEN_EXPIRE_MINUTES"))
)
user_id = payload.get("id")
# Delete expired access tokens using SQLAlchemy ORM
rows_deleted = (
db_session.query(AccessToken)
.filter(AccessToken.created_at < expiration_time)
.delete()
)
db_session.commit()
logger.info(f"{rows_deleted} access tokens deleted from the database")
except Exception as err:
logger.error(f"Error in remove_expired_tokens: {err}", exc_info=True)
def get_user_data(db_session: Session, token: str = Depends(oauth2_scheme)):
try:
validate_token(db_session=db_session, token=token)
user_id = get_user_id_from_token(token)
if user_id is None:
raise HTTPException(
status_code=401, detail="Invalid authentication credentials"
)
with get_db_session() as db_session:
# Retrieve the user details from the database using the user ID
user = db_session.query(User).filter(User.id == user_id).first()
# Retrieve the user details from the database using the user ID
user = db_session.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
if not user:
raise HTTPException(status_code=404, detail="User not found")
if user.strava_token is None:
is_strava_linked = 0
else:
is_strava_linked = 1
if user.strava_token is None:
is_strava_linked = 0
else:
is_strava_linked = 1
# Map the user object to a dictionary that matches the UserResponse model
user_data = {
"id": user.id,
"name": user.name,
"username": user.username,
"email": user.email,
"city": user.city,
"birthdate": user.birthdate,
"preferred_language": user.preferred_language,
"gender": user.gender,
"access_type": user.access_type,
"photo_path": user.photo_path,
"photo_path_aux": user.photo_path_aux,
"is_active": user.is_active,
"is_strava_linked": is_strava_linked,
}
# Map the user object to a dictionary that matches the UserResponse model
user_data = {
"id": user.id,
"name": user.name,
"username": user.username,
"email": user.email,
"city": user.city,
"birthdate": user.birthdate,
"preferred_language": user.preferred_language,
"gender": user.gender,
"access_type": user.access_type,
"photo_path": user.photo_path,
"photo_path_aux": user.photo_path_aux,
"is_active": user.is_active,
"is_strava_linked": is_strava_linked,
}
return user_data
return user_data
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
def validate_token(token: str):
def validate_token(db_session: Session, token: str):
try:
decoded_token = jwt.decode(
token,
os.environ.get("SECRET_KEY"),
algorithms=[os.environ.get("ALGORITHM")],
)
user_id = decoded_token.get("id")
with get_db_session() as db_session:
access_token = (
db_session.query(AccessToken)
.filter(AccessToken.user_id == user_id, AccessToken.token == token)
.first()
)
user_id = get_user_id_from_token(token)
if access_token:
expiration_datetime = datetime.fromtimestamp(decoded_token["exp"])
current_time = datetime.utcnow()
if current_time > expiration_datetime:
raise JWTError("Token expired")
else:
return {"message": "Token is valid"}
else:
exp = get_exp_from_token(token)
access_token = (
db_session.query(AccessToken)
.filter(AccessToken.user_id == user_id, AccessToken.token == token)
.first()
)
if access_token:
expiration_datetime = datetime.fromtimestamp(exp)
current_time = datetime.utcnow()
if current_time > expiration_datetime:
raise JWTError("Token expired")
else:
return {"message": "Token is valid"}
else:
raise JWTError("Token expired")
# if 'exp' not in decoded_token:
# return {"message": "Token is valid"}
@@ -228,12 +225,7 @@ def validate_token(token: str):
def validate_admin_access(token: str):
try:
payload = jwt.decode(
token,
os.environ.get("SECRET_KEY"),
algorithms=[os.environ.get("ALGORITHM")],
)
user_access_type = payload.get("access_type")
user_access_type = get_access_type_from_token(token)
if user_access_type != 2:
raise HTTPException(status_code=401, detail="Unauthorized")
except JWTError:
@@ -247,9 +239,11 @@ class CreateTokenRequest(BaseModel):
@router.post("/token")
async def login_for_access_token(token: CreateTokenRequest):
async def login_for_access_token(
token: CreateTokenRequest, db_session: Session = Depends(get_db_session)
):
access_token = await authenticate_user(
token.username, token.password, token.neverExpires
token.username, token.password, token.neverExpires, db_session
)
if not access_token:
raise HTTPException(status_code=400, detail="Unable to retrieve access token")
@@ -257,33 +251,40 @@ async def login_for_access_token(token: CreateTokenRequest):
@router.delete("/logout/{user_id}")
async def logout(user_id: int, token: str = Depends(oauth2_scheme)):
async def logout(
user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
with get_db_session() as db_session:
access_token = (
db_session.query(AccessToken)
.filter(AccessToken.user_id == user_id, AccessToken.token == token)
.first()
)
if access_token:
db_session.delete(access_token)
db_session.commit()
return {"message": "Logged out successfully"}
else:
raise HTTPException(status_code=404, detail="Token not found")
access_token = (
db_session.query(AccessToken)
.filter(AccessToken.user_id == user_id, AccessToken.token == token)
.first()
)
if access_token:
db_session.delete(access_token)
db_session.commit()
return {"message": "Logged out successfully"}
else:
raise HTTPException(status_code=404, detail="Token not found")
except Exception as e:
logger.error(e)
raise HTTPException(status_code=500, detail="Failed to invalidate access token")
@router.get("/validate_token")
async def check_validate_token(token: str = Depends(oauth2_scheme)):
async def check_validate_token(
token: str = Depends(oauth2_scheme), db_session: Session = Depends(get_db_session)
):
try:
return validate_token(token)
return validate_token(db_session, token)
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
@router.get("/users/me", response_model=UserResponse)
async def read_users_me(token: str = Depends(oauth2_scheme)):
return get_user_data(token)
async def read_users_me(
token: str = Depends(oauth2_scheme), db_session: Session = Depends(get_db_session)
):
return get_user_data(db_session, token)

View File

@@ -1,5 +1,5 @@
from datetime import datetime, timedelta
from db.db import get_db_session, User, Activity
from db.db import User, Activity
from fastapi import APIRouter, HTTPException, Depends
from fastapi.responses import RedirectResponse
from fastapi.security import OAuth2PasswordBearer
@@ -10,7 +10,9 @@ from concurrent.futures import ThreadPoolExecutor
from fastapi import BackgroundTasks
from opentelemetry import trace
from urllib.parse import urlencode
from . import sessionController
from . import sessionController
from dependencies import get_db_session
from sqlalchemy.orm import Session
import logging
import requests
import os
@@ -19,15 +21,17 @@ router = APIRouter()
logger = logging.getLogger("myLogger")
# Load the environment variables from config/.env
#load_dotenv("config/.env")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# Strava route to link user Strava account
@router.get("/strava/strava-callback")
async def strava_callback(state: str, code: str, background_tasks: BackgroundTasks):
async def strava_callback(
state: str,
code: str,
background_tasks: BackgroundTasks,
db_session: Session = Depends(get_db_session),
):
"""
Callback endpoint for Strava OAuth2 authorization.
@@ -63,35 +67,35 @@ async def strava_callback(state: str, code: str, background_tasks: BackgroundTas
tokens = response.json()
with get_db_session() as db_session:
# Query the activities records using SQLAlchemy
db_user = db_session.query(User).filter(User.strava_state == state).first()
# Query the activities records using SQLAlchemy
db_user = db_session.query(User).filter(User.strava_state == state).first()
if db_user:
db_user.strava_token = tokens["access_token"]
db_user.strava_refresh_token = tokens["refresh_token"]
db_user.strava_token_expires_at = datetime.fromtimestamp(
tokens["expires_at"]
)
db_session.commit() # Commit the changes to the database
if db_user:
db_user.strava_token = tokens["access_token"]
db_user.strava_refresh_token = tokens["refresh_token"]
db_user.strava_token_expires_at = datetime.fromtimestamp(
tokens["expires_at"]
)
db_session.commit() # Commit the changes to the database
# get_strava_activities((datetime.utcnow() - timedelta(days=90)).strftime("%Y-%m-%dT%H:%M:%SZ"))
background_tasks.add_task(
get_user_strava_activities,
(
datetime.utcnow()
- timedelta(
days=int(os.environ.get("STRAVA_DAYS_ACTIVITIES_ONLINK"))
)
).strftime("%Y-%m-%dT%H:%M:%SZ"),
db_user.id,
)
# get_strava_activities((datetime.utcnow() - timedelta(days=90)).strftime("%Y-%m-%dT%H:%M:%SZ"))
background_tasks.add_task(
get_user_strava_activities,
(
datetime.utcnow()
- timedelta(
days=int(os.environ.get("STRAVA_DAYS_ACTIVITIES_ONLINK"))
)
).strftime("%Y-%m-%dT%H:%M:%SZ"),
db_user.id,
db_session,
)
# Redirect to the main page or any other desired page after processing
redirect_url = "https://gearguardian.jvslab.pt/settings/settings.php?profileSettings=1&stravaLinked=1" # Change this URL to your main page
return RedirectResponse(url=redirect_url)
else:
raise HTTPException(status_code=404, detail="User not found.")
# Redirect to the main page or any other desired page after processing
redirect_url = "https://gearguardian.jvslab.pt/settings/settings.php?profileSettings=1&stravaLinked=1" # Change this URL to your main page
return RedirectResponse(url=redirect_url)
else:
raise HTTPException(status_code=404, detail="User not found.")
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
@@ -100,7 +104,7 @@ async def strava_callback(state: str, code: str, background_tasks: BackgroundTas
# Strava logic to refresh user Strava account refresh account
def refresh_strava_token():
def refresh_strava_token(db_session: Session):
"""
Refresh Strava access tokens for all users.
@@ -124,71 +128,70 @@ def refresh_strava_token():
token_url = "https://www.strava.com/oauth/token"
try:
with get_db_session() as db_session:
# Query all users from the database
users = db_session.query(User).all()
# Query all users from the database
users = db_session.query(User).all()
for user in users:
# expires_at = user.strava_token_expires_at
if user.strava_token_expires_at is not None:
refresh_time = user.strava_token_expires_at - timedelta(
minutes=60
)
for user in users:
# expires_at = user.strava_token_expires_at
if user.strava_token_expires_at is not None:
refresh_time = user.strava_token_expires_at - timedelta(minutes=60)
if datetime.utcnow() > refresh_time:
# Parameters for the token refresh request
payload = {
"client_id": os.environ.get("STRAVA_CLIENT_ID"),
"client_secret": os.environ.get("STRAVA_CLIENT_SECRET"),
"refresh_token": user.strava_refresh_token,
"grant_type": "refresh_token",
}
if datetime.utcnow() > refresh_time:
# Parameters for the token refresh request
payload = {
"client_id": os.environ.get("STRAVA_CLIENT_ID"),
"client_secret": os.environ.get("STRAVA_CLIENT_SECRET"),
"refresh_token": user.strava_refresh_token,
"grant_type": "refresh_token",
}
try:
# Make a POST request to refresh the Strava token
response = requests.post(token_url, data=payload)
response.raise_for_status() # Raise an error for bad responses
try:
# Make a POST request to refresh the Strava token
response = requests.post(token_url, data=payload)
response.raise_for_status() # Raise an error for bad responses
tokens = response.json()
tokens = response.json()
# Update the user in the database
db_user = (
db_session.query(User)
.filter(User.id == user.id)
.first()
# Update the user in the database
db_user = (
db_session.query(User)
.filter(User.id == user.id)
.first()
)
if db_user:
db_user.strava_token = tokens["access_token"]
db_user.strava_refresh_token = tokens["refresh_token"]
db_user.strava_token_expires_at = (
datetime.fromtimestamp(tokens["expires_at"])
)
if db_user:
db_user.strava_token = tokens["access_token"]
db_user.strava_refresh_token = tokens[
"refresh_token"
]
db_user.strava_token_expires_at = (
datetime.fromtimestamp(tokens["expires_at"])
)
db_session.commit() # Commit the changes to the database
logger.info(
f"Token refreshed successfully for user {user.id}."
)
else:
logger.error("User not found in the database.")
except requests.exceptions.RequestException as req_err:
logger.error(
f"Error refreshing token for user {user.id}: {req_err}"
db_session.commit() # Commit the changes to the database
logger.info(
f"Token refreshed successfully for user {user.id}."
)
else:
logger.info(
f"Token not refreshed for user {user.id}. Will not expire in less than 60min"
else:
logger.error("User not found in the database.")
except requests.exceptions.RequestException as req_err:
logger.error(
f"Error refreshing token for user {user.id}: {req_err}"
)
else:
logger.info(f"User {user.id} does not have strava linked")
logger.info(
f"Token not refreshed for user {user.id}. Will not expire in less than 60min"
)
else:
logger.info(f"User {user.id} does not have strava linked")
except NameError as db_err:
logger.error(f"Database error: {db_err}")
# Define an HTTP PUT route set strava unique state for link logic
@router.put("/strava/set-user-unique-state/{state}")
async def strava_set_user_unique_state(state: str, token: str = Depends(oauth2_scheme)):
async def strava_set_user_unique_state(
state: str,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
"""
Set the Strava unique state for a user.
@@ -208,25 +211,21 @@ async def strava_set_user_unique_state(state: str, token: str = Depends(oauth2_s
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
with get_db_session() as db_session:
# From the token retrieve the user_id
payload = jwt.decode(
token, os.environ.get("SECRET_KEY"), algorithms=[os.environ.get("ALGORITHM")]
)
user_id = payload.get("id")
# Get the user ID from the token
user_id = sessionController.get_user_id_from_token(token)
# Query the database to find the user by their ID
user = db_session.query(User).filter(User.id == user_id).first()
# Query the database to find the user by their ID
user = db_session.query(User).filter(User.id == user_id).first()
# Check if the user with the given ID exists
if not user:
raise HTTPException(status_code=404, detail="User not found")
# Check if the user with the given ID exists
if not user:
raise HTTPException(status_code=404, detail="User not found")
# Set the user's photo paths to None to delete the photo
user.strava_state = state
# Set the user's photo paths to None to delete the photo
user.strava_state = state
# Commit the changes to the database
db_session.commit()
# 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")
@@ -243,7 +242,9 @@ async def strava_set_user_unique_state(state: str, token: str = Depends(oauth2_s
# Define an HTTP PUT route set strava unique state for link logic
@router.put("/strava/unset-user-unique-state")
async def strava_unset_user_unique_state(token: str = Depends(oauth2_scheme)):
async def strava_unset_user_unique_state(
token: str = Depends(oauth2_scheme), db_session: Session = Depends(get_db_session)
):
"""
Unset the Strava unique state for a user.
@@ -262,25 +263,21 @@ async def strava_unset_user_unique_state(token: str = Depends(oauth2_scheme)):
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
with get_db_session() as db_session:
# From the token retrieve the user_id
payload = jwt.decode(
token, os.environ.get("SECRET_KEY"), algorithms=[os.environ.get("ALGORITHM")]
)
user_id = payload.get("id")
# Get the user ID from the token
user_id = sessionController.get_user_id_from_token(token)
# Query the database to find the user by their ID
user = db_session.query(User).filter(User.id == user_id).first()
# Query the database to find the user by their ID
user = db_session.query(User).filter(User.id == user_id).first()
# Check if the user with the given ID exists
if not user:
raise HTTPException(status_code=404, detail="User not found")
# Check if the user with the given ID exists
if not user:
raise HTTPException(status_code=404, detail="User not found")
# Set the user's photo paths to None to delete the photo
user.strava_state = None
# Set the user's photo paths to None to delete the photo
user.strava_state = None
# Commit the changes to the database
db_session.commit()
# 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")
@@ -295,7 +292,7 @@ async def strava_unset_user_unique_state(token: str = Depends(oauth2_scheme)):
return {"message": f"Strava state for user {user_id} has been updated"}
def get_strava_activities(start_date: datetime):
def get_strava_activities(start_date: datetime, db_session: Session):
"""
Retrieve and process Strava activities for all users in the database.
@@ -314,128 +311,21 @@ def get_strava_activities(start_date: datetime):
with tracer.start_as_current_span("get_strava_activities"):
try:
with get_db_session() as db_session:
# Query all users from the database
users = db_session.query(User).all()
# Query all users from the database
users = db_session.query(User).all()
for user in users:
if user.strava_token_expires_at is not None:
# Log an informational event for tracing
trace.get_current_span().add_event(
"InfoEvent",
{
"message": f"User {user.id}: Started periodic activities processing"
},
)
# Create a Strava client with the user's access token
stravaClient = Client(access_token=user.strava_token)
# Fetch Strava activities after the specified start date
strava_activities = list(
stravaClient.get_activities(after=start_date)
)
if strava_activities:
# Initialize an empty list for results
all_results = []
# Use ThreadPoolExecutor for parallel processing of activities
with ThreadPoolExecutor() as executor:
results = list(
executor.map(
lambda activity: process_activity(
activity, user.id, stravaClient
),
strava_activities,
)
)
# Append non-empty and non-None results to the overall results list
all_results.extend(results)
# Flatten the list of results
activities_to_insert = [
activity
for sublist in all_results
for activity in sublist
]
# Bulk insert all activities into the database
with get_db_session() as db_session:
db_session.bulk_save_objects(activities_to_insert)
db_session.commit()
# Log an informational event for tracing
trace.get_current_span().add_event(
"InfoEvent",
{
"message": f"User {user.id}: {len(strava_activities)} periodic activities processed"
},
)
else:
# Log an informational event if no activities were found
trace.get_current_span().add_event(
"InfoEvent",
{
"message": f"User {user.id}: No new activities found after {start_date}"
},
)
else:
# Log an informational event if the user does not have Strava linked
logger.info(f"User {user.id} does not have Strava linked")
trace.get_current_span().add_event(
"InfoEvent",
{"message": f"User {user.id} does not have Strava linked"},
)
except NameError as db_err:
# Log an error event if a NameError occurs (e.g., undefined function or variable)
logger.error(f"Database error: {db_err}")
trace.get_current_span().add_event(
"ErrorEvent",
{"message": f"Database error: {db_err}"},
)
def get_user_strava_activities(start_date: datetime, user_id: int):
"""
Retrieve Strava activities for a user, process them, and store in the database.
This function fetches Strava activities for a specified user after a given start date.
It processes the activities using parallel execution, creates corresponding database
records, and bulk inserts them into the database.
Parameters:
- start_date (datetime): The start date for retrieving Strava activities.
- user_id (int): The ID of the user for whom activities are to be processed.
Returns:
None
"""
# Get the tracer from the main module
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("get_user_strava_activities"):
with get_db_session() as db_session:
# Query user from the database
db_user = db_session.query(User).get(user_id)
# Check if db returned an user object and variable is set
if db_user:
if db_user.strava_token_expires_at is not None:
for user in users:
if user.strava_token_expires_at is not None:
# Log an informational event for tracing
trace.get_current_span().add_event(
"InfoEvent",
{
"message": f"User {db_user.id}: Started initial activities processing"
"message": f"User {user.id}: Started periodic activities processing"
},
)
# Create a Strava client with the user's access token
stravaClient = Client(access_token=db_user.strava_token)
stravaClient = Client(access_token=user.strava_token)
# Fetch Strava activities after the specified start date
strava_activities = list(
@@ -451,7 +341,7 @@ def get_user_strava_activities(start_date: datetime, user_id: int):
results = list(
executor.map(
lambda activity: process_activity(
activity, db_user.id, stravaClient
activity, user.id, stravaClient, db_session
),
strava_activities,
)
@@ -474,7 +364,7 @@ def get_user_strava_activities(start_date: datetime, user_id: int):
trace.get_current_span().add_event(
"InfoEvent",
{
"message": f"User {db_user.id}: {len(strava_activities)} initial activities processed"
"message": f"User {user.id}: {len(strava_activities)} periodic activities processed"
},
)
@@ -483,28 +373,129 @@ def get_user_strava_activities(start_date: datetime, user_id: int):
trace.get_current_span().add_event(
"InfoEvent",
{
"message": f"User {db_user.id}: No new activities found after {start_date}"
"message": f"User {user.id}: No new activities found after {start_date}"
},
)
else:
# Log an informational event if the user does not have Strava linked
logger.info(f"User {db_user.id} does not have Strava linked")
logger.info(f"User {user.id} does not have Strava linked")
trace.get_current_span().add_event(
"InfoEvent",
{"message": f"User {db_user.id} does not have Strava linked"},
{"message": f"User {user.id} does not have Strava linked"},
)
except NameError as db_err:
# Log an error event if a NameError occurs (e.g., undefined function or variable)
logger.error(f"Database error: {db_err}")
trace.get_current_span().add_event(
"ErrorEvent",
{"message": f"Database error: {db_err}"},
)
def get_user_strava_activities(start_date: datetime, user_id: int, db_session: Session):
"""
Retrieve Strava activities for a user, process them, and store in the database.
This function fetches Strava activities for a specified user after a given start date.
It processes the activities using parallel execution, creates corresponding database
records, and bulk inserts them into the database.
Parameters:
- start_date (datetime): The start date for retrieving Strava activities.
- user_id (int): The ID of the user for whom activities are to be processed.
Returns:
None
"""
# Get the tracer from the main module
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("get_user_strava_activities"):
# Query user from the database
db_user = db_session.query(User).get(user_id)
# Check if db returned an user object and variable is set
if db_user:
if db_user.strava_token_expires_at is not None:
# Log an informational event for tracing
trace.get_current_span().add_event(
"InfoEvent",
{
"message": f"User {db_user.id}: Started initial activities processing"
},
)
# Create a Strava client with the user's access token
stravaClient = Client(access_token=db_user.strava_token)
# Fetch Strava activities after the specified start date
strava_activities = list(stravaClient.get_activities(after=start_date))
if strava_activities:
# Initialize an empty list for results
all_results = []
# Use ThreadPoolExecutor for parallel processing of activities
with ThreadPoolExecutor() as executor:
results = list(
executor.map(
lambda activity: process_activity(
activity, db_user.id, stravaClient, db_session
),
strava_activities,
)
)
# Append non-empty and non-None results to the overall results list
all_results.extend(results)
# Flatten the list of results
activities_to_insert = [
activity for sublist in all_results for activity in sublist
]
# Bulk insert all activities into the database
with get_db_session() as db_session:
db_session.bulk_save_objects(activities_to_insert)
db_session.commit()
# Log an informational event for tracing
trace.get_current_span().add_event(
"InfoEvent",
{
"message": f"User {db_user.id}: {len(strava_activities)} initial activities processed"
},
)
else:
# Log an informational event if no activities were found
trace.get_current_span().add_event(
"InfoEvent",
{
"message": f"User {db_user.id}: No new activities found after {start_date}"
},
)
else:
# Log an informational event if the user is not found
logger.info(f"User with ID {user_id} not found.")
# Log an informational event if the user does not have Strava linked
logger.info(f"User {db_user.id} does not have Strava linked")
trace.get_current_span().add_event(
"InfoEvent",
{"message": f"User with ID {user_id} not found"},
{"message": f"User {db_user.id} does not have Strava linked"},
)
else:
# Log an informational event if the user is not found
logger.info(f"User with ID {user_id} not found.")
trace.get_current_span().add_event(
"InfoEvent",
{"message": f"User with ID {user_id} not found"},
)
def process_activity(activity, user_id, stravaClient):
def process_activity(activity, user_id, stravaClient, db_session: Session):
"""
Process a Strava activity and create a corresponding database record.
@@ -527,17 +518,16 @@ def process_activity(activity, user_id, stravaClient):
with tracer.start_as_current_span("process_activity"):
activities_to_insert = []
with get_db_session() as db_session:
# Check if the activity already exists in the database
activity_record = (
db_session.query(Activity)
.filter(Activity.strava_activity_id == activity.id)
.first()
)
# Check if the activity already exists in the database
activity_record = (
db_session.query(Activity)
.filter(Activity.strava_activity_id == activity.id)
.first()
)
if activity_record:
# Skip existing activities
return activities_to_insert
if activity_record:
# Skip existing activities
return activities_to_insert
# Parse start and end dates
start_date_parsed = activity.start_date

View File

@@ -5,13 +5,14 @@ from typing import List, Optional
from jose import JWTError
import logging
from datetime import date
from . import sessionController
from . import sessionController
from sqlalchemy.orm import Session
from db.db import (
get_db_session,
User,
Gear,
)
from urllib.parse import unquote
from dependencies import get_db_session
# Create an instance of an APIRouter
router = APIRouter()
@@ -40,21 +41,22 @@ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# Define an HTTP GET route to retrieve all users
@router.get("/users/all", response_model=list[dict])
async def read_users_all(token: str = Depends(oauth2_scheme)):
async def read_users_all(
token: str = Depends(oauth2_scheme), db_session: Session = Depends(get_db_session)
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Validate that the user has admin access
sessionController.validate_admin_access(token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Query all users from the database
users = db_session.query(User).all()
# Query all users from the database
users = db_session.query(User).all()
# Convert the SQLAlchemy User objects to dictionaries
results = [user.__dict__ for user in users]
# Convert the SQLAlchemy User objects to dictionaries
results = [user.__dict__ for user in users]
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")
@@ -65,18 +67,18 @@ async def read_users_all(token: str = Depends(oauth2_scheme)):
# Define an HTTP GET route to retrieve the number of users
@router.get("/users/number")
async def read_users_number(token: str = Depends(oauth2_scheme)):
async def read_users_number(
token: str = Depends(oauth2_scheme), db_session: Session = Depends(get_db_session)
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Validate that the user has admin access
sessionController.validate_admin_access(token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Count the number of users in the database
user_count = db_session.query(User).count()
# Count the number of users in the database
user_count = db_session.query(User).count()
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")
@@ -91,28 +93,29 @@ async def read_users_number(token: str = Depends(oauth2_scheme)):
response_model=List[dict],
)
async def read_users_all_pagination(
pageNumber: int, numRecords: int, token: str = Depends(oauth2_scheme)
pageNumber: int,
numRecords: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Validate that the user has admin access
sessionController.validate_admin_access(token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Use SQLAlchemy to query the user records with pagination
user_records = (
db_session.query(User)
.order_by(User.name.asc())
.offset((pageNumber - 1) * numRecords)
.limit(numRecords)
.all()
)
# Use SQLAlchemy to query the user records with pagination
user_records = (
db_session.query(User)
.order_by(User.name.asc())
.offset((pageNumber - 1) * numRecords)
.limit(numRecords)
.all()
)
# Convert the SQLAlchemy results to a list of dictionaries
results = [record.__dict__ for record in user_records]
# Convert the SQLAlchemy results to a list of dictionaries
results = [record.__dict__ for record in user_records]
except JWTError:
# Handle JWT (JSON Web Token) authentication error
@@ -128,11 +131,13 @@ async def read_users_all_pagination(
# Define an HTTP GET route to retrieve user records by username
@router.get("/users/{username}/userfromusername", response_model=List[dict])
async def read_users_userFromUsername(
username: str, token: str = Depends(oauth2_scheme)
username: str,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Validate that the user has admin access
sessionController.validate_admin_access(token)
@@ -140,17 +145,15 @@ async def read_users_userFromUsername(
# Define a search term
partial_username = unquote(username).replace("+", " ")
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Use SQLAlchemy to query the user records by username
user_records = (
db_session.query(User)
.filter(User.username.like(f"%{partial_username}%"))
.all()
)
# Use SQLAlchemy to query the user records by username
user_records = (
db_session.query(User)
.filter(User.username.like(f"%{partial_username}%"))
.all()
)
# Convert the SQLAlchemy results to a list of dictionaries
results = [record.__dict__ for record in user_records]
# Convert the SQLAlchemy results to a list of dictionaries
results = [record.__dict__ for record in user_records]
except JWTError:
# Handle JWT (JSON Web Token) authentication error
@@ -165,21 +168,23 @@ async def read_users_userFromUsername(
# Define an HTTP GET route to retrieve user records by user ID
@router.get("/users/{user_id}/userfromid", response_model=List[dict])
async def read_users_userFromId(user_id: int, token: str = Depends(oauth2_scheme)):
async def read_users_userFromId(
user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Validate that the user has admin access
sessionController.validate_admin_access(token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Use SQLAlchemy to query the user records by user ID
user_records = db_session.query(User).filter(User.id == user_id).all()
# Use SQLAlchemy to query the user records by user ID
user_records = db_session.query(User).filter(User.id == user_id).all()
# Convert the SQLAlchemy results to a list of dictionaries
results = [record.__dict__ for record in user_records]
# Convert the SQLAlchemy results to a list of dictionaries
results = [record.__dict__ for record in user_records]
except JWTError:
# Handle JWT (JSON Web Token) authentication error
@@ -195,23 +200,23 @@ async def read_users_userFromId(user_id: int, token: str = Depends(oauth2_scheme
# Define an HTTP GET route to retrieve user ID by username
@router.get("/users/{username}/useridfromusername")
async def read_users_userIDFromUsername(
username: str, token: str = Depends(oauth2_scheme)
username: str,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Validate that the user has admin access
sessionController.validate_admin_access(token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Use SQLAlchemy to query the user ID by username
user_id = (
db_session.query(User.id)
.filter(User.username == unquote(username).replace("+", " "))
.first()
)
# Use SQLAlchemy to query the user ID by username
user_id = (
db_session.query(User.id)
.filter(User.username == unquote(username).replace("+", " "))
.first()
)
except JWTError:
# Handle JWT (JSON Web Token) authentication error
raise HTTPException(status_code=401, detail="Unauthorized")
@@ -222,27 +227,29 @@ async def read_users_userIDFromUsername(
# Define an HTTP GET route to retrieve user photos by user ID
@router.get("/users/{user_id}/userphotofromid")
async def read_users_userPhotoFromID(user_id: int, token: str = Depends(oauth2_scheme)):
async def read_users_userPhotoFromID(
user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Validate that the user has admin access
sessionController.validate_admin_access(token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Use SQLAlchemy to query the user's photo path by user ID
user = db_session.query(User.photo_path).filter(User.id == user_id).first()
# Use SQLAlchemy to query the user's photo path by user ID
user = db_session.query(User.photo_path).filter(User.id == user_id).first()
if user:
# Extract the photo_path attribute from the user object
photo_path = user.photo_path
else:
# Handle the case where the user was not found or doesn't have a photo path
raise HTTPException(
status_code=404, detail="User not found or no photo path available"
)
if user:
# Extract the photo_path attribute from the user object
photo_path = user.photo_path
else:
# Handle the case where the user was not found or doesn't have a photo path
raise HTTPException(
status_code=404, detail="User not found or no photo path available"
)
except JWTError:
# Handle JWT (JSON Web Token) authentication error
@@ -255,31 +262,29 @@ async def read_users_userPhotoFromID(user_id: int, token: str = Depends(oauth2_s
# Define an HTTP GET route to retrieve user photos aux by user ID
@router.get("/users/{user_id}/userphotoauxfromid")
async def read_users_userPhotoAuxFromID(
user_id: int, token: str = Depends(oauth2_scheme)
user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Validate that the user has admin access
sessionController.validate_admin_access(token)
# Create a database session using the get_db_session context manager
with get_db_session() as db_session:
# Use SQLAlchemy to query the user's photo path by user ID
user = (
db_session.query(User.photo_path_aux).filter(User.id == user_id).first()
)
# Use SQLAlchemy to query the user's photo path by user ID
user = db_session.query(User.photo_path_aux).filter(User.id == user_id).first()
if user:
# Extract the photo_path_aux attribute from the user object
photo_path_aux = user.photo_path_aux
else:
# Handle the case where the user was not found or doesn't have a photo path
raise HTTPException(
status_code=404,
detail="User not found or no photo path aux available",
)
if user:
# Extract the photo_path_aux attribute from the user object
photo_path_aux = user.photo_path_aux
else:
# Handle the case where the user was not found or doesn't have a photo path
raise HTTPException(
status_code=404,
detail="User not found or no photo path aux available",
)
except JWTError:
# Handle JWT (JSON Web Token) authentication error
@@ -306,10 +311,14 @@ class CreateUserRequest(BaseModel):
# Define an HTTP POST route to create a new user
@router.post("/users/create")
async def create_user(user: CreateUserRequest, token: str = Depends(oauth2_scheme)):
async def create_user(
user: CreateUserRequest,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Validate that the user has admin access
sessionController.validate_admin_access(token)
@@ -330,10 +339,9 @@ async def create_user(user: CreateUserRequest, token: str = Depends(oauth2_schem
is_active=user.is_active,
)
with get_db_session() as db_session:
# Add the new user to the database
db_session.add(new_user)
db_session.commit()
# Add the new user to the database
db_session.add(new_user)
db_session.commit()
return {"message": "User added successfully"}
except NameError as err:
@@ -361,49 +369,49 @@ class EditUserRequest(BaseModel):
async def edit_user(
user_id: int,
user_attributtes: EditUserRequest,
token: str = Depends(oauth2_scheme), # Add the Request dependency
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Validate that the user has admin access
sessionController.validate_admin_access(token)
with get_db_session() as db_session:
# Query the database to find the user by their ID
user = db_session.query(User).filter(User.id == user_id).first()
# Query the database to find the user by their ID
user = db_session.query(User).filter(User.id == user_id).first()
# Check if the user with the given ID exists
if not user:
raise HTTPException(status_code=404, detail="User not found")
# Check if the user with the given ID exists
if not user:
raise HTTPException(status_code=404, detail="User not found")
# Update user information if provided in the form data
if user_attributtes.name is not None:
user.name = user_attributtes.name
if user_attributtes.username is not None:
user.username = user_attributtes.username
if user_attributtes.email is not None:
user.email = user_attributtes.email
if user_attributtes.preferred_language is not None:
user.preferred_language = user_attributtes.preferred_language
if user_attributtes.city is not None:
user.city = user_attributtes.city
if user_attributtes.birthdate is not None:
user.birthdate = user_attributtes.birthdate
if user_attributtes.gender is not None:
user.gender = user_attributtes.gender
if user_attributtes.access_type is not None:
user.access_type = user_attributtes.access_type
if user_attributtes.photo_path is not None:
user.photo_path = user_attributtes.photo_path
if user_attributtes.photo_path_aux is not None:
user.photo_path_aux = user_attributtes.photo_path_aux
if user_attributtes.is_active is not None:
user.is_active = user_attributtes.is_active
# Update user information if provided in the form data
if user_attributtes.name is not None:
user.name = user_attributtes.name
if user_attributtes.username is not None:
user.username = user_attributtes.username
if user_attributtes.email is not None:
user.email = user_attributtes.email
if user_attributtes.preferred_language is not None:
user.preferred_language = user_attributtes.preferred_language
if user_attributtes.city is not None:
user.city = user_attributtes.city
if user_attributtes.birthdate is not None:
user.birthdate = user_attributtes.birthdate
if user_attributtes.gender is not None:
user.gender = user_attributtes.gender
if user_attributtes.access_type is not None:
user.access_type = user_attributtes.access_type
if user_attributtes.photo_path is not None:
user.photo_path = user_attributtes.photo_path
if user_attributtes.photo_path_aux is not None:
user.photo_path_aux = user_attributtes.photo_path_aux
if user_attributtes.is_active is not None:
user.is_active = user_attributtes.is_active
# Commit the changes to the database
db_session.commit()
# 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")
@@ -417,28 +425,31 @@ async def edit_user(
# Define an HTTP PUT route to delete a user's photo
@router.put("/users/{user_id}/delete-photo")
async def delete_user_photo(user_id: int, token: str = Depends(oauth2_scheme)):
async def delete_user_photo(
user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Validate that the user has admin access
sessionController.validate_admin_access(token)
with get_db_session() as db_session:
# Query the database to find the user by their ID
user = db_session.query(User).filter(User.id == user_id).first()
# Query the database to find the user by their ID
user = db_session.query(User).filter(User.id == user_id).first()
# Check if the user with the given ID exists
if not user:
raise HTTPException(status_code=404, detail="User not found")
# Check if the user with the given ID exists
if not user:
raise HTTPException(status_code=404, detail="User not found")
# Set the user's photo paths to None to delete the photo
user.photo_path = None
user.photo_path_aux = None
# Set the user's photo paths to None to delete the photo
user.photo_path = None
user.photo_path_aux = None
# Commit the changes to the database
db_session.commit()
# 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")
@@ -453,31 +464,34 @@ async def delete_user_photo(user_id: int, token: str = Depends(oauth2_scheme)):
# Define an HTTP DELETE route to delete a user
@router.delete("/users/{user_id}/delete")
async def delete_user(user_id: int, token: str = Depends(oauth2_scheme)):
async def delete_user(
user_id: int,
token: str = Depends(oauth2_scheme),
db_session: Session = Depends(get_db_session),
):
try:
# Validate the user's access token using the oauth2_scheme
sessionController.validate_token(token)
sessionController.validate_token(db_session, token)
# Validate that the user has admin access
sessionController.validate_admin_access(token)
with get_db_session() as db_session:
user = db_session.query(User).filter(User.id == user_id).first()
user = db_session.query(User).filter(User.id == user_id).first()
# Check if the user with the given ID exists
if not user:
raise HTTPException(status_code=404, detail="User not found")
# Check if the user with the given ID exists
if not user:
raise HTTPException(status_code=404, detail="User not found")
# Check for existing dependencies if needed (e.g., related systems)
count_gear = db_session.query(Gear).filter(Gear.user_id == user_id).count()
if count_gear > 0:
raise HTTPException(
status_code=409,
detail="Cannot delete user due to existing dependencies",
)
# Delete the user from the database
db_session.delete(user)
db_session.commit()
# Check for existing dependencies if needed (e.g., related systems)
count_gear = db_session.query(Gear).filter(Gear.user_id == user_id).count()
if count_gear > 0:
raise HTTPException(
status_code=409,
detail="Cannot delete user due to existing dependencies",
)
# Delete the user from the database
db_session.delete(user)
db_session.commit()
except JWTError:
raise HTTPException(status_code=401, detail="Unauthorized")
except Exception as err:

View File

@@ -1,5 +1,5 @@
"""
Module: my_database_logic
Module: db.py
This module defines the SQLAlchemy data models and database logic for managing users, followers,
access tokens, gear, and activities. It establishes the database connection, creates tables,
@@ -60,7 +60,9 @@ db_url = URL.create(
)
# Create the SQLAlchemy engine
engine = create_engine(db_url, pool_size=10, max_overflow=20, pool_timeout=180)
engine = create_engine(
db_url, pool_size=10, max_overflow=20, pool_timeout=180, pool_recycle=3600
)
# Create a session factory
Session = sessionmaker(bind=engine)
@@ -68,18 +70,41 @@ Session = sessionmaker(bind=engine)
# Create a base class for declarative models
Base = declarative_base()
# Data model for followers table using SQLAlchemy's ORM
class Follower(Base):
__tablename__ = "followers"
follower_id = Column(Integer, ForeignKey('users.id'), primary_key=True, index=True, comment="ID of the follower user")
following_id = Column(Integer, ForeignKey('users.id'), primary_key=True, index=True, comment="ID of the following user")
is_accepted = Column(Boolean, nullable=False, default=False, comment="Whether the follow request is accepted or not")
follower_id = Column(
Integer,
ForeignKey("users.id"),
primary_key=True,
index=True,
comment="ID of the follower user",
)
following_id = Column(
Integer,
ForeignKey("users.id"),
primary_key=True,
index=True,
comment="ID of the following user",
)
is_accepted = Column(
Boolean,
nullable=False,
default=False,
comment="Whether the follow request is accepted or not",
)
# Define a relationship to the User model
follower = relationship('User', foreign_keys=[follower_id], back_populates="followers")
follower = relationship(
"User", foreign_keys=[follower_id], back_populates="followers"
)
# Define a relationship to the User model
following = relationship('User', foreign_keys=[following_id], back_populates="following")
following = relationship(
"User", foreign_keys=[following_id], back_populates="following"
)
# Data model for users table using SQLAlchemy's ORM
class User(Base):
@@ -87,7 +112,9 @@ class User(Base):
id = Column(Integer, primary_key=True)
name = Column(
String(length=250), nullable=False, comment="User real name (May include spaces)"
String(length=250),
nullable=False,
comment="User real name (May include spaces)",
)
username = Column(
String(length=250),
@@ -141,9 +168,19 @@ class User(Base):
# user_settings = relationship("UserSettings", back_populates="user")
# Establish a one-to-many relationship between User and Followers
followers = relationship("Follower", back_populates="following", cascade="all, delete-orphan", foreign_keys=[Follower.following_id])
followers = relationship(
"Follower",
back_populates="following",
cascade="all, delete-orphan",
foreign_keys=[Follower.following_id],
)
# Establish a one-to-many relationship between User and Followers
following = relationship("Follower", back_populates="follower", cascade="all, delete-orphan", foreign_keys=[Follower.follower_id])
following = relationship(
"Follower",
back_populates="follower",
cascade="all, delete-orphan",
foreign_keys=[Follower.follower_id],
)
# Data model for access_tokens table using SQLAlchemy's ORM
@@ -271,7 +308,12 @@ class Activity(Base):
comment="Average speed seconds per meter (s/m)",
)
average_power = Column(Integer, nullable=False, comment="Average power (watts)")
visibility = Column(Integer, nullable=False, default=0, comment="0 - public, 1 - followers, 2 - private")
visibility = Column(
Integer,
nullable=False,
default=0,
comment="0 - public, 1 - followers, 2 - private",
)
gear_id = Column(
Integer,
ForeignKey("gear.id", ondelete="SET NULL"),
@@ -287,6 +329,7 @@ class Activity(Base):
# Define a relationship to the Gear model
gear = relationship("Gear", back_populates="activities")
def create_database_tables():
# Create tables
Base.metadata.create_all(bind=engine)
@@ -295,7 +338,7 @@ def create_database_tables():
with get_db_session() as session:
try:
# Check if the user already exists
existing_user = session.query(User).filter_by(username="admin").one()
session.query(User).filter_by(username="admin").one()
print("Admin user already exists. Will skip user creation.")
except NoResultFound:
# Create a new SHA-256 hash object
@@ -325,6 +368,7 @@ def create_database_tables():
print("Admin user created successfully.")
@contextmanager
def get_db_session():
session = Session()

67
backend/dependencies.py Normal file
View File

@@ -0,0 +1,67 @@
from sqlalchemy.orm import Session
from db.db import Session as BaseSession
from fastapi.responses import JSONResponse
from controllers import sessionController
from fastapi import Depends
from fastapi.security import OAuth2PasswordBearer
# Define the OAuth2 scheme for handling bearer tokens
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def get_db_session() -> Session:
"""
Get a SQLAlchemy database session.
Returns:
- Session: SQLAlchemy database session.
Yields:
- Session: SQLAlchemy database session to the calling function.
# Note: The session is automatically committed and closed by FastAPI at the end of the request.
"""
db_session = BaseSession()
try:
yield db_session
finally:
db_session.close()
def get_current_user(
token: str = Depends(oauth2_scheme), db_session: Session = Depends(get_db_session)
):
"""
Get the ID of the current authenticated user.
Parameters:
- token (str): The authentication token for the user.
- db_session (Session): SQLAlchemy database session.
Returns:
- int: The ID of the current authenticated user.
Raises:
- JWTError: If the authentication token is invalid or expired.
- Exception: For other unexpected errors.
"""
sessionController.validate_token(db_session, token)
return sessionController.get_user_id_from_token(token)
# Standardized error response function
def create_error_response(code: str, message: str, status_code: int):
"""
Create a JSON error response.
Parameters:
- code (str): Error code to be included in the response.
- message (str): Error message to be included in the response.
- status_code (int): HTTP status code for the response.
Returns:
- JSONResponse: JSON response containing the specified error information.
"""
return JSONResponse(
content={"error": {"code": code, "message": message}}, status_code=status_code
)

View File

@@ -1,4 +1,4 @@
from fastapi import FastAPI
from fastapi import FastAPI, Depends
from apscheduler.schedulers.background import BackgroundScheduler
from controllers import (
sessionController,
@@ -23,7 +23,9 @@ from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
import os
from db.db import create_database_tables, get_db_session
from db.db import create_database_tables
from dependencies import get_db_session
# from dotenv import load_dotenv
import logging
@@ -60,13 +62,19 @@ logger.addHandler(file_handler)
# trace.set_tracer_provider(TracerProvider(resource=Resource.create().add_attribute("service.name", "backend_api")))
if(os.environ.get("JAEGER_ENABLED") == "true"):
if os.environ.get("JAEGER_ENABLED") == "true":
trace.set_tracer_provider(
TracerProvider(resource=Resource.create({"service.name": "backend_api"}))
)
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(
OTLPSpanExporter(endpoint=os.environ.get("JAEGER_PROTOCOL") + "://" + os.environ.get("JAEGER_HOST") + ":" + os.environ.get("JAGGER_PORT"))
OTLPSpanExporter(
endpoint=os.environ.get("JAEGER_PROTOCOL")
+ "://"
+ os.environ.get("JAEGER_HOST")
+ ":"
+ os.environ.get("JAGGER_PORT")
)
)
)
@@ -86,12 +94,25 @@ app.include_router(stravaController.router)
scheduler = BackgroundScheduler()
scheduler.start()
# Remove the leading space
scheduler.add_job(sessionController.remove_expired_tokens, "interval", minutes=5)
scheduler.add_job(stravaController.refresh_strava_token, "interval", minutes=30)
# Job to remove expired tokens every 5 minutes
scheduler.add_job(
lambda: sessionController.remove_expired_tokens(db_session=get_db_session()),
"interval",
minutes=5,
)
# Job to refresh the Strava token every 30 minutes
scheduler.add_job(
lambda: stravaController.refresh_strava_token(db_session=get_db_session()),
"interval",
minutes=30,
)
# Job to get Strava activities every hour
scheduler.add_job(
lambda: stravaController.get_strava_activities(
(datetime.utcnow() - timedelta(days=1)).strftime("%Y-%m-%dT%H:%M:%SZ")
(datetime.utcnow() - timedelta(days=1)).strftime("%Y-%m-%dT%H:%M:%SZ"),
db_session=get_db_session(),
),
"interval",
minutes=60,
@@ -101,8 +122,7 @@ scheduler.add_job(
@app.on_event("startup")
async def startup_event():
# Create the database and tables if they don't exist
with get_db_session() as session:
create_database_tables()
create_database_tables()
# Add the background scheduler to the app's shutdown event

View File

@@ -102,13 +102,13 @@ if ($activity[0]["gear_id"] != null) {
}
if ($activity[0]["activity_type"] == 1 || $activity[0]["activity_type"] == 2 || $activity[0]["activity_type"] == 3) {
$activityGearOptions = getGearForRunning();
$activityGearOptions = getGearFromType(2);
} else {
if ($activity[0]["activity_type"] == 4 || $activity[0]["activity_type"] == 5 || $activity[0]["activity_type"] == 6 || $activity[0]["activity_type"] == 7 || $activity[0]["activity_type"] == 8) {
$activityGearOptions = getGearForCycling();
$activityGearOptions = getGearFromType(1);
} else {
if ($activity[0]["activity_type"] == 9) {
$activityGearOptions = getGearForSwimming();
$activityGearOptions = getGearFromType(3);
}
}
}

View File

@@ -10,7 +10,7 @@ function getActivities()
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
return json_decode($response[0], true)["activity_records"];
} else {
return -2;
}
@@ -25,7 +25,7 @@ function getUserActivities()
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
return json_decode($response[0], true)["activity_records"];
} else {
return -2;
}
@@ -40,7 +40,7 @@ function getUserActivitiesWeek($userID, $week)
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
return json_decode($response[0], true)["activity_records"];
} else {
return -2;
}
@@ -85,7 +85,7 @@ function getUserActivitiesThisMonthNumber($userID)
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
return json_decode($response[0], true)["activity_count"];
} else {
return -2;
}
@@ -100,7 +100,7 @@ function getGearActivities($gearID)
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
return json_decode($response[0], true)["activity_records"];
} else {
return -2;
}
@@ -131,7 +131,7 @@ function getUserActivitiesPagination($pageNumber, $numRecords)
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
return json_decode($response[0], true)["activity_records"];
} else {
return -2;
}
@@ -146,7 +146,7 @@ function getFollowedUserActivitiesPagination($pageNumber, $numRecords)
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
return json_decode($response[0], true)["activity_records"];
} else {
return -2;
}
@@ -161,8 +161,7 @@ function numActivities()
return -1;
} else {
if ($response[1] === 200) {
$data = json_decode($response[0], true);
return $data[0];
return json_decode($response[0], true)["activity_count"];
} else {
return -2;
}
@@ -177,8 +176,7 @@ function numUserActivities()
return -1;
} else {
if ($response[1] === 200) {
$data = json_decode($response[0], true);
return $data[0];
return json_decode($response[0], true)["activity_count"];
} else {
return -2;
}
@@ -193,8 +191,7 @@ function numFollowedUserActivities()
return -1;
} else {
if ($response[1] === 200) {
$data = json_decode($response[0], true);
return $data[0];
return json_decode($response[0], true)["activity_count"];
} else {
return -2;
}
@@ -209,7 +206,7 @@ function getActivityFromId($id)
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
return json_decode($response[0], true)["activity_records"];
} else {
return -2;
}
@@ -236,24 +233,24 @@ function newActivity($distance, $name, $type, $starttime, $endtime, $town, $coun
$response = callAPIRoute("/activities/create", 0, 4, json_encode(array(
'distance' => $distance,
'name' => $name,
'type' => $type,
'starttime' => $starttime,
'endtime' => $endtime,
'activity_type' => $type,
'start_time' => $starttime,
'end_time' => $endtime,
'city' => $city,
'town' => $town,
'country' => $country,
'waypoints' => $waypoints,
'elevationGain' => $elevationGain,
'elevationLoss' => $elevationLoss,
'elevation_gain' => $elevationGain,
'elevation_loss' => $elevationLoss,
'pace' => $pace,
'averageSpeed' => $averageSpeed,
'averagePower' => $averagePower,
'strava_id' => $strava_id,
'average_speed' => $averageSpeed,
'average_power' => $averagePower,
'strava_activity_id' => $strava_id,
)));
if ($response[0] === false) {
return -1;
} else {
if ($response[1] === 200) {
if ($response[1] === 201) {
return 0;
// $data = json_decode($response[0], true);
// if (isset($data['id'])) {

View File

@@ -10,52 +10,22 @@ function getGear()
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
return json_decode($response[0], true)["gear_records"];
} else {
return -2;
}
}
}
/* Get all gear for running*/
function getGearForRunning()
/* Get all gear from type*/
function getGearFromType($gear_type)
{
$response = callAPIRoute("/gear/all/running", 1, 0, NULL);
$response = callAPIRoute("/gear/all/$gear_type", 1, 0, NULL);
if ($response[0] === false) {
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
} else {
return -2;
}
}
}
/* Get all gear for running*/
function getGearForCycling()
{
$response = callAPIRoute("/gear/all/cycling", 1, 0, NULL);
if ($response[0] === false) {
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
} else {
return -2;
}
}
}
/* Get all gear for running*/
function getGearForSwimming()
{
$response = callAPIRoute("/gear/all/swimming", 1, 0, NULL);
if ($response[0] === false) {
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
return json_decode($response[0], true)["gear_records"];
} else {
return -2;
}
@@ -70,7 +40,7 @@ function getGearPagination($pageNumber, $numRecords)
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
return json_decode($response[0], true)["gear_records"];
} else {
return -2;
}
@@ -85,8 +55,7 @@ function numGears()
return -1;
} else {
if ($response[1] === 200) {
$data = json_decode($response[0], true);
return $data[0];
return json_decode($response[0], true)["gear_count"];
} else {
return -2;
}
@@ -101,7 +70,7 @@ function getGearFromNickname($nickname)
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
return json_decode($response[0], true)["gear_records"];
} else {
return -2;
}
@@ -116,7 +85,7 @@ function getGearFromId($id)
return -1;
} else {
if ($response[1] === 200) {
return json_decode($response[0], true);
return json_decode($response[0], true)["gear_records"];
} else {
return -2;
}
@@ -126,7 +95,7 @@ function getGearFromId($id)
/* Creates a new gear */
function newGear($brand, $model, $nickname, $gear_type, $date)
{
if (getGearFromNickname($nickname) != NULL) {
if (getGearFromNickname($nickname)["gear_records"] != NULL) {
return -3;
}
@@ -140,7 +109,7 @@ function newGear($brand, $model, $nickname, $gear_type, $date)
if ($response[0] === false) {
return -1;
} else {
if ($response[1] === 200) {
if ($response[1] === 201) {
return 0;
} else {
return -2;

View File

@@ -185,7 +185,7 @@ $userFollowingAll = getUserFollowingAll($_GET["userID"]);
<?php echo $translationsUsersUser['user_stats_numberActivitiesMonth']; ?>
</span>
<h1>
<?php echo $numUserActivitiesThisMonth["activity_count"]; ?>
<?php echo $numUserActivitiesThisMonth; ?>
</h1>
<!--<hr class="mb-2 mt-2">-->
<div class="row align-items-center">