# HG changeset patch # User Paul Boddie # Date 1412431495 -7200 # Node ID 853cfec58a0aa3423af53cb8843735e198d13a0a # Parent 23eeb0e8f116ef15bf7680f636bf394eaf55ec00 Introduced month day filtering and bounded occurrences using the start datetime. diff -r 23eeb0e8f116 -r 853cfec58a0a tests/qualifiers.py --- a/tests/qualifiers.py Sat Oct 04 01:07:46 2014 +0200 +++ b/tests/qualifiers.py Sat Oct 04 16:04:55 2014 +0200 @@ -20,7 +20,7 @@ show(l) s = process(l) -l = s.materialise((2003, 12, 24)) +l = s.materialise(dt, (2003, 12, 24)) print len(l) == 7, len(l) print l[0] == (1997, 11, 2), l[0] print l[-1] == (2003, 11, 2), l[-1] @@ -43,9 +43,9 @@ show(l) s = process(l) -l = s.materialise((2003, 12, 24, 0, 0, 0)) -print len(l) == 34, len(l) -print l[0] == (1997, 1, 4, 8, 30, 0), l[0] +l = s.materialise(dt, (2003, 12, 24, 0, 0, 0)) +print len(l) == 32, len(l) +print l[0] == (1997, 1, 11, 8, 30, 0), l[0] print l[-1] == (2003, 1, 25, 9, 30, 0), l[-1] print @@ -62,7 +62,7 @@ show(l) s = process(l) -l = s.materialise((1997, 12, 24), 10) +l = s.materialise(dt, (1997, 12, 24), 10) print len(l) == 10, len(l) print l[0] == (1997, 9, 2, 9, 0, 0), l[0] print l[-1] == (1997, 9, 11, 9, 0, 0), l[-1] @@ -81,7 +81,7 @@ show(l) s = process(l) -l = s.materialise((1997, 12, 24, 0, 0, 0)) +l = s.materialise(dt, (1997, 12, 24, 0, 0, 0)) print len(l) == 113, len(l) print l[0] == (1997, 9, 2, 9, 0, 0), l[0] print l[-1] == (1997, 12, 23, 9, 0, 0), l[-1] @@ -100,7 +100,7 @@ show(l) s = process(l) -l = s.materialise((1997, 12, 24, 0, 0, 0)) +l = s.materialise(dt, (1997, 12, 24, 0, 0, 0)) print len(l) == 57, len(l) print l[0] == (1997, 9, 2, 9, 0, 0), l[0] print l[-1] == (1997, 12, 23, 9, 0, 0), l[-1] @@ -119,7 +119,7 @@ show(l) s = process(l) -l = s.materialise((1997, 12, 24, 0, 0, 0)) +l = s.materialise(dt, (1997, 12, 24, 0, 0, 0)) print len(l) == 17, len(l) print l[0] == (1997, 9, 2, 9, 0, 0), l[0] print l[-1] == (1997, 12, 23, 9, 0, 0), l[-1] @@ -138,7 +138,7 @@ show(l) s = process(l) -l = s.materialise((1997, 12, 24, 0, 0, 0), 5) +l = s.materialise(dt, (1997, 12, 24, 0, 0, 0), 5) print len(l) == 5, len(l) print l[0] == (1997, 9, 2, 9, 0, 0), l[0] print l[-1] == (1997, 10, 12, 9, 0, 0), l[-1] @@ -159,7 +159,7 @@ show(l) s = process(l) -l = s.materialise((2000, 1, 31, 14, 0, 0)) +l = s.materialise(dt, (2000, 1, 31, 14, 0, 0)) print len(l) == 93, len(l) print l[0] == (1998, 1, 1, 9, 0, 0), l[0] print l[-1] == (2000, 1, 31, 9, 0, 0), l[-1] @@ -179,7 +179,7 @@ show(l) s = process(l) -l = s.materialise((2000, 1, 31, 14, 0, 0)) +l = s.materialise(dt, (2000, 1, 31, 14, 0, 0)) print len(l) == 93, len(l) print l[0] == (1998, 1, 1, 9, 0, 0), l[0] print l[-1] == (2000, 1, 31, 9, 0, 0), l[-1] @@ -198,7 +198,7 @@ show(l) s = process(l) -l = s.materialise((1997, 12, 24, 0, 0, 0), 10) +l = s.materialise(dt, (1997, 12, 24, 0, 0, 0), 10) print len(l) == 10, len(l) print l[0] == (1997, 9, 2, 9, 0, 0), l[0] print l[-1] == (1997, 11, 4, 9, 0, 0), l[-1] @@ -217,7 +217,7 @@ show(l) s = process(l) -l = s.materialise((1997, 12, 24, 0, 0, 0)) +l = s.materialise(dt, (1997, 12, 24, 0, 0, 0)) print len(l) == 17, len(l) print l[0] == (1997, 9, 2, 9, 0, 0), l[0] print l[-1] == (1997, 12, 23, 9, 0, 0), l[-1] @@ -236,7 +236,7 @@ show(l) s = process(l) -l = s.materialise((1998, 2, 20, 0, 0, 0)) +l = s.materialise(dt, (1998, 2, 20, 0, 0, 0)) print len(l) == 13, len(l) print l[0] == (1997, 9, 2, 9, 0, 0), l[0] print l[-1] == (1998, 2, 17, 9, 0, 0), l[-1] @@ -256,7 +256,7 @@ show(l) s = process(l) -l = s.materialise((1997, 10, 7, 9, 0, 0)) +l = s.materialise(dt, (1997, 10, 7, 9, 0, 0)) print len(l) == 10, len(l) print l[0] == (1997, 9, 2, 9, 0, 0), l[0] print l[-1] == (1997, 10, 2, 9, 0, 0), l[-1] @@ -276,7 +276,7 @@ show(l) s = process(l) -l = s.materialise((1997, 12, 24, 0, 0, 0), 10) +l = s.materialise(dt, (1997, 12, 24, 0, 0, 0), 10) print len(l) == 10, len(l) print l[0] == (1997, 9, 2, 9, 0, 0), l[0] print l[-1] == (1997, 10, 2, 9, 0, 0), l[-1] @@ -296,7 +296,7 @@ show(l) s = process(l) -l = s.materialise((1997, 12, 24, 0, 0, 0)) +l = s.materialise(dt, (1997, 12, 24, 0, 0, 0)) print len(l) == 25, len(l) print l[0] == (1997, 9, 1, 9, 0, 0), l[0] print l[-1] == (1997, 12, 22, 9, 0, 0), l[-1] @@ -316,7 +316,7 @@ show(l) s = process(l) -l = s.materialise((1997, 12, 24, 0, 0, 0), 8) +l = s.materialise(dt, (1997, 12, 24, 0, 0, 0), 8) print len(l) == 8, len(l) print l[0] == (1997, 9, 2, 9, 0, 0), l[0] print l[-1] == (1997, 10, 16, 9, 0, 0), l[-1] @@ -336,7 +336,7 @@ show(l) s = process(l) -l = s.materialise((1998, 12, 24, 0, 0, 0), 10) +l = s.materialise(dt, (1998, 12, 24, 0, 0, 0), 10) print len(l) == 10, len(l) print l[0] == (1997, 9, 5, 9, 0, 0), l[0] print l[-1] == (1998, 6, 5, 9, 0, 0), l[-1] @@ -356,7 +356,7 @@ show(l) s = process(l) -l = s.materialise((1997, 12, 24, 0, 0, 0)) +l = s.materialise(dt, (1997, 12, 24, 0, 0, 0)) print len(l) == 4, len(l) print l[0] == (1997, 9, 5, 9, 0, 0), l[0] print l[-1] == (1997, 12, 5, 9, 0, 0), l[-1] @@ -376,7 +376,7 @@ show(l) s = process(l) -l = s.materialise((1998, 12, 24, 0, 0, 0), 10) +l = s.materialise(dt, (1998, 12, 24, 0, 0, 0), 10) print len(l) == 10, len(l) print l[0] == (1997, 9, 7, 9, 0, 0), l[0] print l[-1] == (1998, 5, 31, 9, 0, 0), l[-1] @@ -396,10 +396,70 @@ show(l) s = process(l) -l = s.materialise((1998, 12, 24, 0, 0, 0), 6) +l = s.materialise(dt, (1998, 12, 24, 0, 0, 0), 6) print len(l) == 6, len(l) print l[0] == (1997, 9, 22, 9, 0, 0), l[0] print l[-1] == (1998, 2, 16, 9, 0, 0), l[-1] print +qualifiers = [ + ("MONTHLY", {"interval" : 1}), + ("BYMONTHDAY", {"values" : [-3]}) + ] + +l = order_qualifiers(qualifiers) +show(l) +dt = (1997, 9, 28, 9, 0, 0) +l = get_datetime_structure(dt) +show(l) +l = combine_datetime_with_qualifiers(dt, qualifiers) +show(l) + +s = process(l) +l = s.materialise(dt, (1998, 12, 24, 0, 0, 0), 6) +print len(l) == 6, len(l) +print l[0] == (1997, 9, 28, 9, 0, 0), l[0] +print l[-1] == (1998, 2, 26, 9, 0, 0), l[-1] +print + +qualifiers = [ + ("MONTHLY", {"interval" : 1}), + ("BYMONTHDAY", {"values" : [2, 15]}) + ] + +l = order_qualifiers(qualifiers) +show(l) +dt = (1997, 9, 2, 9, 0, 0) +l = get_datetime_structure(dt) +show(l) +l = combine_datetime_with_qualifiers(dt, qualifiers) +show(l) + +s = process(l) +l = s.materialise(dt, (1998, 12, 24, 0, 0, 0), 10) +print len(l) == 10, len(l) +print l[0] == (1997, 9, 2, 9, 0, 0), l[0] +print l[-1] == (1998, 1, 15, 9, 0, 0), l[-1] +print + +qualifiers = [ + ("MONTHLY", {"interval" : 1}), + ("BYMONTHDAY", {"values" : [1, -1]}) + ] + +l = order_qualifiers(qualifiers) +show(l) +dt = (1997, 9, 30, 9, 0, 0) +l = get_datetime_structure(dt) +show(l) +l = combine_datetime_with_qualifiers(dt, qualifiers) +show(l) + +s = process(l) +l = s.materialise(dt, (1998, 12, 24, 0, 0, 0), 10) +print len(l) == 10, len(l) +print l[0] == (1997, 9, 30, 9, 0, 0), l[0] +print l[-1] == (1998, 2, 1, 9, 0, 0), l[-1] +print + # vim: tabstop=4 expandtab shiftwidth=4 diff -r 23eeb0e8f116 -r 853cfec58a0a vRecurrence.py --- a/vRecurrence.py Sat Oct 04 01:07:46 2014 +0200 +++ b/vRecurrence.py Sat Oct 04 16:04:55 2014 +0200 @@ -78,8 +78,6 @@ ("BYMONTH",) ) -special_enum_levels = ("BYDAY", "BYMONTHDAY", "BYYEARDAY") - # Map from levels to lengths of datetime tuples. lengths = [6, 5, 4, 3, 3, 2, 1] @@ -118,9 +116,9 @@ for qualifier, args in qualifiers: if enum.has_key(qualifier): level = enum[qualifier] - if qualifier in special_enum_levels: + if special_enum_levels.has_key(qualifier): args["interval"] = 1 - selector = PatternFilter + selector = special_enum_levels[qualifier] else: selector = Enum else: @@ -295,23 +293,22 @@ def __repr__(self): return "%s(%r, %r, %r, %r)" % (self.__class__.__name__, self.pos, self.args, self.qualifier, self.context) - def materialise(self, end, count=None): + def materialise(self, start, end, count=None): counter = count and [0, count] - return self.materialise_items(self.context, end, counter) + return self.materialise_items(self.context, start, end, counter) - def materialise_item(self, current, next, counter): + def materialise_item(self, current, last, next, counter): if counter is None or counter[0] < counter[1]: if self.selecting: - return self.selecting.materialise_items(current, next, counter) - else: + return self.selecting.materialise_items(current, last, next, counter) + elif last <= current: if counter is not None: counter[0] += 1 return [current] - else: - return [] + return [] class Pattern(Selector): - def materialise_items(self, context, end, counter): + def materialise_items(self, context, start, end, counter): first = scale(self.args["values"][0], self.pos) # Define the step between items. @@ -330,13 +327,13 @@ while current < end and (counter is None or counter[0] < counter[1]): next = update(current, step) current_end = update(current, unit_step) - results += self.materialise_item(current, min(current_end, end), counter) + results += self.materialise_item(current, max(current, start), min(current_end, end), counter) current = next return results -class PatternFilter(Selector): - def materialise_items(self, context, end, counter): +class WeekDayFilter(Selector): + def materialise_items(self, context, start, end, counter): first = scale(bases[self.pos], self.pos) # Define the step between items to be tested. @@ -348,36 +345,49 @@ while current < end and (counter is None or counter[0] < counter[1]): next = update(current, step) - results += self.materialise_item(current, min(next, end), counter) + results += self.materialise_item(current, max(current, start), min(next, end), counter) current = next return results - def materialise_item(self, current, next, counter): + def materialise_item(self, current, last, next, counter): values = self.args["values"] # Select by day in week, also by occurrence in month. - if self.qualifier == "BYDAY": - for value, index in values: - if datetime(*current).isoweekday() == value: - last_day = monthrange(current[0], current[1])[1] - index_from_start = (current[2] - 1) / 7 - index_from_end = -(1 + (last_day - current[2]) / 7) - if index is None or index in (index_from_start, index_from_end): - return Selector.materialise_item(self, current, next, counter) + for value, index in values: + if datetime(*current).isoweekday() == value: + last_day = monthrange(current[0], current[1])[1] + index_from_start = (current[2] - 1) / 7 + index_from_end = -(1 + (last_day - current[2]) / 7) + if index is None or index in (index_from_start, index_from_end): + return Selector.materialise_item(self, current, last, next, counter) return [] class Enum(Selector): - def materialise_items(self, context, end, counter): + def materialise_items(self, context, start, end, counter): step = scale(1, self.pos) results = [] for value in self.args["values"]: current = combine(context, scale(value, self.pos)) if current < end and (counter is None or counter[0] < counter[1]): next = update(current, step) - results += self.materialise_item(current, min(next, end), counter) + results += self.materialise_item(current, max(current, start), min(next, end), counter) + return results + +class MonthDayFilter(Enum): + def materialise_items(self, context, start, end, counter): + last_day = monthrange(context[0], context[1])[1] + step = scale(1, self.pos) + results = [] + for value in self.args["values"]: + if value < 0: + value = last_day + 1 + value + current = combine(context, scale(value, self.pos)) + if current < end and (counter is None or counter[0] < counter[1]): + next = update(current, step) + results += self.materialise_item(current, max(current, start), min(next, end), counter) return results def process(selectors): @@ -387,4 +397,10 @@ current = selector return selectors[0] +special_enum_levels = { + "BYDAY" : WeekDayFilter, + "BYMONTHDAY" : MonthDayFilter, + #"BYYEARDAY" : YearDayFilter, + } + # vim: tabstop=4 expandtab shiftwidth=4