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 11 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
10 changes: 10 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,11 @@ 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
shift_repository = ShiftRepository(session)
history_repository = MessageHistoryRepository(session)
history_service = MessageHistoryService(history_repository, shift_repository)
return history_service
15 changes: 12 additions & 3 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,8 +50,10 @@ 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:
Expand All @@ -66,8 +69,14 @@ async def start(update: Update, context: CallbackContext) -> None:
)
return
await update_user_data(update, context)
# не знаю как сюда попасть чтобы проверить
await history_service.create_history_message(
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
Member

Choose a reason for hiding this comment

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

Путаница, где-то event, а ниже status.

Copy link
Member

Choose a reason for hiding this comment

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

Так неправильно передавать значение status='update_data', лучше так MessageHistory.Event.REGISTRATION.value

user.id, update.effective_chat.id, start_text, status='update_data'
)
else:
await register_user(update, context)
event = MessageHistory.Event.REGISTRATION.value
await history_service.create_history_message(user, update.effective_chat.id, start_text, event)


async def register_user(
Expand Down
15 changes: 12 additions & 3 deletions src/bot/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,27 @@
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()
event = MessageHistory.Event.REPORT_MENTION.value
send_message_tasks = [
bot_service.send_message(
member.user,
Expand All @@ -31,6 +35,7 @@ async def send_no_report_reminder_job(context: CallbackContext) -> None:
f"Напоминаем, что за каждое выполненное задание ты получаешь виртуальные "
f"\"ломбарьерчики\", которые можешь обменять на призы и подарки!"
),
event,
)
for member in members
]
Expand All @@ -42,18 +47,21 @@ 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)
event = MessageHistory.Event.GET_TASK.value
send_message_tasks = [
bot_service.send_photo(
member.user,
Expand All @@ -71,6 +79,7 @@ async def send_daily_task_job(context: CallbackContext) -> None:
f"Не забудь сделать фотографию, как ты выполняешь задание, и отправить на проверку."
),
DAILY_TASK_BUTTONS,
event,
Copy link
Member

Choose a reason for hiding this comment

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

Подставляй событие сразу сюда. Иначе, приходится возвращаться назад, чтобы узнать, какое событие будет.

)
for member in members
]
Expand Down
35 changes: 25 additions & 10 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,19 +54,24 @@ 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:
async def send_message(self, user: models.User, text: str, event: str) -> None:
Copy link
Member

Choose a reason for hiding this comment

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

Event не строка, а enum из модели.

await self.__bot.send_message(user.telegram_id, text)
await self.__history_service.create_history_message(user.id, user.telegram_id, text, event)

@check_user_blocked
@retry()
async def send_photo(self, user: models.User, photo: str, caption: str, reply_markup: ReplyKeyboardMarkup) -> None:
async def send_photo(
self, user: models.User, photo: str, caption: str, reply_markup: ReplyKeyboardMarkup, event: str
) -> None:
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, user.telegram_id, caption, event)

async def notify_approved_request(self, user: models.User, first_task_date: str) -> None:
"""Уведомление участника о решении по заявке в telegram.
Expand All @@ -77,7 +83,8 @@ 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.value
await self.send_message(user, text, event)

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

async def notify_approved_task(self, user: models.User, report: models.Report, shift: models.Shift) -> None:
"""Уведомление участника о проверенном задании.
Expand All @@ -107,7 +115,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.value
await self.send_message(user, text, event)

async def notify_declined_task(self, user: models.User, shift: models.Shift) -> None:
"""Уведомление участника о проверенном задании.
Expand All @@ -120,7 +129,8 @@ 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.value
await self.send_message(user, text, event)

async def notify_excluded_members(self, members: list[models.Member]) -> None:
"""Уведомляет участников об исключении из смены."""
Expand All @@ -131,11 +141,13 @@ 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.value
send_message_tasks = [self.send_message(member.user, text, event) 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:
"""Уведомляет активных участников об окончании смены."""
event = models.MessageHistory.Event.SHIFT_ENDED.value
send_message_tasks = [
self.send_message(
member.user,
Expand All @@ -145,19 +157,22 @@ 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),
),
event,
)
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:
"""Уведомляет пользователей об отмене смены."""
send_message_tasks = [self.send_message(user, final_message) for user in users]
event = models.MessageHistory.Event.SHIFT_CANCELED.value
send_message_tasks = [self.send_message(user, final_message, event) 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
) -> None:
"""Уведомляет пользователей о переносе даты старта смены."""
send_message_tasks = [self.send_message(user, start_date_changed_message) for user in users]
event = models.MessageHistory.Event.START_SHIFT_CHANGED.value
send_message_tasks = [self.send_message(user, start_date_changed_message, event) 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,71 @@
"""add model MessageHistory

Revision ID: 707f448b5271
Revises: 2c304127881b
Create Date: 2023-05-03 12:17:31.013484

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

# revision identifiers, used by Alembic.
revision = '707f448b5271'
down_revision = '2c304127881b'
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('chat_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 @@ -301,3 +301,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)
chat_id = Column(BigInteger, nullable=False)
Copy link
Member

Choose a reason for hiding this comment

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

Зачем нам chat_id? Он же совпадает с user.telegram_id. Нам нужно сохранять message_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.

что подразумевается под message_id это primary key данной таблицы или надо сообщение выносить в отдельную таблицу?

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}"
1 change: 1 addition & 0 deletions src/core/db/repository/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .abstract_repository import AbstractRepository # noqa
from .administrator_invitation import AdministratorInvitationRepository # noqa
from .administrator_repository import AdministratorRepository # noqa
from .history_message_repository import MessageHistoryRepository # noqa
from .member_repository import MemberRepository # noqa
from .report_repository import ReportRepository # noqa
from .request_repository import RequestRepository # noqa
Expand Down
13 changes: 13 additions & 0 deletions src/core/db/repository/history_message_repository.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession

from src.core.db.db import get_session
from src.core.db.models import MessageHistory
from src.core.db.repository import AbstractRepository


class MessageHistoryRepository(AbstractRepository):
"""Класс для работы с моделью HistoryMessage."""

def __init__(self, session: AsyncSession = Depends(get_session)) -> None:
super().__init__(session, MessageHistory)
Loading