""" Scheduler works only on a weekly scale. This is due to the use of the schedule package. Target format: wD:HH:MM:SS, where: wD - day of week unit: '1', '2', ... , '6', '7', '*' HH - hour unit in 24-hours format: '00', '01', ... , '22', '23', '**' MM - minute unit: '00', '01', ... , '58', '59', '**' SS - second unit: '00', '01', ... , '58', '59', '**' Units can be listed separated by commas. Examples: '*:**:**:**' - every second of every minute of every hour of every day '*:**:**:*5' - every 05,15,25,35,45,55 seconds of every minute of every hour of every day '*:2*:**:**' - every second of every minute of every 20,21,22,23 hours of every day '*:2*:**:*5' - every 05,15,25,35,45,55 seconds of every minute of every 20,21,22,23 hours of every day '3,5:2*:**:*5' - every 05,15,25,35,45,55 seconds every minute every 20,21,22,23 hours of Wednesday, Friday '1,7:12:00:**' - every second 00 minutes 12 hours of Monday, Sunday '1:07:00:00' - every 00 seconds 00 minutes 07 hours Monday """ from itertools import cycle from time import sleep from threading import Event, Thread from . import schedule class Scheduler: """ Control wrapper for the schedule package """ def __init__(self): """ Object constructor """ self.scheduler_entity = schedule.Scheduler() self.scheduler_signal = None def _start_thread(self, interval: int = 1): """ Running the scheduler with a thread in the background :param interval: an integer specifying the period in seconds to start pending jobs """ signal = Event() class ScheduleThread(Thread): @classmethod def run(cls): while not signal.is_set(): self.scheduler_entity.run_pending() sleep(interval) ScheduleThread().start() return signal def start(self): """ Scheduler launch """ self.scheduler_signal = self._start_thread() def stop(self): """ Stopping the Scheduler """ self.scheduler_signal.set() def add(self, target: str, action): """ Adding a job to the execution queue :param target: a string in the format wD:HH:MM:SS specifying the target of the launch time :param action: a string with the name of the function to run """ target_undefined = target.split(':') if len(target_undefined) == 4: wd, hh, mm, ss = target_undefined[0], target_undefined[1], target_undefined[2], target_undefined[3] if '*' in wd and '**' in hh and '**' in mm and '**' in ss: self.scheduler_entity.every().seconds.do(action) elif '*' in wd and '**' in hh and '**' in mm: for element in self._expand_target(wd.split(','), hh.split(','), mm.split(','), self._expand_time(ss, 'ss')): ss = element[3] self.scheduler_entity.every().minute.at(':' + ss).do(action) elif '*' in wd and '**' in hh: for element in self._expand_target(wd.split(','), hh.split(','), self._expand_time(mm, 'mm'), self._expand_time(ss, 'ss')): mm, ss = element[2], element[3] self.scheduler_entity.every().hour.at(mm + ':' + ss).do(action) pass elif '*' in wd: for element in self._expand_target(wd.split(','), self._expand_time(hh, 'hh'), self._expand_time(mm, 'mm'), self._expand_time(ss, 'ss')): hh, mm, ss = element[1], element[2], element[3] self.scheduler_entity.every().day.at(hh + ':' + mm + ':' + ss).do(action) else: for element in self._expand_target(self._expand_time(wd, 'wd'), self._expand_time(hh, 'hh'), self._expand_time(mm, 'mm'), self._expand_time(ss, 'ss')): wd, hh, mm, ss = element[0], element[1], element[2], element[3] if wd == '1': self.scheduler_entity.every().monday.at(hh + ':' + mm + ':' + ss).do(action) elif wd == '2': self.scheduler_entity.every().tuesday.at(hh + ':' + mm + ':' + ss).do(action) elif wd == '3': self.scheduler_entity.every().wednesday.at(hh + ':' + mm + ':' + ss).do(action) elif wd == '4': self.scheduler_entity.every().thursday.at(hh + ':' + mm + ':' + ss).do(action) elif wd == '5': self.scheduler_entity.every().friday.at(hh + ':' + mm + ':' + ss).do(action) elif wd == '6': self.scheduler_entity.every().saturday.at(hh + ':' + mm + ':' + ss).do(action) elif wd == '7': self.scheduler_entity.every().sunday.at(hh + ':' + mm + ':' + ss).do(action) @classmethod def _expand_time(cls, time_raw: str, unit: str): """ Converts an undefined time units to a list of specific time units :param time_raw: a string with time unit :param unit: string description of the time unit :return: list of specific time units """ if ',' in time_raw: pass elif '*' in time_raw and (unit == 'wd' or unit == 'hh' or unit == 'mm' or unit == 'ss'): time_out, time_min, time_max = [], 0, 60 if unit == 'hh': time_max = 24 if unit == 'wd': time_min, time_max = 1, 8 for i in range(time_max): out = time_raw.replace('**', format(i, '02d')).replace('*', str(i)) if 0 <= int(out) < time_max: time_out.append(out) return time_out return time_raw.split(',') @classmethod def _expand_target(cls, wd: list, hh: list, mm: list, ss: list): """ Converts an undefined target to a list of specific target :param wd: list with day of week time units :param hh: list with hour time units :param mm: list with minute time units :param ss: list with second time units :return: list of specific time targets """ cwd, chh, cmm, css = cycle(wd), cycle(hh), cycle(mm), cycle(ss) target_defined = [] for a in range(len(wd)): a_wd = next(cwd) for b in range(len(hh)): b_hh = next(chh) for c in range(len(mm)): c_mm = next(cmm) for d in range(len(ss)): d_ss = next(css) target_defined.append([a_wd, b_hh, c_mm, d_ss]) return target_defined if __name__ == "__main__": pass