From 8a0e2e01211216b47d4634862694ddf7b265223c Mon Sep 17 00:00:00 2001 From: Arduinum Date: Fri, 26 Dec 2025 15:30:08 +0300 Subject: [PATCH] feat: added cruds, seed, schemas, session --- pyproject.toml | 1 + src/kawai_focus_v2/core/messages/__init__.py | 0 src/kawai_focus_v2/core/messages/errors.py | 19 ++++++ src/kawai_focus_v2/database/cruds.py | 62 ++++++++++++++++++++ src/kawai_focus_v2/database/data.py | 19 ++++++ src/kawai_focus_v2/database/db.py | 33 +++++++++++ src/kawai_focus_v2/database/seed.py | 18 ++++++ src/kawai_focus_v2/schemas/timer.py | 40 +++++++++++++ 8 files changed, 192 insertions(+) create mode 100644 src/kawai_focus_v2/core/messages/__init__.py create mode 100644 src/kawai_focus_v2/core/messages/errors.py create mode 100644 src/kawai_focus_v2/database/cruds.py create mode 100644 src/kawai_focus_v2/database/data.py create mode 100644 src/kawai_focus_v2/database/db.py create mode 100644 src/kawai_focus_v2/database/seed.py create mode 100644 src/kawai_focus_v2/schemas/timer.py diff --git a/pyproject.toml b/pyproject.toml index b1bedf7..cc51283 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ dependencies = [ [project.scripts] kawai-focus-v2 = "kawai_focus_v2:main" +filling-timers = "kawai_focus_v2.database.seed:start_filling_data" [build-system] requires = ["uv_build>=0.9.18,<0.10.0"] diff --git a/src/kawai_focus_v2/core/messages/__init__.py b/src/kawai_focus_v2/core/messages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/kawai_focus_v2/core/messages/errors.py b/src/kawai_focus_v2/core/messages/errors.py new file mode 100644 index 0000000..cb7bea8 --- /dev/null +++ b/src/kawai_focus_v2/core/messages/errors.py @@ -0,0 +1,19 @@ +from enum import Enum + + +class ErrorMessage(Enum): + """Сообщения об ошибках""" + + NO_TIME = 'Не указано время таймера!' + NEGATIVE_TIME = 'Время таймера не может быть отрицательным!' + SS_MM_BIG = 'Секунды и минуты не могут быть больше 59!' + HH_BIG = 'Часы не могут быть больше 23!' + FOLDER_NOT_FOUND = 'Папка не найдена!' + JSON_NOT_FOUND = 'Файл json не найден!' + TYPE_DICT = 'Тип данных не является словарем!' + NOT_INT_TYPE = 'Тип данных не является целым числом!' + KEY_ERROR = 'Ключ не найден!' + CONNECTION_ERROR = 'Подключение к базе данных не удалось!' + INTEGRITY_ERROR = 'Ошибка целостности данных (нарушение уникальности или связей)!' + OPERATIONAL_ERROR = 'Ошибка выполнения SQL-запроса (возможная блокировка или повреждение БД)!' + VALIDATION_ERROR = 'Ошибка валидации данных (несоответствие ожидаемой схеме)!' diff --git a/src/kawai_focus_v2/database/cruds.py b/src/kawai_focus_v2/database/cruds.py new file mode 100644 index 0000000..abec8d1 --- /dev/null +++ b/src/kawai_focus_v2/database/cruds.py @@ -0,0 +1,62 @@ +from sqlalchemy import select + +from kawai_focus_v2.database.models import Timer +from kawai_focus_v2.schemas.timer import TimerModel, TimerListModel +from kawai_focus_v2.database.db import get_db_instance, SessionDB + + +def get_timer(timer_id: int) -> TimerModel: + """Функция для получения данных таймера""" + + db = get_db_instance() + with db.get_session() as session: + timer = session.get(Timer, timer_id) + return TimerModel.model_validate(timer, from_attributes=True) + + +def list_timers() -> list[TimerListModel]: + """Функция для получения списка таймеров""" + + db = get_db_instance() + with db.get_session() as session: + timers = session.execute( + select(Timer.id, Timer.title, Timer.pomodoro_time, Timer.count_pomodoro) + ).all() + + return [TimerListModel.model_validate(timer, from_attributes=True) for timer in timers] + + +def new_timer(data: TimerModel, db: SessionDB) -> TimerModel: + """Функция для создания нового таймера""" + + with db.get_session() as session: + timer = Timer(**data.model_dump()) + session.add(timer) + session.commit() + session.refresh(timer) + + return TimerModel.model_validate(timer, from_attributes=True) + + +def update_timer(data: TimerModel, db: SessionDB) -> TimerModel: + """Функция для обновления таймера""" + + with db.get_session() as session: + timer = session.get(Timer, data.id) + + for field, value in data.model_dump(exclude={'id'}).items(): + setattr(timer, field, value) + + session.commit() + session.refresh(timer) + + return TimerModel.model_validate(timer, from_attributes=True) + + +def del_timer(timer_id: int, db: SessionDB) -> None: + """Функция для удаления таймера""" + + with db.get_session() as session: + timer = session.get(Timer, timer_id) + session.delete(timer) + session.commit() diff --git a/src/kawai_focus_v2/database/data.py b/src/kawai_focus_v2/database/data.py new file mode 100644 index 0000000..ed9c497 --- /dev/null +++ b/src/kawai_focus_v2/database/data.py @@ -0,0 +1,19 @@ +from kawai_focus_v2.schemas.timer import TimerModel + + +data_timers = [ + TimerModel( + title='Timer mini example', + pomodoro_time=10, + break_time=3, + break_long_time=15, + count_pomodoro=2 + ), + TimerModel( + title='Timer max example', + pomodoro_time=90, + break_time=10, + break_long_time=40, + count_pomodoro=8 + ), +] diff --git a/src/kawai_focus_v2/database/db.py b/src/kawai_focus_v2/database/db.py new file mode 100644 index 0000000..afb4739 --- /dev/null +++ b/src/kawai_focus_v2/database/db.py @@ -0,0 +1,33 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker, Session +from functools import lru_cache + +from kawai_focus_v2.core.settings import settings + + +class SessionDB: + """Класс для управления подключением к базе данных.""" + + def __init__(self) -> None: + self._engine = create_engine( + url=settings.db_settings.get_url_db, + echo=settings.db_settings.echo_db + ) + self._session_factory = sessionmaker( + bind=self._engine, + expire_on_commit=False, + autocommit=False + ) + + @property + def get_session(self) -> sessionmaker[Session]: + """Метод для получения сессии""" + + return self._session_factory + + +@lru_cache(maxsize=1) +def get_db_instance() -> SessionDB: + """Возвращает один и тот же объект SeesionDB""" + + return SessionDB() diff --git a/src/kawai_focus_v2/database/seed.py b/src/kawai_focus_v2/database/seed.py new file mode 100644 index 0000000..fbf489c --- /dev/null +++ b/src/kawai_focus_v2/database/seed.py @@ -0,0 +1,18 @@ +from kawai_focus_v2.schemas.timer import TimerModel +from kawai_focus_v2.database.cruds import new_timer +from kawai_focus_v2.database.db import get_db_instance +from kawai_focus_v2.database.data import data_timers + + +def new_temers(data: list[TimerModel]) -> None: + """Заполняет базу данных образцами таймеров""" + + db = get_db_instance() + for timer in data: + new_timer(data=timer, db=db) + + +def start_filling_data() -> None: + """Функция для заполнения бд данными""" + + new_temers(data=data_timers) diff --git a/src/kawai_focus_v2/schemas/timer.py b/src/kawai_focus_v2/schemas/timer.py new file mode 100644 index 0000000..62ace44 --- /dev/null +++ b/src/kawai_focus_v2/schemas/timer.py @@ -0,0 +1,40 @@ +from pydantic import BaseModel, field_validator, Field +from kawai_focus_v2.core.messages.errors import ErrorMessage + + +class TimerModel(BaseModel): + """Модель схемы данных таймера""" + + id: int | None = None + title: str + pomodoro_time: int + break_time: int + break_long_time: int + count_pomodoro: int + + +class TimerListModel(BaseModel): + """Модель схемы данных таймера для списка""" + + id: int + title: str + pomodoro_time: int + count_pomodoro: int + + +class TimerTimeModel(BaseModel): + """Модель схемы данных времени таймера""" + + hh: int = Field(0, ge=0, le=23) + mm: int = Field(0, ge=0, le=59) + + @field_validator('hh', 'mm') + @classmethod + def check_all_time_fields(cls, value: int) -> int: + """Метод валидирует все поля времени""" + + # гарантирует, что время не равно 00:00:00 + if value == 0: + raise ValueError(ErrorMessage.NO_TIME.value) + + return value