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

Add chat repo #133

Merged
merged 3 commits into from
Nov 22, 2023
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
5 changes: 3 additions & 2 deletions app/handlers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from aiogram.utils.markdown import hbold, hpre

from app.infrastructure.database.models import Chat
from app.infrastructure.database.repo.chat import ChatRepo
from app.utils.log import Logger

logger = Logger(__name__)
Expand Down Expand Up @@ -83,9 +84,9 @@ async def cancel_state(message: types.Message, state: FSMContext):


@router.message(F.message.content_types == types.ContentType.MIGRATE_TO_CHAT_ID)
async def chat_migrate(message: types.Message, chat: Chat):
async def chat_migrate(message: types.Message, chat: Chat, chat_repo: ChatRepo):
old_id = message.chat.id
new_id = message.migrate_to_chat_id
chat.chat_id = new_id
await chat.save()
await chat_repo.update(chat)
logger.info(f"Migrate chat from {old_id} to {new_id}")
5 changes: 3 additions & 2 deletions app/handlers/karma.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from aiogram.utils.text_decorations import html_decoration as hd

from app.infrastructure.database.models import Chat, User
from app.infrastructure.database.repo.chat import ChatRepo
from app.models.config import Config
from app.services.karma import get_me_chat_info, get_me_info
from app.services.karma import get_top as get_karma_top
Expand All @@ -16,10 +17,10 @@


@router.message(Command("top", prefix="!"), F.chat.type == "private")
async def get_top_from_private(message: types.Message, user: User):
async def get_top_from_private(message: types.Message, user: User, chat_repo: ChatRepo):
parts = message.text.split(maxsplit=1)
if len(parts) > 1:
chat = await Chat.get(chat_id=int(parts[1]))
chat = await chat_repo.get_by_id(chat_id=int(parts[1]))
else:
return await message.reply(
"Эту команду можно использовать только в группах "
Expand Down
Empty file.
100 changes: 100 additions & 0 deletions app/infrastructure/database/repo/chat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from collections import namedtuple

from tortoise import BaseDBAsyncClient
from tortoise.exceptions import DoesNotExist

from app.infrastructure.database.models import UserKarma
from app.infrastructure.database.models.chat import Chat
from app.infrastructure.database.models.user import User
from app.models.db.db import karma_filters
from app.utils.exceptions import NotHaveNeighbours

TopResultEntry = namedtuple("TopResult", ("user", "karma"))
Neighbours = namedtuple("Neighbours", ("prev_id", "next_id"))


class ChatRepo:
def __init__(self, session: BaseDBAsyncClient | None = None):
self.session = session

async def get_by_id(self, chat_id: int) -> Chat:
return await Chat.get(chat_id=chat_id, using_db=self.session)

async def update(self, chat: Chat):
await chat.save(using_db=self.session)

async def create_from_tg_chat(self, chat) -> Chat:
chat = await Chat.create(
chat_id=chat.id,
type_=chat.type,
title=chat.title,
username=chat.username,
using_db=self.session,
)
return chat

async def get_or_create_from_tg_chat(self, chat) -> Chat:
try:
chat = await Chat.get(chat_id=chat.id)
except DoesNotExist:
chat = await self.create_from_tg_chat(chat=chat)
return chat

async def get_top_karma_list(
self, chat: Chat, limit: int = 15
) -> list[TopResultEntry]:
await chat.fetch_related("user_karma", using_db=self.session)
users_karmas = (
await chat.user_karma.order_by(*karma_filters)
.limit(limit)
.prefetch_related("user")
.all()
)
rez = []
for user_karma in users_karmas:
user = user_karma.user
karma = user_karma.karma_round
rez.append(TopResultEntry(user, karma))

return rez

async def get_neighbours(
self, user: User, chat: Chat
) -> tuple[UserKarma, UserKarma, UserKarma]:
prev_id, next_id = await self.get_neighbours_id(chat.chat_id, user.id)
uk = (
await chat.user_karma.filter(user_id__in=(prev_id, next_id))
.prefetch_related("user")
.order_by(*karma_filters)
.all()
)

user_uk = (
await chat.user_karma.filter(user=user).prefetch_related("user").first()
)
return uk[0], user_uk, uk[1]

async def get_neighbours_id(self, chat_id, user_id) -> Neighbours:
neighbours = await self.session.execute_query(
query="""
SELECT prev_user_id, next_user_id
FROM (
SELECT
user_id,
LAG(user_id) OVER(ORDER BY karma) prev_user_id,
LEAD(user_id) OVER(ORDER BY karma) next_user_id
FROM user_karma
WHERE chat_id = ?
)
WHERE user_id = ?""",
values=[chat_id, user_id],
)
try:
rez = dict(neighbours[1][0])
except IndexError:
raise NotHaveNeighbours
try:
rez = int(rez["prev_user_id"]), int(rez["next_user_id"])
except TypeError:
raise NotHaveNeighbours
return rez
23 changes: 18 additions & 5 deletions app/middlewares/db_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
from aiogram import BaseMiddleware, types
from aiogram.dispatcher.event.bases import CancelHandler
from aiogram.types import TelegramObject
from tortoise import BaseDBAsyncClient
from tortoise.transactions import in_transaction

from app.infrastructure.database.models import Chat, User
from app.infrastructure.database.models import User
from app.infrastructure.database.repo.chat import ChatRepo
from app.services.settings import get_chat_settings
from app.utils.lock_factory import LockFactory
from app.utils.log import Logger
Expand All @@ -29,22 +32,32 @@ async def __call__(
user: types.User = data.get("event_from_user", None)
if isinstance(event, types.Message) and event.sender_chat:
raise CancelHandler
await self.setup_chat(data, user, chat)
return await handler(event, data)

async with in_transaction() as session:
await self.setup_chat(session, data, user, chat)
return await handler(event, data)

async def setup_chat(
self, data: dict, user: types.User, chat: Optional[types.Chat] = None
self,
session: BaseDBAsyncClient,
data: dict,
user: types.User,
chat: Optional[types.Chat] = None,
):
try:
chat_repo = ChatRepo(session)

async with self.lock_factory.get_lock(user.id):
user = await User.get_or_create_from_tg_user(user)

if chat and chat.type != "private":
async with self.lock_factory.get_lock(chat.id):
chat = await Chat.get_or_create_from_tg_chat(chat)
chat = await chat_repo.get_or_create_from_tg_chat(chat)
data["chat_settings"] = await get_chat_settings(chat=chat)

except Exception as e:
logger.exception("troubles with db", exc_info=e)
raise e
data["user"] = user
data["chat"] = chat
data["chat_repo"] = chat_repo