diff options
| -rw-r--r-- | date_calculator.py | 9 | ||||
| -rw-r--r-- | prediction_controller.py | 47 | ||||
| -rw-r--r-- | test.py | 60 |
3 files changed, 47 insertions, 69 deletions
diff --git a/date_calculator.py b/date_calculator.py index 576d6a1..cec4d40 100644 --- a/date_calculator.py +++ b/date_calculator.py @@ -64,14 +64,14 @@ class DateCalculator: last_start, last_end, last_pid = adjusted[-1] if start <= last_end: - # Fully contained in previous period → discard + # Fully contained in previous period \u2192 discard if end <= last_end: continue # Overlaps head; push start to the day after last_end new_start = last_end + timedelta(days=1) if new_start <= end: adjusted.append((new_start, end, period_id)) - # else new_start > end → discard + # else new_start > end \u2192 discard else: adjusted.append((start, end, period_id)) @@ -104,6 +104,11 @@ class DateCalculator: return non_overlapping_periods @staticmethod + def filter_valid_periods(periods: List[Tuple[datetime, datetime, str]]) -> List[Tuple[datetime, datetime, str]]: + """Filter out periods where start date is after end date""" + return [(start, end, period_id) for start, end, period_id in periods if start <= end] + + @staticmethod def calculate_total_days(periods: List[Tuple[datetime, datetime, str]]) -> int: """Calculate total days across all periods""" return sum((end - start).days + 1 for start, end, _ in periods) diff --git a/prediction_controller.py b/prediction_controller.py index 916a325..168ad62 100644 --- a/prediction_controller.py +++ b/prediction_controller.py @@ -102,8 +102,11 @@ class PredictionController: sorted_projects = DateCalculator.sort_periods(full_projects) considered_projects = DateCalculator.truncate_periods(sorted_projects, self.launch_date) + # Filter out any invalid periods + valid_projects = DateCalculator.filter_valid_periods(considered_projects) + # Round to month boundaries - rounded_projects, total_months = DateCalculator.round_periods(considered_projects) + rounded_projects, total_months = DateCalculator.round_periods(valid_projects) return rounded_projects @@ -114,15 +117,20 @@ class PredictionController: sorted_projects = DateCalculator.sort_periods(half_projects) considered_projects = DateCalculator.truncate_periods(sorted_projects, self.launch_date) + # Filter out any invalid periods + valid_projects = DateCalculator.filter_valid_periods(considered_projects) + # Find non-overlapping periods with full projects non_overlapping_projects = [] - for test_interval in considered_projects: - non_overlapping_projects.extend( - DateCalculator.find_non_overlapping_periods(full_projects, test_interval) - ) + for test_interval in valid_projects: + non_overlapping_results = DateCalculator.find_non_overlapping_periods(full_projects, test_interval) + non_overlapping_projects.extend(non_overlapping_results) + + # Filter valid periods again after overlap processing + valid_non_overlapping = DateCalculator.filter_valid_periods(non_overlapping_projects) # Round to month boundaries - rounded_projects, total_months = DateCalculator.round_periods(non_overlapping_projects) + rounded_projects, total_months = DateCalculator.round_periods(valid_non_overlapping) return rounded_projects @@ -134,18 +142,26 @@ class PredictionController: sorted_events = DateCalculator.sort_periods(other_events) considered_events = DateCalculator.truncate_periods(sorted_events, self.launch_date) - # Adjust overlapping periods - adjusted_events = DateCalculator.adjust_periods(considered_events) + # Filter out any invalid periods + valid_events = DateCalculator.filter_valid_periods(considered_events) # Find non-overlapping periods with all projects all_projects = DateCalculator.sort_periods(full_projects + half_projects) non_overlapping_events = [] - for test_interval in adjusted_events: - non_overlapping_events.extend( - DateCalculator.find_non_overlapping_periods(all_projects, test_interval) - ) + for test_interval in valid_events: + non_overlapping_results = DateCalculator.find_non_overlapping_periods(all_projects, test_interval) + non_overlapping_events.extend(non_overlapping_results) - return non_overlapping_events + # Filter valid periods again after overlap processing + valid_non_overlapping = DateCalculator.filter_valid_periods(non_overlapping_events) + + # Adjust overlapping periods + adjusted_events = DateCalculator.adjust_periods(valid_non_overlapping) + + # Final validation to ensure all periods are valid + final_valid_events = DateCalculator.filter_valid_periods(adjusted_events) + + return final_valid_events def _calculate_final_prediction(self, prediction_start: datetime, processed_events: Dict[str, List[Tuple[datetime, datetime, str]]]) -> datetime: @@ -179,7 +195,7 @@ class PredictionController: return final_prediction def _apply_corrections_to_calendar(self): - """Apply corrected dates to calendar entries""" + """Apply corrected dates to calendar entries and calculate time_period""" # Collect all corrected events from processed results all_corrected_events = [] @@ -225,5 +241,4 @@ class PredictionController: if launch_dt > datetime(2100, 1, 1): errors.append("Launch date too far in the future") - return errors - + return errors
\ No newline at end of file @@ -10,14 +10,14 @@ def test_calculate_prediction(): print("Testing refactored prediction calculation system...") # Input event dates as strings - event1_str = ["2023-01-01", "2023-01-10"] + event1_str = ["2023-01-01", "2023-01-02"] event2_str = ["2023-01-05", "2023-01-15"] # Input full project dates as strings - project1_str = ["2025-01-02", "2025-02-11"] + project1_str = ["2023-01-01", "2024-12-31"] # Input half project dates as strings - project2_str = ["2025-02-01", "2025-06-30"] + project2_str = ["2025-01-01", "2026-12-31"] project3_str = ["2024-05-05", "2024-06-07"] # Initialize components @@ -27,11 +27,11 @@ def test_calculate_prediction(): # Add entries to calendar print("Adding calendar entries...") + # calendar_manager.add_entry(event2_str[0], event2_str[1], "Sonstige") + calendar_manager.add_entry(project1_str[0], project1_str[1], "EZ pauschal") calendar_manager.add_entry(event1_str[0], event1_str[1], "Sonstige") - calendar_manager.add_entry(event2_str[0], event2_str[1], "Sonstige") - calendar_manager.add_entry(project1_str[0], project1_str[1], "EZ 100%") - calendar_manager.add_entry(project2_str[0], project2_str[1], "EZ 50%") - calendar_manager.add_entry(project3_str[0], project3_str[1], "EZ 50%") + calendar_manager.add_entry(project2_str[0], project2_str[1], "EZ pauschal") + # calendar_manager.add_entry(project3_str[0], project3_str[1], "EZ 50%") # Test validation print("Testing input validation...") @@ -43,12 +43,12 @@ def test_calculate_prediction(): # Calculate prediction print("Calculating prediction...") - success = prediction_controller.make_prediction("2023-01-01", 2) + success = prediction_controller.make_prediction("2023-01-01", 1) if success: prediction = prediction_controller.get_prediction() print(f"Predicted completion date: {prediction}") - + print(calendar_manager.entries) # Test event type handler print("\nTesting EventTypeHandler...") entries = calendar_manager.list_entries() @@ -64,48 +64,6 @@ def test_calculate_prediction(): else: print("Prediction calculation failed") -def test_event_type_handler(): - """Test the EventTypeHandler functionality""" - print("\nTesting EventTypeHandler...") - - # Test keyword validation - for keyword in EventConfig.KEYWORDS: - is_valid = EventTypeHandler.validate_event_type(keyword) - print(f"Keyword '{keyword}' is valid: {is_valid}") - - # Test duration calculation - from datetime import datetime - start_date = datetime(2023, 1, 1) - duration = EventTypeHandler.get_duration_for_type("EZ pauschal", start_date) - print(f"EZ pauschal duration from {start_date}: {duration}") - - # Test display names - for keyword in EventConfig.KEYWORDS: - display_name = EventTypeHandler.get_event_type_display_name(keyword) - print(f"'{keyword}' -> '{display_name}'") - -def test_date_calculator(): - """Test the refactored DateCalculator""" - print("\nTesting DateCalculator...") - - from datetime import datetime - - # Test period sorting - periods = [ - (datetime(2023, 3, 1), datetime(2023, 3, 10), "id1"), - (datetime(2023, 1, 1), datetime(2023, 1, 10), "id2"), - (datetime(2023, 2, 1), datetime(2023, 2, 10), "id3") - ] - - sorted_periods = DateCalculator.sort_periods(periods) - print(f"Sorted periods: {[(p[0].strftime('%Y-%m-%d'), p[1].strftime('%Y-%m-%d'), p[2]) for p in sorted_periods]}") - - # Test truncation - launch_date = datetime(2023, 1, 15) - truncated = DateCalculator.truncate_periods(sorted_periods, launch_date) - print(f"Truncated periods: {[(p[0].strftime('%Y-%m-%d'), p[1].strftime('%Y-%m-%d'), p[2]) for p in truncated]}") if __name__ == "__main__": test_calculate_prediction() - test_event_type_handler() - test_date_calculator()
\ No newline at end of file |
