Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 14 additions & 60 deletions backend/backlog_app/api/crud.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from uuid import UUID

from fastapi import HTTPException
from sqlalchemy import or_
Expand All @@ -8,37 +9,25 @@

from backlog_app.models import User
from backlog_app.models.movie import Movie
from backlog_app.schemas.movie import MovieCreate, MovieRead, MovieUpdate
from backlog_app.schemas.movie import MovieCreate, MovieList, MovieRead, MovieUpdate

logger = logging.getLogger(__name__)


async def create_movie(
db: AsyncSession, movie_in: MovieCreate, user: User
) -> MovieRead:
movie = Movie(**movie_in.model_dump(), user=user)
movie = Movie(**movie_in.model_dump(), user_id=user.id)
db.add(movie)
await db.commit()
await db.refresh(movie)

logger.info("Movie <%s> has been created.", movie.id)

return MovieRead(
id=movie.id,
title=movie.title,
description=movie.description,
year=movie.year,
rating=movie.rating,
kp_id=movie.kp_id,
imdb_id=movie.imdb_id,
watch_link=movie.watch_link,
watched=movie.watched,
created_at=movie.created_at,
user=user.email.split("@")[0],
)
return MovieRead.model_validate(movie)


async def get_movies(db: AsyncSession, user_id: str | None = None):
async def get_movies(db: AsyncSession, user_id: str | None = None) -> MovieList:
query = select(Movie).options(selectinload(Movie.user))

if user_id:
Expand All @@ -49,26 +38,13 @@ async def get_movies(db: AsyncSession, user_id: str | None = None):
result = await db.execute(query)
movies = result.scalars().all()

return [
MovieRead(
id=m.id,
title=m.title,
description=m.description,
year=m.year,
rating=m.rating,
imdb_id=m.imdb_id,
watch_link=m.watch_link,
watched=m.watched,
created_at=m.created_at,
kp_id=m.kp_id,
user=m.user.email.split("@")[0],
)
for m in movies
]
logger.debug("Size of movies list: %s", len(movies))

return MovieList.model_validate({"movies": movies})


async def get_movie_by_id(
db: AsyncSession, movie_id: int, user_id: int | None = None
db: AsyncSession, movie_id: int, user_id: UUID | None = None
) -> MovieRead | None:
query = select(Movie).options(selectinload(Movie.user)).where(Movie.id == movie_id)

