eee_alarm_clock/clock/data_classes.py

171 lines
6.0 KiB
Python

import hashlib
from dataclasses import dataclass, field, asdict
from typing import List, Optional, Dict, Any
from datetime import datetime
import os
import re
from logging_config import setup_logging
# Set up logging
logger = setup_logging()
@dataclass
class RepeatRule:
type: str # "daily" or "weekly" or "once"
days_of_week: Optional[List[str]] = field(default_factory=list)
at: Optional[str] = None
def validate(self) -> bool:
"""Validate repeat rule configuration"""
valid_types = {"daily", "weekly", "once"}
valid_days = {"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"}
if self.type not in valid_types:
logger.error(f"Invalid RepeatRule type: '{self.type}'. Must be one of {valid_types}")
return False
if self.type == "weekly":
invalid_days = [day for day in self.days_of_week if day.lower() not in valid_days]
if invalid_days:
logger.error(f"Invalid RepeatRule weekday names: {invalid_days}. Must be one of {valid_days}")
return False
if not self.days_of_week:
logger.error("Weekly repeat rule must specify at least one day")
return False
if self.type == "once" and self.days_of_week:
if self.days_of_week:
logger.error("One-time alert does not support days_of_week")
return False
if not self.at:
logger.error("One-time repeat rule must specify a valid 'at' date")
return False
try:
datetime.strptime(self.at, "%d.%m.%Y")
except ValueError:
logger.error(f"Invalid 'at' date format: '{self.at}'. Expected format: 'dd.mm.yyyy'")
return False
logger.debug(f"RepeatRule validation passed: {self.__dict__}")
return True
@dataclass
class Snooze:
enabled: bool = True
duration: int = 10 # minutes
max_count: int = 3
def validate(self) -> bool:
"""Validate snooze configuration"""
if not isinstance(self.enabled, bool):
logger.error(f"Snooze enabled must be boolean, got {type(self.enabled)}")
return False
if not isinstance(self.duration, int):
logger.error(f"Snooze duration must be integer, got {type(self.duration)}")
return False
if self.duration <= 0:
logger.error(f"Snooze duration must be positive, got {self.duration}")
return False
if not isinstance(self.max_count, int):
logger.error(f"Snooze max_count must be integer, got {type(self.max_count)}")
return False
if self.max_count <= 0:
logger.error(f"Snooze max_count must be positive, got {self.max_count}")
return False
logger.debug(f"Snooze validation passed: {self.__dict__}")
return True
@dataclass
class Metadata:
volume: int = 100
notes: str = ""
md5sum: str = ""
def validate(self) -> bool:
"""Validate metadata configuration"""
if not isinstance(self.volume, int):
logger.error(f"Volume must be integer, got {type(self.volume)}")
return False
if not 0 <= self.volume <= 100:
logger.error(f"Volume must be between 0 and 100, got {self.volume}")
return False
logger.debug(f"Metadata validation passed: {self.__dict__}")
return True
@dataclass
class Alarm:
name: str
time: str # Format: "HH:MM:SS"
repeat_rule: RepeatRule
file_to_play: str = field(default=os.path.expanduser("~/.alarms/alarm-lofi.mp3"))
enabled: bool = field(default=True)
snooze: Snooze = field(default_factory=Snooze)
metadata: Metadata = field(default_factory=Metadata)
id: Optional[int] = field(default=None)
def validate(self) -> bool:
"""Validate complete alarm configuration"""
logger.debug(f"Starting validation for alarm: {self.name}")
# Validate name
if not isinstance(self.name, str) or len(self.name.strip()) == 0:
logger.error(f"Invalid alarm name: '{self.name}'. Must be non-empty string")
return False
# Validate time format
time_pattern = re.compile(r'^([0-1]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$')
if not time_pattern.match(self.time):
logger.error(f"Invalid time format: '{self.time}'. Must match HH:MM:SS")
return False
# Validate enabled flag
if not isinstance(self.enabled, bool):
logger.error(f"Enabled must be boolean, got {type(self.enabled)}")
return False
# Create default alarms directory if it doesn't exist
default_alarms_dir = os.path.expanduser("~/.alarms")
os.makedirs(default_alarms_dir, exist_ok=True)
# Validate audio file
if self.file_to_play != os.path.expanduser("~/.alarms/alarm-lofi.mp3") and not os.path.exists(self.file_to_play):
logger.error(f"Audio file not found: '{self.file_to_play}'")
return False
# Validate repeat rule
if not isinstance(self.repeat_rule, RepeatRule):
logger.error(f"Invalid repeat_rule type: {type(self.repeat_rule)}")
return False
if not self.repeat_rule.validate():
logger.error("Repeat rule validation failed")
return False
# Validate snooze
if not isinstance(self.snooze, Snooze):
logger.error(f"Invalid snooze type: {type(self.snooze)}")
return False
if not self.snooze.validate():
logger.error("Snooze validation failed")
return False
# Validate metadata
if not isinstance(self.metadata, Metadata):
logger.error(f"Invalid metadata type: {type(self.metadata)}")
return False
if not self.metadata.validate():
logger.error("Metadata validation failed")
return False
logger.debug(f"Alarm validation passed: {self.name}")
return True