# coding=utf-8 __author__ = "Anatoly Rr Burov (me@anatolyrr.ru)" __version__ = "2013" __date__ = "2012-12-31" __license__ = "CC-BY" from calendar import Calendar # used in render_month class SvgCalendar: def __init__ (self,year): self.year = year font = 'Consolas' self.style = { 'units' : 'mm', 'width' : 100, 'height' : 70, 'border-color' : '#ccc', 'year-color' : '#666666', 'year-padding-top' : 5, 'year-padding-left': 2, 'year-font-family' : font, 'year-font-size' : 5, 'month-width' : 24, 'month-height' : 21, 'day-width' : 23.0 / 7.0, 'day-height' : 12.0 / 5.0, 'month-margin-right' : 0, 'month-margin-bottom' : 0, 'month-font-family' : font, 'month-font-size' : 3, 'month-color' : '#FF9525', 'month-padding-top' : 3, 'month-offset-top' : 5, 'week-padding-top' : 6, 'week-font-family' : font, 'week-font-size' : 1.5, 'day-padding-top' : 6, 'day-font-family' : font, 'day-font-size' : 2.5, 'day-color' : '#000000', 'day-holiday-color' : '#79B1D4', 'week-color' : '#999', 'week-holiday-color' : '#79B1D4', } self.year_name = "0x" + hex(year)[2:].upper() self.month_names = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '0A', '0B', '0C'] self.weekdays_names = ['001', '010', '011', '100', '101', '110', '111'] # self.days_names = [ "%02x" % (i + 1) for i in range (32)] self.days_names = [ "%02x" % (i + 1) for i in range (32)] # well, actually holidays depend on the production calendar self.holidays = [] self.not_holidays = [] if year == 2013: # got dates for 2012 # tuples (month, day) self.holidays = [ (1,1), (1,2), (1,3), (1,4), (1,5), (1,6), (1,7), (1,8), (2,23), (3, 8), (5, 1), (5, 2), (5, 3), (5, 9), (5, 10), (6,12), (7, 4), (11,4), ] self.holidays.append ((4, 4)) # (4, 4) - Webmasters' Day # last Friday of July is Sysadmin's Day self.holidays.append ((7,26)) # Calculated for 2012 manually, have no time before publication to implement general case # self.not_holidays = [(3,11), (4, 28), (6, 9), (12, 29)] # 256th day of year is Programmers' Day if year % 4 == 0: # leap year, I didn't bother with %100 and %400 years, sorry ;) We will not need them in the nearest 88 years anyway self.holidays.append ((9,12)) else: self.holidays.append ((9,13)) def is_holiday (self, month, day, day_of_week): if day_of_week in [5, 6]: return (month, day) not in self.not_holidays return (month, day) in self.holidays def render_day (self, x, y, month, day, day_of_week): svg = '' if self.is_holiday (month, day, day_of_week): color = self.style ['day-holiday-color'] else: color = self.style ['day-color'] svg += ''% (x + 0.5*self.style['day-width'], y, self.style['day-font-family'], self.style['day-font-size'], color) svg += '%s' % self.days_names [day-1] svg += '' return svg def render_week (self, x, y): svg = '' svg += '' for i in range (7): if i < 5: color = self.style['week-color'] else: color = self.style['week-holiday-color'] svg += ''% (x + (i +0.5)* self.style['day-width'],y, self.style['week-font-family'], self.style['week-font-size'], color) svg += '%s' % (self.weekdays_names [i]) svg += '' svg += '' return svg def render_month (self, x,y, month_no): svg = '' svg += '' svg += ''% (x + self.style['month-width']/2,y+self.style['month-padding-top'], self.style['month-font-family'], self.style['month-font-size'], self.style['month-color']) svg += '%s' % (self.month_names [month_no-1]) svg += '' svg += self.render_week (x, y+self.style['week-padding-top']) day_of_week = -1 # will start from Monday week_no = 0 c = Calendar (0) for day_no in c.itermonthdays (self.year, month_no): day_of_week = (day_of_week + 1) % 7 if day_of_week == 0: week_no += 1 if day_no == 0: continue # month not yet started xx = x + self.style['day-width'] * (day_of_week) yy = y + self.style['day-padding-top'] + week_no * self.style['day-height'] svg += self.render_day (xx, yy, month_no, day_no, day_of_week) svg += '' return svg def render_year (self, x, y): svg = '' svg += '' svg += ''% (x + self.style['width']/2,y+self.style['year-padding-top'], self.style['year-font-family'], self.style['year-font-size'], self.style['year-color']) svg += self.year_name svg += '' for i in range (12): xx = i % 4 yy = i / 4 svg += self.render_month (x+ xx*self.style['month-width'] + xx*self.style['month-margin-right'], y + self.style['month-offset-top']+ yy*self.style['month-height'] + yy*self.style['month-margin-bottom'], i+1) svg += '' return svg def render (self): svg = '' svg += '' svg += 'Calendar 2012' % (self.style['width'], self.style['height']) svg += '' % (self.style['width']-0.75, self.style['height']-0.75, self.style['border-color']) svg += self.render_year (self.style['year-padding-left'], 0) svg += '' return svg import sys if __name__ == '__main__': c = SvgCalendar (2013) # normal, 100x70 mm if not 'a4' in sys.argv: print c.render() # a4 else: k = 3; c.style.update ({ 'units' : 'mm', 'border-color' : '#fff', 'width' : 297, 'height' : 210, 'year-padding-top' : 6 * k, 'year-padding-left': 2 * k, 'year-font-size' : 5 * k, 'month-width' : 21 * k , 'month-height' : 20 * k, 'day-width' : 22.0 * k / 7.0, 'day-height' : 10.0 * k / 5.0, 'month-margin-right' : 9, 'month-margin-bottom' : 1, 'month-font-size' : 3 * k, 'month-padding-top' : 5 * k , 'month-offset-top' : 6 * k, 'week-padding-top' : 9 * k, 'week-font-size' : 1.5 * k, 'day-padding-top' : 10 * k, 'day-font-size' : 2 * k, }) print c.render()