From 95d784fb414c6270e560fc0cf7ed289765ddd3ab Mon Sep 17 00:00:00 2001 From: matin Date: Fri, 12 Sep 2025 20:45:28 +0200 Subject: AI refactoring (see architecture analysis and refactoring_summary) --- calendar_gui.py | 117 +++++++++++++++++++++++++++----------------------------- 1 file changed, 56 insertions(+), 61 deletions(-) (limited to 'calendar_gui.py') diff --git a/calendar_gui.py b/calendar_gui.py index 932d97e..75f77fb 100644 --- a/calendar_gui.py +++ b/calendar_gui.py @@ -7,20 +7,21 @@ from PyQt5.QtCore import Qt, QDate, QLocale from PyQt5.QtGui import QFont, QFontDatabase from datetime import datetime, timedelta from dateutil.relativedelta import relativedelta + from calendar_manager import CalendarManager from date_calculator import DateCalculator from prediction_controller import PredictionController -import math - -DATEFORMAT = "dd.MM.yyyy" +from event_type_handler import EventTypeHandler +from date_service import DateService +from file_service import FileService +from config import EventConfig class EventDialog(QDialog): - def __init__(self, keyword_list, entry=None, parent=None, dateformat: str = DATEFORMAT): + def __init__(self, entry=None, parent=None): super().__init__(parent) - self.keyword_list = keyword_list self.entry = entry - self.dateformat = dateformat + self.dateformat = EventConfig.DATE_FORMAT # Set title based on mode self.setWindowTitle("Neuer Eintrag" if not entry else "Eintrag editieren") @@ -74,11 +75,11 @@ class EventDialog(QDialog): # Keyword selector self.keyword = QComboBox() self.keyword.setFont(font) - self.keyword.addItems(self.keyword_list) + self.keyword.addItems(EventConfig.KEYWORDS) # Set initial keyword based on mode - if self.entry and self.entry.keyword in self.keyword_list: - current_index = self.keyword_list.index(self.entry.keyword) + if self.entry and self.entry.keyword in EventConfig.KEYWORDS: + current_index = EventConfig.KEYWORDS.index(self.entry.keyword) self.keyword.setCurrentIndex(current_index) self.keyword.currentTextChanged.connect(self.on_keyword_changed) @@ -121,14 +122,14 @@ class EventDialog(QDialog): end_date_label = self.layout.itemAt(self.end_date_row, QFormLayout.LabelRole).widget() end_date_field = self.layout.itemAt(self.end_date_row, QFormLayout.FieldRole).widget() - if keyword == "EZ pauschal": - # Hide end date field for EZ pauschals since they have fixed 4-week duration + if EventTypeHandler.should_hide_end_date_input(keyword): + # Hide end date field for EZ pauschals since they have fixed duration end_date_label.setVisible(False) end_date_field.setVisible(False) # Update end date automatically if changing to EZ pauschal start_dt = self.start_date.date().toPyDate() - end_dt = start_dt + relativedelta(years = 2, days = -1) + end_dt = EventTypeHandler.get_duration_for_type(keyword, start_dt) self.end_date.setDate(QDate(end_dt.year, end_dt.month, end_dt.day)) else: # Show end date field for other event types @@ -140,17 +141,17 @@ class EventDialog(QDialog): if self.end_date.date() < self.start_date.date(): self.end_date.setDate(self.start_date.date()) def get_data(self): - start_date = self.start_date.date().toString("yyyy-MM-dd") + start_date = DateService.format_date_for_iso(self.start_date.date()) keyword = self.keyword.currentText() commentary = self.commentary_input.toPlainText().strip() - if keyword == "EZ pauschal": - # For EZ pauschals, calculate end date as start + 4 weeks - start_dt = datetime.fromisoformat(start_date) - end_dt = start_dt + relativedelta(years = 2, days = -1) - end_date = end_dt.strftime("%Y-%m-%d") + if EventTypeHandler.should_hide_end_date_input(keyword): + # For EZ pauschals, calculate end date automatically + start_dt = DateService.parse_date_from_string(start_date) + end_dt = EventTypeHandler.get_duration_for_type(keyword, start_dt) + end_date = DateService.format_date_for_iso(end_dt) else: - end_date = self.end_date.date().toString("yyyy-MM-dd") + end_date = DateService.format_date_for_iso(self.end_date.date()) return start_date, end_date, keyword, commentary @@ -166,16 +167,14 @@ class CalendarManagerGUI(QMainWindow): # Get system locale for consistent date formatting self.locale = QLocale.system() - self.dateformat = DATEFORMAT + self.dateformat = EventConfig.DATE_FORMAT # Initialize backend components - self.keyword_list = ["EZ 100%", "EZ 50%", "EZ pauschal", "Sonstige"] self.calendar_manager = CalendarManager() self.date_calculator = DateCalculator() self.prediction_controller = PredictionController( self.calendar_manager, - self.date_calculator, - self.keyword_list + self.date_calculator ) self.init_ui() @@ -314,10 +313,16 @@ class CalendarManagerGUI(QMainWindow): print(f"Error calculating prediction: {str(e)}") def add_event(self): - dialog = EventDialog(self.keyword_list, parent=self, dateformat=self.dateformat) + dialog = EventDialog(parent=self) if dialog.exec_(): start_date, end_date, keyword, commentary = dialog.get_data() try: + # Validate data before adding + errors = self.calendar_manager.validate_entry_data(start_date, end_date, keyword) + if errors: + QMessageBox.warning(self, "Validation Error", "\n".join(errors)) + return + self.calendar_manager.add_entry(start_date, end_date, keyword, commentary) self.update_events_table() self.update_prediction() # Auto-update prediction @@ -327,15 +332,17 @@ class CalendarManagerGUI(QMainWindow): def modify_event(self, event_id): entry = self.calendar_manager.get_entry_by_id(event_id) if entry: - dialog = EventDialog(self.keyword_list, entry=entry, parent=self, dateformat=self.dateformat) + dialog = EventDialog(entry=entry, parent=self) if dialog.exec_(): start_date, end_date, keyword, commentary = dialog.get_data() try: - self.calendar_manager.modify_entry(event_id, - datetime.fromisoformat(start_date), - datetime.fromisoformat(end_date), - keyword, - commentary) + # Validate data before modifying + errors = self.calendar_manager.validate_entry_data(start_date, end_date, keyword) + if errors: + QMessageBox.warning(self, "Validation Error", "\n".join(errors)) + return + + self.calendar_manager.modify_entry(event_id, start_date, end_date, keyword, commentary) self.update_events_table() self.update_prediction() # Auto-update prediction except Exception as e: @@ -366,32 +373,14 @@ class CalendarManagerGUI(QMainWindow): self.events_table.setItem(i, 0, QTableWidgetItem(entry.id)) # Format dates using unified display format - start_date_qdate = QDate(entry.start_date.year, entry.start_date.month, entry.start_date.day) - end_date_qdate = QDate(entry.end_date.year, entry.end_date.month, entry.end_date.day) - - start_date_text = start_date_qdate.toString(self.dateformat) - end_date_text = end_date_qdate.toString(self.dateformat) + start_date_text = DateService.format_date_for_display(entry.start_date, self.dateformat) + end_date_text = DateService.format_date_for_display(entry.end_date, self.dateformat) self.events_table.setItem(i, 1, QTableWidgetItem(start_date_text)) self.events_table.setItem(i, 2, QTableWidgetItem(end_date_text)) self.events_table.setItem(i, 3, QTableWidgetItem(entry.keyword)) - # Relevant accounted time based on corrected dates - relevant_text = "" - if entry.corrected_start_date and entry.corrected_end_date: - start_dt = entry.corrected_start_date - end_dt = entry.corrected_end_date - if end_dt < start_dt: - continue - delta_days = (end_dt.date() - start_dt.date()).days + 1 - # Determine if less than 3 months using relativedelta - rd = relativedelta(end_dt.date(), start_dt.date()) - total_months = rd.years * 12 + rd.months + 1 - if entry.keyword == "Sonstige": - relevant_text = f"{delta_days} Tage" - else: - if entry.keyword == "EZ 50%": - total_months = math.ceil(total_months / 2) - relevant_text = f"{total_months} Monate" + # Relevant accounted time from stored time_period + relevant_text = getattr(entry, 'time_period', "") or "" self.events_table.setItem(i, 4, QTableWidgetItem(relevant_text)) # Commentary @@ -420,29 +409,35 @@ class CalendarManagerGUI(QMainWindow): def save_file(self): """Save calendar entries to a JSON file""" - # if not self.calendar_manager.filename: - file_path, _ = QFileDialog.getSaveFileName(self, "Einträge speichern", "", "JSON Files (*.json)") + file_path, _ = QFileDialog.getSaveFileName(self, "Einträge speichern", "", EventConfig.FILE_FILTER) if not file_path: return + + # Ensure .json extension + file_path = FileService.ensure_json_extension(file_path) self.calendar_manager.switch_file(file_path) try: - self.calendar_manager.save_entries() - QMessageBox.information(self, "Speichern erfolgreich", f"Einträge gespeichert in {self.calendar_manager.filename}") + if self.calendar_manager.save_entries(): + QMessageBox.information(self, "Speichern erfolgreich", f"Einträge gespeichert in {self.calendar_manager.filename}") + else: + QMessageBox.critical(self, "Error", "Failed to save calendar entries") except Exception as e: QMessageBox.critical(self, "Error", f"Failed to save calendar: {str(e)}") def load_file(self): """Load calendar entries from a JSON file""" - file_path, _ = QFileDialog.getOpenFileName(self, "Einträge laden", "", "JSON Files (*.json)") + file_path, _ = QFileDialog.getOpenFileName(self, "Einträge laden", "", EventConfig.FILE_FILTER) if not file_path: return try: - self.calendar_manager.load_file(file_path) - self.update_events_table() - self.update_prediction() # Auto-update prediction - QMessageBox.information(self, "Laden erfolgreich", f"Einträge erfolgreich von {file_path} geladen") + if self.calendar_manager.load_file(file_path): + self.update_events_table() + self.update_prediction() # Auto-update prediction + QMessageBox.information(self, "Laden erfolgreich", f"Einträge erfolgreich von {file_path} geladen") + else: + QMessageBox.warning(self, "Warning", "No entries loaded from file") except Exception as e: QMessageBox.critical(self, "Error", f"Failed to load calendar: {str(e)}") -- cgit v1.1