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

Feature/add_message_history #336

Open
wants to merge 25 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d5ca066
create table in db MessageHistory
vomerf Apr 24, 2023
6ee2768
create class MessageHistoryRepository
vomerf Apr 24, 2023
984714b
create class MessageHistoryService
vomerf Apr 24, 2023
42e13b8
change model
vomerf Apr 25, 2023
9e70f53
Merge branch 'develop' of github.com:Studio-Yandex-Practicum/lomaya_b…
vomerf May 2, 2023
b037146
Merge branch 'develop' of github.com:Studio-Yandex-Practicum/lomaya_b…
vomerf May 3, 2023
19f2ead
create file migration 2023-05-03_12.17.31_add_model_messagehistory.py
vomerf May 3, 2023
4860b76
change model MessageHistory
vomerf May 3, 2023
2f84ec0
create function for create object MessageHistoryService
vomerf May 3, 2023
b59430b
add class attribute history_service for MemberService ReportService R…
vomerf May 3, 2023
e139cad
create message in db when send message and change method create_histo…
vomerf May 3, 2023
77df88b
rename field from chat_id to message_id
vomerf May 5, 2023
4ccd67c
change file migration
vomerf May 5, 2023
8b99676
add message_id
vomerf May 5, 2023
018ac55
Merge branch 'develop' of github.com:Studio-Yandex-Practicum/lomaya_b…
vomerf May 13, 2023
0cdcde0
Merge branch 'develop' of github.com:Studio-Yandex-Practicum/lomaya_b…
vomerf May 16, 2023
cc8711a
add shift_id in all methods how parametr in order to don't get it fro…
vomerf May 16, 2023
65baead
change location becase add new migration from repository
vomerf May 16, 2023
cff6ebd
Merge branch 'develop' of github.com:Studio-Yandex-Practicum/lomaya_b…
vomerf May 22, 2023
a996312
save message from web_app_data function instead start function
vomerf May 22, 2023
8195063
Merge branch 'develop' of github.com:Studio-Yandex-Practicum/lomaya_b…
vomerf Jun 2, 2023
e204f35
if user is None message don't save in db
vomerf Jun 3, 2023
25f163c
change __repr__ in model from self.status to self.event
vomerf Jun 3, 2023
c4f2094
add middleware for save bundle of message instead single save every time
vomerf Jun 3, 2023
c753006
after save all messages, db list objects became empty
vomerf Jun 3, 2023
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
9 changes: 9 additions & 0 deletions src/bot/api_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

from src.core.db.repository import (
MemberRepository,
MessageHistoryRepository,
ReportRepository,
RequestRepository,
ShiftRepository,
TaskRepository,
UserRepository,
)
from src.core.services.history_message_service import MessageHistoryService
from src.core.services.member_service import MemberService
from src.core.services.report_service import ReportService
from src.core.services.shift_service import ShiftService
Expand Down Expand Up @@ -58,3 +60,10 @@ async def get_shift_service_callback(sessions):
shift_repository, task_service, report_repository, user_repository, request_repository
)
return shift_service


async def get_history_service(sessions):
async for session in sessions: # noqa R503
history_repository = MessageHistoryRepository(session)
history_service = MessageHistoryService(history_repository)
return history_service
14 changes: 10 additions & 4 deletions src/bot/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from telegram.ext import CallbackContext

