from datetime import datetime, time, timedelta from log import log class ServerTime: DELTA = timedelta(seconds=0) # server_time = time.time() - DELTA def __init__(self, dt): self.dt = dt # datetime based on the SERVER time @classmethod def set_server_time(cls, server_time): cls.DELTA = datetime.now() - server_time @classmethod def fromlocal(cls, local): return cls(local - cls.DELTA) @classmethod def abstime(cls, hh=0, mm=0, ss=0): t = time(hh, mm, ss) timenow = datetime.now().time() dt = datetime.combine(datetime.today(), t) if t <= timenow: # assume it's tomorrow dt += timedelta(days=1) return cls(dt) @classmethod def relative(cls, delta): return cls.fromlocal(datetime.now() + delta) @classmethod def now(cls): return cls.fromlocal(datetime.now()) def from_now(self): server_now = datetime.now() - self.DELTA diff = self.dt - server_now return (24*3600*diff.days) + diff.seconds + (diff.microseconds/1000000.0) def localtime(self): return self.dt + self.DELTA def sleep_until(self): from time import sleep wait = self.from_now() if wait > 0: sleep(wait) def __cmp__(self, other): return cmp(self.dt, other.dt) class CannotDoTask(Exception): def __init__(self, wait): assert isinstance(wait, timedelta) self.wait = wait class Task: def __init__(self, name, func, *args, **kwds): self.name = name self.func = func self.args = args self.kwds = kwds def run(self, sched): raise NotImplementedError def do(self): return self.func(*self.args, **self.kwds) class OneShotTask(Task): def run(self, sched): try: self.do() except CannotDoTask, e: sched.schedule(self, ServerTime.relative(e.wait)) class RepetitiveTask(Task): def __init__(self, *args, **kwds): self.default_wait = kwds.pop('default_wait', None) self.max_wait = kwds.pop('max_wait', None) self.wait = kwds.pop('wait', None) Task.__init__(self, *args, **kwds) def run(self, sched): wait = None try: self.do() except CannotDoTask, e: wait = e.wait if self.wait is not None: wait = self.wait elif wait is None: assert self.default_wait is not None wait = self.default_wait assert isinstance(wait, timedelta) if self.max_wait is not None: wait = min(wait, self.max_wait) sched.schedule(self, ServerTime.relative(wait)) class Scheduler: def __init__(self, tasks=None): self.tasks = tasks or [] def format_seconds(self, totalsecs): totalsecs = int(round(totalsecs)) hrs = totalsecs/3600 min = (totalsecs/60) % 60; sec = totalsecs % 60 return '%02d:%02d:%02d' % (hrs, min, sec) def sleep_until(self, msg, t): now = datetime.now() localtime = t.localtime() if localtime > now: seconds = t.from_now() log('Waiting until %s (%s hours)' % (localtime.strftime("%H:%M:%S"), self.format_seconds(seconds))) t.sleep_until() def schedule(self, task, time): assert isinstance(time, ServerTime) self.tasks.append((time, task)) self.tasks.sort() def run(self): while self.tasks: self.run_once() def run_once(self): time_scheduled, task = self.tasks.pop(0) self.sleep_until('Task %s' % task.name, time_scheduled) task.run(self)