-
Notifications
You must be signed in to change notification settings - Fork 58
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
Solution #30
base: main
Are you sure you want to change the base?
Solution #30
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
from src.database.models import CountryModel | ||
|
||
|
||
def get_movies_on_page(page, per_page, model, db): | ||
return ( | ||
db.query(model) | ||
.order_by(model.id.desc()) | ||
.offset((page - 1) * per_page) | ||
.limit(per_page) | ||
.all() | ||
) | ||
|
||
|
||
def create_new_movie(movie, db): | ||
db.add(movie) | ||
db.commit() | ||
db.refresh(movie) | ||
return movie | ||
|
||
|
||
def get_movie_by_id(movie_id, model, db): | ||
return db.query(model).filter(model.id == movie_id).first() | ||
|
||
|
||
def get_movie_by_name_and_date(name, date, model, db): | ||
return db.query(model).filter(model.name == name).filter(model.date == date).first() | ||
|
||
|
||
def get_instance_by_name(name, model, db): | ||
return db.query(model).filter(model.name == name).first() | ||
|
||
|
||
def create_country_by_code(code, model, db): | ||
country = model(code=code) | ||
db.add(country) | ||
db.commit() | ||
db.refresh(country) | ||
return country | ||
|
||
|
||
def create_instance_by_name(name, model, db): | ||
instance = model(name=name) | ||
db.add(instance) | ||
db.commit() | ||
db.refresh(instance) | ||
return instance | ||
|
||
|
||
def check_or_create_many_instances_by_name(names, model, db): | ||
list_of_instances = [] | ||
for name in names: | ||
instance = get_instance_by_name(name=name, model=model, db=db) | ||
if not instance: | ||
instance = create_instance_by_name(name=name, model=model, db=db) | ||
list_of_instances.append(instance) | ||
return list_of_instances |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,158 @@ | ||
from fastapi import APIRouter, Depends, HTTPException, Query | ||
from sqlalchemy.exc import IntegrityError | ||
from sqlalchemy.orm import Session, joinedload | ||
import datetime | ||
|
||
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 MovieListResponse, MovieDetail, MovieCreate, MovieUpdate | ||
|
||
from crud import ( | ||
create_country_by_code, | ||
create_instance_by_name, | ||
check_or_create_many_instances_by_name, | ||
get_movies_on_page, | ||
get_movie_by_name_and_date, | ||
get_movie_by_id, | ||
) | ||
|
||
from src.crud import create_new_movie | ||
|
||
router = APIRouter() | ||
|
||
|
||
# Write your code here | ||
@router.get("/movies/", response_model=MovieListResponse) | ||
def get_movies( | ||
db: Session = Depends(get_db), | ||
page: int = Query(1, ge=1), | ||
per_page: int = Query(10, ge=1, le=20), | ||
) -> MovieListResponse: | ||
movies = get_movies_on_page(page, per_page, MovieModel, db) | ||
|
||
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}" | ||
total_items = db.query(MovieModel).count() | ||
total_pages = (total_items // per_page) + (1 if total_items % per_page > 0 else 0) | ||
|
||
if total_items == 0: | ||
raise HTTPException(status_code=404, detail="No movies found") | ||
|
||
return { | ||
"movies": movies, | ||
"prev_page": prev_page if page > 1 else None, | ||
"next_page": next_page if total_pages > page else None, | ||
"total_items": total_items, | ||
"total_pages": total_pages, | ||
} | ||
|
||
|
||
@router.post("/movies/", response_model=MovieDetail, status_code=201) | ||
def create_movie(movie: MovieCreate, db: Session = Depends(get_db)) -> MovieDetail: | ||
db_movie = get_movie_by_name_and_date( | ||
name=movie.name, date=movie.date, model=MovieModel, db=db | ||
) | ||
|
||
if db_movie: | ||
raise HTTPException( | ||
status_code=409, | ||
detail=f"A movie with the name '{db_movie.name}' and release date '{db_movie.date}' already exists.", | ||
) | ||
|
||
if movie.date > datetime.datetime.now().date() + datetime.timedelta(days=365): | ||
raise HTTPException( | ||
status_code=400, | ||
detail="Invalid input data.", | ||
) | ||
Comment on lines
+71
to
+75
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. Ensure that the |
||
|
||
country = db.query(CountryModel).filter(CountryModel.code == movie.country).first() | ||
genres = [] | ||
actors = [] | ||
languages = [] | ||
|
||
if movie.languages: | ||
languages.extend( | ||
check_or_create_many_instances_by_name(movie.languages, LanguageModel, db) | ||
) | ||
|
||
if movie.actors: | ||
actors.extend( | ||
check_or_create_many_instances_by_name(movie.actors, ActorModel, db) | ||
) | ||
|
||
if movie.genres: | ||
genres.extend( | ||
check_or_create_many_instances_by_name(movie.genres, GenreModel, db) | ||
) | ||
|
||
if not country: | ||
country = create_country_by_code(code=movie.country, model=CountryModel, db=db) | ||
|
||
try: | ||
new_movie = MovieModel( | ||
**movie.model_dump(exclude={"country", "genres", "actors", "languages"}), | ||
country=country, | ||
genres=genres, | ||
actors=actors, | ||
languages=languages, | ||
) | ||
return create_new_movie(movie=new_movie, db=db) | ||
|
||
except IntegrityError: | ||
raise HTTPException(status_code=400, detail="Invalid input data.") | ||
|
||
|
||
@router.get("/movies/{movie_id}/", response_model=MovieDetail) | ||
def get_movie(movie_id: int, db: Session = Depends(get_db)) -> MovieDetail: | ||
movie = get_movie_by_id(movie_id, MovieModel, db) | ||
|
||
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=204) | ||
def delete_movie(movie_id: int, db: Session = Depends(get_db)) -> None: | ||
movie = get_movie_by_id(movie_id, MovieModel, db) | ||
|
||
if not movie: | ||
raise HTTPException( | ||
status_code=404, detail="Movie with the given ID was not found." | ||
) | ||
db.delete(movie) | ||
db.commit() | ||
|
||
|
||
@router.patch("/movies/{movie_id}/", status_code=200) | ||
def edit_movie( | ||
movie_id: int, movie_data: MovieUpdate, db: Session = Depends(get_db) | ||
) -> dict[str, str]: | ||
movie = get_movie_by_id(movie_id, MovieModel, db) | ||
|
||
if not movie: | ||
raise HTTPException( | ||
status_code=404, detail="Movie with the given ID was not found." | ||
) | ||
|
||
try: | ||
movie_date = movie_data.model_dump(exclude_unset=True) | ||
for key, value in movie_date.items(): | ||
if value: | ||
setattr(movie, key, value) | ||
db.commit() | ||
db.refresh(movie) | ||
except IntegrityError: | ||
raise HTTPException(status_code=400, detail="Invalid input data.") | ||
Comment on lines
+149
to
+156
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. In the |
||
|
||
return {"detail": "Movie updated successfully."} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,124 @@ | ||
# Write your code here | ||
from pydantic import BaseModel, Field | ||
import datetime | ||
from typing import Optional | ||
from enum import Enum | ||
|
||
|
||
class MovieStatus(str, Enum): | ||
RELEASED: str = "Released" | ||
POST_PRODUCTION: str = "Post Production" | ||
IN_PRODUCTION: str = "In Production" | ||
|
||
|
||
class Movie(BaseModel): | ||
name: str | ||
date: datetime.date | ||
score: float | ||
overview: str | ||
status: MovieStatus | ||
budget: float | ||
revenue: float | ||
|
||
|
||
class Genre(BaseModel): | ||
name: str | ||
movies: list[Movie] | ||
|
||
model_config = {"from_attributes": True} | ||
|
||
|
||
class GenreDetail(BaseModel): | ||
id: int | ||
name: str | ||
|
||
|
||
class Actor(BaseModel): | ||
name: str | ||
movies: list[Movie] | ||
|
||
model_config = {"from_attributes": True} | ||
|
||
|
||
class ActorDetail(BaseModel): | ||
id: int | ||
name: str | ||
|
||
|
||
class Language(BaseModel): | ||
name: str | ||
movies: list[Movie] | ||
|
||
model_config = {"from_attributes": True} | ||
|
||
|
||
class LanguageDetail(BaseModel): | ||
id: int | ||
name: str | ||
|
||
|
||
class Country(BaseModel): | ||
code: str | ||
name: Optional[str] | ||
movies: list[Movie] | ||
|
||
model_config = {"from_attributes": True} | ||
|
||
|
||
class CountryDetail(BaseModel): | ||
id: int | ||
code: str | ||
name: Optional[str] | ||
|
||
|
||
class MovieCreate(BaseModel): | ||
name: str = Field(max_length=255) | ||
date: ( | ||
datetime.date | ||
) | ||
score: float = Field(ge=0, le=100) | ||
overview: str | ||
status: MovieStatus | ||
budget: float = Field(gt=0) | ||
revenue: float = Field(gt=0) | ||
country: str | ||
Comment on lines
+81
to
+82
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. Consider allowing the |
||
genres: list[str] | ||
actors: list[str] | ||
languages: list[str] | ||
|
||
model_config = {"from_attributes": True} | ||
|
||
|
||
class MovieDetail(Movie): | ||
id: int | ||
country: Optional[CountryDetail] = None | ||
genres: Optional[list[GenreDetail]] = None | ||
actors: Optional[list[ActorDetail]] = None | ||
languages: Optional[list[LanguageDetail]] = None | ||
|
||
|
||
class MovieList(BaseModel): | ||
id: int | ||
name: str | ||
date: datetime.date | ||
score: float | ||
overview: str | ||
|
||
|
||
class MovieListResponse(BaseModel): | ||
movies: list[MovieList] | ||
prev_page: str | None | ||
next_page: str | None | ||
total_pages: int | ||
total_items: int | ||
|
||
|
||
class MovieUpdate(Movie): | ||
name: Optional[str] = Field(max_length=255, default=None) | ||
date: Optional[datetime.date] = Field( | ||
lt=datetime.date.today() + datetime.timedelta(days=365), default=None | ||
) | ||
overview: Optional[str] = None | ||
status: Optional[MovieStatus] = None | ||
score: Optional[float] = Field(ge=0, le=100, default=None) | ||
budget: Optional[float] = Field(gt=0, default=None) | ||
revenue: Optional[float] = Field(gt=0, default=None) |
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.
Consider adding a check to see if a country with the given code already exists in the database before creating a new one. This can prevent duplicate entries and ensure data integrity.