1.1 --- a/vRecurrence.py Mon Oct 06 16:19:16 2014 +0200
1.2 +++ b/vRecurrence.py Wed Oct 08 18:18:11 2014 +0200
1.3 @@ -176,7 +176,7 @@
1.4 else:
1.5 if not have_q:
1.6 if isinstance(from_q, Enum) and level > 0:
1.7 - repeat = Pattern(level - 1, {"interval" : 1}, "REPEAT")
1.8 + repeat = Pattern(level - 1, {"interval" : 1}, None)
1.9 repeat.context = tuple(context)
1.10 l.append(repeat)
1.11 have_q = True
1.12 @@ -198,7 +198,7 @@
1.13 while from_q:
1.14 if not have_q:
1.15 if isinstance(from_q, Enum) and level > 0:
1.16 - repeat = Pattern(level - 1, {"interval" : 1}, "REPEAT")
1.17 + repeat = Pattern(level - 1, {"interval" : 1}, None)
1.18 repeat.context = tuple(context)
1.19 l.append(repeat)
1.20 have_q = True
1.21 @@ -293,24 +293,44 @@
1.22 def __repr__(self):
1.23 return "%s(%r, %r, %r, %r)" % (self.__class__.__name__, self.level, self.args, self.qualifier, self.context)
1.24
1.25 - def materialise(self, start, end, count=None):
1.26 + def materialise(self, start, end, count=None, setpos=None):
1.27 counter = count and [0, count]
1.28 - results = self.materialise_items(self.context, start, end, counter)
1.29 + results = self.materialise_items(self.context, start, end, counter, setpos)
1.30 results.sort()
1.31 return results[:count]
1.32
1.33 - def materialise_item(self, current, last, next, counter):
1.34 - if counter is None or counter[0] < counter[1]:
1.35 - if self.selecting:
1.36 - return self.selecting.materialise_items(current, last, next, counter)
1.37 - elif last <= current:
1.38 - if counter is not None:
1.39 - counter[0] += 1
1.40 - return [current]
1.41 - return []
1.42 + def materialise_item(self, current, last, next, counter, setpos=None):
1.43 + if self.selecting:
1.44 + return self.selecting.materialise_items(current, last, next, counter, setpos)
1.45 + elif last <= current:
1.46 + return [current]
1.47 + else:
1.48 + return []
1.49 +
1.50 + def convert_positions(self, setpos):
1.51 + l = []
1.52 + for pos in setpos:
1.53 + lower = pos < 0 and pos or pos - 1
1.54 + upper = pos > 0 and pos or pos < -1 and pos + 1 or None
1.55 + l.append((lower, upper))
1.56 + return l
1.57 +
1.58 + def select_positions(self, results, setpos):
1.59 + results.sort()
1.60 + l = []
1.61 + for lower, upper in self.convert_positions(setpos):
1.62 + l += results[lower:upper]
1.63 + return l
1.64 +
1.65 + def filter_by_period(self, results, start, end):
1.66 + l = []
1.67 + for result in results:
1.68 + if start <= result < end:
1.69 + l.append(result)
1.70 + return l
1.71
1.72 class Pattern(Selector):
1.73 - def materialise_items(self, context, start, end, counter):
1.74 + def materialise_items(self, context, start, end, counter, setpos=None):
1.75 first = scale(self.context[self.pos], self.pos)
1.76
1.77 # Define the step between items.
1.78 @@ -329,13 +349,16 @@
1.79 while current < end and (counter is None or counter[0] < counter[1]):
1.80 next = update(current, step)
1.81 current_end = update(current, unit_step)
1.82 - results += self.materialise_item(current, max(current, start), min(current_end, end), counter)
1.83 + interval_results = self.materialise_item(current, max(current, start), min(current_end, end), counter, setpos)
1.84 + if counter is not None:
1.85 + counter[0] += len(interval_results)
1.86 + results += interval_results
1.87 current = next
1.88
1.89 return results
1.90
1.91 class WeekDayFilter(Selector):
1.92 - def materialise_items(self, context, start, end, counter):
1.93 + def materialise_items(self, context, start, end, counter, setpos=None):
1.94 step = scale(1, 2)
1.95 results = []
1.96
1.97 @@ -358,12 +381,16 @@
1.98 current = context
1.99 values = [value for (value, index) in self.args["values"]]
1.100
1.101 - while current < end and (counter is None or counter[0] < counter[1]):
1.102 + while current < end:
1.103 next = update(current, step)
1.104 if date(*current).isoweekday() in values:
1.105 results += self.materialise_item(current, max(current, start), min(next, end), counter)
1.106 current = next
1.107 - return results
1.108 +
1.109 + if setpos:
1.110 + return self.select_positions(results, setpos)
1.111 + else:
1.112 + return results
1.113
1.114 # Find each of the given days.
1.115
1.116 @@ -376,9 +403,12 @@
1.117 else:
1.118 current = to_tuple(get_first_day(first_day, value) + offset, 3)
1.119
1.120 - if current < end:
1.121 - next = update(current, step)
1.122 - results += self.materialise_item(current, max(current, start), min(next, end), counter)
1.123 + next = update(current, step)
1.124 +
1.125 + # To support setpos, only current and next bound the search, not
1.126 + # the period in addition.
1.127 +
1.128 + results += self.materialise_item(current, current, next, counter)
1.129
1.130 else:
1.131 if index < 0:
1.132 @@ -389,26 +419,43 @@
1.133 direction = operator.add
1.134
1.135 while first_day <= current <= last_day:
1.136 - if current < end:
1.137 - next = update(current, step)
1.138 - results += self.materialise_item(current, max(current, start), min(next, end), counter)
1.139 + next = update(current, step)
1.140 +
1.141 + # To support setpos, only current and next bound the search, not
1.142 + # the period in addition.
1.143 +
1.144 + results += self.materialise_item(current, current, next, counter)
1.145 current = to_tuple(direction(date(*current), timedelta(7)), 3)
1.146
1.147 - return results
1.148 + # Extract selected positions and remove out-of-period instances.
1.149 +
1.150 + if setpos:
1.151 + results = self.select_positions(results, setpos)
1.152 +
1.153 + return self.filter_by_period(results, start, end)
1.154
1.155 class Enum(Selector):
1.156 - def materialise_items(self, context, start, end, counter):
1.157 + def materialise_items(self, context, start, end, counter, setpos=None):
1.158 step = scale(1, self.pos)
1.159 results = []
1.160 for value in self.args["values"]:
1.161 current = combine(context, scale(value, self.pos))
1.162 - if current < end:
1.163 - next = update(current, step)
1.164 - results += self.materialise_item(current, max(current, start), min(next, end), counter)
1.165 - return results
1.166 + next = update(current, step)
1.167 +
1.168 + # To support setpos, only current and next bound the search, not
1.169 + # the period in addition.
1.170 +
1.171 + results += self.materialise_item(current, current, next, counter, setpos)
1.172 +
1.173 + # Extract selected positions and remove out-of-period instances.
1.174 +
1.175 + if setpos:
1.176 + results = self.select_positions(results, setpos)
1.177 +
1.178 + return self.filter_by_period(results, start, end)
1.179
1.180 class MonthDayFilter(Enum):
1.181 - def materialise_items(self, context, start, end, counter):
1.182 + def materialise_items(self, context, start, end, counter, setpos=None):
1.183 last_day = monthrange(context[0], context[1])[1]
1.184 step = scale(1, self.pos)
1.185 results = []
1.186 @@ -416,13 +463,22 @@
1.187 if value < 0:
1.188 value = last_day + 1 + value
1.189 current = combine(context, scale(value, self.pos))
1.190 - if current < end:
1.191 - next = update(current, step)
1.192 - results += self.materialise_item(current, max(current, start), min(next, end), counter)
1.193 - return results
1.194 + next = update(current, step)
1.195 +
1.196 + # To support setpos, only current and next bound the search, not
1.197 + # the period in addition.
1.198 +
1.199 + results += self.materialise_item(current, current, next, counter)
1.200 +
1.201 + # Extract selected positions and remove out-of-period instances.
1.202 +
1.203 + if setpos:
1.204 + results = self.select_positions(results, setpos)
1.205 +
1.206 + return self.filter_by_period(results, start, end)
1.207
1.208 class YearDayFilter(Enum):
1.209 - def materialise_items(self, context, start, end, counter):
1.210 + def materialise_items(self, context, start, end, counter, setpos=None):
1.211 first_day = date(context[0], 1, 1)
1.212 next_first_day = date(context[0] + 1, 1, 1)
1.213 year_length = (next_first_day - first_day).days
1.214 @@ -432,10 +488,19 @@
1.215 if value < 0:
1.216 value = year_length + 1 + value
1.217 current = to_tuple(first_day + timedelta(value - 1), 3)
1.218 - if current < end:
1.219 - next = update(current, step)
1.220 - results += self.materialise_item(current, max(current, start), min(next, end), counter)
1.221 - return results
1.222 + next = update(current, step)
1.223 +
1.224 + # To support setpos, only current and next bound the search, not
1.225 + # the period in addition.
1.226 +
1.227 + results += self.materialise_item(current, current, next, counter)
1.228 +
1.229 + # Extract selected positions and remove out-of-period instances.
1.230 +
1.231 + if setpos:
1.232 + results = self.select_positions(results, setpos)
1.233 +
1.234 + return self.filter_by_period(results, start, end)
1.235
1.236 def process(selectors):
1.237 current = selectors[0]