Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recurring time window filter #51

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open

Conversation

mrm9084
Copy link
Contributor

@mrm9084 mrm9084 commented Dec 11, 2024

Description

Add the Recurring time window filter to python Feature Management.

@mrm9084 mrm9084 requested a review from Copilot December 11, 2024 23:46
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 suggestions.

Comments skipped due to low confidence (1)

featuremanagement/_time_window_filter/_recurrence_validator.py:113

  • The error message should specify the exact parameters for clarity, e.g., 'The Recurrence.Range.EndDate should be after the Start date'.
raise ValueError("The Recurrence.Range.EndDate should be after the Start")

sorted_days_of_week = _sort_days_of_week(pattern.days_of_week, pattern.first_day_of_week)
max_day_offset = _get_passed_week_days(sorted_days_of_week[-1], pattern.first_day_of_week)
min_day_offset = _get_passed_week_days(sorted_days_of_week[0], pattern.first_day_of_week)
num_of_occurrences = number_of_interval * len(sorted_days_of_week) - sorted_days_of_week.index(start.weekday())
Copy link
Preview

Copilot AI Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The calculation of num_of_occurrences is incorrect. It should be 'number_of_interval * len(sorted_days_of_week) + sorted_days_of_week.index(start.weekday())'.

Suggested change
num_of_occurrences = number_of_interval * len(sorted_days_of_week) - sorted_days_of_week.index(start.weekday())
num_of_occurrences = number_of_interval * len(sorted_days_of_week) + sorted_days_of_week.index(start.weekday())

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

featuremanagement/_time_window_filter/_models.py Outdated Show resolved Hide resolved
featuremanagement/_time_window_filter/_models.py Outdated Show resolved Hide resolved
featuremanagement/_defaultfilters.py Outdated Show resolved Hide resolved
if range_data.get("EndDate") and isinstance(range_data.get("EndDate"), str):
end_date_str = range_data.get("EndDate", "")
self.end_date = parsedate_to_datetime(end_date_str) if end_date_str else None
self.num_of_occurrences = range_data.get("NumberOfOccurrences")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed the default value. BTW, it is possible to get non number value or negative value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the default value 0?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Python doesn't have a "Max Int" value. We could do something like math.pow(2, 63) - 1.

@zhiyuanliang-ms
Copy link
Contributor

zhiyuanliang-ms commented Dec 12, 2024

@mrm9084 Will you add testcases in this PR? The testsuite in .NET FM repo or in this PR are very comprehensive.

raise ValueError("The interval must be greater than 0.")
days_of_week = pattern_data.get("DaysOfWeek", [])
for day in days_of_week:
self.days_of_week.append(self.days.index(day))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You didn't initialize self.days_of_week as []

@@ -31,6 +41,8 @@
FEATURE_FILTER_NAME_KEY = "Name"
IGNORE_CASE_KEY = "ignore_case"

logger = logging.getLogger(__name__)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra empty line

if recurrence:
if start_time and end_time:
settings = TimeWindowFilterSettings(start_time, end_time, recurrence)
return is_match(settings, current_time)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better timewindow evaluation is

if not start and not end:
    return False

if (start_time is None or start_time <= current_time) and (end_time is None or current_time < end_time):
    return True

if recurrence_data:
    recurrence = Recurrence(recurrence_data)
    settings = TimeWindowFilterSettings(start_time, end_time, recurrence)
    return is_match(settings, current_time)

return False

In is_match, you will validate settings (which may take some time), if the current time hits the first time window (specified by start and end), we can save the recurrence evaluation.

"""
validate_settings(settings)

previous_occurrence = _get_previous_occurrence(settings, now)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about returning False directly here if now < start here. In this way, we can save a function call (very slightly improve the performance).

pattern_type = settings.recurrence.pattern.type
if pattern_type == RecurrencePatternType.DAILY:
occurrence_info = _get_daily_previous_occurrence(settings, now)
else:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You didn't validate pattern type. Here you assume the pattern type must be either DAILY or WEEKLY. But this is not guaranteed.

How about

if pattern_type == RecurrencePatternType.DAILY:
     occurrence_info = _get_daily_previous_occurrence(settings, now)
elif pattern_type == RecurrencePatternType.WEEKLY:
     occurrence_info = _get_weekly_previous_occurrence(settings, now)
else:
     throw

recurrence_range = settings.recurrence.range
range_type = recurrence_range.type
previous_occurrence = occurrence_info.previous_occurrence
end_date = recurrence_range.end_date
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a string or datetime type?


def _sort_days_of_week(days_of_week: List[int], first_day_of_week: int) -> List[int]:
sorted_days = sorted(days_of_week)
return sorted_days[sorted_days.index(first_day_of_week) :] + sorted_days[: sorted_days.index(first_day_of_week)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will happen if first_day_of_week is not in the list?

from ._models import RecurrencePatternType, RecurrenceRangeType, TimeWindowFilterSettings


DAYS_PER_WEEK = 7
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other files, you used number 7 directly. So maybe we don't need this variable here.

for day in days_of_week:
self.days_of_week.append(self.days.index(day))
first_day_of_week = pattern_data.get("FirstDayOfWeek", "Sunday")
self.first_day_of_week = self.days.index(first_day_of_week) if first_day_of_week in self.days else 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from email.utils import parsedate_to_datetime

s = "Sun, 05 Jan 2025 20:00:00 GMT"
t = parsedate_to_datetime(s)
print(t.weekday())

This produced 6 on my laptop, I am using python 3.11.9.

first day of week should be Sunday by default. Sunday index is 6 and Monday is 0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants