#!/usr/bin/python # $Id: freebusy.py 6090 2007-02-20 15:44:06Z bremner $ # This festering piece of hackery was written by David Bremner, bremner@unb.ca # it is placed in the public domain # # It may destroy all of your files, ruin your credit rating, put you on the # no-fly lists of 103 nations and transform your favourite relative into # a camel. # # Don't say I didn't warn you. # # If you're not scared off yet, you need the iCalendar python package # from http://codespeak.net/icalendar, as well as pytz and elementtree # packages this are semi-standard, and are e.g. in the debian repositories. # # you will also need to create a directory ~/.freebusy and put # schedule.vfb there (e.g. export from Korganizer) # # Release 0.3 from icalendar import Calendar, Event from datetime import timedelta,time,datetime from pytz import timezone,utc import elementtree.ElementTree as ET from ConfigParser import ConfigParser from os.path import expanduser import os.path daynames=['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'] class calpage(object): _config=dict( busystyle='{ background-color: red ; border-style: solid; border-color: gray ; border-width: thin }', idlestyle='{ background-color: gray }', calfile=expanduser('~/.freebusy/schedule.vfb'), outfile=expanduser('~/.freebusy/schedule.html'), lastrun=expanduser('~/.freebusy/lastrun'), tzname='UTC', starthour=9, dayhours=9, maxlines=10, deltaminutes=15, pagetitle='Schedule', alwaysrun=True ) def __getattr__(self,attr): return self._config[attr] def __init__(self,**config): self._day={} for k,v in config.items(): if not self._config.has_key(k): raise UserWarning('Unknown parameter '+k) self._config[k]=v self.localtz=timezone(self.tzname) self.delta= timedelta(minutes=self.deltaminutes) self._html_init() def _html_init(self): self._root = ET.Element("html") head = ET.SubElement(self._root, "head") style=ET.SubElement(head,"style", {'type':'text/css'}) style.text='TD { text-align: center; border-style:none }' style.text+= 'TD.busy '+self.busystyle style.text+= 'TD.idle '+self.idlestyle title = ET.SubElement(head, "title") title.text = self.pagetitle body = ET.SubElement(self._root, "body") btitle=ET.SubElement(body,"h1") btitle.text=self.pagetitle ET.SubElement(body,"h2").text='Last modified '+datetime.now(self.localtz).isoformat() self._table= ET.SubElement(body,"table") def add_row(self,attr={}): self._cur_row=ET.SubElement(self._table,"tr",attr) def add_element(self,text,attr={}): elt=ET.SubElement(self._cur_row,"td",attr) elt.text=str(text) def cal_header(self): self.add_row() self.add_element('') self.add_element('') for hour in range(self.starthour,self.starthour+self.dayhours): self.add_element("%02d" % hour) for skip in range(1,60/self.deltaminutes): self.add_element('') self.add_row() self.add_element('') self.add_element('') for hour in range(self.starthour,self.starthour+self.dayhours): self.add_element('') for min in range(self.deltaminutes,60,self.deltaminutes): self.add_element(min) def cal_row(self,today): hash=self._day[today] starttime=time(hour=self.starthour) startofday=datetime.combine(today,starttime) startofday =self.localtz.localize(startofday).astimezone(utc) endofday=startofday+timedelta(hours=self.dayhours) minutes=0 daytime=startofday while (daytime < endofday): if hash.has_key(daytime.time()): minutes+=self.deltaminutes daytime+=self.delta if minutes==0: return daytime=startofday self.add_row() self.add_element(today.isoformat()); self.add_element(daynames[today.weekday()]) while (daytime < endofday): if hash.has_key(daytime.time()): self.add_element('*',{'class':'busy'}) else: self.add_element('_',{'class':'idle'}) daytime+=self.delta def write(self,filename=None): if (filename !=None): self.outfile=filename days=self._day.keys() days.sort() lines=self.maxlines+1 for today in days: if lines>self.maxlines: lines=1 self.cal_header() else: lines += 1 self.cal_row(today) # wrap it in an ElementTree instance, and save as XML tree = ET.ElementTree(self._root) tree.write(self.outfile) def process_interval(self,start,end): date=start.date() if not self._day.has_key(date): self._day[date]={} time=start # note, assumption delta is less than 1 hour offset=time.minute % (self.delta.seconds/60) if ( offset != 0): time=time.replace(minute=time.minute - offset) while (time < end): if time.date() != date: date=time.date() if not self._day.has_key(date): self._day[date]={} self._day[date][time.time()]=1 time += self.delta def parse(self,name=None): if (name != None): self.calfile=name cal = Calendar.from_string(open(self.calfile,'rb').read()) for component in cal.walk('VFREEBUSY'): for start,end in component.decoded('FREEBUSY'): self.process_interval(start,end) def outofdate(self): if self.alwaysrun: return True if not os.path.exists(self.outfile): return True if os.path.getmtime(self.outfile) <= os.path.getmtime(self.calfile): return True return False config=ConfigParser() config.read(expanduser("~/.freebusy/config")) cdict={} for section in config.sections(): for k,v in config.items(section): cdict[k]=v page=calpage(**cdict) if (page.outofdate()): page.parse() page.write()