from src.api.request_models.user import UserCreateRequest, UserWebhookTelegram
from src.bot.api_services import get_user_service_callback
from src.bot.api_services import get_history_service, get_user_service_callback
from src.bot.ui import (
CONFIRM_SKIP_TASK,
CONFIRM_SKIP_TASK_KEYBOARD,
Expand All @@ -23,6 +23,7 @@
)
from src.core import exceptions
from src.core.db.db import get_session
from src.core.db.models import MessageHistory
from src.core.db.repository import (
MemberRepository,
ReportRepository,
Expand All @@ -49,13 +50,16 @@ async def start(update: Update, context: CallbackContext) -> None:
"Каждый месяц мы будем подводить итоги "
"и награждать самых активных и старательных ребят!"
)
session = get_session()
user_service = await get_user_service_callback(session)
user_session = get_session()
history_session = get_session()
user_service = await get_user_service_callback(user_session)
history_service = await get_history_service(history_session)
user = await user_service.get_user_by_telegram_id(update.effective_chat.id)
context.user_data["user"] = user
if user and user.telegram_blocked:
await user_service.unset_telegram_blocked(user)
await context.bot.send_message(chat_id=update.effective_chat.id, text=start_text)
message = await context.bot.send_message(chat_id=update.effective_chat.id, text=start_text)
event = MessageHistory.Event.REGISTRATION
if user:
try:
await user_service.check_before_change_user_data(user.id)
Expand All @@ -66,8 +70,10 @@ async def start(update: Update, context: CallbackContext) -> None:
)
return
await update_user_data(update, context)
await history_service.create_history_message(user.id, message.message_id, start_text, event)
else:
await register_user(update, context)
await history_service.create_history_message(user, message.message_id, start_text, event)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Здесь точно user = None

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это я знаю поэтому и написал user, а не user.id или как-то по другому надо, немного не понял если честно. Сделать как со сменой если не передать юзера то по умолчанию None ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если пользователя нет, то и сообщения сохранять нет смысла.
Здесь, после регистрации пользователь уже есть. Его надо получить и использовать при сохранении.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут получается что функция register только форму с регистрацией кидает, но еще пользователя в базу не сохраняет, это как-то пометить надо будет в event? событие перед формой регистрации?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

я убрал и после update и после register, я написал вызов в функцию web_app_data там как я понял и обновление данных обрабатывается и регистрация



async def register_user(
Expand Down
23 changes: 20 additions & 3 deletions src/bot/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,30 @@
from telegram.ext import CallbackContext

from src.bot.api_services import (
get_history_service,
get_member_service_callback,
get_report_service_callback,
get_shift_service_callback,
)
from src.bot.services import BotService
from src.bot.ui import DAILY_TASK_BUTTONS
from src.core.db.db import get_session
from src.core.db.models import Report
from src.core.db.models import MessageHistory, Report
from src.core.settings import settings


async def send_no_report_reminder_job(context: CallbackContext) -> None:
"""Отправить напоминание об отчёте."""
member_session_generator = get_session()
history_session = get_session()
history_service = await get_history_service(history_session)
member_service = await get_member_service_callback(member_session_generator)
bot_service = BotService(context)
bot_service = BotService(context, history_service)
members = await member_service.get_members_with_no_reports()
if members:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поле shift_id обязательное у модели Member, поэтому эта проверка не имеет смысла. Можно ниже просто подставлять member.shift_id.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поле у Member обязательное, а если список пустой будет то ошибка вылетит. Как раз и проверяю, что список не пустой такое наверно маловероятно, но наверное возможно

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Всё просто. Если в members придёт пустой список, то генератор списка также вернёт пустой список. А если не пустой, то смену возьмёт из member, как я написал выше.

    send_message_tasks = [
        bot_service.send_message(
            member.user,
            (
                f"{member.user.name} {member.user.surname}, мы потеряли тебя! "
                f"Задание все еще ждет тебя. "
                f"Напоминаем, что за каждое выполненное задание ты получаешь виртуальные "
                f"\"ломбарьерчики\", которые можешь обменять на призы и подарки!"
            ),
            MessageHistory.Event.REPORT_MENTION,
            member.shift_id,
        )
        for member in members
    ]

Так точно ошибки не будет.
Твой вариант будет совсем немного быстрее, но менее очевиден. Возникает вопрос, почему смену выделили отдельно и именно у первого элемента списка. Это какой-то особый случай... Непонятно.

shift_id = members[0].shift_id
else:
shift_id = None
send_message_tasks = [
bot_service.send_message(
member.user,
Expand All @@ -31,6 +38,8 @@ async def send_no_report_reminder_job(context: CallbackContext) -> None:
f"Напоминаем, что за каждое выполненное задание ты получаешь виртуальные "
f"\"ломбарьерчики\", которые можешь обменять на призы и подарки!"
),
MessageHistory.Event.REPORT_MENTION,
shift_id,
)
for member in members
]
Expand All @@ -42,18 +51,24 @@ async def send_daily_task_job(context: CallbackContext) -> None:
shift_session = get_session()
report_session = get_session()
member_session = get_session()
history_session = get_session()
shift_service = await get_shift_service_callback(shift_session)
report_service = await get_report_service_callback(report_session)
member_service = await get_member_service_callback(member_session)
history_service = await get_history_service(history_session)

