44# license information.
55# -------------------------------------------------------------------------
66from datetime import datetime , timedelta
7- from . _models import RecurrencePatternType , RecurrenceRangeType , TimeWindowFilterSettings
8- from ._recurrence_evaluator import _get_passed_week_days , _sort_days_of_week
7+ from typing import List
8+ from ._models import RecurrencePatternType , RecurrenceRangeType , TimeWindowFilterSettings , Recurrence
99
1010
1111DAYS_PER_WEEK = 7
@@ -27,13 +27,21 @@ def validate_settings(settings: TimeWindowFilterSettings) -> None:
2727 :param TimeWindowFilterSettings settings: The settings for the time window filter.
2828 :raises ValueError: If the settings are invalid.
2929 """
30- _validate_recurrence_required_parameter ( settings )
31- _validate_recurrence_pattern ( settings )
32- _validate_recurrence_range ( settings )
30+ recurrence = settings . recurrence
31+ if recurrence is None :
32+ raise ValueError ( REQUIRED_PARAMETER % "Recurrence" )
3333
34+ start = settings .start
35+ end = settings .end
36+ if start is None or end is None :
37+ raise ValueError (REQUIRED_PARAMETER % "Start or End" )
3438
35- def _validate_recurrence_required_parameter (settings : TimeWindowFilterSettings ) -> None :
36- recurrence = settings .recurrence
39+ _validate_recurrence_required_parameter (recurrence , start , end )
40+ _validate_recurrence_pattern (recurrence , start , end )
41+ _validate_recurrence_range (recurrence , start )
42+
43+
44+ def _validate_recurrence_required_parameter (recurrence : Recurrence , start : datetime , end : datetime ) -> None :
3745 param_name = ""
3846 reason = ""
3947 if recurrence .pattern is None :
@@ -42,86 +50,88 @@ def _validate_recurrence_required_parameter(settings: TimeWindowFilterSettings)
4250 if recurrence .range is None :
4351 param_name = f"{ RECURRENCE_RANGE } "
4452 reason = REQUIRED_PARAMETER
45- if not settings . end > settings . start :
53+ if not end > start :
4654 param_name = "end"
4755 reason = OUT_OF_RANGE
48- if settings . end > settings . start + timedelta (days = TEN_YEARS ):
56+ if end > start + timedelta (days = TEN_YEARS ):
4957 param_name = "end"
5058 reason = TIME_WINDOW_DURATION_TEN_YEARS
5159
5260 if param_name :
5361 raise ValueError (reason % param_name )
5462
5563
56- def _validate_recurrence_pattern (settings : TimeWindowFilterSettings ) -> None :
57- pattern_type = settings .recurrence .pattern .type
64+ def _validate_recurrence_pattern (recurrence : Recurrence , start : datetime , end : datetime ) -> None :
65+ if recurrence is None :
66+ raise ValueError (REQUIRED_PARAMETER % "Recurrence" )
67+ pattern_type = recurrence .pattern .type
5868
5969 if pattern_type == RecurrencePatternType .DAILY :
60- _validate_daily_recurrence_pattern (settings )
70+ _validate_daily_recurrence_pattern (recurrence , start , end )
6171 else :
62- _validate_weekly_recurrence_pattern (settings )
72+ _validate_weekly_recurrence_pattern (recurrence , start , end )
6373
6474
65- def _validate_recurrence_range (settings : TimeWindowFilterSettings ) -> None :
66- range_type = settings . recurrence .range .type
75+ def _validate_recurrence_range (recurrence : Recurrence , start : datetime ) -> None :
76+ range_type = recurrence .range .type
6777 if range_type == RecurrenceRangeType .END_DATE :
68- _validate_end_date (settings )
78+ _validate_end_date (recurrence , start )
6979
7080
71- def _validate_daily_recurrence_pattern (settings : TimeWindowFilterSettings ) -> None :
81+ def _validate_daily_recurrence_pattern (recurrence : Recurrence , start : datetime , end : datetime ) -> None :
7282 # "Start" is always a valid first occurrence for "Daily" pattern.
7383 # Only need to check if time window validated
74- _validate_time_window_duration (settings )
84+ _validate_time_window_duration (recurrence , start , end )
7585
7686
77- def _validate_weekly_recurrence_pattern (settings : TimeWindowFilterSettings ) -> None :
78- _validate_days_of_week (settings )
87+ def _validate_weekly_recurrence_pattern (recurrence : Recurrence , start : datetime , end : datetime ) -> None :
88+ _validate_days_of_week (recurrence )
7989
8090 # Check whether "Start" is a valid first occurrence
81- pattern = settings . recurrence .pattern
82- if settings . start .weekday () not in pattern .days_of_week :
91+ pattern = recurrence .pattern
92+ if start .weekday () not in pattern .days_of_week :
8393 raise ValueError (NOT_MATCHED % "start" )
8494
8595 # Time window duration must be shorter than how frequently it occurs
86- _validate_time_window_duration (settings )
96+ _validate_time_window_duration (recurrence , start , end )
8797
8898 # Check whether the time window duration is shorter than the minimum gap between days of week
89- if not _is_duration_compliant_with_days_of_week (settings ):
99+ if not _is_duration_compliant_with_days_of_week (recurrence , start , end ):
90100 raise ValueError (TIME_WINDOW_DURATION_OUT_OF_RANGE % "Recurrence.Pattern.DaysOfWeek" )
91101
92102
93- def _validate_time_window_duration (settings : TimeWindowFilterSettings ) -> None :
94- pattern = settings . recurrence .pattern
103+ def _validate_time_window_duration (recurrence : Recurrence , start : datetime , end : datetime ) -> None :
104+ pattern = recurrence .pattern
95105 interval_duration = (
96106 timedelta (days = pattern .interval )
97107 if pattern .type == RecurrencePatternType .DAILY
98108 else timedelta (days = pattern .interval * DAYS_PER_WEEK )
99109 )
100- time_window_duration = settings . end - settings . start
110+ time_window_duration = end - start
101111 if time_window_duration > interval_duration :
102112 raise ValueError (TIME_WINDOW_DURATION_OUT_OF_RANGE % "Recurrence.Pattern.Interval" )
103113
104114
105- def _validate_days_of_week (settings : TimeWindowFilterSettings ) -> None :
106- days_of_week = settings . recurrence .pattern .days_of_week
115+ def _validate_days_of_week (recurrence : Recurrence ) -> None :
116+ days_of_week = recurrence .pattern .days_of_week
107117 if not days_of_week :
108118 raise ValueError (REQUIRED_PARAMETER % "Recurrence.Pattern.DaysOfWeek" )
109119
110120
111- def _validate_end_date (settings : TimeWindowFilterSettings ) -> None :
112- end_date = settings . recurrence .range .end_date
113- if end_date and end_date < settings . start :
121+ def _validate_end_date (recurrence : Recurrence , start : datetime ) -> None :
122+ end_date = recurrence .range .end_date
123+ if end_date and end_date < start :
114124 raise ValueError ("The Recurrence.Range.EndDate should be after the Start" )
115125
116126
117- def _is_duration_compliant_with_days_of_week (settings : TimeWindowFilterSettings ) -> bool :
118- days_of_week = settings . recurrence .pattern .days_of_week
127+ def _is_duration_compliant_with_days_of_week (recurrence : Recurrence , start : datetime , end : datetime ) -> bool :
128+ days_of_week = recurrence .pattern .days_of_week
119129 if len (days_of_week ) == 1 :
120130 return True
121131
122132 # Get the date of first day of the week
123133 today = datetime .now ()
124- first_day_of_week = settings . recurrence .pattern .first_day_of_week
134+ first_day_of_week = recurrence .pattern .first_day_of_week
125135 offset = _get_passed_week_days (today .weekday (), first_day_of_week )
126136 first_date_of_week = today - timedelta (days = offset )
127137 sorted_days_of_week = _sort_days_of_week (days_of_week , first_day_of_week )
@@ -137,7 +147,7 @@ def _is_duration_compliant_with_days_of_week(settings: TimeWindowFilterSettings)
137147 min_gap = min (min_gap , current_gap )
138148 prev_occurrence = date
139149
140- if settings . recurrence .pattern .interval == 1 :
150+ if recurrence .pattern .interval == 1 :
141151 # It may cross weeks. Check the adjacent week
142152 date = first_date_of_week + timedelta (
143153 days = DAYS_PER_WEEK + _get_passed_week_days (sorted_days_of_week [0 ], first_day_of_week )
@@ -149,5 +159,21 @@ def _is_duration_compliant_with_days_of_week(settings: TimeWindowFilterSettings)
149159 current_gap = date - prev_occurrence
150160 min_gap = min (min_gap , current_gap )
151161
152- time_window_duration = settings . end - settings . start
162+ time_window_duration = end - start
153163 return min_gap >= time_window_duration
164+
165+
166+ def _get_passed_week_days (current_day : int , first_day_of_week : int ) -> int :
167+ """
168+ Get the number of days passed since the first day of the week.
169+ :param int current_day: The current day of the week, where Monday == 0 ... Sunday == 6.
170+ :param int first_day_of_week: The first day of the week (0-6), where Monday == 0 ... Sunday == 6.
171+ :return: The number of days passed since the first day of the week.
172+ :rtype: int
173+ """
174+ return (current_day - first_day_of_week + DAYS_PER_WEEK ) % DAYS_PER_WEEK
175+
176+
177+ def _sort_days_of_week (days_of_week : List [int ], first_day_of_week : int ) -> List [int ]:
178+ sorted_days = sorted (days_of_week )
179+ return sorted_days [sorted_days .index (first_day_of_week ) :] + sorted_days [: sorted_days .index (first_day_of_week )]
0 commit comments