From fe53e3051bf870cd94613321eeead5b74f90acbf Mon Sep 17 00:00:00 2001 From: Tobias Mueller Date: Mon, 3 May 2010 00:34:38 +0100 Subject: [PATCH] mkical.py: Make it print a textual calendar It works quite well, with the exception of multiple events in one week. --- foundation.gnome.org/elections/2010/mkical.py | 120 +++++++++++++++++- 1 file changed, 116 insertions(+), 4 deletions(-) diff --git a/foundation.gnome.org/elections/2010/mkical.py b/foundation.gnome.org/elections/2010/mkical.py index 4c128fb..f42d6d4 100644 --- a/foundation.gnome.org/elections/2010/mkical.py +++ b/foundation.gnome.org/elections/2010/mkical.py @@ -4,8 +4,11 @@ This Python script creates a simple iCal file based on hardcoded events in this file. ''' +import calendar import datetime import logging +import math +import os import vobject @@ -149,10 +152,112 @@ def create_ical(eventlist): return stream +def wraptext(s, width): + '''Wraps a string @s at @width characters. + + >>> wraptext('fooo', 2) + ['fo','oo'] + ''' + l = len(s) + nr_frames = int(math.ceil(float(l)/width)) + print nr_frames + frames = [] + for i in xrange(nr_frames): + start, end = i*width, (i+1) * width + frames.append(s[start:end]) + # One could (and prolly should) yield that + return frames + +def ordinal(n): + n = int(n) + if 10 <= n % 100 < 20: + return str(n) + 'th' + else: + return str(n) + {1 : 'st', 2 : 'nd', 3 : 'rd'}.get(n % 10, "th") + + +def cal_for_month(month, events, width=80, year=datetime.datetime.now().year): + '''Generates a textual calendar for the @month in @year. + It will return a string with the calendar on the left hand side and the + events on the right hand side. + @events shall be a list with tuples: timestamp, summary, description. + + Returns a string with the calendar + ''' + log = logging.getLogger('cal_for_month') + + cal = calendar.TextCalendar() + calstrings = cal.formatmonth(year, month, 3).splitlines() + + for (timestamp, summary, description) in events: + log.debug('creating %s, %s', timestamp, summary) + year, month, day = timestamp.year, timestamp.month, timestamp.day + maxwidth = max([len(cs) for cs in calstrings]) + rightwidth = 80 - maxwidth + for i, line in enumerate(calstrings): + needles = (" %d " % day, + " %d\n" % day) + replacement = "(%d)" % day + # Find the day so that we can highlight it and add a comment + day_in_week = False + for needle in needles: + if needle in line+"\n": + # k, this looks a bit weird but we have that corner + # case with the day being at the end of the line + # which in turn will have been split off + day_in_week = True + break # Set the needle to the found one + if day_in_week == False: # Nothing found, try next week + log.debug('Day (%d) not found in %s', day, line) + continue + else: + log.debug('Day (%d) found in %s', day, line) + new_line = (line+"\n").replace(needle, replacement).rstrip() + new_line += " %s (%s)" % (summary, ordinal(day)) + # Replace in-place for two events in the same week + # FIXME: This has bugs :-( + calstrings[i] = new_line + + return os.linesep.join(calstrings) + +def create_textcal(eventlist): + '''Generates a multiline string containing a calendar with the + events written on the side + The list shall contain elements with a tuple with a + (date, string, string) object, serving as date when the event takes place, + summary and description respectively. + ''' + log = logging.getLogger('textcal') + log.debug('Generating from %s', eventlist) + months = set(map(lambda x: x[0].month, eventlist)) + year = set(map(lambda x: x[0].year, eventlist)).pop() + + final_cal = [] + for month in months: + events = filter(lambda x: x[0].month == month, eventlist) + log.debug('Events for %d: %s', month, events) + month_cal = cal_for_month(month, events, year=year) + final_cal.append(month_cal) + + return os.linesep.join(final_cal) if __name__ == "__main__": + from optparse import OptionParser + parser = OptionParser("usage: %prog [options]") + parser.add_option("-l", "--loglevel", dest="loglevel", help="Sets the loglevel to one of debug, info, warn, error, critical", + default=None) + parser.add_option("-i", "--ical", + action="store_true", dest="ical", default=False, + help="print iCal file to stdout") + parser.add_option("-t", "--textcal", + action="store_true", dest="tcal", default=False, + help="print textual calendar to stdout") + (options, args) = parser.parse_args() - logging.basicConfig( level=logging.CRITICAL ) + loglevel = {'debug': logging.DEBUG, 'info': logging.INFO, + 'warn': logging.WARN, 'error': logging.ERROR, + 'critical': logging.CRITICAL}.get(options.loglevel, "warn") + logging.basicConfig( level=loglevel ) log = logging.getLogger() eventlist = [ @@ -166,6 +271,13 @@ if __name__ == "__main__": PRELIMINARY_RESULTS, CHALLENGE_CLOSED, ] - - ical = create_ical( eventlist ) - print ical + + if not any([options.ical, options.tcal]): + parser.error("You want to select either ical or textcal output. See --help for details") + if options.ical: + ical = create_ical( eventlist ) + print ical + if options.tcal: + tcal = create_textcal( eventlist ) + print tcal +