-
Notifications
You must be signed in to change notification settings - Fork 59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Develop #4
base: main
Are you sure you want to change the base?
Develop #4
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,192 @@ | ||
from fastapi import APIRouter, Depends, HTTPException, Query | ||
import math | ||
from typing import Union | ||
import logging | ||
from fastapi import ( | ||
APIRouter, | ||
Depends, | ||
HTTPException, | ||
Query, | ||
status | ||
) | ||
from sqlalchemy.exc import IntegrityError | ||
from sqlalchemy.orm import Session, joinedload | ||
from sqlalchemy.orm import Session | ||
|
||
from database import get_db | ||
from database.models import MovieModel, CountryModel, GenreModel, ActorModel, LanguageModel | ||
from database.models import ( | ||
MovieModel, | ||
CountryModel, | ||
GenreModel, | ||
ActorModel, | ||
LanguageModel | ||
) | ||
from schemas.movies import ( | ||
MovieListSchema, | ||
MovieCreateSchema, | ||
MovieUpdateSchema, | ||
MovieDetailSchema, | ||
) | ||
|
||
|
||
router = APIRouter() | ||
|
||
|
||
# Write your code here | ||
def handle_movie_dependencies( | ||
elements: list[str], | ||
model: Union[GenreModel, ActorModel, LanguageModel], | ||
db: Session = Depends(get_db) | ||
): | ||
|
||
not_created_elements = [] | ||
created_elements = [] | ||
for element_name in elements: | ||
element = db.query(model).filter(model.name == element_name).first() | ||
if not element: | ||
element = model( | ||
name=element_name | ||
) | ||
not_created_elements.append(element) | ||
else: | ||
created_elements.append(element) | ||
|
||
return not_created_elements, created_elements | ||
|
||
|
||
@router.get("/movies/") | ||
def get_movies( | ||
page: int = Query(ge=1, default=1), | ||
per_page: int = Query(ge=1, le=20, default=10), | ||
db: Session = Depends(get_db) | ||
): | ||
total_items = db.query(MovieModel).count() | ||
total_pages = math.ceil(total_items / per_page) | ||
|
||
first_element = (page - 1) * per_page | ||
|
||
movies = db.query(MovieModel).order_by(MovieModel.id.desc()).offset(first_element).limit(per_page).all() | ||
|
||
if not movies: | ||
raise HTTPException(status_code=404, detail="No movies found.") | ||
|
||
prev_page = f"/theater/movies/?page={page - 1}&per_page={per_page}" | ||
next_page = f"/theater/movies/?page={page + 1}&per_page={per_page}" | ||
|
||
Comment on lines
+70
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The pagination URLs should be updated to |
||
return { | ||
"movies": [MovieListSchema.model_validate(movie) for movie in movies], | ||
"prev_page": prev_page if page > 1 else None, | ||
"next_page": next_page if page < total_pages else None, | ||
"total_pages": total_pages, | ||
"total_items": total_items | ||
} | ||
|
||
|
||
@router.post("/movies/", response_model=MovieDetailSchema, status_code=status.HTTP_201_CREATED) | ||
def create_movie(movie: MovieCreateSchema, db: Session = Depends(get_db)): | ||
|
||
try: | ||
with db.begin(): | ||
exciting_movie = db.query(MovieModel).filter( | ||
MovieModel.name == movie.name, | ||
MovieModel.date == movie.date | ||
).first() | ||
if exciting_movie: | ||
raise HTTPException( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a typo here: |
||
status_code=status.HTTP_409_CONFLICT, | ||
detail=f"A movie with the name '{movie.name}' and " | ||
f"release date '{movie.date}' already exists." | ||
) | ||
|
||
country = db.query(CountryModel).filter( | ||
CountryModel.code == movie.country | ||
).first() | ||
if not country: | ||
country = CountryModel(code=movie.country, name=None) | ||
db.add(country) | ||
db.flush() | ||
|
||
not_created_genres, created_genres = handle_movie_dependencies(movie.genres, GenreModel, db) | ||
db.add_all(not_created_genres) | ||
all_genres = not_created_genres + created_genres | ||
db.flush() | ||
|
||
not_created_actors, created_actors = handle_movie_dependencies(movie.actors, ActorModel, db) | ||
db.add_all(not_created_actors) | ||
all_actors = not_created_actors + created_actors | ||
db.flush() | ||
|
||
not_created_languages, created_languages = handle_movie_dependencies(movie.languages, LanguageModel, db) | ||
db.add_all(not_created_languages) | ||
all_languages = not_created_languages + created_languages | ||
db.flush() | ||
|
||
new_movie = MovieModel( | ||
name=movie.name, | ||
date=movie.date, | ||
score=movie.score, | ||
overview=movie.overview, | ||
status=movie.status, | ||
budget=movie.budget, | ||
revenue=movie.revenue, | ||
country=country, | ||
) | ||
db.add(new_movie) | ||
db.flush() | ||
|
||
new_movie.genres.extend(all_genres) | ||
new_movie.actors.extend(all_actors) | ||
new_movie.languages.extend(all_languages) | ||
|
||
return new_movie | ||
|
||
except IntegrityError as exc: | ||
db.rollback() | ||
raise HTTPException( | ||
status_code=status.HTTP_400_BAD_REQUEST, | ||
detail=str(exc) | ||
) | ||
|
||
|
||
@router.get("/movies/{movie_id}/", response_model=MovieDetailSchema) | ||
def get_movie_by_id(movie_id: int, db: Session = Depends(get_db)): | ||
movie = db.query(MovieModel).filter(MovieModel.id == movie_id).first() | ||
if not movie: | ||
raise HTTPException( | ||
status_code=404, | ||
detail="Movie with the given ID was not found." | ||
) | ||
return movie | ||
|
||
|
||
@router.delete("/movies/{movie_id}/", status_code=status.HTTP_204_NO_CONTENT) | ||
def delete_movie(movie_id: int, db: Session = Depends(get_db)): | ||
movie = db.query(MovieModel).filter(MovieModel.id == movie_id).first() | ||
if not movie: | ||
raise HTTPException( | ||
status_code=404, | ||
detail="Movie with the given ID was not found." | ||
) | ||
db.delete(movie) | ||
db.commit() | ||
return None | ||
|
||
|
||
@router.patch("/movies/{movie_id}/", status_code=status.HTTP_200_OK) | ||
def update_movie(movie_id: int, movie_data: MovieUpdateSchema, db: Session = Depends(get_db)): | ||
movie = db.query(MovieModel).filter(MovieModel.id == movie_id).first() | ||
logging.info(movie_data) | ||
if not movie: | ||
raise HTTPException( | ||
status_code=404, | ||
detail="Movie with the given ID was not found." | ||
) | ||
|
||
movie.name = movie_data.name if movie_data.name else movie.name | ||
movie.date = movie_data.date if movie_data.date else movie.date | ||
movie.score = movie_data.score if movie_data.score else movie.score | ||
movie.overview = movie_data.overview if movie_data.overview else movie.overview | ||
movie.status = movie_data.status if movie_data.status else movie.status | ||
movie.budget = movie_data.budget if movie_data.budget else movie.budget | ||
movie.revenue = movie_data.revenue if movie_data.revenue else movie.revenue | ||
|
||
db.commit() | ||
|
||
return {"detail": "Movie updated successfully."} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,100 @@ | ||
# Write your code here | ||
from datetime import date, timedelta | ||
from enum import Enum | ||
from typing import Optional | ||
|
||
from pydantic import ( | ||
BaseModel, | ||
Field, | ||
field_validator, | ||
ValidationError | ||
) | ||
from pydantic_extra_types.country import CountryAlpha3, CountryAlpha2 | ||
|
||
|
||
class CountrySchema(BaseModel): | ||
id: int | ||
code: CountryAlpha3 | CountryAlpha2 | ||
name: str | None | ||
|
||
|
||
class GenreSchema(BaseModel): | ||
id: int | ||
name: str | ||
|
||
|
||
class ActorSchema(BaseModel): | ||
id: int | ||
name: str | ||
|
||
|
||
class LanguageSchema(BaseModel): | ||
id: int | ||
name: str | ||
|
||
|
||
class StatusEnum(str, Enum): | ||
Released = "Released" | ||
PostProduction = "Post Production" | ||
InProduction = "In Production" | ||
|
||
|
||
class MovieCreateSchema(BaseModel): | ||
name: str = Field(max_length=255) | ||
date: date | ||
score: float = Field(ge=0, le=100) | ||
overview: str | ||
status: StatusEnum | ||
budget: float = Field(gt=0) | ||
revenue: float = Field(gt=0) | ||
country: CountryAlpha3 | CountryAlpha2 | ||
genres: list[str] | ||
actors: list[str] | ||
languages: list[str] | ||
|
||
@field_validator("date") | ||
@classmethod | ||
def check_date_on_the_future(cls, date: date): | ||
if date > date.today() + timedelta(days=365): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The parameter |
||
raise ValidationError( | ||
"The date must not be more than one year in the future." | ||
) | ||
return date | ||
|
||
|
||
class MovieDetailSchema(BaseModel): | ||
id: int | ||
name: str | ||
date: date | ||
score: float = Field(ge=0, le=100) | ||
overview: str | ||
status: StatusEnum | ||
budget: float = Field(gt=0) | ||
revenue: float = Field(gt=0) | ||
country: CountrySchema | ||
genres: list[GenreSchema] | ||
actors: list[ActorSchema] | ||
languages: list[LanguageSchema] | ||
|
||
class Config: | ||
from_attributes = True | ||
|
||
|
||
class MovieUpdateSchema(BaseModel): | ||
name: Optional[str] = None | ||
date: Optional[date] = None | ||
score: Optional[float] = Field(None, ge=0, le=100) | ||
overview: Optional[str] = None | ||
status: Optional[StatusEnum] = None | ||
budget: Optional[float] = Field(None, gt=0) | ||
revenue: Optional[float] = Field(None, gt=0) | ||
|
||
|
||
class MovieListSchema(BaseModel): | ||
id: int | ||
name: str | ||
date: date | ||
score: float | ||
overview: str | ||
|
||
class Config: | ||
from_attributes = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pagination URLs should match the route defined for movies. Change
/theater/movies/
to/movies/
to ensure consistency and correctness.