mirror of
https://github.com/koloideal/DutyLog.git
synced 2026-06-10 18:35:29 +03:00
update
This commit is contained in:
+3
-3
@@ -7,9 +7,9 @@ from alembic import context
|
|||||||
from sqlalchemy.engine import Connection
|
from sqlalchemy.engine import Connection
|
||||||
from sqlalchemy.ext.asyncio import async_engine_from_config
|
from sqlalchemy.ext.asyncio import async_engine_from_config
|
||||||
|
|
||||||
from src.dutylog.infrastructure.database.models.base import Base
|
from dutylog.infrastructure.database.models.base import Base
|
||||||
from src.dutylog.infrastructure.database.models.user import User as User
|
from dutylog.infrastructure.database.models import *
|
||||||
from src.dutylog.infrastructure.utils.config import load_config
|
from dutylog.infrastructure.utils.config import load_config
|
||||||
|
|
||||||
config = context.config
|
config = context.config
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
"""add models
|
||||||
|
|
||||||
|
Revision ID: fc1269fbc645
|
||||||
|
Revises: f012f3ef4b65
|
||||||
|
Create Date: 2026-02-27 17:24:00.349948
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = 'fc1269fbc645'
|
||||||
|
down_revision: Union[str, Sequence[str], None] = 'f012f3ef4b65'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('hours_transactions',
|
||||||
|
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('user_id', sa.BigInteger(), nullable=False),
|
||||||
|
sa.Column('transaction_type', sa.String(length=50), nullable=False),
|
||||||
|
sa.Column('amount', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('admin_id', sa.BigInteger(), nullable=True),
|
||||||
|
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['admin_id'], ['users.id'], ondelete='SET NULL'),
|
||||||
|
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.add_column('users', sa.Column('first_name', sa.String(length=255), nullable=True))
|
||||||
|
op.add_column('users', sa.Column('last_name', sa.String(length=255), nullable=True))
|
||||||
|
op.add_column('users', sa.Column('is_admin', sa.Boolean(), server_default='false', nullable=False))
|
||||||
|
op.add_column('users', sa.Column('active_hours', sa.Integer(), server_default='0', nullable=False))
|
||||||
|
op.add_column('users', sa.Column('inactive_hours', sa.Integer(), server_default='0', nullable=False))
|
||||||
|
op.add_column('users', sa.Column('created_at', sa.DateTime(timezone=True), nullable=False))
|
||||||
|
op.add_column('users', sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False))
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column('users', 'updated_at')
|
||||||
|
op.drop_column('users', 'created_at')
|
||||||
|
op.drop_column('users', 'inactive_hours')
|
||||||
|
op.drop_column('users', 'active_hours')
|
||||||
|
op.drop_column('users', 'is_admin')
|
||||||
|
op.drop_column('users', 'last_name')
|
||||||
|
op.drop_column('users', 'first_name')
|
||||||
|
op.drop_table('hours_transactions')
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -8,10 +8,10 @@ from aiogram_dialog import setup_dialogs
|
|||||||
from dishka import make_async_container
|
from dishka import make_async_container
|
||||||
from dishka.integrations.aiogram import setup_dishka
|
from dishka.integrations.aiogram import setup_dishka
|
||||||
|
|
||||||
from src.dutylog.application.bot.user_handlers import router as user_router
|
from dutylog.application.bot.user_handlers import router as user_router
|
||||||
from src.dutylog.application.bot.user_dialogs import main_menu_dialog
|
from dutylog.application.bot.user_dialogs import main_menu_dialog
|
||||||
from src.dutylog.infrastructure.ioc import ConfigProvider, DatabaseProvider, DAOProvider
|
from dutylog.infrastructure.ioc import ConfigProvider, DatabaseProvider, DAOProvider
|
||||||
from src.dutylog.infrastructure.utils.config import load_config
|
from dutylog.infrastructure.utils.config import load_config
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
from src.dutylog.application.bot.user_dialogs.main_menu_dialog import main_menu_dialog
|
from dutylog.application.bot.user_dialogs.main_menu_dialog import main_menu_dialog
|
||||||
|
|
||||||
__all__ = ["main_menu_dialog"]
|
__all__ = ["main_menu_dialog"]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from aiogram_dialog import Dialog, Window
|
from aiogram_dialog import Dialog, Window
|
||||||
from aiogram_dialog.widgets.text import Const
|
from aiogram_dialog.widgets.text import Const
|
||||||
|
|
||||||
from src.dutylog.application.bot.user_dialogs.states import MainMenuSG
|
from dutylog.application.bot.user_dialogs.states import MainMenuSG
|
||||||
|
|
||||||
main_menu_dialog = Dialog(
|
main_menu_dialog = Dialog(
|
||||||
Window(
|
Window(
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from aiogram.filters import CommandStart
|
|||||||
from aiogram.types import Message
|
from aiogram.types import Message
|
||||||
from aiogram_dialog import DialogManager, StartMode
|
from aiogram_dialog import DialogManager, StartMode
|
||||||
|
|
||||||
from src.dutylog.application.bot.user_dialogs.states import MainMenuSG
|
from dutylog.application.bot.user_dialogs.states import MainMenuSG
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
from sqlalchemy import select, delete
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from dutylog.infrastructure.database.models.hours_transaction import HoursTransaction
|
||||||
|
|
||||||
|
|
||||||
|
class HoursTransactionsDAO:
|
||||||
|
def __init__(self, session: AsyncSession):
|
||||||
|
self.session = session
|
||||||
|
|
||||||
|
async def get_by_id(self, transaction_id: int) -> HoursTransaction | None:
|
||||||
|
result = await self.session.execute(
|
||||||
|
select(HoursTransaction).where(HoursTransaction.id == transaction_id)
|
||||||
|
)
|
||||||
|
return result.scalar_one_or_none()
|
||||||
|
|
||||||
|
async def get_all(self) -> list[HoursTransaction]:
|
||||||
|
result = await self.session.execute(
|
||||||
|
select(HoursTransaction).order_by(HoursTransaction.created_at.desc())
|
||||||
|
)
|
||||||
|
return list(result.scalars().all())
|
||||||
|
|
||||||
|
async def get_by_user_id(self, user_id: int) -> list[HoursTransaction]:
|
||||||
|
result = await self.session.execute(
|
||||||
|
select(HoursTransaction)
|
||||||
|
.where(HoursTransaction.user_id == user_id)
|
||||||
|
.order_by(HoursTransaction.created_at.desc())
|
||||||
|
)
|
||||||
|
return list(result.scalars().all())
|
||||||
|
|
||||||
|
async def create(self, transaction: HoursTransaction) -> HoursTransaction:
|
||||||
|
self.session.add(transaction)
|
||||||
|
await self.session.commit()
|
||||||
|
await self.session.refresh(transaction)
|
||||||
|
return transaction
|
||||||
|
|
||||||
|
async def delete(self, transaction_id: int) -> None:
|
||||||
|
await self.session.execute(
|
||||||
|
delete(HoursTransaction).where(HoursTransaction.id == transaction_id)
|
||||||
|
)
|
||||||
|
await self.session.commit()
|
||||||
@@ -1,21 +1,38 @@
|
|||||||
from sqlalchemy import select
|
from sqlalchemy import select, update, delete
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from src.dutylog.infrastructure.database.models.user import User
|
from dutylog.infrastructure.database.models.user import User
|
||||||
|
|
||||||
|
|
||||||
class UsersDAO:
|
class UsersDAO:
|
||||||
def __init__(self, session: AsyncSession):
|
def __init__(self, session: AsyncSession):
|
||||||
self.session = session
|
self.session = session
|
||||||
|
|
||||||
async def get_user(self, user_id: int) -> User | None:
|
async def get_by_id(self, user_id: int) -> User | None:
|
||||||
result = await self.session.execute(
|
result = await self.session.execute(
|
||||||
select(User).where(User.id == user_id)
|
select(User).where(User.id == user_id)
|
||||||
)
|
)
|
||||||
return result.scalar_one_or_none()
|
return result.scalar_one_or_none()
|
||||||
|
|
||||||
async def create_user(self, user_id: int, username: str | None) -> User:
|
async def get_all(self) -> list[User]:
|
||||||
user = User(id=user_id, username=username)
|
result = await self.session.execute(select(User))
|
||||||
|
return list(result.scalars().all())
|
||||||
|
|
||||||
|
async def create(self, user: User) -> User:
|
||||||
self.session.add(user)
|
self.session.add(user)
|
||||||
await self.session.commit()
|
await self.session.commit()
|
||||||
|
await self.session.refresh(user)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
async def update(self, user_id: int, **kwargs) -> User | None:
|
||||||
|
await self.session.execute(
|
||||||
|
update(User).where(User.id == user_id).values(**kwargs)
|
||||||
|
)
|
||||||
|
await self.session.commit()
|
||||||
|
return await self.get_by_id(user_id)
|
||||||
|
|
||||||
|
async def delete(self, user_id: int) -> None:
|
||||||
|
await self.session.execute(
|
||||||
|
delete(User).where(User.id == user_id)
|
||||||
|
)
|
||||||
|
await self.session.commit()
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from dutylog.infrastructure.database.models.base import Base
|
||||||
|
from dutylog.infrastructure.database.models.user import User
|
||||||
|
from dutylog.infrastructure.database.models.hours_transaction import HoursTransaction
|
||||||
|
|
||||||
|
__all__ = ["Base", "User", "HoursTransaction"]
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
from datetime import datetime, timezone, timedelta
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from sqlalchemy import BigInteger, Integer, String, DateTime, ForeignKey
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
|
from dutylog.infrastructure.database.models.base import Base
|
||||||
|
from dutylog.infrastructure.utils.datetime import msk_now
|
||||||
|
|
||||||
|
|
||||||
|
class TransactionType(str, Enum):
|
||||||
|
INCREASE = "increase"
|
||||||
|
DECREASE = "decrease"
|
||||||
|
|
||||||
|
|
||||||
|
class HoursTransaction(Base):
|
||||||
|
__tablename__ = "hours_transactions"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
user_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||||
|
transaction_type: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||||
|
amount: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
admin_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
||||||
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=msk_now)
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
from sqlalchemy import BigInteger, String
|
from datetime import datetime
|
||||||
|
|
||||||
|
from sqlalchemy import BigInteger, Boolean, Integer, String, DateTime
|
||||||
from sqlalchemy.orm import Mapped, mapped_column
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
from src.dutylog.infrastructure.database.models.base import Base
|
from dutylog.infrastructure.database.models.base import Base
|
||||||
|
from dutylog.infrastructure.utils.datetime import msk_now
|
||||||
|
|
||||||
|
|
||||||
class User(Base):
|
class User(Base):
|
||||||
@@ -9,3 +12,10 @@ class User(Base):
|
|||||||
|
|
||||||
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
||||||
username: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
username: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||||
|
first_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||||
|
last_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||||
|
is_admin: Mapped[bool] = mapped_column(Boolean, default=False, server_default="false")
|
||||||
|
active_hours: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
|
||||||
|
inactive_hours: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
|
||||||
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=msk_now)
|
||||||
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=msk_now, onupdate=msk_now)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker
|
|||||||
|
|
||||||
from dutylog.infrastructure.database.config import create_engine, create_session_maker
|
from dutylog.infrastructure.database.config import create_engine, create_session_maker
|
||||||
from dutylog.infrastructure.database.dao.users_dao import UsersDAO
|
from dutylog.infrastructure.database.dao.users_dao import UsersDAO
|
||||||
|
from dutylog.infrastructure.database.dao.hours_transactions_dao import HoursTransactionsDAO
|
||||||
from dutylog.infrastructure.utils.config import Config, load_config
|
from dutylog.infrastructure.utils.config import Config, load_config
|
||||||
|
|
||||||
|
|
||||||
@@ -36,3 +37,8 @@ class DAOProvider(Provider):
|
|||||||
def get_users_dao(self, session: AsyncSession) -> UsersDAO:
|
def get_users_dao(self, session: AsyncSession) -> UsersDAO:
|
||||||
return UsersDAO(session)
|
return UsersDAO(session)
|
||||||
|
|
||||||
|
@provide(scope=Scope.REQUEST)
|
||||||
|
def get_hours_transactions_dao(self, session: AsyncSession) -> HoursTransactionsDAO:
|
||||||
|
return HoursTransactionsDAO(session)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
from datetime import datetime, timezone, timedelta
|
||||||
|
|
||||||
|
|
||||||
|
MSK_TZ = timezone(timedelta(hours=3))
|
||||||
|
|
||||||
|
|
||||||
|
def msk_now() -> datetime:
|
||||||
|
return datetime.now(MSK_TZ)
|
||||||
Reference in New Issue
Block a user