-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
070a2d1
commit 6d3aa25
Showing
6 changed files
with
281 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
"""notification_models | ||
Revision ID: 20d3addee5ba | ||
Revises: 981474c39706 | ||
Create Date: 2024-07-30 20:18:41.017469 | ||
""" | ||
from alembic import op | ||
import sqlalchemy as sa | ||
|
||
|
||
# revision identifiers, used by Alembic. | ||
revision = '20d3addee5ba' | ||
down_revision = '981474c39706' | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade(): | ||
# ### commands auto generated by Alembic - please adjust! ### | ||
with op.batch_alter_table('notification', schema=None) as batch_op: | ||
batch_op.add_column(sa.Column('message', sa.Text(), nullable=False)) | ||
batch_op.drop_column('description') | ||
|
||
with op.batch_alter_table('notification_user_association', schema=None) as batch_op: | ||
batch_op.add_column(sa.Column('viewed', sa.Boolean(), nullable=True)) | ||
batch_op.add_column(sa.Column('date', sa.DateTime(timezone=True), nullable=True)) | ||
batch_op.drop_constraint('constraint_notification_user', type_='unique') | ||
|
||
# ### end Alembic commands ### | ||
|
||
|
||
def downgrade(): | ||
# ### commands auto generated by Alembic - please adjust! ### | ||
with op.batch_alter_table('notification_user_association', schema=None) as batch_op: | ||
batch_op.create_unique_constraint('constraint_notification_user', ['notification_id', 'user_id']) | ||
batch_op.drop_column('date') | ||
batch_op.drop_column('viewed') | ||
|
||
with op.batch_alter_table('notification', schema=None) as batch_op: | ||
batch_op.add_column(sa.Column('description', sa.TEXT(), nullable=True)) | ||
batch_op.drop_column('message') | ||
|
||
# ### end Alembic commands ### |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,156 @@ | ||
from fastapi import APIRouter, Depends | ||
from fastapi import APIRouter, Depends, Query, Response | ||
from sqlalchemy.ext.asyncio import AsyncSession | ||
from starlette import status | ||
|
||
from app.api.validators import check_obj_duplicate, check_obj_exists | ||
from app.core.db import get_async_session | ||
from app.core.user import current_superuser, current_user | ||
from app.crud import notification_crud | ||
from app.schemas.notification import NotificationCreate, NotificationRead | ||
from app.crud import notification_crud, user_crud | ||
from app.models import User | ||
from app.schemas.notification import ( | ||
AddNotificationForUser, NotificationCreate, NotificationRead, | ||
ReadNotificationForUser, | ||
) | ||
from app.services.utils import ( | ||
Pagination, add_response_headers, get_pagination_params, paginated, | ||
) | ||
|
||
router = APIRouter() | ||
|
||
|
||
@router.post('/', dependencies=[Depends(current_superuser)]) | ||
@router.post( | ||
'/', | ||
status_code=status.HTTP_201_CREATED, | ||
dependencies=[Depends(current_superuser)], | ||
) | ||
async def create_notification( | ||
notification: NotificationCreate, | ||
session: AsyncSession = Depends(get_async_session) | ||
session: AsyncSession = Depends(get_async_session) | ||
): | ||
return await notification_crud.create(notification, session) | ||
"""Создаст уведомление.""" | ||
obj = await notification_crud.get_by_attr( | ||
attr_name='name', | ||
attr_value=notification.name, | ||
session=session, | ||
) | ||
await check_obj_duplicate(obj=obj) | ||
await notification_crud.create(obj_in=notification, session=session) | ||
|
||
|
||
@router.get( | ||
'/', | ||
response_model=list[NotificationRead], | ||
dependencies=[Depends(current_user)] | ||
dependencies=[Depends(current_superuser)], | ||
) | ||
async def get_all_notifications( | ||
session: AsyncSession = Depends(get_async_session) | ||
session: AsyncSession = Depends(get_async_session), | ||
): | ||
"""Вернет список всех уведомлений.""" | ||
return await notification_crud.get_multi(session=session) | ||
|
||
|
||
@router.delete( | ||
'/{notification_id}', | ||
status_code=status.HTTP_204_NO_CONTENT, | ||
dependencies=[Depends(current_superuser)], | ||
) | ||
async def delete_notification( | ||
notification_id: int, | ||
session: AsyncSession = Depends(get_async_session) | ||
): | ||
"""Удалит уведомление.""" | ||
obj = await notification_crud.get_by_attr( | ||
attr_name='id', | ||
attr_value=notification_id, | ||
session=session, | ||
) | ||
await check_obj_exists(obj=obj) | ||
await notification_crud.remove( | ||
db_obj=obj, | ||
session=session, | ||
) | ||
|
||
|
||
@router.post( | ||
'/add-for-user', | ||
status_code=status.HTTP_201_CREATED, | ||
dependencies=[Depends(current_superuser)], | ||
) | ||
async def add_notification_for_user( | ||
notification: AddNotificationForUser, | ||
session: AsyncSession = Depends(get_async_session) | ||
): | ||
"""Создаст связь пользователя и уведомления.""" | ||
db_user = await user_crud.get_by_attr( | ||
attr_name='id', | ||
attr_value=notification.user_id, | ||
session=session, | ||
) | ||
await check_obj_exists(db_user) | ||
db_notification = await notification_crud.get_by_attr( | ||
attr_name='id', | ||
attr_value=notification.notification_id, | ||
session=session, | ||
) | ||
await check_obj_exists(db_notification) | ||
|
||
await notification_crud.add_notification_for_user( | ||
obj_in=notification, | ||
session=session, | ||
) | ||
|
||
|
||
@router.get( | ||
'/get-for-user', | ||
response_model=list[ReadNotificationForUser], | ||
response_model_exclude_none=True, | ||
dependencies=[Depends(current_user)], | ||
) | ||
async def get_notifications_for_user( | ||
response: Response, | ||
pagination: Pagination = Depends(get_pagination_params), | ||
user: User = Depends(current_user), | ||
viewed: bool = Query(default=False, alias="viewed"), | ||
bell: bool = Query(default=False, alias="bell"), | ||
session: AsyncSession = Depends(get_async_session), | ||
): | ||
""" | ||
Вернет список уведомлений для пользователя. | ||
Параметры: | ||
- **viewed**: (bool) Если True, вернет прочитанные уведомления. | ||
По умолчанию False. | ||
- **bell**: (bool) Если True, вернет непрочитанные уведомления без поля | ||
message. Дополнительно указывать параметр viewed не требуется. | ||
По умолчанию False. | ||
""" | ||
notifications = await notification_crud.get_user_notifications( | ||
user_id=user.id, | ||
viewed=viewed, | ||
bell=bell, | ||
session=session, | ||
) | ||
add_response_headers(response, notifications, pagination) | ||
return paginated(notifications, pagination) | ||
|
||
|
||
@router.patch( | ||
'/mark-as-viewed/{user_notification_id}', | ||
dependencies=[Depends(current_user)], | ||
) | ||
async def mark_as_viewed( | ||
user_notification_id: int, | ||
user: User = Depends(current_user), | ||
session: AsyncSession = Depends(get_async_session) | ||
): | ||
return await notification_crud.get_multi(session) | ||
"""Пометит уведомление как прочитанное.""" | ||
obj = await notification_crud.get_user_notification_by_id( | ||
obj_id=user_notification_id, | ||
user_id=user.id, | ||
session=session, | ||
) | ||
await check_obj_exists(obj=obj) | ||
await notification_crud.mark_user_notification_as_viewed( | ||
obj_id=user_notification_id, | ||
session=session, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,78 @@ | ||
from sqlalchemy import and_, select, update | ||
from sqlalchemy.ext.asyncio import AsyncSession | ||
|
||
from app.crud.base import CRUDBase | ||
from app.models import Notification | ||
from app.models.notification import notification_user_association | ||
|
||
|
||
class CRUDNotification(CRUDBase): | ||
pass | ||
async def add_notification_for_user( | ||
self, | ||
obj_in, | ||
session: AsyncSession, | ||
): | ||
stmt = notification_user_association.insert().values( | ||
user_id=obj_in.user_id, | ||
notification_id=obj_in.notification_id, | ||
) | ||
await session.execute(stmt) | ||
await session.commit() | ||
|
||
async def get_user_notifications( | ||
self, | ||
user_id: int, | ||
viewed: bool, | ||
bell: bool, | ||
session: AsyncSession, | ||
): | ||
stmt = select( | ||
self.model.name, | ||
self.model.message if not bell else None, | ||
notification_user_association.c.id, | ||
notification_user_association.c.date, | ||
).join( | ||
notification_user_association, | ||
).where(and_( | ||
notification_user_association.c.user_id == user_id, | ||
notification_user_association.c.viewed == ( | ||
viewed if not bell else False | ||
), | ||
)) | ||
db_objs = await session.execute(stmt) | ||
return db_objs.mappings().all() | ||
|
||
async def get_user_notification_by_id( | ||
self, | ||
obj_id: int, | ||
user_id: int, | ||
session: AsyncSession, | ||
): | ||
stmt = select( | ||
notification_user_association, | ||
).where(and_( | ||
notification_user_association.c.id == obj_id, | ||
notification_user_association.c.user_id == user_id, | ||
)) | ||
db_obj = await session.execute(stmt) | ||
return db_obj.mappings().all() | ||
|
||
async def mark_user_notification_as_viewed(self, obj_id, session): | ||
stmt = update( | ||
notification_user_association, | ||
).where( | ||
notification_user_association.c.id == obj_id, | ||
).values(viewed=True) | ||
await session.execute(stmt) | ||
await session.commit() | ||
|
||
async def remove(self, db_obj: Notification, session: AsyncSession): | ||
stmt = notification_user_association.delete().where( | ||
notification_user_association.c.notification_id == db_obj.id | ||
) | ||
await session.execute(stmt) | ||
await session.delete(db_obj) | ||
await session.commit() | ||
|
||
|
||
notification_crud = CRUDNotification(Notification) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,29 @@ | ||
from typing import Optional | ||
from datetime import datetime | ||
|
||
from pydantic import BaseModel | ||
|
||
|
||
class NotificationCreate(BaseModel): | ||
name: str | ||
description: Optional[str] | ||
message: str | ||
|
||
|
||
class NotificationRead(BaseModel): | ||
id: int | ||
name: Optional[str] | ||
description: Optional[str] | ||
name: str | ||
message: str | ||
|
||
|
||
class AddNotificationForUser(BaseModel): | ||
user_id: int | ||
notification_id: int | ||
|
||
|
||
class ReadNotificationForUser(BaseModel): | ||
id: int | ||
name: str | ||
message: str = None | ||
date: datetime | ||
|
||
class Config: | ||
from_attributes = True |