|
| 1 | +import time as _time |
1 | 2 | from datetime import datetime, date, time, timedelta
|
2 |
| -from .models import ExecutionRecord |
3 | 3 |
|
4 | 4 |
|
5 | 5 | class Schedule(object):
|
6 |
| - |
7 |
| - def get_previous_record(self, task): |
8 |
| - record_list = ExecutionRecord.objects \ |
9 |
| - .filter(task_id=task.task_id, schedule_id=self.__hash__()) \ |
10 |
| - .order_by('-start_time') |
11 |
| - if record_list: |
12 |
| - return record_list[0] |
13 |
| - else: |
14 |
| - return None |
15 |
| - |
16 | 6 | def __eq__(self, other):
|
17 |
| - return self.__dict__ == other.__dict__ |
| 7 | + other_id = getattr(other, 'schedule_id', None) |
| 8 | + return self.schedule_id == other_id |
18 | 9 |
|
19 | 10 | def __ne__(self, other):
|
20 | 11 | return not self.__eq__(other)
|
21 | 12 |
|
| 13 | + def __hash__(self): |
| 14 | + return self.schedule_id |
| 15 | + |
| 16 | + |
| 17 | +class BaseSchedule(object): |
| 18 | + """A base class for the built-in schedules. Don't use this yourself. |
| 19 | +
|
| 20 | + """ |
22 | 21 |
|
23 |
| -class PeriodicSchedule(Schedule): |
| 22 | + @property |
| 23 | + def schedule_id(self): |
| 24 | + class_name = '%s.%s' % (self.__class__.__module__, |
| 25 | + self.__class__.__name__) |
| 26 | + time_args = tuple([getattr(self, name) for name in self._time_attrs]) |
| 27 | + return hash((class_name, time_args)) |
24 | 28 |
|
25 |
| - def get_scheduled_time(self, task): |
26 |
| - previous_record = self.get_previous_record(task) |
27 |
| - if previous_record: |
28 |
| - next_run_time = previous_record.scheduled_time \ |
29 |
| - + self.repeat_interval |
| 29 | + def time_before(self, time): |
| 30 | + kwargs = dict((k, getattr(self, k)) for k in self._time_attrs) |
| 31 | + t = time.replace(**kwargs) |
| 32 | + if t > time: |
| 33 | + previous_time = t - self.repeat_interval |
30 | 34 | else:
|
31 |
| - next_run_time = datetime.now() |
32 |
| - return next_run_time |
| 35 | + previous_time = t |
| 36 | + return previous_time |
33 | 37 |
|
34 |
| - def __hash__(self): |
35 |
| - class_name = '%s.%s' % (self.__module__, self.__class__) |
36 |
| - return hash((class_name, self.repeat_interval)) |
| 38 | + def time_after(self, time): |
| 39 | + return self.time_before(time) + self.repeat_interval |
37 | 40 |
|
38 | 41 |
|
39 |
| -class Daily(Schedule): |
| 42 | +class Hourly(BaseSchedule): |
| 43 | + repeat_interval = timedelta(hours=1) |
| 44 | + _time_attrs = ['minute', 'second', 'microsecond'] |
40 | 45 |
|
41 |
| - def __init__(self, hour=0, minute=0, second=0, microsecond=0): |
42 |
| - self.hour = hour |
| 46 | + def __init__(self, minute=0, second=0, microsecond=0): |
| 47 | + super(Hourly, self).__init__() |
43 | 48 | self.minute = minute
|
44 | 49 | self.second = second
|
45 | 50 | self.microsecond = microsecond
|
46 | 51 |
|
47 |
| - def __hash__(self): |
48 |
| - class_name = '%s.%s' % (self.__module__, self.__class__) |
49 |
| - return hash((class_name, self.hour, self.minute, self.second, |
50 |
| - self.microsecond)) |
51 |
| - |
52 |
| - |
53 |
| - def get_scheduled_time(self, task): |
54 |
| - previous_record = self.get_previous_record(task) |
55 |
| - today = date.today() |
56 |
| - t = time(self.hour, self.minute, self.second, self.microsecond) |
57 |
| - now = datetime.now() |
58 |
| - |
59 |
| - if previous_record: |
60 |
| - previous_run_date = \ |
61 |
| - datetime.date(previous_record.scheduled_time) |
62 |
| - scheduled_date = previous_run_date + timedelta(days=1) |
63 |
| - scheduled_time = datetime.combine(scheduled_date, t) |
64 |
| - use_last_skipped_time = scheduled_time < now |
65 |
| - else: |
66 |
| - use_last_skipped_time = True |
67 |
| - |
68 |
| - # If the next scheduled time already happened, just use the last |
69 |
| - # skipped time. Otherwise, we'd end up scheduling an execution for |
70 |
| - # each missed day. |
71 |
| - if use_last_skipped_time: |
72 |
| - todays_run_time = datetime.combine(today, t) |
73 |
| - if todays_run_time < now: |
74 |
| - scheduled_time = todays_run_time |
75 |
| - else: |
76 |
| - scheduled_time = todays_run_time - timedelta(days=1) |
| 52 | + def __unicode__(self): |
| 53 | + return 'Hourly @ %s' % time(minute=self.minute, second=self.second, |
| 54 | + microsecond=self.microsecond) |
77 | 55 |
|
78 |
| - return scheduled_time |
79 | 56 |
|
| 57 | +class Daily(BaseSchedule): |
| 58 | + repeat_interval = timedelta(days=1) |
| 59 | + _time_attrs = ['hour', 'minute', 'second', 'microsecond'] |
80 | 60 |
|
81 |
| -class Hourly(PeriodicSchedule): |
82 |
| - repeat_interval = timedelta(hours=1) |
| 61 | + def __init__(self, hour=0, minute=0, second=0, microsecond=0): |
| 62 | + super(Daily, self).__init__() |
| 63 | + self.hour = hour |
| 64 | + self.minute = minute |
| 65 | + self.second = second |
| 66 | + self.microsecond = microsecond |
83 | 67 |
|
84 | 68 | def __unicode__(self):
|
85 |
| - return 'Hourly' |
| 69 | + return 'Daily @ %s' % time(hour=self.hour, minute=self.minute, |
| 70 | + second=self.second, microsecond=self.microsecond) |
| 71 | + |
86 | 72 |
|
| 73 | +class Weekly(BaseSchedule): |
| 74 | + repeat_interval = timedelta(days=7) |
| 75 | + _time_attrs = ['day', 'hour', 'minute', 'second', 'microsecond'] |
87 | 76 |
|
88 |
| -class Weekly(PeriodicSchedule): |
89 |
| - repeat_interval = timedelta(weeks=1) |
| 77 | + def __init__(self, day=0, hour=0, minute=0, second=0, microsecond=0): |
| 78 | + super(Weekly, self).__init__() |
| 79 | + self.day = day |
| 80 | + self.hour = hour |
| 81 | + self.minute = minute |
| 82 | + self.second = second |
| 83 | + self.microsecond = microsecond |
90 | 84 |
|
91 | 85 | def __unicode__(self):
|
92 |
| - return 'Weekly' |
| 86 | + day_name = _time.strftime('%A', _time.strptime(str(self.day), '%w')) |
| 87 | + return '%s @ %s' % (day_name, time(hour=self.hour, minute=self.minute, |
| 88 | + second=self.second, microsecond=self.microsecond)) |
93 | 89 |
|
94 | 90 |
|
95 |
| -class Every(PeriodicSchedule): |
| 91 | +class Every(BaseSchedule): |
| 92 | + _time_attrs = ['repeat_interval'] |
96 | 93 |
|
97 | 94 | def __init__(self, interval=None, days=0, seconds=0, microseconds=0,
|
98 |
| - milliseconds=0, minutes=0, hours=0, weeks=0): |
| 95 | + milliseconds=0, minutes=0, hours=0, weeks=0, |
| 96 | + starting_at=datetime(1970, 1, 1)): |
99 | 97 | super(Every, self).__init__()
|
100 | 98 | if interval is None:
|
101 | 99 | interval = timedelta(days, seconds, microseconds,
|
102 | 100 | milliseconds, minutes, hours, weeks)
|
103 | 101 | self.repeat_interval = interval
|
| 102 | + self.starting_at = starting_at |
| 103 | + |
| 104 | + def time_before(self, time): |
| 105 | + # Uses "perfect time" (no leap years, etc.) |
| 106 | + interval = self.repeat_interval.microseconds / 1000 \ |
| 107 | + + self.repeat_interval.total_seconds() * 1000 |
| 108 | + time_since_start = time - self.starting_at |
| 109 | + ms_since_start = time_since_start.microseconds / 1000 \ |
| 110 | + + time_since_start.total_seconds() * 1000 |
| 111 | + time = interval * int(ms_since_start / interval) |
| 112 | + return datetime.fromtimestamp(time / 1000) |
104 | 113 |
|
105 | 114 | def __unicode__(self):
|
106 | 115 | return 'Every %s' % self.repeat_interval
|
0 commit comments