await shift_service.start_prepared_shift()

bot_service = BotService(context)
bot_service = BotService(context, history_service)
await report_service.set_status_to_waiting_reports(Report.Status.SKIPPED)
await member_service.exclude_lagging_members(context.application)
task, members = await report_service.get_today_task_and_active_members(date.today().day)
await report_service.create_daily_reports(members, task)
task_photo = urljoin(settings.APPLICATION_URL, task.url)
if members:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Здесь так же.

shift_id = members[0].shift_id
else:
shift_id = None
send_message_tasks = [
bot_service.send_photo(
member.user,
Expand All @@ -71,6 +86,8 @@ async def send_daily_task_job(context: CallbackContext) -> None:
f"Не забудь сделать фотографию, как ты выполняешь задание, и отправить на проверку."
),
DAILY_TASK_BUTTONS,
MessageHistory.Event.GET_TASK,
shift_id,
)
for member in members
]
Expand Down
59 changes: 42 additions & 17 deletions src/bot/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from src.api.request_models.request import RequestDeclineRequest
from src.bot.error_handler import error_handler
from src.core.db import models
from src.core.services.history_message_service import MessageHistoryService
from src.core.settings import settings
from src.core.utils import get_lombaryers_for_quantity

Expand Down Expand Up @@ -53,21 +54,36 @@ async def _inner(*args, **kwargs):


class BotService:
def __init__(self, telegram_bot: Application) -> None:
def __init__(self, telegram_bot: Application, history_service: MessageHistoryService) -> None:
self.__bot = telegram_bot.bot
self.__bot_application = telegram_bot
self.__history_service = history_service

@check_user_blocked
@retry()
async def send_message(self, user: models.User, text: str) -> None:
await self.__bot.send_message(user.telegram_id, text)
async def send_message(
self, user: models.User, text: str, event: models.MessageHistory.Event, shift_id=None
) -> None:
message = await self.__bot.send_message(user.telegram_id, text)
await self.__history_service.create_history_message(user.id, message.message_id, text, event, shift_id)

@check_user_blocked
@retry()
async def send_photo(self, user: models.User, photo: str, caption: str, reply_markup: ReplyKeyboardMarkup) -> None:
await self.__bot.send_photo(chat_id=user.telegram_id, photo=photo, caption=caption, reply_markup=reply_markup)
async def send_photo(
self,
user: models.User,
photo: str,
caption: str,
reply_markup: ReplyKeyboardMarkup,
event: models.MessageHistory.Event,
shift_id: models.Shift | None,
) -> None:
message = await self.__bot.send_photo(
chat_id=user.telegram_id, photo=photo, caption=caption, reply_markup=reply_markup
)
await self.__history_service.create_history_message(user.id, message.message_id, caption, event, shift_id)

