Skip to content

Commit

Permalink
updatr
Browse files Browse the repository at this point in the history
  • Loading branch information
kenmoh committed Oct 16, 2023
1 parent a2af9d7 commit 894f686
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 22 deletions.
3 changes: 3 additions & 0 deletions app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

class Settings(BaseSettings):
MOVIES_DB_URL: str
BUCKET_NAME: str
AWSAccessKeyId: str
AWSSecretKey: str

class Config:
env_file = '.env'
Expand Down
21 changes: 21 additions & 0 deletions app/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from fastapi import Form, Depends, UploadFile
from app.utils import add_image


class AddMovieForm:
def __init__(self,
length: float = Form(...),
title: str = Form(...),
descr: str = Form(...),
casts: str = Form(...),
genre: str = Form(...),
thriller: str = Form(...),
image: UploadFile = Depends(add_image)
):
self.title = title
self.length = length
self.descr = descr
self.casts = casts
self.genre = genre
self.thriller = thriller
self.image = image
2 changes: 0 additions & 2 deletions app/models/movie_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ class Movie(Base):
genre: Mapped[str]
thriller: Mapped[str]

def get_casts_list(self):
return self.casts.split(',')

def __str__(self):
return self.title
Expand Down
48 changes: 45 additions & 3 deletions app/routes/movie_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
from sqlalchemy.orm import Session

from app.database import get_db
from app.schema.movie_schema import MovieCreateSchema, MovieResponseSchema, ReviewResponseSchema, ReviewCreateSchema, AverageMovieReview
from app.forms import AddMovieForm
from app.schema.movie_schema import MovieResponseSchema, ReviewResponseSchema, ReviewCreateSchema, AverageMovieReview

from app.services import services


movie_router = APIRouter(tags=['Movies'], prefix='/api/movies')
REVIEW_URL = 'https://reviewapi.onrender.com/api/reviews'

Expand All @@ -15,26 +17,46 @@

@movie_router.get('', status_code=status.HTTP_200_OK)
def get_movies(db: Session = Depends(get_db)) -> list[MovieResponseSchema]:
"""
Get all movies from the database
:param db:
:return: All Movies
"""
return services.get_all_movies(db)


@movie_router.post('', status_code=status.HTTP_201_CREATED)
def add_new_movie(movie: MovieCreateSchema, db: Session = Depends(get_db)) -> MovieResponseSchema:
def add_new_movie(movie: AddMovieForm = Depends(), db: Session = Depends(get_db)) -> (
MovieResponseSchema):
"""
Add new movie to the database
"""

return services.add_movie(movie, db)


@movie_router.put('/{movie_id}', status_code=status.HTTP_202_ACCEPTED)
def update_movie(movie_id, movie: MovieCreateSchema, db: Session = Depends(get_db)) -> MovieResponseSchema:
def update_movie(movie_id, movie: AddMovieForm = Depends(), db: Session = Depends(get_db)) -> MovieResponseSchema:
"""
Update a movie by it ID
"""

return services.update_movie(movie_id, movie, db)


@movie_router.get('/{movie_id}', status_code=status.HTTP_200_OK)
def get_movie(movie_id, db: Session = Depends(get_db)) -> MovieResponseSchema:
"""
Get a single movie from the database
"""
return services.get_movie(movie_id, db)


@movie_router.delete('/{movie_id}', status_code=status.HTTP_204_NO_CONTENT)
def delete_movie(movie_id: int, db: Session = Depends(get_db)):
"""
Delete a movie from database
"""
requests.delete(f'{REVIEW_URL}/delete-reviews/{movie_id}')
return services.delete_movie(movie_id, db)

Expand All @@ -46,13 +68,23 @@ def delete_movie(movie_id: int, db: Session = Depends(get_db)):

@movie_router.get('/reviews/{movie_id}', status_code=status.HTTP_200_OK)
def get_movie_reviews(movie_id: int) -> list[ReviewResponseSchema]:
"""
Get all reviews by a movie
"""
response = requests.get(f'{REVIEW_URL}/{movie_id}')
data = response.json()
return data


