www.pudn.com > clamwin-0.85.1-src.zip > Scheduler.py


#-----------------------------------------------------------------------------
# Name:        Scheduler.py
# Product:     ClamWin Antivirus
#
# Author:      alch [alch at users dot sourceforge dot net]
#
# Created:     2004/19/03
# Copyright:   Copyright alch (c) 2004
# Licence:     
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
# 
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
# 
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#-----------------------------------------------------------------------------
#!/usr/bin/env python


import os, tempfile
import sched, time, locale
import types
import threading


class Scheduler(threading.Thread):
    # empty tuple for status_filenames parameter will reuslt in no checking
    # for missed schedules
    def __init__(self, frequency, startTime, weekDay, action, argument=(), 
            status_filenames=(), loopDelay = 0.5):           
        threading.Thread.__init__(self)    	    
        self._filenames = status_filenames
        self._delay = 0.5
        self._loopDelay = loopDelay
        self._cancelling = False
        self._frequency = frequency                    
        self._weekDay = weekDay
        self._startTime = startTime                             
                
        self._action = action
        self._argument = argument
        self._lastRun = self._ReadLastRun()                
            
        self._sched = sched.scheduler(time.time, self._DelayFunc)        
        self._missedSchedule = False
        self._id = self._sched.enterabs(self._CalcNextRun(), 0, self._RunTask, ())
        
    def reset(self, frequency, startTime, weekDay, action, argument=(), loopDelay = 0.2):
        self._delay = 1.0
        self._loopDelay = loopDelay
        self._cancelling = False
        self._frequency = frequency                    
        self._weekDay = weekDay
        self._startTime = startTime                             
                
        self._action = action
        self._argument = argument
        self._lastRun = self._ReadLastRun()
        self._missedSchedule = False
        
        # stop current thread
        self.stop()
        threading.Thread.__init__(self)    	    
        
        
        # ensure it stopped
        i = 0        
        while self.isAlive() and i < 50:
            time.sleep(0.1)
            i+=1
            
        # recreate scheduled event                                                
        self._id = self._sched.enterabs(self._CalcNextRun(), 0, self._RunTask, ())
                
        
    def _ReadLastRun(self):        
        # 0 signifies an error  	
        t = 0

        # read when the task was run last        
        for filename in self._filenames:
            try:
                f = file(os.path.join(tempfile.gettempdir(), filename), 'r')
                t = f.read()
                f.close()            
            except:
                t = 0
                continue
            # check that we have a float
            if not isinstance(t, types.FloatType):
                t = 0
                continue
            if time.time() < float(t):                
                # got time in future, ignore it
                t = 0
                continue    
            else:
                break
                
        return float(t)        
            
    def _WriteLastRun(self):
        # save time when the task was run for future
        for filename in self._filenames:
            try:                        
                f = file(os.path.join(tempfile.gettempdir(), filename), 'w')
                f.write(str(self._lastRun))
                f.close()                       
            except IOError:            
                pass
            
    def _AdjustDST(self, t):
        # deal with daylight savings, if we're on the edge        
        dstDiff = time.localtime().tm_isdst - time.localtime(t).tm_isdst
        t += dstDiff * 3600.00
        return t

    # returns time in seconds (as in time.time())
    # when next scheduled run occurs
    def _CalcNextRun(self):
        # calculate when the next task should run
        # depending on last run time, update frequency,
        # task start time and day of the week
        
        # set C locale, otherwise python and wxpython complain
        locale.setlocale(locale.LC_ALL, 'C')            
        # get current time, skip milliseconds
        t = time.time()
        if self._frequency == 'Hourly':                        
            try:
                # use only MM:SS part of starttime                            
                schedTime = time.mktime(time.strptime(time.strftime('%d-%m-%Y %H:') + self._startTime.split(':', 1)[1],'%d-%m-%Y %H:%M:%S'))                                
            except ValueError, e:            
                print "couldn't parse time, self._startTime = %s.\n Error: %s" % (self._startTime, str(e))
                self._missedSchedule = True
                schedTime = t + 120
            addTime = 3600.0            
        elif self._frequency in ('Weekly', 'Once'):        
            try:
                lt = time.localtime(t)
                # use  weekday and HH:MM:SS part of starttime
                schedTime = time.mktime(time.strptime(str(lt.tm_yday - lt.tm_wday + self._weekDay) + \
                            time.strftime(' %Y ', lt) + self._startTime, '%j %Y %H:%M:%S')) 
            except ValueError, e:
                print "couldn't parse time, self._startTime = %s. self._weekDay = %i\n Error: %s" % (self._startTime, self._weekDay, str(e))
                self._missedSchedule = True
                schedTime = t + 120
            addTime = 3600.0*24*7                    
        else: #'Daily' or 'Workdays' is default                    
            try:
                # use HH:MM:SS part of starttime
                schedTime = time.mktime(time.strptime(time.strftime('%d-%m-%Y ') + self._startTime,'%d-%m-%Y %H:%M:%S'))                                
            except ValueError, e:                
                self._missedSchedule = True
                schedTime = t + 120
                print "couldn't parse time, self._startTime = %s.\n Error: %s" % (self._startTime, str(e))
            addTime = 3600.0*24
            
        
        # go to next time interval if it is out
        tmp = schedTime
        while self._AdjustDST(schedTime) < t:            
            schedTime += addTime                 
        
        # move out of the weekend for workdays            
        if self._frequency == 'Workdays':                        
            while time.localtime(self._AdjustDST(schedTime)).tm_wday in (5,6):
                schedTime += addTime  
            if tmp < schedTime:             
                addTime = schedTime - tmp
                
        #don't return for missed schedule if frequency is workdays and it is weekend now
        if self._frequency != 'Workdays' or time.localtime(t).tm_wday not in (5,6):        	
            # check if we missed the scheduled run
            # and return now (+ 2 minutes) instead            
            if  self._lastRun != 0 and self._AdjustDST(schedTime) - addTime > self._lastRun:
                t = t + 120
                print 'Schedule missed, returning: %s' % time.asctime(time.localtime(t))
                try:
		              print 'LastRun: %s' % time.asctime(time.localtime(self._lastRun))
                except:
		              pass
                self._missedSchedule = True
                return t
                
        schedTime = self._AdjustDST(schedTime)
        print 'Scheduling task for: %s' % time.asctime(time.localtime(schedTime))        
        return schedTime + self._delay
                                  

    def _RunTask(self):
        # get current time        
        if self._cancelling:            
            return 
        # set C locale, otherwise python and wxpython complain
        locale.setlocale(locale.LC_ALL, 'C')
                                           
        t = time.time()
                                
        # execute the action
        print 'running task on: %s. Frequency is: %s\n' % (time.strftime('%d-%m-%y %H:%M:%S', time.localtime(t)), self._frequency)
        void = self._action(*self._argument)
        self._lastRun = t
        self._WriteLastRun()
        
        # schedule next action        
        if self._frequency != 'Once' and not self._missedSchedule:                        
            self._id = self._sched.enterabs(self._CalcNextRun(), 0, self._RunTask, ())               
        
    def _DelayFunc(self, delay):
        start = time.time()
        while not self._cancelling and int(time.time() - start) < int(delay):
            time.sleep(self._loopDelay)                                    
                        
    def run(self):
        self._sched.run()
        print 'Scheduler terminated'
        
    def stop(self):
        try:
           self._sched.cancel(self._id)        
        except:
           pass        
        self._cancelling = True                                        

if __name__ == '__main__':
    import sys
    def action():
        print 'execute'
        
    s = Scheduler('Workdays', '20:20:00', 5, action)
    s.start()
    while sys.stdin.read(1) != 'c':
        time.sleep(0)
    s.stop()    
    s.join(1)
    print 'completed'