OLD | NEW |
(Empty) | |
| 1 """ |
| 2 Triggers determine the times when a job should be executed. |
| 3 """ |
| 4 from datetime import datetime, timedelta |
| 5 from math import ceil |
| 6 |
| 7 from apscheduler.fields import * |
| 8 from apscheduler.util import * |
| 9 |
| 10 __all__ = ('CronTrigger', 'DateTrigger', 'IntervalTrigger') |
| 11 |
| 12 |
| 13 class CronTrigger(object): |
| 14 FIELD_NAMES = ('year', 'month', 'day', 'week', 'day_of_week', 'hour', |
| 15 'minute', 'second') |
| 16 FIELDS_MAP = {'year': BaseField, |
| 17 'month': BaseField, |
| 18 'week': WeekField, |
| 19 'day': DayOfMonthField, |
| 20 'day_of_week': DayOfWeekField, |
| 21 'hour': BaseField, |
| 22 'minute': BaseField, |
| 23 'second': BaseField} |
| 24 |
| 25 def __init__(self, **values): |
| 26 self.fields = [] |
| 27 for field_name in self.FIELD_NAMES: |
| 28 exprs = values.get(field_name) or '*' |
| 29 field_class = self.FIELDS_MAP[field_name] |
| 30 field = field_class(field_name, exprs) |
| 31 self.fields.append(field) |
| 32 |
| 33 def _increment_field_value(self, dateval, fieldnum): |
| 34 """ |
| 35 Increments the designated field and resets all less significant fields |
| 36 to their minimum values. |
| 37 |
| 38 :type dateval: datetime |
| 39 :type fieldnum: int |
| 40 :type amount: int |
| 41 :rtype: tuple |
| 42 :return: a tuple containing the new date, and the number of the field |
| 43 that was actually incremented |
| 44 """ |
| 45 i = 0 |
| 46 values = {} |
| 47 while i < len(self.fields): |
| 48 field = self.fields[i] |
| 49 if not field.REAL: |
| 50 if i == fieldnum: |
| 51 fieldnum -= 1 |
| 52 i -= 1 |
| 53 else: |
| 54 i += 1 |
| 55 continue |
| 56 |
| 57 if i < fieldnum: |
| 58 values[field.name] = field.get_value(dateval) |
| 59 i += 1 |
| 60 elif i > fieldnum: |
| 61 values[field.name] = field.get_min(dateval) |
| 62 i += 1 |
| 63 else: |
| 64 value = field.get_value(dateval) |
| 65 maxval = field.get_max(dateval) |
| 66 if value == maxval: |
| 67 fieldnum -= 1 |
| 68 i -= 1 |
| 69 else: |
| 70 values[field.name] = value + 1 |
| 71 i += 1 |
| 72 |
| 73 return datetime(**values), fieldnum |
| 74 |
| 75 def _set_field_value(self, dateval, fieldnum, new_value): |
| 76 values = {} |
| 77 for i, field in enumerate(self.fields): |
| 78 if field.REAL: |
| 79 if i < fieldnum: |
| 80 values[field.name] = field.get_value(dateval) |
| 81 elif i > fieldnum: |
| 82 values[field.name] = field.get_min(dateval) |
| 83 else: |
| 84 values[field.name] = new_value |
| 85 |
| 86 return datetime(**values) |
| 87 |
| 88 def get_next_fire_time(self, start_date): |
| 89 next_date = datetime_ceil(start_date) |
| 90 fieldnum = 0 |
| 91 while 0 <= fieldnum < len(self.fields): |
| 92 field = self.fields[fieldnum] |
| 93 curr_value = field.get_value(next_date) |
| 94 next_value = field.get_next_value(next_date) |
| 95 |
| 96 if next_value is None: |
| 97 # No valid value was found |
| 98 next_date, fieldnum = self._increment_field_value(next_date, |
| 99 fieldnum - 1) |
| 100 elif next_value > curr_value: |
| 101 # A valid, but higher than the starting value, was found |
| 102 if field.REAL: |
| 103 next_date = self._set_field_value(next_date, fieldnum, |
| 104 next_value) |
| 105 fieldnum += 1 |
| 106 else: |
| 107 next_date, fieldnum = self._increment_field_value(next_date, |
| 108 fieldnum) |
| 109 else: |
| 110 # A valid value was found, no changes necessary |
| 111 fieldnum += 1 |
| 112 |
| 113 if fieldnum >= 0: |
| 114 return next_date |
| 115 |
| 116 def __repr__(self): |
| 117 field_reprs = ("%s='%s'" % (f.name, str(f)) for f in self.fields |
| 118 if str(f) != '*') |
| 119 return '%s(%s)' % (self.__class__.__name__, ', '.join(field_reprs)) |
| 120 |
| 121 |
| 122 class DateTrigger(object): |
| 123 def __init__(self, run_date): |
| 124 self.run_date = convert_to_datetime(run_date) |
| 125 |
| 126 def get_next_fire_time(self, start_date): |
| 127 if self.run_date >= start_date: |
| 128 return self.run_date |
| 129 |
| 130 def __repr__(self): |
| 131 return '%s(%s)' % (self.__class__.__name__, repr(self.run_date)) |
| 132 |
| 133 |
| 134 class IntervalTrigger(object): |
| 135 def __init__(self, interval, repeat, start_date=None): |
| 136 if not isinstance(interval, timedelta): |
| 137 raise TypeError('interval must be a timedelta') |
| 138 if repeat < 0: |
| 139 raise ValueError('Illegal value for repeat; expected >= 0, ' |
| 140 'received %s' % repeat) |
| 141 |
| 142 self.interval = interval |
| 143 self.interval_length = timedelta_seconds(self.interval) |
| 144 if self.interval_length == 0: |
| 145 self.interval = timedelta(seconds=1) |
| 146 self.interval_length = 1 |
| 147 self.repeat = repeat |
| 148 if start_date is None: |
| 149 self.first_fire_date = datetime.now() + self.interval |
| 150 else: |
| 151 self.first_fire_date = convert_to_datetime(start_date) |
| 152 self.first_fire_date -= timedelta(microseconds=\ |
| 153 self.first_fire_date.microsecond) |
| 154 if repeat > 0: |
| 155 self.last_fire_date = self.first_fire_date + interval * (repeat - 1) |
| 156 else: |
| 157 self.last_fire_date = None |
| 158 |
| 159 def get_next_fire_time(self, start_date): |
| 160 if start_date < self.first_fire_date: |
| 161 return self.first_fire_date |
| 162 if self.last_fire_date and start_date > self.last_fire_date: |
| 163 return None |
| 164 timediff_seconds = timedelta_seconds(start_date - self.first_fire_date) |
| 165 next_interval_num = int(ceil(timediff_seconds / self.interval_length)) |
| 166 return self.first_fire_date + self.interval * next_interval_num |
| 167 |
| 168 def __repr__(self): |
| 169 return "%s(interval=%s, repeat=%d, start_date=%s)" % ( |
| 170 self.__class__.__name__, repr(self.interval), self.repeat, |
| 171 repr(self.first_fire_date)) |
OLD | NEW |