# calendar_manager.py from datetime import datetime from typing import List, Optional, Tuple from file_service import FileService from event_type_handler import EventTypeHandler from date_service import DateService from config import EventConfig from calendar_entry import CalendarEntry class CalendarManager: """Manages calendar entries with CRUD operations and data integrity""" def __init__(self, filename: Optional[str] = None): self.filename = filename self.entries: List[CalendarEntry] = [] if filename: self.load_file(filename) def switch_file(self, new_filename: str): """Switch to a new file without loading data""" self.filename = new_filename def load_file(self, new_filename: str) -> bool: """Clear current entries and load entries from new file""" self.clear_entries() new_entries = FileService.load_calendar_from_file(new_filename) if new_entries: self.entries.extend(new_entries) self.filename = new_filename return True return False def add_entries_from_file(self, file_path: str) -> List[CalendarEntry]: """Add events from another JSON file to the current calendar""" new_entries = FileService.load_calendar_from_file(file_path) if new_entries: self.entries.extend(new_entries) self.save_entries() return new_entries def clear_entries(self): """Clear all entries from the calendar""" self.entries = [] def save_entries(self) -> bool: """Save the current list of events to the file""" if self.filename: return FileService.save_calendar_to_file(self.entries, self.filename) return False def add_entry(self, start_date: str, end_date: str, keyword: str, commentary: str = "") -> Optional[CalendarEntry]: """Add a new event to the calendar with validation""" try: # Validate inputs if not EventTypeHandler.validate_event_type(keyword): raise ValueError(f"Invalid keyword: {keyword}") # Create and add entry new_entry = CalendarEntry(start_date, end_date, keyword, commentary=commentary) self.entries.append(new_entry) self.save_entries() return new_entry except Exception as e: print(f"Error adding entry: {e}") return None def modify_entry(self, entry_id: str, start_date: Optional[str] = None, end_date: Optional[str] = None, keyword: Optional[str] = None, commentary: Optional[str] = None) -> Optional[CalendarEntry]: """Modify an existing event by ID with validation""" entry = self.get_entry_by_id(entry_id) if not entry: return None try: if start_date: entry.start_date = datetime.fromisoformat(start_date) if end_date: entry.end_date = datetime.fromisoformat(end_date) if keyword: if not EventTypeHandler.validate_event_type(keyword): raise ValueError(f"Invalid keyword: {keyword}") entry.keyword = keyword if commentary is not None: entry.commentary = commentary self.save_entries() return entry except Exception as e: print(f"Error modifying entry: {e}") return None def delete_entry(self, entry_id: str) -> Optional[CalendarEntry]: """Delete an event by ID""" entry = self.get_entry_by_id(entry_id) if entry: self.entries.remove(entry) self.save_entries() return entry return None def get_entry_by_id(self, entry_id: str) -> Optional[CalendarEntry]: """Get an event by its ID""" return next((entry for entry in self.entries if entry.id == entry_id), None) def list_entries(self) -> List[CalendarEntry]: """List all calendar entries""" return self.entries.copy() # Return copy to prevent external modification def correct_dates(self, corrected_events: List[Tuple[datetime, datetime, str]]): """Apply corrected dates to calendar entries and calculate time_period""" # Clear all corrections first for entry in self.entries: entry.corrected_start_date = None entry.corrected_end_date = None entry.time_period = None # Apply corrections and calculate time_period for start_date, end_date, original_id in corrected_events: entry = self.get_entry_by_id(original_id) if entry: entry.corrected_start_date = start_date entry.corrected_end_date = end_date # Calculate and store time_period based on corrected dates entry.time_period = self._calculate_time_period(entry) def get_entries_by_keyword(self, keyword: str) -> List[CalendarEntry]: """Get all entries with a specific keyword""" return [entry for entry in self.entries if entry.keyword == keyword] def _calculate_time_period(self, entry: CalendarEntry) -> str: """Calculate time_period for an entry based on corrected dates""" if not entry.corrected_start_date or not entry.corrected_end_date: return "" start_dt = entry.corrected_start_date end_dt = entry.corrected_end_date if end_dt < start_dt: return "" if entry.keyword == "Sonstige": # For "Sonstige", show days delta_days = DateService.calculate_days_between(start_dt, end_dt) return f"{delta_days} Tage" else: # For EZ types, show months total_months = DateService.calculate_months_between(start_dt, end_dt) if entry.keyword == "EZ 50%": # Half-time projects count as half months total_months = (total_months + 1) // 2 # Round up return f"{total_months} Monate" def validate_entry_data(self, start_date: str, end_date: str, keyword: str) -> List[str]: """Validate entry data and return list of errors""" errors = [] try: start_dt = datetime.fromisoformat(start_date) end_dt = datetime.fromisoformat(end_date) if start_dt > end_dt: errors.append("Start date cannot be after end date") except ValueError: errors.append("Invalid date format") if not EventTypeHandler.validate_event_type(keyword): errors.append(f"Invalid keyword: {keyword}") return errors