@movie_router.post('/reviews/{movie_id}', status_code=status.HTTP_201_CREATED)
def add_review(movie_id: int, review: ReviewCreateSchema, db: Session = Depends(get_db)) -> ReviewResponseSchema:
"""
Add review to a movie
:param movie_id:
:param review:
:param db:
:return: New Review
"""
movie = services.get_movie(movie_id, db)
data = {
"comment": review.comment, "author": review.author, 'rating': review.rating
Expand All @@ -66,12 +98,22 @@ def add_review(movie_id: int, review: ReviewCreateSchema, db: Session = Depends(

@movie_router.delete('/reviews/{review_id}', status_code=status.HTTP_204_NO_CONTENT)
def delete_review(review_id: int):
"""
Delete movie review
:param review_id:
:return: None
"""
response = requests.delete(f'{REVIEW_URL}/delete-review/{review_id}')
return response


@movie_router.get('/average-rating/{movie_id}', status_code=status.HTTP_200_OK)
def get_avg_movie_rating(movie_id: int) -> AverageMovieReview:
"""
Get the average rating of a movie
:param movie_id:
:return: average movie rating (float)
"""
response = requests.get(f'{REVIEW_URL}/average-rating/{movie_id}')
avg_rating = response.json()
return avg_rating
Expand Down
10 changes: 5 additions & 5 deletions app/schema/movie_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@


class MovieCreateSchema(BaseModel):
title: str = Field(min_length=1, max_length=50, title='Movie title')
length: float = Field(title='Movie duration')
description: str = Field(max_length=225, title='Summary of the book')
cover_image_url: str = Field(title='Movie cover image')
casts: str = Field(title="List of casts separated by comma")
title: str = Field(min_length=1, max_length=50, description='Movie title')
length: float = Field(description='Movie duration')
description: str = Field(max_length=225, description='Summary of the book')
casts: str = Field(description="List of casts separated by comma")
genre: str
thriller: str


class MovieResponseSchema(MovieCreateSchema):
id: int
cover_image_url: str


""" END MOVIE SCHEMA"""
Expand Down
45 changes: 38 additions & 7 deletions app/services/services.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
from fastapi import HTTPException, status
from app.schema.movie_schema import MovieCreateSchema
from app.models.movie_model import Movie
from app.database import session
from app.forms import AddMovieForm
from app.utils import delete_image

""" START MOVIE OPERATIONS """


def get_all_movies(db: session):
"""
This function
gets all movies from the database
:param db:
:return: All movies in the database
"""
return db.query(Movie).all()


def add_movie(movie: MovieCreateSchema, db: session):
def add_movie(movie: AddMovieForm, db: session):
"""
Add new movie to the database service
"""
try:
with session.begin():

new_movie = Movie(
title=movie.title,
length=movie.length,
description=movie.description,
cover_image_url=movie.cover_image_url,
description=movie.descr,
cover_image_url=movie.image,
casts=movie.casts,
thriller=movie.thriller,
genre=movie.genre
Expand All @@ -34,19 +45,29 @@ def add_movie(movie: MovieCreateSchema, db: session):


def get_movie(movie_id, db: session):
"""
This function retrieves a single movie from the database
:param movie_id:
:param db:
:return: Movie object
"""
movie = db.query(Movie).filter(Movie.id == movie_id).first()
if movie is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f'Movie with ID: {movie_id} not found!')
return movie


def update_movie(movie_id: int, movie: MovieCreateSchema, db: session):
def update_movie(movie_id: int, movie: AddMovieForm, db: session):
"""
This function updates a movie in the database by its id
:return: Updated movie object
"""
db_movie = db.query(Movie).filter(Movie.id == movie_id).first()

if db_movie is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f'Movie with ID: {movie_id} not found!')
db_movie.description = movie.description
db_movie.cover_image_url = movie.cover_image_url
db_movie.description = movie.descr
db_movie.cover_image_url = movie.image
db_movie.length = movie.length
db_movie.title = movie.title
db.casts = movie.casts
Expand All @@ -60,11 +81,21 @@ def update_movie(movie_id: int, movie: MovieCreateSchema, db: session):


def delete_movie(movie_id: int, db: session):
"""
Delete a movie from the database by it ID
:param movie_id:
:param db:
:return: None
"""
db_movie = db.query(Movie).filter(Movie.id == movie_id).first()
print(db_movie.cover_image_url.split('/'))
image_name = db_movie.cover_image_url.split('/')[1]

if db_movie is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f'Movie with ID: {movie_id} not found!')

delete_image(image_name)
print(image_name)
db.delete(db_movie)
db.commit()

Expand Down
4 changes: 2 additions & 2 deletions app/test_movies_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"cover_image_url": "string.png",
"casts": "Ramsey Noah Jr, Liz Benson",
"genre": "Drama",
"thriller": "thriller url"
"trailer": "thriller url"
}

update_data = {
Expand All @@ -24,7 +24,7 @@
"cover_image_url": "string.png",
"casts": "Zubi Michael, Kanayo O. Kanayo",
"genre": "Drama",
"thriller": "thriller url"
"trailer": "thriller url"
}

review_data = {
Expand Down
38 changes: 38 additions & 0 deletions app/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import boto3
from fastapi import UploadFile, HTTPException, status
from PIL import Image
import secrets

from app.config import settings

aws_bucket_name = settings.BUCKET_NAME
s3 = boto3.resource(
"s3",
aws_access_key_id=settings.AWSAccessKeyId,
aws_secret_access_key=settings.AWSSecretKey,
)


async def add_image(image: UploadFile):
token_name = secrets.token_hex(12)
file_name = f"{token_name}{image.filename}"
supported_ext = ['png', 'jpg', 'jpeg']
file_ext = file_name.split('.')[1]

if file_ext not in supported_ext:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail='Unsupported file type.')
bucket = s3.Bucket(aws_bucket_name)
bucket.upload_fileobj(image.file, file_name)

image_url = f"https://{aws_bucket_name}.s3.amazonaws.com/{file_name}"

return image_url


async def delete_image(image_name: str):
bucket = s3.Bucket(aws_bucket_name)

return bucket.delete_fileobj(bucket, image_name)



13 changes: 10 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
alembic==1.11.3
annotated-types==0.5.0
anyio==3.7.1
boto3==1.28.62
botocore==1.31.62
certifi==2023.7.22
charset-normalizer==3.2.0
click==8.1.7
Expand All @@ -12,22 +14,27 @@ httpcore==0.17.3
httpx==0.24.1
idna==3.4
iniconfig==2.0.0
jmespath==1.0.1
Mako==1.2.4
MarkupSafe==2.1.3
packaging==23.1
pluggy
pluggy==1.3.0
psycopg2==2.9.7
psycopg2-binary==2.9.7
pydantic==2.3.0
pydantic-settings==2.0.3
pydantic_core==2.6.3
pytest==7.4.0
python-dotenv
python-dateutil==2.8.2
python-dotenv==1.0.0
python-multipart==0.0.6
requests==2.31.0
s3transfer==0.7.0
six==1.16.0
sniffio==1.3.0
SQLAlchemy==2.0.20
starlette==0.27.0
tomli==2.0.1
typing_extensions==4.7.1
urllib3==2.0.4
uvicorn
uvicorn==0.23.2

0 comments on commit 894f686

Please sign in to comment.