feat: added validation for timer constructor, added function to calculate hours, minutes and seconds from entered minutes

This commit is contained in:
Arduinum628
2025-07-10 12:18:20 +03:00
parent f4da6d9f12
commit de717633ea
7 changed files with 118 additions and 47 deletions

View File

@@ -1,52 +1,95 @@
from kivy.uix.textinput import TextInput from kivy.uix.textinput import TextInput
class BaseNumBehavior:
class BaseNumInput(TextInput): """Псевдо-интерфейс для числового ввода"""
"""Базовый класс для поля числа"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.text = '0'
self.halign = 'center'
def increment(self): def increment(self):
"""Метод для прибавки 1""" raise NotImplementedError('Метод increment() должен быть реализован')
self.text = str(int(self.text) + 1)
def decrement(self): def decrement(self):
"""Метод для вычитания 1""" raise NotImplementedError('Метод decrement() должен быть реализован')
self.text = str(int(self.text) - 1)
class TimeTomatoInput(BaseNumInput): class TimeTomatoInput(TextInput, BaseNumBehavior):
"""Класс для поля ввода колличества помидорово""" """Класс для поля ввода колличества помидорово"""
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.text = '25' 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): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.text = '5' 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): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.text = '15' 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): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.text = '4' 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)

View File

@@ -25,9 +25,8 @@ class TimerTimeModel(BaseModel):
hh: int = Field(0, ge=0, le=23) hh: int = Field(0, ge=0, le=23)
mm: int = Field(0, ge=0, le=59) 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 @classmethod
def check_all_time_fields(cls, value: int) -> int: def check_all_time_fields(cls, value: int) -> int:
"""Метод валидирует все поля времени""" """Метод валидирует все поля времени"""

View File

@@ -3,6 +3,8 @@ from kivy.uix.screenmanager import Screen
from kawai_focus.custom_widgets.timer_wigets import TimeTomatoInput from kawai_focus.custom_widgets.timer_wigets import TimeTomatoInput
from kawai_focus.schemas import TimerModel from kawai_focus.schemas import TimerModel
from kawai_focus.database.cruds import new_timer 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): class TimerConstructorScreen(Screen):
@@ -14,6 +16,12 @@ class TimerConstructorScreen(Screen):
def create_timer(self, instance): def create_timer(self, instance):
"""Метод для создания таймера""" """Метод для создания таймера"""
validate_title(self, self.ids.title.text)
# прекратить создание таймера если поле пустое
if not self.ids.title.text:
return
timer = new_timer( timer = new_timer(
data=TimerModel( data=TimerModel(
title=self.ids.title.text, title=self.ids.title.text,
@@ -27,12 +35,10 @@ class TimerConstructorScreen(Screen):
if timer: if timer:
screen_timer = self.manager.get_screen('timers_screen') screen_timer = self.manager.get_screen('timers_screen')
screen_timer.timer = timer screen_timer.timer = timer
# Todo: временное решение: нужно сделать механизм для 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.ids.time_label.text = f'00:{ screen_timer.timer_generator = custom_timer(mm_user=timer.pomodoro_time)
"0" + str(timer.pomodoro_time) if timer.pomodoro_time <= 9 else timer.pomodoro_time
}:00'
self.manager.current = 'timers_screen' self.manager.current = 'timers_screen'

View File

@@ -2,8 +2,7 @@ from kivy.uix.screenmanager import Screen
from kivy.clock import Clock from kivy.clock import Clock
from kivy.core.audio import SoundLoader from kivy.core.audio import SoundLoader
from kawai_focus.utils.utils import custom_timer, data_json from kawai_focus.utils.utils import data_json
from kawai_focus.schemas import TimerTimeModel
class TimerScreen(Screen): class TimerScreen(Screen):
@@ -22,6 +21,7 @@ class TimerScreen(Screen):
self.remaining_time = None self.remaining_time = None
self.sound_stop_event = None self.sound_stop_event = None
self.timer = None self.timer = None
self.timer_start_time = None
def start_timer(self, instance) -> None: def start_timer(self, instance) -> None:
"""Метод для запуска таймера""" """Метод для запуска таймера"""
@@ -30,8 +30,6 @@ class TimerScreen(Screen):
self.paused = False self.paused = False
else: 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) self.remaining_time = next(self.timer_generator, self.zero_time)
# Запуск обновления времени каждую секунду # Запуск обновления времени каждую секунду
@@ -51,14 +49,8 @@ class TimerScreen(Screen):
# Остановка обновления времени # Остановка обновления времени
Clock.unschedule(self.update_time) Clock.unschedule(self.update_time)
self.paused = False self.paused = False
self.remaining_time = TimerTimeModel(mm=self.timer.pomodoro_time).mm self.remaining_time = next(self.timer_generator, self.zero_time)
# Todo: временное решение: нужно сделать механизм для self.ids.time_label.text = self.timer_start_time
# автоматического рассчёта часов, минут и секунд из
# колличества минут, введённого пользователем
self.ids.time_label.text = f'00:{
"0" + str(self.remaining_time) if self.remaining_time <= 9 else self.remaining_time
}:00'
self.sound.stop() self.sound.stop()
if self.sound_stop_event: if self.sound_stop_event:

View File

@@ -0,0 +1,5 @@
def validate_title(obj, text):
"""Валидатор поля title"""
if not text:
obj.ids.title_error.text = 'Введите название!'

View File

@@ -2,7 +2,6 @@ from typing import Generator
import json import json
from os import path, listdir from os import path, listdir
from kawai_focus.main import Logger
from kawai_focus.schemas import TimerTimeModel from kawai_focus.schemas import TimerTimeModel
from kawai_focus.utils.errors import ErrorMessage from kawai_focus.utils.errors import ErrorMessage
@@ -45,16 +44,30 @@ class ReadJson:
data_json = ReadJson(folder_json='json') 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'. Возвращает генератор, который возвращает текущее время
в формате 'hh:mm:ss'.""" в формате 'hh:mm:ss'."""
try: total_seconds = mm_user * 60
total_seconds = (int(valid_data.hh) * 3600) + (int(valid_data.mm) * 60) + (int(valid_data.ss))
for remaining in range(total_seconds, -1, -1): for remaining in range(total_seconds, -1, -1):
yield f"{remaining // 3600:02d}:{(remaining % 3600) // 60:02d}:{remaining % 60:02d}" 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}')
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"
)

View File

@@ -10,6 +10,15 @@
pos_hint: {"center_x": 0.5, "center_y": 0.9} pos_hint: {"center_x": 0.5, "center_y": 0.9}
hint_text: "название" 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: Label:
text: "помидор" text: "помидор"
@@ -25,6 +34,7 @@
TimeTomatoInput: TimeTomatoInput:
id: time_tomato_input id: time_tomato_input
disabled: True
size_hint: None, None size_hint: None, None
size: 40, 30 size: 40, 30
pos_hint: {"center_x": 0.6, "center_y": 0.7} pos_hint: {"center_x": 0.6, "center_y": 0.7}
@@ -51,6 +61,7 @@
TimeBreakInput: TimeBreakInput:
id: time_break_input id: time_break_input
disabled: True
size_hint: None, None size_hint: None, None
size: 40, 30 size: 40, 30
pos_hint: {"center_x": 0.6, "center_y": 0.6} pos_hint: {"center_x": 0.6, "center_y": 0.6}
@@ -77,6 +88,7 @@
TimeLoongBreakInput: TimeLoongBreakInput:
id: time_long_break_input id: time_long_break_input
disabled: True
size_hint: None, None size_hint: None, None
size: 40, 30 size: 40, 30
pos_hint: {"center_x": 0.6, "center_y": 0.5} pos_hint: {"center_x": 0.6, "center_y": 0.5}
@@ -103,6 +115,7 @@
CountTomatosInput: CountTomatosInput:
id: count_tomatos_input id: count_tomatos_input
disabled: True
size_hint: None, None size_hint: None, None
size: 40, 30 size: 40, 30
pos_hint: {"center_x": 0.6, "center_y": 0.4} pos_hint: {"center_x": 0.6, "center_y": 0.4}