Refactor models and endpoints, add logging, and improve error handling.
This commit is contained in:
+55
-18
@@ -1,10 +1,14 @@
|
||||
from datetime import datetime
|
||||
import bcrypt
|
||||
import requests
|
||||
|
||||
from lib.database.database import db_run
|
||||
from models import Message
|
||||
from models.user import UserCredentials
|
||||
from models.movie import Movie
|
||||
from models.movie import MovieIn, Movie
|
||||
from models.card import Card
|
||||
|
||||
import bcrypt
|
||||
from lib.settings import settings
|
||||
from lib.logger import logger
|
||||
|
||||
|
||||
def add_user(user: UserCredentials):
|
||||
@@ -14,40 +18,71 @@ def add_user(user: UserCredentials):
|
||||
sql = f"INSERT INTO users (username, password) VALUES ('{user.username}', '{hashed_password}')"
|
||||
try:
|
||||
db_run(sql)
|
||||
except:
|
||||
return Message(message="User already exists", message_type="error")
|
||||
except Exception as e:
|
||||
return Message(message=f"User already exists. Error: {e}", message_type="error")
|
||||
|
||||
return Message(message="User added successfully", message_type="success")
|
||||
|
||||
|
||||
def remove_user(user_id: str):
|
||||
logger.debug(f"Removing user {user_id}")
|
||||
sql = f"DELETE FROM users WHERE id = '{user_id}'"
|
||||
|
||||
try:
|
||||
db_run(sql)
|
||||
except:
|
||||
return Message(message="User not found", message_type="error")
|
||||
except Exception as e:
|
||||
return Message(message=f"User not found. Error: {e}", message_type="error")
|
||||
|
||||
return Message(message="User removed successfully", message_type="success")
|
||||
|
||||
|
||||
def add_movie(movie: Movie):
|
||||
sql = "INSERT INTO movies (name, imdb_id, actors, release_date, plot, showtime) VALUES (?, ?, ?, ?, ?, ?)"
|
||||
def add_movie(movie: MovieIn):
|
||||
logger.info(f"Adding movie {movie.imdb_id}")
|
||||
logger.debug(f"Requesting movie data from OMDB: {movie.imdb_id}")
|
||||
request_url = "https://omdbapi.com/"
|
||||
request_params = {
|
||||
"apikey": settings.OMDB_API_KEY,
|
||||
"i": movie.imdb_id,
|
||||
}
|
||||
|
||||
response = requests.get(request_url, params=request_params)
|
||||
if response.status_code != 200:
|
||||
logger.error(f"Failed to add movie {movie.imdb_id}")
|
||||
return Message(message="Failed to add movie", message_type="error")
|
||||
movie_data = response.json()
|
||||
logger.debug(f"Movie data received from OMDB: {movie_data}")
|
||||
if "Released" in movie_data:
|
||||
movie_data["Released"] = datetime.strptime(movie_data["Released"], "%d %b %Y")
|
||||
|
||||
new_movie = Movie(
|
||||
name=movie_data["Title"],
|
||||
imdb_id=movie_data["imdbID"],
|
||||
actors=[actor.strip() for actor in movie_data["Actors"].split(",")],
|
||||
release_date=movie_data["Released"],
|
||||
plot=movie.plot,
|
||||
poster_url=movie_data["Poster"],
|
||||
showtime=movie.showtime,
|
||||
)
|
||||
logger.debug(f"Movie data: {new_movie.model_dump()}")
|
||||
sql = "INSERT INTO movies (name, imdb_id, actors, release_date, plot, showtime, poster_url) VALUES (?, ?, ?, ?, ?, ?, ?)"
|
||||
try:
|
||||
db_run(
|
||||
sql,
|
||||
(
|
||||
movie.name,
|
||||
movie.imdb_id,
|
||||
movie.actors,
|
||||
movie.release_date,
|
||||
movie.plot,
|
||||
movie.showtime,
|
||||
new_movie.name,
|
||||
new_movie.imdb_id,
|
||||
new_movie.actors,
|
||||
new_movie.release_date,
|
||||
new_movie.plot,
|
||||
new_movie.showtime,
|
||||
new_movie.poster_url,
|
||||
),
|
||||
)
|
||||
except:
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to add movie {movie.imdb_id}. Error: {e}")
|
||||
return Message(message="Failed to add movie", message_type="error")
|
||||
|
||||
logger.info(f"Movie {movie.imdb_id} added successfully")
|
||||
return Message(message="Movie added successfully", message_type="success")
|
||||
|
||||
|
||||
@@ -55,8 +90,10 @@ def set_movie_watched(movie_id: str):
|
||||
sql = f"UPDATE movies SET is_watched = TRUE WHERE id = '{movie_id}'"
|
||||
try:
|
||||
db_run(sql)
|
||||
except:
|
||||
return Message(message="Failed to set movie as watched", message_type="error")
|
||||
except Exception as e:
|
||||
return Message(
|
||||
message=f"Failed to set movie as watched. Error: {e}", message_type="error"
|
||||
)
|
||||
|
||||
return Message(message="Movie set as watched successfully", message_type="success")
|
||||
|
||||
|
||||
+12
-15
@@ -1,25 +1,22 @@
|
||||
from models.movie import Movie
|
||||
from lib.database.database import get_db
|
||||
from lib.logger import logger
|
||||
|
||||
|
||||
@logger.catch
|
||||
def get_all_movies() -> list[Movie]:
|
||||
logger.info("Getting all movies")
|
||||
sql = "SELECT * FROM movies"
|
||||
with get_db() as conn:
|
||||
result = conn.execute(sql).fetchall()
|
||||
result = conn.execute(sql).df()
|
||||
result_dict = result.to_dict(orient="records")
|
||||
movies = []
|
||||
for row in result:
|
||||
movie = Movie(
|
||||
id=str(row[0]),
|
||||
name=row[1],
|
||||
imdb_id=row[2],
|
||||
actors=row[3],
|
||||
release_date=row[4],
|
||||
plot=row[5],
|
||||
showtime=row[6],
|
||||
is_watched=row[7],
|
||||
created_at=row[8],
|
||||
updated_at=row[9],
|
||||
)
|
||||
for row in result_dict:
|
||||
row["id"] = str(row["id"])
|
||||
logger.debug(f"Found movie: {row}")
|
||||
print("Type of name:", type(row["name"]))
|
||||
movie = Movie(**row)
|
||||
|
||||
movies.append(movie)
|
||||
|
||||
return movies
|
||||
@@ -28,7 +25,7 @@ def get_all_movies() -> list[Movie]:
|
||||
|
||||
|
||||
def get_next_movie() -> Movie | None:
|
||||
sql = "SELECT * FROM movies WHERE is_watched = FALSE AND showtime > CURRENT_TIMESTAMP ORDER BY showtime ASC"
|
||||
sql = "SELECT * FROM movies WHERE is_watched = FALSE AND showtime = CURRENT_DATE ORDER BY showtime ASC"
|
||||
with get_db() as conn:
|
||||
result = conn.execute(sql).fetchdf()
|
||||
if result.empty:
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from datetime import datetime
|
||||
|
||||
from models.user import UserIn, UserOut
|
||||
from lib.database.database import get_db, db_run
|
||||
from lib.database.database import get_db
|
||||
from lib.logger import logger
|
||||
|
||||
|
||||
def set_last_login(user_id: str) -> datetime:
|
||||
logger.debug(f"Setting last login for user {user_id}")
|
||||
cur_time = datetime.now()
|
||||
sql = f"UPDATE users SET last_login = '{cur_time}' WHERE id = '{user_id}'"
|
||||
with get_db() as conn:
|
||||
|
||||
+24
-28
@@ -1,44 +1,40 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from datetime import datetime
|
||||
from datetime import datetime, date
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class Movie(BaseModel):
|
||||
id: Optional[str] = Field(
|
||||
default=None, examples=["123e4567-e89b-12d3-a456-426655440000"]
|
||||
)
|
||||
name: str = Field(
|
||||
description="Name of the movie", examples=["A Maple Valley Christmas"]
|
||||
class MovieBase(BaseModel):
|
||||
name: Optional[str] | None = Field(
|
||||
None, description="Name of the movie", examples=["A Maple Valley Christmas"]
|
||||
)
|
||||
imdb_id: str = Field(description="IMDB ID of the movie", examples=["tt1234567"])
|
||||
actors: list[str] = Field(
|
||||
description="Actors of the movie",
|
||||
examples=[["Peyton List", "Andrew W. Walker"]],
|
||||
)
|
||||
showtime: date = Field(description="Showtime of the movie", examples=["2024-12-01"])
|
||||
plot: str = Field(
|
||||
description="Plot of the movie", examples=["Erica is a rancher..."]
|
||||
)
|
||||
release_date: datetime = Field(
|
||||
description="Release date of the movie", examples=["2022-11-05"]
|
||||
|
||||
|
||||
class MovieIn(MovieBase):
|
||||
actors: Optional[list[str]] | None = Field(
|
||||
None,
|
||||
description="Actors of the movie",
|
||||
examples=[["Peyton List", "Andrew W. Walker"]],
|
||||
)
|
||||
poster_url: str = Field(
|
||||
description="Poster URL of the movie", examples=["https://..."]
|
||||
release_date: datetime | None = Field(
|
||||
None, description="Release date of the movie", examples=["2022-11-05"]
|
||||
)
|
||||
showtime: datetime = Field(
|
||||
description="Showtime of the movie", examples=["2024-12-01"]
|
||||
poster_url: str | None = Field(
|
||||
None, description="Poster URL of the movie", examples=["https://..."]
|
||||
)
|
||||
is_watched: bool = Field(
|
||||
default=False,
|
||||
description="Whether the movie is watched or not",
|
||||
examples=[True],
|
||||
|
||||
|
||||
class Movie(MovieIn):
|
||||
id: Optional[str] = Field(
|
||||
default=None, examples=["123e4567-e89b-12d3-a456-426655440000"]
|
||||
)
|
||||
created_at: datetime = Field(
|
||||
default_factory=datetime.utcnow,
|
||||
description="Creation date of the movie",
|
||||
examples=[datetime.utcnow()],
|
||||
default_factory=datetime.utcnow, description="Creation date of the movie"
|
||||
)
|
||||
updated_at: datetime = Field(
|
||||
default_factory=datetime.utcnow,
|
||||
description="Modification date of the movie",
|
||||
examples=[datetime.utcnow()],
|
||||
modified_at: datetime = Field(
|
||||
default_factory=datetime.utcnow, description="Modification date of the movie"
|
||||
)
|
||||
|
||||
+50
-8
@@ -2,7 +2,7 @@ from fastapi import APIRouter
|
||||
|
||||
from models import Message
|
||||
from models.user import UserCredentials
|
||||
from models.movie import Movie
|
||||
from models.movie import MovieBase
|
||||
from models.card import Card
|
||||
from lib.database.admin import (
|
||||
add_user,
|
||||
@@ -22,35 +22,77 @@ router = APIRouter(
|
||||
|
||||
@router.post("/user", response_model=Message)
|
||||
async def add_user_endpoint(user: UserCredentials):
|
||||
"""Add a user to the database"""
|
||||
"""Add a user to the database
|
||||
|
||||
Args:
|
||||
user (UserCredentials): The user to add
|
||||
|
||||
Returns:
|
||||
Message: A message indicating the result of the operation
|
||||
"""
|
||||
return add_user(user)
|
||||
|
||||
|
||||
@router.delete("/user", response_model=Message)
|
||||
async def remove_user_endpoint(user_id: str):
|
||||
"""Remove a user from the database"""
|
||||
"""Remove a user from the database
|
||||
|
||||
Args:
|
||||
user_id (str): The id of the user to remove
|
||||
|
||||
Returns:
|
||||
Message: A message indicating the result of the operation
|
||||
"""
|
||||
return remove_user(user_id)
|
||||
|
||||
|
||||
@router.post("/movie", response_model=Message)
|
||||
async def add_movie_endpoint(movie: Movie):
|
||||
"""Add a movie to the database"""
|
||||
async def add_movie_endpoint(movie: MovieBase):
|
||||
"""Add a movie to the database
|
||||
|
||||
Args:
|
||||
movie (MovieBase): The movie to add
|
||||
|
||||
Returns:
|
||||
Message: A message indicating the result of the operation
|
||||
"""
|
||||
return add_movie(movie)
|
||||
|
||||
|
||||
@router.patch("/movie", response_model=Message)
|
||||
async def set_movie_watched_endpoint(movie_id: str):
|
||||
"""Set movie as watched"""
|
||||
"""Set movie as watched
|
||||
|
||||
Args:
|
||||
movie_id (str): The id of the movie to set as watched
|
||||
|
||||
Returns:
|
||||
Message: A message indicating the result of the operation
|
||||
"""
|
||||
return set_movie_watched(movie_id)
|
||||
|
||||
|
||||
@router.post("/card", response_model=Message)
|
||||
async def add_card_endpoint(card: Card):
|
||||
"""Add a card to the database"""
|
||||
"""Add a card to the database
|
||||
|
||||
Args:
|
||||
card (Card): The card to add
|
||||
|
||||
Returns:
|
||||
Message: A message indicating the result of the operation
|
||||
"""
|
||||
return add_card(card)
|
||||
|
||||
|
||||
@router.delete("/card", response_model=Message)
|
||||
async def remove_card_endpoint(card_id: str):
|
||||
"""Remove a card from the database"""
|
||||
"""Remove a card from the database
|
||||
|
||||
Args:
|
||||
card_id (str): The id of the card to remove
|
||||
|
||||
Returns:
|
||||
Message: A message indicating the result of the operation
|
||||
"""
|
||||
return remove_card(card_id)
|
||||
|
||||
+5
-1
@@ -12,5 +12,9 @@ router = APIRouter(
|
||||
|
||||
@router.get("/", responses={200: {"model": list[Card]}})
|
||||
async def get_cards() -> list[Card]:
|
||||
"""Get all cards"""
|
||||
"""Get all cards
|
||||
|
||||
Returns:
|
||||
list[Card]: List of cards
|
||||
"""
|
||||
return get_all_cards()
|
||||
|
||||
+5
-1
@@ -18,7 +18,11 @@ async def get_movies():
|
||||
|
||||
@router.get("/next", response_model=Movie)
|
||||
async def get_next_movie_endpoint():
|
||||
"""Get the next movie to watch"""
|
||||
"""Get the next movie to watch
|
||||
|
||||
Returns:
|
||||
Movie: The next movie to watch
|
||||
"""
|
||||
next_movie = get_next_movie()
|
||||
if next_movie is None:
|
||||
return Response(
|
||||
|
||||
+21
-5
@@ -17,7 +17,11 @@ router = APIRouter(
|
||||
responses={200: {"model": list[UserOut]}, 404: {"model": Message}},
|
||||
)
|
||||
async def get_users() -> list[UserOut]:
|
||||
"""Get all users"""
|
||||
"""Get all users
|
||||
|
||||
Returns:
|
||||
list[UserOut]: List of users
|
||||
"""
|
||||
return get_all_users()
|
||||
|
||||
|
||||
@@ -28,11 +32,20 @@ async def get_users() -> list[UserOut]:
|
||||
400: {"model": Message},
|
||||
404: {"model": Message},
|
||||
},
|
||||
summary="Login a user",
|
||||
description="Login a user by providing their username and password",
|
||||
)
|
||||
async def login(user: UserCredentials):
|
||||
"""Login a user"""
|
||||
user_from_db: UserIn = get_user_by_username(user.username)
|
||||
async def login(user: UserCredentials) -> UserOut | Response:
|
||||
"""Login a user by providing their username and password
|
||||
|
||||
Returns:
|
||||
UserOut: User information
|
||||
Response: Error message
|
||||
"""
|
||||
# Get the user from the database
|
||||
user_from_db: UserIn | None = get_user_by_username(user.username)
|
||||
if user_from_db is None:
|
||||
# User not found
|
||||
return Response(
|
||||
status_code=404,
|
||||
media_type="application/json",
|
||||
@@ -40,11 +53,14 @@ async def login(user: UserCredentials):
|
||||
message="User not found", message_type="error"
|
||||
).model_dump_json(),
|
||||
)
|
||||
password = user.password.encode("utf-8")
|
||||
|
||||
# Check the password
|
||||
password = user.password.encode("utf-8")
|
||||
if bcrypt.checkpw(password, user_from_db.password.encode("utf-8")):
|
||||
# Login successful
|
||||
return UserOut(**user_from_db.model_dump())
|
||||
|
||||
# Invalid password
|
||||
return Response(
|
||||
status_code=400,
|
||||
media_type="application/json",
|
||||
|
||||
Reference in New Issue
Block a user