mirror of https://github.com/evilhero/mylar
136 lines
4.8 KiB
Python
Executable File
136 lines
4.8 KiB
Python
Executable File
from datetime import date, datetime
|
|
|
|
from lib.apscheduler.triggers.cron.fields import *
|
|
from lib.apscheduler.util import datetime_ceil, convert_to_datetime
|
|
|
|
|
|
class CronTrigger(object):
|
|
FIELD_NAMES = ('year', 'month', 'day', 'week', 'day_of_week', 'hour',
|
|
'minute', 'second')
|
|
FIELDS_MAP = {'year': BaseField,
|
|
'month': BaseField,
|
|
'week': WeekField,
|
|
'day': DayOfMonthField,
|
|
'day_of_week': DayOfWeekField,
|
|
'hour': BaseField,
|
|
'minute': BaseField,
|
|
'second': BaseField}
|
|
|
|
def __init__(self, **values):
|
|
self.start_date = values.pop('start_date', None)
|
|
if self.start_date:
|
|
self.start_date = convert_to_datetime(self.start_date)
|
|
|
|
self.fields = []
|
|
for field_name in self.FIELD_NAMES:
|
|
if field_name in values:
|
|
exprs = values.pop(field_name)
|
|
is_default = False
|
|
elif not values:
|
|
exprs = DEFAULT_VALUES[field_name]
|
|
is_default = True
|
|
else:
|
|
exprs = '*'
|
|
is_default = True
|
|
|
|
field_class = self.FIELDS_MAP[field_name]
|
|
field = field_class(field_name, exprs, is_default)
|
|
self.fields.append(field)
|
|
|
|
def _increment_field_value(self, dateval, fieldnum):
|
|
"""
|
|
Increments the designated field and resets all less significant fields
|
|
to their minimum values.
|
|
|
|
:type dateval: datetime
|
|
:type fieldnum: int
|
|
:type amount: int
|
|
:rtype: tuple
|
|
:return: a tuple containing the new date, and the number of the field
|
|
that was actually incremented
|
|
"""
|
|
i = 0
|
|
values = {}
|
|
while i < len(self.fields):
|
|
field = self.fields[i]
|
|
if not field.REAL:
|
|
if i == fieldnum:
|
|
fieldnum -= 1
|
|
i -= 1
|
|
else:
|
|
i += 1
|
|
continue
|
|
|
|
if i < fieldnum:
|
|
values[field.name] = field.get_value(dateval)
|
|
i += 1
|
|
elif i > fieldnum:
|
|
values[field.name] = field.get_min(dateval)
|
|
i += 1
|
|
else:
|
|
value = field.get_value(dateval)
|
|
maxval = field.get_max(dateval)
|
|
if value == maxval:
|
|
fieldnum -= 1
|
|
i -= 1
|
|
else:
|
|
values[field.name] = value + 1
|
|
i += 1
|
|
|
|
return datetime(**values), fieldnum
|
|
|
|
def _set_field_value(self, dateval, fieldnum, new_value):
|
|
values = {}
|
|
for i, field in enumerate(self.fields):
|
|
if field.REAL:
|
|
if i < fieldnum:
|
|
values[field.name] = field.get_value(dateval)
|
|
elif i > fieldnum:
|
|
values[field.name] = field.get_min(dateval)
|
|
else:
|
|
values[field.name] = new_value
|
|
|
|
return datetime(**values)
|
|
|
|
def get_next_fire_time(self, start_date):
|
|
if self.start_date:
|
|
start_date = max(start_date, self.start_date)
|
|
next_date = datetime_ceil(start_date)
|
|
fieldnum = 0
|
|
while 0 <= fieldnum < len(self.fields):
|
|
field = self.fields[fieldnum]
|
|
curr_value = field.get_value(next_date)
|
|
next_value = field.get_next_value(next_date)
|
|
|
|
if next_value is None:
|
|
# No valid value was found
|
|
next_date, fieldnum = self._increment_field_value(next_date,
|
|
fieldnum - 1)
|
|
elif next_value > curr_value:
|
|
# A valid, but higher than the starting value, was found
|
|
if field.REAL:
|
|
next_date = self._set_field_value(next_date, fieldnum,
|
|
next_value)
|
|
fieldnum += 1
|
|
else:
|
|
next_date, fieldnum = self._increment_field_value(next_date,
|
|
fieldnum)
|
|
else:
|
|
# A valid value was found, no changes necessary
|
|
fieldnum += 1
|
|
|
|
if fieldnum >= 0:
|
|
return next_date
|
|
|
|
def __str__(self):
|
|
options = ["%s='%s'" % (f.name, str(f)) for f in self.fields
|
|
if not f.is_default]
|
|
return 'cron[%s]' % (', '.join(options))
|
|
|
|
def __repr__(self):
|
|
options = ["%s='%s'" % (f.name, str(f)) for f in self.fields
|
|
if not f.is_default]
|
|
if self.start_date:
|
|
options.append("start_date='%s'" % self.start_date.isoformat(' '))
|
|
return '<%s (%s)>' % (self.__class__.__name__, ', '.join(options))
|