From ea68725b63782ec8099d977d843a8b0c7c7902b9 Mon Sep 17 00:00:00 2001 From: Sergey Natalenko Date: Sun, 14 Jan 2024 10:51:17 +0300 Subject: [PATCH] Fix mailings --- docker-compose.dev.yaml | 5 - .../dialogs/admins/mailings/create/confirm.py | 4 +- ...b69ba4_.py => 2024_01_14_f0bb0f56f4aa_.py} | 98 ++++++++++--------- idb/db/models.py | 3 + idb/db/repositories/mailing.py | 5 +- idb/logic/mailing.py | 8 +- tests/factories.py | 6 +- .../test_mailing/test_update_mailing_by_id.py | 18 ++++ 8 files changed, 92 insertions(+), 55 deletions(-) rename idb/db/migrations/versions/{2024_01_03_41c796b69ba4_.py => 2024_01_14_f0bb0f56f4aa_.py} (95%) create mode 100644 tests/test_logic/test_mailing/test_update_mailing_by_id.py diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml index cae31ec..ae67675 100644 --- a/docker-compose.dev.yaml +++ b/docker-compose.dev.yaml @@ -9,8 +9,6 @@ services: POSTGRES_DB: pgdb ports: - 5432:5432 - volumes: - - postgres_data:/var/lib/postgresql/data redis: image: redis @@ -18,6 +16,3 @@ services: ports: - 6379:6379 -volumes: - postgres_data: - driver: local diff --git a/idb/bot/dialogs/admins/mailings/create/confirm.py b/idb/bot/dialogs/admins/mailings/create/confirm.py index 0b4ffcf..aac45b1 100644 --- a/idb/bot/dialogs/admins/mailings/create/confirm.py +++ b/idb/bot/dialogs/admins/mailings/create/confirm.py @@ -34,16 +34,18 @@ async def on_click( t = dialog_manager.dialog_data.get("time") d = dialog_manager.dialog_data.get("date") scheduled_at = parse_dt(t=t, d=d) + author_id: int = c.from_user.id await save_mailing( uow=uow, bot=dialog_manager.middleware_data["bot"], + author_id=author_id, title=dialog_manager.dialog_data["title"], content=dialog_manager.dialog_data["content"], scheduled_at=None if not scheduled_at else scheduled_at, user_type_ids=dialog_manager.dialog_data["user_types"], ) dialog_manager.show_mode = ShowMode.SEND - await c.bot.send_message(c.from_user.id, text="Рассылка создана") # type: ignore[union-attr] + await c.bot.send_message(author_id, text="Рассылка создана") # type: ignore[union-attr] await dialog_manager.done() diff --git a/idb/db/migrations/versions/2024_01_03_41c796b69ba4_.py b/idb/db/migrations/versions/2024_01_14_f0bb0f56f4aa_.py similarity index 95% rename from idb/db/migrations/versions/2024_01_03_41c796b69ba4_.py rename to idb/db/migrations/versions/2024_01_14_f0bb0f56f4aa_.py index d7c0e64..ac74df0 100644 --- a/idb/db/migrations/versions/2024_01_03_41c796b69ba4_.py +++ b/idb/db/migrations/versions/2024_01_14_f0bb0f56f4aa_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: 41c796b69ba4 +Revision ID: f0bb0f56f4aa Revises: -Create Date: 2024-01-03 22:29:54.480730 +Create Date: 2024-01-14 10:49:52.849965 """ import sqlalchemy as sa @@ -10,7 +10,7 @@ from sqlalchemy.dialects import postgresql # revision identifiers, used by Alembic. -revision = "41c796b69ba4" +revision = "f0bb0f56f4aa" down_revision = None branch_labels = None depends_on = None @@ -18,32 +18,6 @@ def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.create_table( - "mailing", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("scheduled_at", sa.DateTime(timezone=True), nullable=True), - sa.Column("status", sa.String(length=16), nullable=False), - sa.Column("sent_at", sa.DateTime(timezone=True), nullable=True), - sa.Column("cancelled_at", sa.DateTime(timezone=True), nullable=True), - sa.Column("title", sa.String(length=512), nullable=False), - sa.Column("content", sa.String(length=3072), nullable=False), - sa.Column( - "created_at", - sa.DateTime(timezone=True), - server_default=sa.text("TIMEZONE('utc', now())"), - nullable=False, - ), - sa.Column( - "updated_at", - sa.DateTime(timezone=True), - server_default=sa.text("TIMEZONE('utc', now())"), - nullable=False, - ), - sa.PrimaryKeyConstraint("id", name=op.f("pk__mailing")), - ) - op.create_index( - op.f("ix__mailing__scheduled_at"), "mailing", ["scheduled_at"], unique=False - ) op.create_table( "submenu", sa.Column("id", sa.Integer(), nullable=False), @@ -145,22 +119,37 @@ def upgrade() -> None: op.f("ix__feedback__user_id"), "feedback", ["user_id"], unique=False ) op.create_table( - "mailing_user_type", - sa.Column("mailing_id", sa.Integer(), nullable=False), - sa.Column("user_type_id", sa.Integer(), nullable=False), - sa.ForeignKeyConstraint( - ["mailing_id"], - ["mailing.id"], - name=op.f("fk__mailing_user_type__mailing_id__mailing"), + "mailing", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("author_id", sa.BigInteger(), nullable=False), + sa.Column("scheduled_at", sa.DateTime(timezone=True), nullable=True), + sa.Column("status", sa.String(length=16), nullable=False), + sa.Column("sent_at", sa.DateTime(timezone=True), nullable=True), + sa.Column("cancelled_at", sa.DateTime(timezone=True), nullable=True), + sa.Column("title", sa.String(length=512), nullable=False), + sa.Column("content", sa.String(length=3072), nullable=False), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.text("TIMEZONE('utc', now())"), + nullable=False, ), - sa.ForeignKeyConstraint( - ["user_type_id"], - ["user_type.id"], - name=op.f("fk__mailing_user_type__user_type_id__user_type"), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.text("TIMEZONE('utc', now())"), + nullable=False, ), - sa.PrimaryKeyConstraint( - "mailing_id", "user_type_id", name=op.f("pk__mailing_user_type") + sa.ForeignKeyConstraint( + ["author_id"], ["users.id"], name=op.f("fk__mailing__author_id__users") ), + sa.PrimaryKeyConstraint("id", name=op.f("pk__mailing")), + ) + op.create_index( + op.f("ix__mailing__author_id"), "mailing", ["author_id"], unique=False + ) + op.create_index( + op.f("ix__mailing__scheduled_at"), "mailing", ["scheduled_at"], unique=False ) op.create_table( "user_type_user", @@ -219,17 +208,38 @@ def upgrade() -> None: op.create_index( op.f("ix__answer__to_user_id"), "answer", ["to_user_id"], unique=False ) + op.create_table( + "mailing_user_type", + sa.Column("mailing_id", sa.Integer(), nullable=False), + sa.Column("user_type_id", sa.Integer(), nullable=False), + sa.ForeignKeyConstraint( + ["mailing_id"], + ["mailing.id"], + name=op.f("fk__mailing_user_type__mailing_id__mailing"), + ), + sa.ForeignKeyConstraint( + ["user_type_id"], + ["user_type.id"], + name=op.f("fk__mailing_user_type__user_type_id__user_type"), + ), + sa.PrimaryKeyConstraint( + "mailing_id", "user_type_id", name=op.f("pk__mailing_user_type") + ), + ) # ### end Alembic commands ### def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("mailing_user_type") op.drop_index(op.f("ix__answer__to_user_id"), table_name="answer") op.drop_index(op.f("ix__answer__from_user_id"), table_name="answer") op.drop_index(op.f("ix__answer__feedback_id"), table_name="answer") op.drop_table("answer") op.drop_table("user_type_user") - op.drop_table("mailing_user_type") + op.drop_index(op.f("ix__mailing__scheduled_at"), table_name="mailing") + op.drop_index(op.f("ix__mailing__author_id"), table_name="mailing") + op.drop_table("mailing") op.drop_index(op.f("ix__feedback__user_id"), table_name="feedback") op.drop_table("feedback") op.drop_table("users") @@ -237,6 +247,4 @@ def downgrade() -> None: op.drop_table("user_type") op.drop_table("url") op.drop_table("submenu") - op.drop_index(op.f("ix__mailing__scheduled_at"), table_name="mailing") - op.drop_table("mailing") # ### end Alembic commands ### diff --git a/idb/db/models.py b/idb/db/models.py index 44f9de3..42d3ca0 100644 --- a/idb/db/models.py +++ b/idb/db/models.py @@ -102,6 +102,9 @@ class Answer(TimestampMixin, Base): class Mailing(TimestampMixin, Base): id: Mapped[int] = mapped_column(Integer, primary_key=True) + author_id: Mapped[int] = mapped_column( + BigInteger, ForeignKey("users.id"), nullable=False, index=True + ) scheduled_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True, index=True ) diff --git a/idb/db/repositories/mailing.py b/idb/db/repositories/mailing.py index 66ebc45..2df4682 100644 --- a/idb/db/repositories/mailing.py +++ b/idb/db/repositories/mailing.py @@ -10,7 +10,6 @@ from idb.db.models import MailingUserType as MailingUserTypeDb from idb.db.repositories.base import Repository from idb.exceptions import ( - EntityNotFoundError, InclusiveDanceError, MailingNotFoundError, ) @@ -34,6 +33,7 @@ async def get_by_id(self, mailing_id: int) -> Mailing: async def create( self, *, + author_id: int, title: str, content: str, scheduled_at: datetime | None, @@ -43,6 +43,7 @@ async def create( stmt = ( insert(MailingDb) .values( + author_id=author_id, title=title, content=content, scheduled_at=scheduled_at, @@ -119,7 +120,7 @@ async def _update(self, *args: Any, **kwargs: Any) -> MailingDb: obj = result.one() await self._session.flush(obj) except NoResultFound as e: - raise EntityNotFoundError from e + raise MailingNotFoundError from e await self._session.refresh(obj) return obj diff --git a/idb/logic/mailing.py b/idb/logic/mailing.py index d4264f9..e28f6cb 100644 --- a/idb/logic/mailing.py +++ b/idb/logic/mailing.py @@ -39,19 +39,25 @@ async def process_new_mailing(uow: UnitOfWork, bot: Bot, mailing: Mailing) -> No except Exception: log.exception("Occured something") now = datetime.now(tz=pytz.utc) - await uow.mailings.update_by_id(mailing_id=mailing.id, is_sent=True, sent_at=now) + await uow.mailings.update_by_id( + mailing_id=mailing.id, + status=MailingStatus.SENT, + sent_at=now, + ) await uow.commit() async def save_mailing( uow: UnitOfWork, bot: Bot, + author_id: int, title: str, content: str, scheduled_at: datetime | None, user_type_ids: list[int], ) -> None: mailing = await uow.mailings.create( + author_id=author_id, title=title, content=content, scheduled_at=scheduled_at, diff --git a/tests/factories.py b/tests/factories.py index ab5d493..d2c0530 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -7,7 +7,7 @@ handle_constrained_string_or_bytes, ) -from idb.db.models import Feedback, Submenu, Url, User, UserType +from idb.db.models import Feedback, Mailing, Submenu, Url, User, UserType from idb.generals.enums import SubmenuType @@ -66,6 +66,10 @@ class FeedbackFactory(SQLAlchemyFactory[Feedback]): __set_relationships__ = True +class MailingFeedback(SQLAlchemyFactory[Mailing]): + __model__ = Mailing + + FACTORIES: tuple[SQLAlchemyFactory, ...] = ( UserFactory, UserTypeFactory, diff --git a/tests/test_logic/test_mailing/test_update_mailing_by_id.py b/tests/test_logic/test_mailing/test_update_mailing_by_id.py new file mode 100644 index 0000000..b2d2be5 --- /dev/null +++ b/tests/test_logic/test_mailing/test_update_mailing_by_id.py @@ -0,0 +1,18 @@ +from datetime import datetime + +import pytest + +from idb.db.uow import UnitOfWork +from idb.exceptions.mailing import MailingNotFoundError +from idb.logic.mailing import update_mailing_by_id + +DEFAULT_DT = datetime(year=2021, month=1, day=1) + + +async def test_mailing_not_found(uow: UnitOfWork) -> None: + with pytest.raises(MailingNotFoundError): + await update_mailing_by_id( + uow=uow, + mailing_id=-1, + sent_at=DEFAULT_DT, + )