diff --git a/app/chat/router.py b/app/chat/router.py index ac3fd80..a316feb 100644 --- a/app/chat/router.py +++ b/app/chat/router.py @@ -206,7 +206,8 @@ async def unpinn_chat(chat_id: int, user: SUser = Depends(check_verificated_user ) async def get_pinned_chats(user: SUser = Depends(check_verificated_user_with_exc), uow=Depends(UnitOfWork)): async with uow: - return {"pinned_chats": await uow.chat.get_pinned_chats(user_id=user.id)} + pinned_chats = await uow.chat.get_pinned_chats(user_id=user.id) + return pinned_chats @router.get( diff --git a/app/chat/shemas.py b/app/chat/shemas.py index 46dd8a5..eea6624 100644 --- a/app/chat/shemas.py +++ b/app/chat/shemas.py @@ -42,7 +42,7 @@ class SAllowedChats(BaseModel): class SPinnedChats(BaseModel): - pinned_chats: list[SChat] + pinned_chats: list[SChat] | None class SPinnedMessages(BaseModel): diff --git a/app/dao/chat.py b/app/dao/chat.py index 4173192..3d2edcb 100644 --- a/app/dao/chat.py +++ b/app/dao/chat.py @@ -3,17 +3,17 @@ from sqlalchemy.exc import IntegrityError, NoResultFound from sqlalchemy.orm import aliased from app.dao.base import BaseDAO -from app.database import engine # noqa +from app.database import engine from app.chat.exceptions import ( UserAlreadyInChatException, UserAlreadyPinnedChatException, MessageNotFoundException, MessageAlreadyPinnedException, ) -from app.chat.shemas import SMessage, SMessageList, SPinnedMessages +from app.chat.shemas import SMessage, SMessageList, SPinnedMessages, SPinnedChats from app.models.users import Users from app.models.message_answer import MessageAnswer -from app.models.chat import Chats +from app.models.chat import Chat from app.models.message import Message from app.models.pinned_chat import PinnedChat from app.models.pinned_message import PinnedMessage @@ -21,16 +21,20 @@ from app.models.user_chat import UserChat class ChatDAO(BaseDAO): - model = Chats + model = Chat + + @staticmethod + def check_query_compile(query): + print(query.compile(engine, compile_kwargs={"literal_binds": True})) # Проверка SQL запроса async def find_one_or_none(self, **filter_by): - query = select(Chats.__table__.columns).filter_by(**filter_by) + query = select(Chat.__table__.columns).filter_by(**filter_by) result = await self.session.execute(query) result = result.mappings().one_or_none() return result async def create_chat(self, user_id: int, chat_name: str, created_by: int) -> int: - stmt = insert(Chats).values(chat_for=user_id, chat_name=chat_name, created_by=created_by).returning(Chats.id) + stmt = insert(Chat).values(chat_for=user_id, chat_name=chat_name, created_by=created_by).returning(Chat.id) result = await self.session.execute(stmt) chat_id = result.scalar() @@ -115,7 +119,7 @@ class ChatDAO(BaseDAO): select(msg.message) .select_from(MessageAnswer) .join(msg, msg.id == MessageAnswer.answer_id, isouter=True) - .where(MessageAnswer.self_id == Message.id, msg.visibility == True) # noqa: E712 + .where(MessageAnswer.self_id == Message.id, msg.visibility == True) # noqa: E712 .scalar_subquery() ).label("answer_message"), ) @@ -167,7 +171,7 @@ class ChatDAO(BaseDAO): await self.session.execute(stmt) async def delete_chat(self, chat_id: int) -> None: - stmt = update(Chats).where(Chats.id == chat_id).values(visibility=False) + stmt = update(Chat).where(Chat.id == chat_id).values(visibility=False) await self.session.execute(stmt) async def delete_user_from_chat(self, chat_id: int, user_id: int) -> None: @@ -185,11 +189,34 @@ class ChatDAO(BaseDAO): stmt = delete(PinnedChat).where(PinnedChat.chat_id == chat_id, PinnedChat.user_id == user_id) await self.session.execute(stmt) - async def get_pinned_chats(self, user_id: int): + async def get_pinned_chats(self, user_id: int) -> SPinnedChats: + + pinned_chats = ( + select( + func.json_build_object( + "pinned_chats", func.json_agg( + select( + func.json_build_object( + "chat_id", Chat.id, + "chat_for", Chat.chat_for, + "chat_name", Chat.chat_name, + "avatar_image", Users.avatar_image, + ) + ) + .scalar_subquery() + ) + ) + ) + .select_from(PinnedChat) + .join(Users, PinnedChat.user_id == Users.id) + .join(Chat, PinnedChat.chat_id == Chat.id) + .where(PinnedChat.user_id == user_id, Chat.visibility == True) # noqa: E712 + ) + chats_with_descriptions = ( - select(UserChat.__table__.columns, Chats.__table__.columns) + select(UserChat.__table__.columns, Chat.__table__.columns) .select_from(UserChat) - .join(Chats, UserChat.chat_id == Chats.id) + .join(Chat, UserChat.chat_id == Chat.id) .cte("chats_with_descriptions") ) @@ -219,10 +246,10 @@ class ChatDAO(BaseDAO): .join(chats_with_avatars, PinnedChat.chat_id == chats_with_avatars.c.chat_id) # noqa .where(chats_with_avatars.c.id == user_id, chats_with_avatars.c.visibility == True) # noqa: E712 ) - # print(query.compile(engine, compile_kwargs={"literal_binds": True})) # Проверка SQL запроса + result = await self.session.execute(query) result = result.mappings().all() - return result + return SPinnedChats.model_validate(result) async def pin_message(self, chat_id: int, message_id: int, user_id: int) -> None: try: @@ -293,5 +320,5 @@ class ChatDAO(BaseDAO): result = await self.session.execute(query) result = result.scalar_one() - - return SPinnedMessages.model_validate(result) + pinned_messages = SPinnedMessages.model_validate(result) + return pinned_messages diff --git a/app/dao/user.py b/app/dao/user.py index 713b62f..4afb19a 100644 --- a/app/dao/user.py +++ b/app/dao/user.py @@ -6,7 +6,7 @@ from app.dao.base import BaseDAO from app.database import engine # noqa from app.exceptions import IncorrectDataException from app.users.exceptions import UserAlreadyExistsException -from app.models.chat import Chats +from app.models.chat import Chat from app.models.user_avatar import UserAvatar from app.models.users import Users from app.models.user_chat import UserChat @@ -68,9 +68,9 @@ class UserDAO(BaseDAO): WHERE user_id = 1 """ chats_with_descriptions = ( - select(UserChat.__table__.columns, Chats.__table__.columns) + select(UserChat.__table__.columns, Chat.__table__.columns) .select_from(UserChat) - .join(Chats, UserChat.chat_id == Chats.id) + .join(Chat, UserChat.chat_id == Chat.id) ).cte("chats_with_descriptions") chats_with_avatars = ( diff --git a/app/images/router.py b/app/images/router.py index ce290e8..628f572 100644 --- a/app/images/router.py +++ b/app/images/router.py @@ -27,23 +27,14 @@ async def upload_file_returning_str(file: UploadFile, sub_str: str) -> str: return image_url -async def upload_avatar_returning_dict(file: UploadFile, sub_str: str) -> dict: - remote_server_url = settings.IMAGE_UPLOAD_SERVER + sub_str - response = await upload_file(file, remote_server_url) - image_url = response.json()["image_url"] - hex_color = response.json()["hex_color"] - image_url = settings.IMAGE_UPLOAD_SERVER + image_url - return {"image_url": image_url, "hex_color": hex_color} - - @router.post( "/upload_avatar", status_code=status.HTTP_200_OK, response_model=SImageUrl, ) async def upload_avatar(file: UploadFile): - image_data = await upload_avatar_returning_dict(file, "upload_avatar") - return image_data + image_url = await upload_file_returning_str(file, "upload_avatar") + return {"image_url": image_url} @router.post( diff --git a/app/migrations/env.py b/app/migrations/env.py index 612790a..69dfdb1 100644 --- a/app/migrations/env.py +++ b/app/migrations/env.py @@ -7,7 +7,7 @@ from alembic import context from app.database import DATABASE_URL, Base from app.models.users import Users # noqa from app.models.message_answer import MessageAnswer # noqa -from app.models.chat import Chats # noqa +from app.models.chat import Chat # noqa from app.models.message import Message # noqa from app.models.pinned_chat import PinnedChat # noqa from app.models.pinned_message import PinnedMessage # noqa diff --git a/app/models/chat.py b/app/models/chat.py index 61573f2..1e4d5d4 100644 --- a/app/models/chat.py +++ b/app/models/chat.py @@ -4,7 +4,7 @@ from sqlalchemy.orm import mapped_column, Mapped from app.database import Base -class Chats(Base): +class Chat(Base): __tablename__ = "chat" id: Mapped[int] = mapped_column(primary_key=True) diff --git a/tests/integration_tests/conftest.py b/tests/integration_tests/conftest.py index 33ff549..90d17c0 100644 --- a/tests/integration_tests/conftest.py +++ b/tests/integration_tests/conftest.py @@ -8,7 +8,7 @@ from httpx import AsyncClient from app.config import settings from app.database import Base, async_session_maker, engine from app.models.users import Users -from app.models.chat import Chats +from app.models.chat import Chat from app.models.message import Message from app.models.user_chat import UserChat from app.main import app as fastapi_app @@ -39,7 +39,7 @@ async def prepare_database(): async with async_session_maker() as session: add_users = insert(Users).values(new_users) - add_chats = insert(Chats).values(chats) + add_chats = insert(Chat).values(chats) add_users_x_chats = insert(UserChat).values(users_x_chats) add_messages = insert(Message).values(messages) set_verified_user = update(Users).values(role=1).where(Users.id == 3)