diff --git a/src/app/alembic/versions/64fe2045bf28_.py b/src/app/alembic/versions/64fe2045bf28_.py new file mode 100644 index 0000000..baa6432 --- /dev/null +++ b/src/app/alembic/versions/64fe2045bf28_.py @@ -0,0 +1,40 @@ +"""empty message + +Revision ID: 64fe2045bf28 +Revises: 750640043cd4 +Create Date: 2023-01-05 18:28:05.296720 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "64fe2045bf28" +down_revision = "750640043cd4" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "user_activity", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("user", sa.Integer(), nullable=False), + sa.Column("updated", sa.DateTime(), nullable=False), + sa.ForeignKeyConstraint( + ["user"], + ["user_settings.id"], + name="fk_user_activity_user_settings_id_user", + ), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("user"), + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("user_activity") + # ### end Alembic commands ### diff --git a/src/app/models.py b/src/app/models.py index b50df48..24cc810 100644 --- a/src/app/models.py +++ b/src/app/models.py @@ -1,3 +1,5 @@ +from datetime import datetime + import ormar from core.db import metadata, database @@ -30,3 +32,15 @@ class User(ormar.Model): source: str = ormar.String(max_length=32) # type: ignore allowed_langs = ormar.ManyToMany(Language) + + +class UserActivity(ormar.Model): + class Meta(BaseMeta): + tablename = "user_activity" + + id: int = ormar.Integer(primary_key=True) # type: ignore + + user: User = ormar.ForeignKey( + User, nullable=False, unique=True, related_name="last_activity" + ) + updated: datetime = ormar.DateTime(timezone=False) # type: ignore diff --git a/src/app/serializers.py b/src/app/serializers.py index 5778913..977bb5a 100644 --- a/src/app/serializers.py +++ b/src/app/serializers.py @@ -33,4 +33,5 @@ class UserUpdate(BaseModel): class UserDetail(UserBase): + id: int allowed_langs: list[LanguageDetail] diff --git a/src/app/services/users_data_manager.py b/src/app/services/users_data_manager.py index 0d39c4d..c68ed4a 100644 --- a/src/app/services/users_data_manager.py +++ b/src/app/services/users_data_manager.py @@ -50,7 +50,7 @@ class UsersDataManager: @classmethod async def get_user( cls, user_id: int, redis: aioredis.Redis - ) -> Optional[UserDetail]: + ) -> Optional[UserDetail | User]: if cached_user := await cls._get_user_from_cache(user_id, redis): return cached_user @@ -60,7 +60,7 @@ class UsersDataManager: return None await cls._cache_user(user, redis) - return user # type: ignore + return user @classmethod def _is_has_data_to_update(cls, new_user: UserUpdate) -> bool: @@ -106,7 +106,7 @@ class UsersDataManager: @classmethod async def create_or_update_user( cls, data: UserCreateOrUpdate, redis: aioredis.Redis - ): + ) -> User | UserDetail: user = await cls.get_user(data.user_id, redis) if user is None: @@ -121,7 +121,9 @@ class UsersDataManager: @classmethod def _is_need_update( - cls, old_user: UserDetail, new_user: Union[UserUpdate, UserCreateOrUpdate] + cls, + old_user: UserDetail | User, + new_user: Union[UserUpdate, UserCreateOrUpdate], ) -> bool: old_data = old_user.dict() new_data = new_user.dict() diff --git a/src/app/views.py b/src/app/views.py index a4a65d0..9af94a1 100644 --- a/src/app/views.py +++ b/src/app/views.py @@ -1,3 +1,5 @@ +from datetime import datetime + from fastapi import APIRouter, HTTPException, status, Depends, Request from fastapi_pagination import Page, Params @@ -5,7 +7,7 @@ from fastapi_pagination.ext.ormar import paginate from redis import asyncio as aioredis from app.depends import check_token -from app.models import User, Language +from app.models import User, Language, UserActivity from app.serializers import ( UserCreateOrUpdate, UserUpdate, @@ -49,6 +51,23 @@ async def update_user(request: Request, user_id: int, data: UserUpdate): return await UsersDataManager.update_user(user_id, data, redis) +@users_router.post("/{user_id}/update_activity") +async def update_activity( + request: Request, user_id: int, data: UserCreateOrUpdate +) -> None: + redis: aioredis.Redis = request.app.state.redis + user = await UsersDataManager.create_or_update_user(data, redis) + + activity = await UserActivity.objects.get_or_none(user__user_id=user_id) + + if activity is None: + await UserActivity.objects.create(user=user.id, updated=datetime.now()) + return + + activity.updated = datetime.now() + await activity.update() + + languages_router = APIRouter( prefix="/languages", tags=["languages"], dependencies=[Depends(check_token)] )