From de717633ea4417c0270f7651052672f5c57f049a Mon Sep 17 00:00:00 2001 From: Arduinum628 Date: Thu, 10 Jul 2025 12:18:20 +0300 Subject: [PATCH] feat: added validation for timer constructor, added function to calculate hours, minutes and seconds from entered minutes --- kawai_focus/custom_widgets/timer_wigets.py | 79 ++++++++++++++----- kawai_focus/schemas.py | 3 +- .../screens/timer_constructor_screen.py | 18 +++-- kawai_focus/screens/timer_screen.py | 16 +--- kawai_focus/screens/validators_fields.py | 5 ++ kawai_focus/utils/utils.py | 31 +++++--- kv/timer_constructor_screen.kv | 13 +++ 7 files changed, 118 insertions(+), 47 deletions(-) create mode 100644 kawai_focus/screens/validators_fields.py diff --git a/kawai_focus/custom_widgets/timer_wigets.py b/kawai_focus/custom_widgets/timer_wigets.py index 99e1805..f4eb800 100644 --- a/kawai_focus/custom_widgets/timer_wigets.py +++ b/kawai_focus/custom_widgets/timer_wigets.py @@ -1,52 +1,95 @@ from kivy.uix.textinput import TextInput - -class BaseNumInput(TextInput): - """Базовый класс для поля числа""" - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.text = '0' - self.halign = 'center' +class BaseNumBehavior: + """Псевдо-интерфейс для числового ввода""" def increment(self): - """Метод для прибавки 1""" - - self.text = str(int(self.text) + 1) + raise NotImplementedError('Метод increment() должен быть реализован') def decrement(self): - """Метод для вычитания 1""" - - self.text = str(int(self.text) - 1) + raise NotImplementedError('Метод decrement() должен быть реализован') -class TimeTomatoInput(BaseNumInput): +class TimeTomatoInput(TextInput, BaseNumBehavior): """Класс для поля ввода колличества помидорово""" def __init__(self, **kwargs): super().__init__(**kwargs) self.text = '25' + self.halign = 'center' + + def increment(self): + """Метод для прибавки 1""" + + if int(self.text) < 90: + self.text = str(int(self.text) + 1) + + def decrement(self): + """Метод для вычитания 1""" + + if int(self.text) > 10: + self.text = str(int(self.text) - 1) -class TimeBreakInput(BaseNumInput): +class TimeBreakInput(TextInput, BaseNumBehavior): """Класс для поля ввода времени перерыва""" def __init__(self, **kwargs): super().__init__(**kwargs) self.text = '5' + self.halign = 'center' -class TimeLoongBreakInput(BaseNumInput): + def increment(self): + """Метод для прибавки 1""" + + if int(self.text) < 10: + self.text = str(int(self.text) + 1) + + def decrement(self): + """Метод для вычитания 1""" + + if int(self.text) > 3: + self.text = str(int(self.text) - 1) + + +class TimeLoongBreakInput(TextInput, BaseNumBehavior): """Класс для поля ввода времени длительного перерыва""" def __init__(self, **kwargs): super().__init__(**kwargs) self.text = '15' + self.halign = 'center' + + def increment(self): + """Метод для прибавки 1""" + + if int(self.text) < 40: + self.text = str(int(self.text) + 1) + + def decrement(self): + """Метод для вычитания 1""" + + if int(self.text) > 15: + self.text = str(int(self.text) - 1) -class CountTomatosInput(BaseNumInput): +class CountTomatosInput(TextInput, BaseNumBehavior): """Класс для поля ввода количества помидоров""" def __init__(self, **kwargs): super().__init__(**kwargs) self.text = '4' + self.halign = 'center' + + def increment(self): + """Метод для прибавки 1""" + + if int(self.text) < 8: + self.text = str(int(self.text) + 1) + + def decrement(self): + """Метод для вычитания 1""" + + if int(self.text) > 2: + self.text = str(int(self.text) - 1) diff --git a/kawai_focus/schemas.py b/kawai_focus/schemas.py index a6c8325..5bbca6f 100644 --- a/kawai_focus/schemas.py +++ b/kawai_focus/schemas.py @@ -25,9 +25,8 @@ class TimerTimeModel(BaseModel): hh: int = Field(0, ge=0, le=23) mm: int = Field(0, ge=0, le=59) - ss: int = Field(0, ge=0, le=59) - @field_validator('hh', 'mm', 'ss') + @field_validator('hh', 'mm') @classmethod def check_all_time_fields(cls, value: int) -> int: """Метод валидирует все поля времени""" diff --git a/kawai_focus/screens/timer_constructor_screen.py b/kawai_focus/screens/timer_constructor_screen.py index ad89881..d9cc454 100644 --- a/kawai_focus/screens/timer_constructor_screen.py +++ b/kawai_focus/screens/timer_constructor_screen.py @@ -3,6 +3,8 @@ from kivy.uix.screenmanager import Screen from kawai_focus.custom_widgets.timer_wigets import TimeTomatoInput from kawai_focus.schemas import TimerModel from kawai_focus.database.cruds import new_timer +from kawai_focus.utils.utils import calculate_time, custom_timer +from kawai_focus.screens.validators_fields import validate_title class TimerConstructorScreen(Screen): @@ -14,6 +16,12 @@ class TimerConstructorScreen(Screen): def create_timer(self, instance): """Метод для создания таймера""" + validate_title(self, self.ids.title.text) + + # прекратить создание таймера если поле пустое + if not self.ids.title.text: + return + timer = new_timer( data=TimerModel( title=self.ids.title.text, @@ -27,12 +35,10 @@ class TimerConstructorScreen(Screen): if timer: screen_timer = self.manager.get_screen('timers_screen') screen_timer.timer = timer - # Todo: временное решение: нужно сделать механизм для - # автоматического рассчёта часов, минут и секунд из - # колличества минут, введённого пользователем - screen_timer.ids.time_label.text = f'00:{ - "0" + str(timer.pomodoro_time) if timer.pomodoro_time <= 9 else timer.pomodoro_time - }:00' + time_culc = calculate_time(mm_user=timer.pomodoro_time) + screen_timer.timer_start_time = time_culc + screen_timer.ids.time_label.text = time_culc + screen_timer.timer_generator = custom_timer(mm_user=timer.pomodoro_time) self.manager.current = 'timers_screen' diff --git a/kawai_focus/screens/timer_screen.py b/kawai_focus/screens/timer_screen.py index de51c59..725f638 100644 --- a/kawai_focus/screens/timer_screen.py +++ b/kawai_focus/screens/timer_screen.py @@ -2,8 +2,7 @@ from kivy.uix.screenmanager import Screen from kivy.clock import Clock from kivy.core.audio import SoundLoader -from kawai_focus.utils.utils import custom_timer, data_json -from kawai_focus.schemas import TimerTimeModel +from kawai_focus.utils.utils import data_json class TimerScreen(Screen): @@ -22,6 +21,7 @@ class TimerScreen(Screen): self.remaining_time = None self.sound_stop_event = None self.timer = None + self.timer_start_time = None def start_timer(self, instance) -> None: """Метод для запуска таймера""" @@ -30,8 +30,6 @@ class TimerScreen(Screen): self.paused = False else: # Инициализация генератора таймера - self.valid_timer_data = TimerTimeModel(mm=self.timer.pomodoro_time) - self.timer_generator = custom_timer(valid_data=self.valid_timer_data) self.remaining_time = next(self.timer_generator, self.zero_time) # Запуск обновления времени каждую секунду @@ -51,14 +49,8 @@ class TimerScreen(Screen): # Остановка обновления времени Clock.unschedule(self.update_time) self.paused = False - self.remaining_time = TimerTimeModel(mm=self.timer.pomodoro_time).mm - # Todo: временное решение: нужно сделать механизм для - # автоматического рассчёта часов, минут и секунд из - # колличества минут, введённого пользователем - self.ids.time_label.text = f'00:{ - "0" + str(self.remaining_time) if self.remaining_time <= 9 else self.remaining_time - }:00' - + self.remaining_time = next(self.timer_generator, self.zero_time) + self.ids.time_label.text = self.timer_start_time self.sound.stop() if self.sound_stop_event: diff --git a/kawai_focus/screens/validators_fields.py b/kawai_focus/screens/validators_fields.py new file mode 100644 index 0000000..ec95190 --- /dev/null +++ b/kawai_focus/screens/validators_fields.py @@ -0,0 +1,5 @@ +def validate_title(obj, text): + """Валидатор поля title""" + + if not text: + obj.ids.title_error.text = 'Введите название!' diff --git a/kawai_focus/utils/utils.py b/kawai_focus/utils/utils.py index 8570339..bce9aac 100644 --- a/kawai_focus/utils/utils.py +++ b/kawai_focus/utils/utils.py @@ -2,7 +2,6 @@ from typing import Generator import json from os import path, listdir -from kawai_focus.main import Logger from kawai_focus.schemas import TimerTimeModel from kawai_focus.utils.errors import ErrorMessage @@ -45,16 +44,30 @@ class ReadJson: data_json = ReadJson(folder_json='json') -def custom_timer(valid_data: TimerTimeModel) -> Generator[str, None, None]: +def custom_timer(mm_user: int) -> Generator[str, None, None]: """Функция отсчитывает время, установленное для таймера в формате 'hh:mm:ss'. Возвращает генератор, который возвращает текущее время в формате 'hh:mm:ss'.""" - try: - total_seconds = (int(valid_data.hh) * 3600) + (int(valid_data.mm) * 60) + (int(valid_data.ss)) + total_seconds = mm_user * 60 - for remaining in range(total_seconds, -1, -1): - yield f"{remaining // 3600:02d}:{(remaining % 3600) // 60:02d}:{remaining % 60:02d}" - except (TypeError, ValueError) as err: - Logger.error(f'Logger: {err.__class__.__name__}: {err}') - \ No newline at end of file + for remaining in range(total_seconds, -1, -1): + yield f"{remaining // 3600:02d}:{(remaining % 3600) // 60:02d}:{remaining % 60:02d}" + + +def calculate_time(mm_user: int) -> str: + """Функция для подсчёта часов, минут и секунд из минут""" + + valid_data = None + hh = mm_user // 60 + mm = mm_user % 60 + + if hh == 0: + valid_data = TimerTimeModel(mm=mm) + else: + valid_data = TimerTimeModel(hh=hh, mm=mm) + + return ( + f"{'0' + str(valid_data.hh) if valid_data.hh <= 9 else valid_data.hh}:" + f"{'0' + str(valid_data.mm) if valid_data.mm <= 9 else valid_data.mm}:00" + ) diff --git a/kv/timer_constructor_screen.kv b/kv/timer_constructor_screen.kv index 79183a2..4d03a32 100644 --- a/kv/timer_constructor_screen.kv +++ b/kv/timer_constructor_screen.kv @@ -10,6 +10,15 @@ pos_hint: {"center_x": 0.5, "center_y": 0.9} hint_text: "название" + Label: + id: title_error + text: "" + color: (1, 0.3, 0.3, 1) + font_size: 14 + size_hint_y: None + height: 20 + pos_hint: {"center_x": 0.5, "center_y": 0.8} + # числовой счётчик "время помидора" Label: text: "помидор" @@ -25,6 +34,7 @@ TimeTomatoInput: id: time_tomato_input + disabled: True size_hint: None, None size: 40, 30 pos_hint: {"center_x": 0.6, "center_y": 0.7} @@ -51,6 +61,7 @@ TimeBreakInput: id: time_break_input + disabled: True size_hint: None, None size: 40, 30 pos_hint: {"center_x": 0.6, "center_y": 0.6} @@ -77,6 +88,7 @@ TimeLoongBreakInput: id: time_long_break_input + disabled: True size_hint: None, None size: 40, 30 pos_hint: {"center_x": 0.6, "center_y": 0.5} @@ -103,6 +115,7 @@ CountTomatosInput: id: count_tomatos_input + disabled: True size_hint: None, None size: 40, 30 pos_hint: {"center_x": 0.6, "center_y": 0.4}