Skip to content
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

ref(chat): chat apis and models refactored and optimized. #9

Merged
merged 1 commit into from
Aug 4, 2024
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
59 changes: 42 additions & 17 deletions chatApp/models/message.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from collections.abc import Mapping
from datetime import datetime
from typing import Any

from pydantic import BaseModel, Field

from chatApp.config.database import get_messages_collection
from chatApp.config.database import (
get_messages_collection,
get_private_rooms_collection,
)
from chatApp.utils.object_id import PydanticObjectId

from .public_room import fetch_public_room_by_id
Expand All @@ -29,27 +30,51 @@ class MessageInDB(Message):
# return result.inserted_id


async def get_public_messages(
room_id: str,
) -> tuple[bool, list[Mapping[str, Any]]]:
async def get_public_messages(room_id: str) -> list[MessageInDB]:
"""
Fetch public messages from a specific room.

:param room_id: The ID of the public room to fetch messages from.
:return: A tuple where the first element is a boolean indicating success,
and the second element is a list of messages (each message represented as a dictionary).
"""
messages_collection = get_messages_collection()

# Fetch the public room by ID
room = await fetch_public_room_by_id(room_id)
if room is None:
return False, []
return []

room_id_obj = PydanticObjectId(room_id)
query = {"room_id": room_id_obj, "room_type": "public"}

# Fetch the documents using the query
cursor = messages_collection.find(query)

# Convert the cursor to a list and await the result
messages = await cursor.to_list(length=None)

# Convert each document to MessageInDB
return [MessageInDB(**message) for message in messages]


async def get_private_messages(
room_id: str,
) -> list[MessageInDB]:
"""
Fetch private messages from a specific room between two users.
"""
# Fetch the private room by ID
room_collection = get_private_rooms_collection()
room_id_obj = PydanticObjectId(room_id)
room = await room_collection.find_one({"_id": room_id_obj})
if room is None:
return []

messages_collection = get_messages_collection()
query = {"room_id": room_id_obj, "room_type": "private"}

# Fetch the documents using the query
cursor = messages_collection.find(query)

# Fetch messages from the messages collection
cursor = messages_collection.find({"room_id": room_id})
messages = await cursor.to_list(
length=None
) # Await the cursor to list conversion
# Convert the cursor to a list and await the result
messages = await cursor.to_list(length=None)

return True, messages
# Convert each document to MessageInDB
return [MessageInDB(**message) for message in messages]
76 changes: 61 additions & 15 deletions chatApp/models/private_room.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from collections.abc import Mapping
from datetime import datetime
from typing import Any

from pydantic import BaseModel, Field

from chatApp.config.database import get_private_rooms_collection
from chatApp.models.message import MessageInDB, get_private_messages
from chatApp.utils.object_id import PydanticObjectId


Expand All @@ -16,14 +19,17 @@ class PrivateRoomInDB(PrivateRoom):
id: PydanticObjectId = Field(alias="_id")


async def fetch_private_room_by_id(id: str):
async def fetch_private_room_by_id(id: str) -> PrivateRoomInDB | None:
room_collection = get_private_rooms_collection()
return await room_collection.find_one({"_id": PydanticObjectId(id)})
room = await room_collection.find_one({"_id": PydanticObjectId(id)})
return PrivateRoomInDB(**room) if room else None


