1.1 --- a/vRecurrence.py Thu Feb 26 19:38:14 2015 +0100
1.2 +++ b/vRecurrence.py Sat Feb 28 22:30:52 2015 +0100
1.3 @@ -3,7 +3,7 @@
1.4 """
1.5 Recurrence rule calculation.
1.6
1.7 -Copyright (C) 2014 Paul Boddie <paul@boddie.org.uk>
1.8 +Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>
1.9
1.10 This program is free software; you can redistribute it and/or modify it under
1.11 the terms of the GNU General Public License as published by the Free Software
1.12 @@ -124,7 +124,7 @@
1.13
1.14 """
1.15 Process the list of 'values' of the form "key=value", returning a list of
1.16 - qualifiers.
1.17 + qualifiers of the form (qualifier name, args).
1.18 """
1.19
1.20 qualifiers = []
1.21 @@ -382,7 +382,19 @@
1.22 # Classes for producing instances from recurrence structures.
1.23
1.24 class Selector:
1.25 +
1.26 + "A generic selector."
1.27 +
1.28 def __init__(self, level, args, qualifier, selecting=None):
1.29 +
1.30 + """
1.31 + Initialise at the given 'level' a selector employing the given 'args'
1.32 + defined in the interpretation of recurrence rule qualifiers, with the
1.33 + 'qualifier' being the name of the rule qualifier, and 'selecting' being
1.34 + an optional selector used to find more specific instances from those
1.35 + found by this selector.
1.36 + """
1.37 +
1.38 self.level = level
1.39 self.pos = positions[level]
1.40 self.args = args
1.41 @@ -394,6 +406,14 @@
1.42 return "%s(%r, %r, %r, %r)" % (self.__class__.__name__, self.level, self.args, self.qualifier, self.context)
1.43
1.44 def materialise(self, start, end, count=None, setpos=None):
1.45 +
1.46 + """
1.47 + Starting at 'start', materialise instances up to but not including any
1.48 + at 'end' or later, returning at most 'count' if specified, and returning
1.49 + only the occurrences indicated by 'setpos' if specified. A list of
1.50 + instances is returned.
1.51 + """
1.52 +
1.53 start = to_tuple(start)
1.54 end = to_tuple(end)
1.55 counter = count and [0, count]
1.56 @@ -401,15 +421,27 @@
1.57 results.sort()
1.58 return results[:count]
1.59
1.60 - def materialise_item(self, current, last, next, counter, setpos=None):
1.61 + def materialise_item(self, current, earliest, next, counter, setpos=None):
1.62 +
1.63 + """
1.64 + Given the 'current' instance, the 'earliest' acceptable instance, the
1.65 + 'next' instance, an instance 'counter', and the optional 'setpos'
1.66 + criteria, return a list of result items. Where no selection within the
1.67 + current instance occurs, the current instance will be returned as a
1.68 + result if the same or later than the earliest acceptable instance.
1.69 + """
1.70 +
1.71 if self.selecting:
1.72 - return self.selecting.materialise_items(current, last, next, counter, setpos)
1.73 - elif last <= current:
1.74 + return self.selecting.materialise_items(current, earliest, next, counter, setpos)
1.75 + elif earliest <= current:
1.76 return [current]
1.77 else:
1.78 return []
1.79
1.80 def convert_positions(self, setpos):
1.81 +
1.82 + "Convert 'setpos' to 0-based indexes."
1.83 +
1.84 l = []
1.85 for pos in setpos:
1.86 lower = pos < 0 and pos or pos - 1
1.87 @@ -418,6 +450,9 @@
1.88 return l
1.89
1.90 def select_positions(self, results, setpos):
1.91 +
1.92 + "Select in 'results' the 1-based positions given by 'setpos'."
1.93 +
1.94 results.sort()
1.95 l = []
1.96 for lower, upper in self.convert_positions(setpos):
1.97 @@ -425,6 +460,12 @@
1.98 return l
1.99
1.100 def filter_by_period(self, results, start, end):
1.101 +
1.102 + """
1.103 + Filter 'results' so that only those at or after 'start' and before 'end'
1.104 + are returned.
1.105 + """
1.106 +
1.107 l = []
1.108 for result in results:
1.109 if start <= result < end:
1.110 @@ -432,6 +473,9 @@
1.111 return l
1.112
1.113 class Pattern(Selector):
1.114 +
1.115 + "A selector of instances according to a repeating pattern."
1.116 +
1.117 def materialise_items(self, context, start, end, counter, setpos=None):
1.118 first = scale(self.context[self.pos], self.pos)
1.119
1.120 @@ -460,6 +504,9 @@
1.121 return results
1.122
1.123 class WeekDayFilter(Selector):
1.124 +
1.125 + "A selector of instances specified in terms of day numbers."
1.126 +
1.127 def materialise_items(self, context, start, end, counter, setpos=None):
1.128 step = scale(1, 2)
1.129 results = []
1.130 @@ -613,6 +660,13 @@
1.131 # Public functions.
1.132
1.133 def connect_selectors(selectors):
1.134 +
1.135 + """
1.136 + Make the 'selectors' reference each other in a hierarchy so that
1.137 + materialising the principal selector causes the more specific ones to be
1.138 + employed in the operation.
1.139 + """
1.140 +
1.141 current = selectors[0]
1.142 for selector in selectors[1:]:
1.143 current.selecting = selector