async def notify_approved_request(self, user: models.User, first_task_date: str) -> None:
async def notify_approved_request(self, user: models.User, first_task_date: str, shift_id: int) -> None:
"""Уведомление участника о решении по заявке в telegram.

- Заявка принята.
Expand All @@ -77,10 +93,11 @@ async def notify_approved_request(self, user: models.User, first_task_date: str)
f"{first_task_date} в {settings.formatted_task_time} часов утра "
"тебе поступит первое задание."
)
await self.send_message(user, text)
event = models.MessageHistory.Event.REQUEST_ACCEPTED
await self.send_message(user, text, event, shift_id)

async def notify_declined_request(
self, user: models.User, decline_request_data: RequestDeclineRequest | None
self, user: models.User, decline_request_data: RequestDeclineRequest | None, shift_id: int
) -> None:
"""Уведомление участника о решении по заявке в telegram.

Expand All @@ -96,7 +113,8 @@ async def notify_declined_request(
f" новости Центра \"Ломая барьеры\" - вступайте в нашу группу "
f"{settings.ORGANIZATIONS_GROUP}"
)
await self.send_message(user, text)
event = models.MessageHistory.Event.REQUEST_CANCELED
await self.send_message(user, text, event, shift_id)

async def notify_approved_task(self, user: models.User, report: models.Report, shift: models.Shift) -> None:
"""Уведомление участника о проверенном задании.
Expand All @@ -107,7 +125,8 @@ async def notify_approved_task(self, user: models.User, report: models.Report, s
text = f"Твой отчет от {photo_date} принят! Тебе начислен 1 \"ломбарьерчик\". "
if date.today() < shift.finished_at:
text = text + f"Следующее задание придет в {settings.formatted_task_time} часов утра."
await self.send_message(user, text)
event = models.MessageHistory.Event.TASK_ACCEPTED
await self.send_message(user, text, event, shift.id)

async def notify_declined_task(self, user: models.User, shift: models.Shift) -> None:
"""Уведомление участника о проверенном задании.
Expand All @@ -120,9 +139,10 @@ async def notify_declined_task(self, user: models.User, shift: models.Shift) ->
)
if date.today() < shift.finished_at:
text = text + f"Ты можешь отправить отчет повторно до {settings.formatted_task_time} часов утра."
await self.send_message(user, text)
event = models.MessageHistory.Event.TASK_NOT_ACCEPTED
await self.send_message(user, text, event, shift.id)

async def notify_excluded_members(self, members: list[models.Member]) -> None:
async def notify_excluded_members(self, members: list[models.Member], shift_id: int) -> None:
"""Уведомляет участников об исключении из смены."""
text = (
"К сожалению, мы заблокировали Ваше участие в смене из-за неактивности - "
Expand All @@ -131,7 +151,8 @@ async def notify_excluded_members(self, members: list[models.Member]) -> None:
"Если Вы считаете, что произошла ошибка - обращайтесь "
f"за помощью на электронную почту {settings.ORGANIZATIONS_EMAIL}."
)
send_message_tasks = [self.send_message(member.user, text) for member in members]
event = models.MessageHistory.Event.EXCLUDE_FROM_SHIFT
send_message_tasks = [self.send_message(member.user, text, event, shift_id) for member in members]
self.__bot_application.create_task(asyncio.gather(*send_message_tasks))

async def notify_that_shift_is_finished(self, shift: models.Shift) -> None:
Expand All @@ -145,19 +166,23 @@ async def notify_that_shift_is_finished(self, shift: models.Shift) -> None:
numbers_lombaryers=member.numbers_lombaryers,
lombaryers_case=get_lombaryers_for_quantity(member.numbers_lombaryers),
),
models.MessageHistory.Event.SHIFT_ENDED,
shift.id,
)
for member in shift.members
]
self.__bot_application.create_task(asyncio.gather(*send_message_tasks))

async def notify_that_shift_is_cancelled(self, users: list[models.User], final_message: str) -> None:
async def notify_that_shift_is_cancelled(self, users: list[models.User], final_message: str, shift_id: int) -> None:
"""Уведомляет пользователей об отмене смены."""
send_message_tasks = [self.send_message(user, final_message) for user in users]
event = models.MessageHistory.Event.SHIFT_CANCELED
send_message_tasks = [self.send_message(user, final_message, event, shift_id) for user in users]
self.__bot_application.create_task(asyncio.gather(*send_message_tasks))