async def check_members_in_room(user1_id: str, user2_id: str):
async def fetch_private_room_by_members(
user1_id: str, user2_id: str
) -> PrivateRoomInDB | None:
rooms_collection = get_private_rooms_collection()
room = await rooms_collection.find_one(
room: Mapping[str, Any] | None = await rooms_collection.find_one(
{
"$or": [
{
Expand All @@ -37,21 +43,61 @@ async def check_members_in_room(user1_id: str, user2_id: str):
],
}
)
return str(room["_id"]) if room is not None else None
return PrivateRoomInDB(**room) if room else None


async def join_private_room(user1_id: str, user2_id: str) -> str:
async def check_user_in_private_room(room_id: str, user_id: str) -> bool:
room: PrivateRoomInDB | None = await fetch_private_room_by_id(room_id)
user_id_obj = PydanticObjectId(user_id)
return user_id_obj in [room.member1, room.member2] if room else False


async def get_user_private_rooms(user_id: str) -> list[PrivateRoomInDB]:
rooms_collection = get_private_rooms_collection()

# Create a query to find private rooms for the specified user
query = {
"$or": [
{"member1": PydanticObjectId(user_id)},
{"member2": PydanticObjectId(user_id)},
]
}

# Fetch the documents using the query
cursor = rooms_collection.find(query)

# Convert the cursor to a list and await the result
rooms = await cursor.to_list(length=None)

# Convert each document to PrivateRoomInDB
return [PrivateRoomInDB(**room) for room in rooms]


async def get_private_room_messages(room_id: str) -> list[MessageInDB]:
return await get_private_messages(room_id)


async def create_private_room(user1_id: str, user2_id: str) -> PrivateRoomInDB:
if user1_id == user2_id:
raise ValueError("Users cannot be the same")

rooms_collection = get_private_rooms_collection()

room_id = await check_members_in_room(user1_id, user2_id)
room = await fetch_private_room_by_members(user1_id, user2_id)

if room_id is not None:
return room_id
if room is not None:
return room
else:
room = await rooms_collection.insert_one(
{
"member1": PydanticObjectId(user1_id),
"member2": PydanticObjectId(user2_id),
}
user1_id_obj = PydanticObjectId(user1_id)
user2_id_obj = PydanticObjectId(user2_id)
new_room = PrivateRoom(
member1=user1_id_obj,
member2=user2_id_obj,
created_at=datetime.now(),
)
new_room_obj = await rooms_collection.insert_one(
new_room.model_dump(by_alias=True)
)
return PrivateRoomInDB(
**new_room.model_dump(by_alias=True), _id=new_room_obj.inserted_id
)
return str(room.inserted_id)
85 changes: 71 additions & 14 deletions chatApp/models/public_room.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from datetime import datetime
from typing import Any

from fastapi import status
from pydantic import BaseModel, Field

from chatApp.config.database import get_public_rooms_collection
from chatApp.schemas.public_room import GetPublicRoomSchema
from chatApp.utils.object_id import PydanticObjectId


Expand Down Expand Up @@ -44,25 +47,36 @@ class PublicRoomInDB(PublicRoom):
id: PydanticObjectId = Field(alias="_id", serialization_alias="id")


async def fetch_public_room_by_id(id: str):
async def fetch_all_public_rooms() -> list[GetPublicRoomSchema]:
"""Fetch all public rooms and return them as a list of GetPublicRoomSchema."""
rooms_collection = get_public_rooms_collection()
rooms = await rooms_collection.find().to_list(length=None)
return [
GetPublicRoomSchema(**room, members_count=len(room["members"]))
for room in rooms
]


async def fetch_public_room_by_id(id: str) -> PublicRoomInDB | None:
room_collection = get_public_rooms_collection()
return await room_collection.find_one({"_id": PydanticObjectId(id)})
room = await room_collection.find_one({"_id": PydanticObjectId(id)})
return PublicRoomInDB(**room) if room else None


async def join_public_room(room_id: str, user_id: str) -> bool:
async def join_public_room(
room_id: str, user_id: str
) -> tuple[bool, str | None, int]:
rooms_collection = get_public_rooms_collection()
room = await rooms_collection.find_one({"_id": PydanticObjectId(room_id)})
room: PublicRoomInDB | None = await fetch_public_room_by_id(room_id)

# Ensure room is not None
if room is None:
return False

# Define the expected structure of the room dictionary
# Adjust this according to the actual structure
ban_list: list[PydanticObjectId] = room.get("ban_list", [])
members: list[PydanticObjectId] = room.get("members", [])
return False, "room not found", status.HTTP_404_NOT_FOUND

ban_list: list[PydanticObjectId] = room.ban_list
members: list[PydanticObjectId] = room.members
userObjId = PydanticObjectId(user_id)

if userObjId not in ban_list:
if userObjId not in members:
members.append(userObjId)
Expand All @@ -74,8 +88,51 @@ async def join_public_room(room_id: str, user_id: str) -> bool:
)

if result.modified_count == 1:
return True
return True, None, status.HTTP_204_NO_CONTENT
else:
return False # Failed to update the room in the database
return True # User is already a member
return False # User is banned
return (
False,
"something went wrong, please try again",
status.HTTP_500_INTERNAL_SERVER_ERROR,
) # Failed to update the room in the database
return (
True,
None,
status.HTTP_204_NO_CONTENT,
) # User is already a member
return (
False,
"you are banned from this room",
status.HTTP_403_FORBIDDEN,
) # User is banned


async def check_user_in_public_room(room_id: str, user_id: str) -> bool:
room: PublicRoomInDB | None = await fetch_public_room_by_id(room_id)
user_id_obj = PydanticObjectId(user_id)
if room is None:
return False
if user_id_obj not in room.ban_list or user_id_obj not in room.members:
return False
return True


async def create_public_room(
owner: str, room_info: dict[str, Any]
) -> PublicRoomInDB | None:
rooms_collection = get_public_rooms_collection()

user_id_obj = PydanticObjectId(owner)
room = PublicRoom(
**room_info,
owner=user_id_obj,
created_at=datetime.now(),
members=[user_id_obj],
)

room_obj = await rooms_collection.insert_one(
room.model_dump(by_alias=True)
)
return PublicRoomInDB(
**room.model_dump(by_alias=True), _id=room_obj.inserted_id
)
Loading
Loading