sbc/scheduler.py
2o 90b6c969e8 Backport to Python 3.7.6
Note that for compatability with Win7SP1, Pyside 6.1.3 and playsound 1.2.2 will be needed(instead of the latest versions).
2024-10-18 11:44:18 +03:00

134 lines
5.0 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
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 = 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 = dataclasses.field(default_factory=lambda: [True, True, True, True, True, True, False])
sound_files: list = dataclasses.field(default_factory=lambda: ["", "", ""])
first_bell_before_first_lesson: bool = dataclasses.field(default_factory=lambda: True)
custom_sounds: list = 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:
bells_list: list = []
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:
if button == 0: self.apply_config()
elif button == 1: self.save_config()
elif button == 2: self.bells_enabled = True
elif button == 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)
if bell.sound == SoundType.FIRST_BELL: play(self.config.sound_files[0])
elif bell.sound == SoundType.SECOND_BELL: play(self.config.sound_files[1])
elif bell.sound == SoundType.BREAK: play(self.config.sound_files[2])
elif bell.sound == 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)