sbc/scheduler.py
2024-10-18 11:38:23 +03:00

136 lines
5.1 KiB
Python

import time, json, dataclasses
from enum import Enum
from playsound import playsound
from sound import play
class SoundType(Enum):
FIRST_BELL = 1
SECOND_BELL = 2
BREAK = 3
CUSTOM = 4
BELL_NAMES = {
SoundType.FIRST_BELL: "1-й дзвоник",
SoundType.SECOND_BELL: "2-й дзвоник",
SoundType.BREAK: "Перерва"
}
@dataclasses.dataclass
class Bell:
sound: SoundType
hour: int
minute: int
played: bool = False
# only used when sound = SoundType.CUSTOM
custom_file: str = ""
custom_name: str = ""
@dataclasses.dataclass
class CustomSound:
name: str
times: list[list[int]]
sound_file: str
@dataclasses.dataclass
class Config:
# yes, this is permanently "borrowed" from the internet
# thx to https://stackoverflow.com/questions/53632152/why-cant-dataclasses-have-mutable-defaults-in-their-class-attributes-declaratio
lessons_start: list[int] = dataclasses.field(default_factory=lambda: [8, 0])
lesson_length: int = dataclasses.field(default_factory=lambda: 40)
break_length: int = dataclasses.field(default_factory=lambda: 5)
first_bell: int = dataclasses.field(default_factory=lambda: 1)
num_lessons: int = dataclasses.field(default_factory=lambda: 12)
workdays: list[bool] = dataclasses.field(default_factory=lambda: [True, True, True, True, True, True, False])
sound_files: list[str] = dataclasses.field(default_factory=lambda: ["", "", ""])
first_bell_before_first_lesson: bool = dataclasses.field(default_factory=lambda: True)
custom_sounds: list[CustomSound] = dataclasses.field(default_factory=lambda: [])
class Scheduler:
def __init__(self) -> None:
self.bells: list[Bell] = []
self.bells_enabled: bool = True
self.load_config()
def set_ui_class(self, ui_class) -> None:
self.ui = ui_class
self.generate_bells()
self.ui.set_settings(self.config)
def apply_config(self) -> None:
self.ui.get_settings(self.config, CustomSound)
self.generate_bells()
def generate_bells(self) -> None:
self.bells = []
if not self.config.workdays[time.localtime().tm_wday]:
self.ui.set_schedule(self.get_bells_list())
return
day_minute: int = self.config.lessons_start[0] * 60 + self.config.lessons_start[1] - self.config.first_bell
for lesson_number in range(self.config.num_lessons):
if lesson_number != 0 or self.config.first_bell_before_first_lesson:
self.bells.append(Bell(SoundType.FIRST_BELL, day_minute // 60, day_minute % 60))
day_minute += self.config.first_bell
self.bells.append(Bell(SoundType.SECOND_BELL, day_minute // 60, day_minute % 60))
day_minute += self.config.lesson_length
self.bells.append(Bell(SoundType.BREAK, day_minute // 60, day_minute % 60))
day_minute += self.config.break_length - self.config.first_bell
for sound in self.config.custom_sounds:
for sound_time in sound.times:
self.bells.append(Bell(SoundType.CUSTOM, *sound_time, custom_file=sound.sound_file, custom_name=sound.name))
# so that the first bell before the first lesson can't roll time into the negatives
self.bells = [bell for bell in self.bells if bell.hour <= 23]
self.bells.sort(key=lambda sound: sound.hour * 60 + sound.minute)
self.ui.set_schedule(self.get_bells_list())
def get_bells_list(self) -> list[str]:
bells_list: list[str] = []
for bell in self.bells:
if bell.sound != SoundType.CUSTOM: bell_name: str = BELL_NAMES[bell.sound]
else: bell_name: str = '"' + bell.custom_name + '"'
bells_list.append(f"{bell.hour:02}:{bell.minute:02} - {bell_name}")
return bells_list
def menu_event(self, button) -> None:
match button:
case 0: self.apply_config()
case 1: self.save_config()
case 2: self.bells_enabled = True
case 3: self.bells_enabled = False
def update(self) -> None:
if not self.bells_enabled:
return
t = time.localtime()
for bell_n, bell in enumerate(self.bells):
if (not bell.played) and (bell.hour == t.tm_hour) and (bell.minute == t.tm_min):
bell.played = True
self.ui.select_bell(bell_n)
match bell.sound:
case SoundType.FIRST_BELL: play(self.config.sound_files[0])
case SoundType.SECOND_BELL: play(self.config.sound_files[1])
case SoundType.BREAK: play(self.config.sound_files[2])
case SoundType.CUSTOM: play(bell.custom_file)
break
def load_config(self) -> None:
try:
with open("config.json", "r") as fp:
self.config = Config(**json.load(fp))
for i in range(len(self.config.custom_sounds)):
self.config.custom_sounds[i] = CustomSound(**self.config.custom_sounds[i])
except:
# if something goes wrong, load the default values
# nothing should go wrong with the config file, if the user doesn't edit
# it manually. if that's the case - it's their fault, not mine :P
self.config = Config()
def save_config(self) -> None:
temp_config = Config()
self.ui.get_settings(temp_config, CustomSound)
with open("config.json", "w") as fp:
json.dump(dataclasses.asdict(temp_config), fp)