Expand All @@ -83,19 +59,7 @@ async def get_movie_by_id(

logger.info("Movie has been found.")

return MovieRead(
id=movie.id,
title=movie.title,
description=movie.description,
year=movie.year,
rating=movie.rating,
imdb_id=movie.imdb_id,
watch_link=movie.watch_link,
watched=movie.watched,
created_at=movie.created_at,
kp_id=movie.kp_id,
user=movie.user.email.split("@")[0],
)
return MovieRead.model_validate(movie)


async def update_movie(
Expand All @@ -122,19 +86,7 @@ async def update_movie(

logger.info("Movie has been updated.")

return MovieRead(
id=movie.id,
title=movie.title,
description=movie.description,
year=movie.year,
rating=movie.rating,
kp_id=movie.kp_id,
imdb_id=movie.imdb_id,
watch_link=movie.watch_link,
watched=movie.watched,
user=movie.user.email.split("@")[0],
created_at=movie.created_at.isoformat(),
)
return MovieRead.model_validate(movie)


async def partial_update_movie(
Expand All @@ -157,7 +109,7 @@ async def delete_movie(
db: AsyncSession,
movie_id: int,
user: User,
) -> None:
) -> MovieRead:
result = await db.execute(select(Movie).where(Movie.id == movie_id))
movie = result.scalars().first()
if not movie:
Expand All @@ -170,6 +122,8 @@ async def delete_movie(

logger.info("Movie <%s> has been deleted.", movie_id)

return MovieRead.model_validate(movie)


def check_movie_ownership(movie: Movie, user: User) -> None:
"""Проверяет, может ли пользователь изменять фильм"""
Expand Down
4 changes: 2 additions & 2 deletions backend/backlog_app/api/view/movie_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
current_active_user,
)
from backlog_app.models.users import User
from backlog_app.schemas.movie import MovieCreate, MovieRead, MovieUpdate
from backlog_app.schemas.movie import MovieCreate, MovieList, MovieRead, MovieUpdate
from backlog_app.storages.database import get_async_session

router = APIRouter(prefix="/movies", tags=["Movies"])
Expand All @@ -23,7 +23,7 @@ async def add_movie(
return await crud.create_movie(db, movie_create, user=user)


@router.get("/", response_model=List[MovieRead])
@router.get("/", response_model=MovieList)
async def get_movie_list(
db: Annotated[AsyncSession, Depends(get_async_session)],
user: Annotated[User, Depends(current_active_user)],
Expand Down
2 changes: 1 addition & 1 deletion backend/backlog_app/models/movie.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,4 @@ class Movie(Base):
user_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("user.id"), nullable=False
)
user: Mapped["User"] = relationship("User", back_populates="movies")
user: Mapped["User"] = relationship("User", back_populates="movies", lazy="joined")
3 changes: 3 additions & 0 deletions backend/backlog_app/schemas/helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def to_camel_case(snake_str: str) -> str:
parts = snake_str.split("_")
return parts[0] + "".join(word.capitalize() for word in parts[1:])
18 changes: 13 additions & 5 deletions backend/backlog_app/schemas/movie.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
from typing import Annotated

from annotated_types import Len
from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field

from .helper import to_camel_case
from .user import UserRead


Expand All @@ -15,15 +16,19 @@ class MovieBase(BaseModel):
watch_link: str | None = None
kp_id: int | None = None
imdb_id: int | None = None
published: bool = False

model_config = {"from_attributes": True}
model_config = ConfigDict(
from_attributes=True,
alias_generator=to_camel_case,
validate_by_name=True,
)


class MovieCreate(MovieBase):
description: Annotated[str, Len(min_length=20, max_length=1000)] | None = None
year: int | None = None
rating: float | None = Field(default=None, ge=1.0, le=10.0)
published: bool = False


class MovieUpdate(MovieBase):
Expand All @@ -32,14 +37,17 @@ class MovieUpdate(MovieBase):
year: int | None = None
watched: bool | None = None
rating: float | None = Field(default=None, ge=1.0, le=10.0)
published: bool = False


class MovieRead(MovieBase):
id: int
user: str
user: UserRead
description: Annotated[str, Len(min_length=20, max_length=1000)] | None
year: int | None
watched: bool
rating: float | None
created_at: datetime


class MovieList(BaseModel):
movies: list[MovieRead]
17 changes: 14 additions & 3 deletions backend/backlog_app/schemas/user.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import uuid
from typing import Annotated
from uuid import UUID

from annotated_types import Len
from fastapi_users import schemas
from pydantic import BaseModel, ConfigDict, EmailStr, computed_field

from .helper import to_camel_case

class UserRead(schemas.BaseUser[uuid.UUID]):
pass

class UserRead(BaseModel):
id: UUID
email: EmailStr

@computed_field
@property
def username(self) -> str:
return self.email.split("@")[0]

model_config = ConfigDict(from_attributes=True, alias_generator=to_camel_case)


class UserCreate(schemas.BaseUserCreate):
Expand Down
2 changes: 2 additions & 0 deletions backend/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ async def init_db():
await connection.run_sync(Base.metadata.create_all)
yield

await engine_test.dispose()

if os.path.exists(DB_PATH):
os.remove(DB_PATH)

Expand Down
14 changes: 10 additions & 4 deletions backend/tests/test_schemas/test_movie.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from datetime import datetime
from uuid import uuid4

import pytest
from pydantic import ValidationError

from backlog_app.schemas.movie import MovieCreate, MovieRead, MovieUpdate
from backlog_app.schemas.user import UserRead


def test_movie_can_be_create_from_create_schema() -> None:
Expand All @@ -13,12 +15,13 @@ def test_movie_can_be_create_from_create_schema() -> None:
watch_link="https://example.com",
rating=6.7,
)
user = UserRead(id=uuid4(), email="test@example.com")
movie = MovieRead(
**movie_in.model_dump(),
id=0,
watched=False,
created_at=datetime.now(),
user="test",
user=user,
)

assert movie_in.title == movie.title
Expand Down Expand Up @@ -50,22 +53,25 @@ def test_movie_create_max_value(
title=title,
description=description,
)
user = UserRead(id=uuid4(), email="test@example.com")
movie = MovieRead(
**movie_in.model_dump(),
id=0,
watched=False,
created_at=datetime.now(),
user="test",
user=user,
)

assert movie.title == title
assert movie.description == description


def test_movie_update_from_update_schema() -> None:
user = UserRead(id=uuid4(), email="test@example.com")

movie = MovieRead(
id=0,
user="test",
user=user,
title="Test Movie",
description="Test Movie Description",
watch_link="https://example.com",
Expand All @@ -81,7 +87,7 @@ def test_movie_update_from_update_schema() -> None:
description="Test Movie Update Description",
watch_link="https://abc.example.com",
)
for field, value in movie_update:
for field, value in movie_update.model_dump(exclude_unset=True).items():
setattr(movie, field, value)

assert movie_update.title == movie.title
Expand Down
7 changes: 1 addition & 6 deletions backend/tests/test_schemas/test_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,8 @@ def test_user_read():
data = {
"id": user_id,
"email": "read@example.com",
"is_active": True,
"is_superuser": False,
"is_verified": True,
}
user = UserRead(**data)
assert user.id == user_id
assert user.email == data["email"]
assert user.is_active is True
assert user.is_superuser is False
assert user.is_verified is True
assert user.username == "read"
Loading
Loading