#!/usr/bin/python -O # -*- coding: iso-8859-1 -*- # ####################################################################### # # Copyright (c) 2010 Gr��gory Duchatelet # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation # files (the "Software"), to deal in the Software without # restriction, including without limitation the rights to use, # copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following # conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # ####################################################################### """ Display a daily colored map of runned crons """ __revision__ = "$Rev: 5198 $" import sys import re import types class CronParser: weekdays = {0:'SUN', 1:'MON', 2:'TUE', 3:'WED', 4:'THU', 5:'FRI', 6:'SAT', 7:'SUN'} months = {1:'JAN', 2:'FEB', 3:'MAR', 4:'APR', 5:'MAY', 6:'JUN', 7:'JUL', 8:'AUG', 9:'SEP', 10:'OCT', 11:'NOV', 12:'DEC'} re_number = '\d{1,2}' re_interval = re.compile('^(%s-%s)$' % (re_number, re_number)) re_separated = re.compile('^(%s;%s)$' % (re_number, re_number)) re_full = re.compile('^(\*)$') re_alone = re.compile('^(\d+)$') re_intercalated = re.compile('^(\d+)-(\d+)\b?/(\d+)$') re_full_intercalated = re.compile('^[\*]\b?/(\d+)$') re_zero_full_intercalated = re.compile('^0\b?/(\d+)$') def parse(self, expression, start=1, end=31, zero_full=False): result = [] expression_parts = expression.split(';') for exp in expression_parts: # If it's full, don't verify the rest if self.re_full.match(exp): return range(start, end + 1) elif exp == '0' and zero_full: return range(start, end + 1) elif exp == '0' and not zero_full: return [start] elif exp.find(',') > 0: for v in exp.split(","): result.extend(self.parse(v, start, end, zero_full)) return result elif self.re_full_intercalated.match(exp) or\ (self.re_zero_full_intercalated.match(exp) and zero_full): try: step = int(self.re_full_intercalated.match(exp).groups()[0]) except: raise Exception("Wrong step!") return range(start, end + 1, step) elif self.re_interval.match(exp): intervals = self.re_interval.match(exp).groups() for inter in intervals: numbers = re.findall("(%s)" % self.re_number, inter) if len(numbers) != 2: raise Exception("") numbers_range = range(int(numbers[0]), int(numbers[1])+1) result += numbers_range elif self.re_intercalated.match(exp): i = self.re_intercalated.match(exp).groups() result += range(int(i[0]),int(i[1]),int(i[2])) elif self.re_alone.match(exp): result.append(int(exp)) else: raise Exception("Wrong parameter: '%s'" % exp) res = [] for i in result: if i < start or i > end: raise Exception("Expression '%s' out of boundaries. Start at %s until %s"\ % (i, start, end)) if i not in res: res.append(i) del(result) res.sort() return res def parse_day(self, expression): return self.parse(expression, 1, 31) def parse_month(self, expression): return self.parse(self.__convert_expression(expression, self.months), 1, 12) def parse_hour(self, expression): return self.parse(expression, 0, 23) def parse_minute(self, expression): return self.parse(expression, 0, 59) def parse_weekday(self, expression): return self.parse(self.__convert_expression(expression, self.weekdays), 0, 7) def __convert_expression(self, expression, conv_dic): expression = expression.upper() for i in conv_dic: expression = str(i).join(expression.split(conv_dic[i])) return expression if __name__ == '__main__': re_split = re.compile("\s+") re_start_dec = re.compile("^[\d\*]") minutes = [0 for i in range(60*24)] # parse crontabs from stdin while True: line = sys.stdin.readline() if not line: break if not re_start_dec.match(line): continue parts = re_split.split(line) for minute in CronParser().parse_minute(parts[0]): for hour in CronParser().parse_hour(parts[1]): if type(hour) == types.ListType: for h in hour: m = h*60 + minute minutes[m] += 1 else: m = hour*60 + minute minutes[m] += 1 # header header = "M/H| " for th in range(24): header += "%02d "%th print '\x1b[48;5;4m%s\x1b[0m'%header # find the maxcrons value maxcrons = 0 for value in minutes: if value > maxcrons: maxcrons = value # tab for tm in range(60): hline = "\x1b[48;5;4m[%02d]\x1b[0m "%tm for th in range(24): # minute index m = th*60 + tm # find a cool color color = "" if minutes[m] > int(maxcrons-(maxcrons*20/100)): color = 1 elif minutes[m] > int(maxcrons-(maxcrons*30/100)): color = 9 elif minutes[m] > int(maxcrons-(maxcrons*40/100)): color = 3 elif minutes[m] > int(maxcrons-(maxcrons*50/100)): color = 8 elif minutes[m] > int(maxcrons-(maxcrons*60/100)): color = 7 elif minutes[m] > int(maxcrons-(maxcrons*70/100)): color = 6 elif minutes[m] > int(maxcrons-(maxcrons*80/100)): color = 2 else: color = 0 hline += "\x1b[48;5;%sm%02d\x1b[0m|"%(color, minutes[m]) # finally, print the colored line print hline