integrate dialog management with initial "main menu" state and update SRS schema

This commit is contained in:
ruslangilfanov 2025-08-17 00:24:16 +03:00
parent 54155a99c5
commit 40ed52777e
No known key found for this signature in database
8 changed files with 128 additions and 50 deletions

View File

@ -5,6 +5,7 @@ Revises: 747797032526
Create Date: 2025-08-16 19:40:06.376743
"""
from typing import Sequence, Union
from alembic import op
@ -12,8 +13,8 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '9a2898513cf2'
down_revision: Union[str, None] = '747797032526'
revision: str = "9a2898513cf2"
down_revision: Union[str, None] = "747797032526"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
@ -21,51 +22,91 @@ depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('srs_progress',
sa.Column('id', sa.BigInteger(), nullable=False),
sa.Column('user_id', sa.BigInteger(), nullable=False),
sa.Column('word_id', sa.BigInteger(), nullable=False),
sa.Column('due_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.Column('interval_days', sa.Integer(), nullable=False),
sa.Column('ease', sa.Float(), nullable=False),
sa.Column('reps', sa.Integer(), nullable=False),
sa.Column('lrn_step', sa.Integer(), nullable=False),
sa.Column('state', sa.Enum('learning', 'review', 'lapsed', name='reviewstate', native_enum=False), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name=op.f('fk_srs_progress_user_id_users')),
sa.ForeignKeyConstraint(['word_id'], ['glossary_word.id'], name=op.f('fk_srs_progress_word_id_glossary_word')),
sa.PrimaryKeyConstraint('id', name=op.f('pk_srs_progress')),
sa.UniqueConstraint('user_id', 'word_id', name='uq_srs_user_word')
op.create_table(
"srs_progress",
sa.Column("id", sa.BigInteger(), nullable=False),
sa.Column("user_id", sa.BigInteger(), nullable=False),
sa.Column("word_id", sa.BigInteger(), nullable=False),
sa.Column(
"due_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.Column("interval_days", sa.Integer(), nullable=False),
sa.Column("ease", sa.Float(), nullable=False),
sa.Column("reps", sa.Integer(), nullable=False),
sa.Column("lrn_step", sa.Integer(), nullable=False),
sa.Column(
"state",
sa.Enum(
"learning", "review", "lapsed", name="reviewstate", native_enum=False
),
nullable=False,
),
sa.ForeignKeyConstraint(
["user_id"], ["users.id"], name=op.f("fk_srs_progress_user_id_users")
),
sa.ForeignKeyConstraint(
["word_id"],
["glossary_word.id"],
name=op.f("fk_srs_progress_word_id_glossary_word"),
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_srs_progress")),
sa.UniqueConstraint("user_id", "word_id", name="uq_srs_user_word"),
)
op.create_index(op.f('ix_srs_progress_due_at'), 'srs_progress', ['due_at'], unique=False)
op.create_index(op.f('ix_srs_progress_user_id'), 'srs_progress', ['user_id'], unique=False)
op.create_index(op.f('ix_srs_progress_word_id'), 'srs_progress', ['word_id'], unique=False)
op.create_table('srs_review_log',
sa.Column('id', sa.BigInteger(), nullable=False),
sa.Column('user_id', sa.BigInteger(), nullable=False),
sa.Column('word_id', sa.BigInteger(), nullable=False),
sa.Column('ts', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.Column('grade', sa.Integer(), nullable=False),
sa.Column('prev_interval', sa.Integer(), nullable=False),
sa.Column('new_interval', sa.Integer(), nullable=False),
sa.Column('prev_ease', sa.Float(), nullable=False),
sa.Column('new_ease', sa.Float(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name=op.f('fk_srs_review_log_user_id_users')),
sa.ForeignKeyConstraint(['word_id'], ['glossary_word.id'], name=op.f('fk_srs_review_log_word_id_glossary_word')),
sa.PrimaryKeyConstraint('id', name=op.f('pk_srs_review_log'))
op.create_index(
op.f("ix_srs_progress_due_at"), "srs_progress", ["due_at"], unique=False
)
op.create_index(
op.f("ix_srs_progress_user_id"), "srs_progress", ["user_id"], unique=False
)
op.create_index(
op.f("ix_srs_progress_word_id"), "srs_progress", ["word_id"], unique=False
)
op.create_table(
"srs_review_log",
sa.Column("id", sa.BigInteger(), nullable=False),
sa.Column("user_id", sa.BigInteger(), nullable=False),
sa.Column("word_id", sa.BigInteger(), nullable=False),
sa.Column(
"ts",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.Column("grade", sa.Integer(), nullable=False),
sa.Column("prev_interval", sa.Integer(), nullable=False),
sa.Column("new_interval", sa.Integer(), nullable=False),
sa.Column("prev_ease", sa.Float(), nullable=False),
sa.Column("new_ease", sa.Float(), nullable=False),
sa.ForeignKeyConstraint(
["user_id"], ["users.id"], name=op.f("fk_srs_review_log_user_id_users")
),
sa.ForeignKeyConstraint(
["word_id"],
["glossary_word.id"],
name=op.f("fk_srs_review_log_word_id_glossary_word"),
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_srs_review_log")),
)
op.create_index(
op.f("ix_srs_review_log_user_id"), "srs_review_log", ["user_id"], unique=False
)
op.create_index(
op.f("ix_srs_review_log_word_id"), "srs_review_log", ["word_id"], unique=False
)
op.create_index(op.f('ix_srs_review_log_user_id'), 'srs_review_log', ['user_id'], unique=False)
op.create_index(op.f('ix_srs_review_log_word_id'), 'srs_review_log', ['word_id'], unique=False)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_srs_review_log_word_id'), table_name='srs_review_log')
op.drop_index(op.f('ix_srs_review_log_user_id'), table_name='srs_review_log')
op.drop_table('srs_review_log')
op.drop_index(op.f('ix_srs_progress_word_id'), table_name='srs_progress')
op.drop_index(op.f('ix_srs_progress_user_id'), table_name='srs_progress')
op.drop_index(op.f('ix_srs_progress_due_at'), table_name='srs_progress')
op.drop_table('srs_progress')
op.drop_index(op.f("ix_srs_review_log_word_id"), table_name="srs_review_log")
op.drop_index(op.f("ix_srs_review_log_user_id"), table_name="srs_review_log")
op.drop_table("srs_review_log")
op.drop_index(op.f("ix_srs_progress_word_id"), table_name="srs_progress")
op.drop_index(op.f("ix_srs_progress_user_id"), table_name="srs_progress")
op.drop_index(op.f("ix_srs_progress_due_at"), table_name="srs_progress")
op.drop_table("srs_progress")
# ### end Alembic commands ###

View File

@ -3,6 +3,7 @@ from aiogram import Bot, Dispatcher, BaseMiddleware
from aiogram.fsm.storage.base import BaseStorage, DefaultKeyBuilder
from aiogram.fsm.storage.redis import RedisStorage
from aiogram.types import BotCommandScopeAllPrivateChats
from aiogram_dialog import setup_dialogs
from dependency_injector.wiring import Provide, inject
from ..configs.container import ConfigContainer
@ -24,6 +25,7 @@ def create_bot(
async def create_dispatcher() -> Dispatcher:
from .router import router as root_router
from .dialogs import dialog
fsm_storage = await create_fsm_storage()
@ -35,7 +37,10 @@ async def create_dispatcher() -> Dispatcher:
for middleware in middlewares:
dp.update.middleware(middleware)
dp.include_routers(root_router)
dp.include_routers(dialog, root_router)
setup_dialogs(dp)
return dp

View File

@ -1,10 +1,9 @@
from gettext import gettext
from aiogram import Router
from aiogram.enums import ParseMode
from aiogram.filters import CommandStart
from aiogram.types import Message
from aiogram_dialog import DialogManager, StartMode
from greek_lang.tg_bot.dialogs.states import States
from greek_lang.users.manager import get_or_create_telegram_user
@ -12,12 +11,9 @@ router = Router()
@router.message(CommandStart())
async def start(message: Message) -> None:
async def start(message: Message, dialog_manager: DialogManager) -> None:
user = message.from_user
if user is None:
return
await get_or_create_telegram_user(user)
await message.answer(
text=gettext("Регистрация прошла.").format(user=user.full_name),
parse_mode=ParseMode.HTML,
)
await dialog_manager.start(States.main_menu, mode=StartMode.RESET_STACK)

View File

@ -0,0 +1,8 @@
from aiogram_dialog import Dialog
from .main_menu import windows as main_windows
dialog = Dialog(
main_windows.main_window,
)

View File

@ -0,0 +1,23 @@
from typing import Any
from aiogram.enums import ParseMode
from aiogram_dialog import DialogManager, Window
from aiogram_dialog.widgets.text import Format
from ..states import States
async def main_getter(dialog_manager: DialogManager, **kwargs: Any) -> dict[str, Any]:
return {}
main_window = Window(
Format(
"Выбери действие:",
when=lambda data, widget, dialog_manager: data["dialog_data"].get("action")
is None,
),
state=States.main_menu,
getter=main_getter,
parse_mode=ParseMode.HTML,
)

View File

@ -0,0 +1,5 @@
from aiogram.fsm.state import State, StatesGroup
class States(StatesGroup):
main_menu = State()