async def notify_that_shift_start_date_is_changed(
self, users: list[models.User], start_date_changed_message: str
self, users: list[models.User], start_date_changed_message: str, shift_id: int
) -> None:
"""Уведомляет пользователей о переносе даты старта смены."""
send_message_tasks = [self.send_message(user, start_date_changed_message) for user in users]
event = models.MessageHistory.Event.START_SHIFT_CHANGED
send_message_tasks = [self.send_message(user, start_date_changed_message, event, shift_id) for user in users]
self.__bot_application.create_task(asyncio.gather(*send_message_tasks))
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""add model MessageHistory

Revision ID: 5163a5140a0a
Revises: 5a1ecb2d17c4
Create Date: 2023-05-13 16:34:39.932886

"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = '5163a5140a0a'
down_revision = '5a1ecb2d17c4'
branch_labels = None
depends_on = None


STATUS_ENUM_POSTGRES = postgresql.ENUM(
'registration',
'get_task',
'report_mention',
'request_accepted',
'request_canceled',
'task_accepted',
'task_not_accepted',
'exclude_from_shift',
'shift_ended',
'shift_canceled',
'start_shift_changed',
name='message_history_event',
create_type=False
)
STATUS_ENUM = sa.Enum(
'registration',
'get_task',
'report_mention',
'request_accepted',
'request_canceled',
'task_accepted',
'task_not_accepted',
'exclude_from_shift',
'shift_ended',
'shift_canceled',
'start_shift_changed',
name='message_history_event'
)
STATUS_ENUM.with_variant(STATUS_ENUM_POSTGRES, 'postgresql')

def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('message_history',
sa.Column('user_id', sa.UUID(), nullable=True),
sa.Column('message', sa.String(length=400), nullable=False),
sa.Column('message_id', sa.BigInteger(), nullable=False),
sa.Column('event', STATUS_ENUM, nullable=False),
sa.Column('shift_id', sa.UUID(), nullable=True),
sa.Column('id', sa.UUID(), nullable=False),
sa.Column('created_at', sa.TIMESTAMP(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False),
sa.Column('updated_at', sa.TIMESTAMP(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False),
sa.ForeignKeyConstraint(['shift_id'], ['shifts.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('message_history')
STATUS_ENUM.drop(op.get_bind(), checkfirst=True)
# ### end Alembic commands ###
33 changes: 33 additions & 0 deletions src/core/db/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,36 @@ class AdministratorInvitation(Base):

def __repr__(self) -> str:
return f"<AdministratorInvitation: {self.id}, email: {self.email}, surname: {self.surname}, name: {self.name}>"


class MessageHistory(Base):
"""Хрениние истории отправленных сообщений."""

class Event(str, enum.Enum):
"""Статус отправленного сообщения."""

REGISTRATION = "registration"
GET_TASK = "get_task"
REPORT_MENTION = "report_mention"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Именование статусов потом нужно будет пересмотреть. Не все удачные.

REQUEST_ACCEPTED = "request_accepted"
REQUEST_CANCELED = "request_canceled"
TASK_ACCEPTED = "task_accepted"
TASK_NOT_ACCEPTED = "task_not_accepted"
EXCLUDE_FROM_SHIFT = "exclude_from_shift"
SHIFT_ENDED = "shift_ended"
SHIFT_CANCELED = "shift_canceled"
START_SHIFT_CHANGED = "start_shift_changed"

__tablename__ = 'message_history'

user_id = Column(UUID(as_uuid=True), ForeignKey(User.id))
message = Column(String(400), nullable=False)
message_id = Column(BigInteger, nullable=False)
event = Column(
Enum(Event, name="message_history_event", values_callable=lambda obj: [e.value for e in obj]),
nullable=False,
)
shift_id = Column(UUID(as_uuid=True), ForeignKey(Shift.id))

def __repr__(self) -> str:
return f"<MessageHistory: {self.user_id} - {self.status}"
Loading