Friday, September 4, 2009

Okay, Now I'm Really Done

Finished porting my street cleaning reminder program to Python for AppEngine, where it seems to be working fine. One unfortunate thing about moving the original desktop C# 3.5 code to ASP.NET 2.0 was that I had to replace all my nice Linq queries with regular loops, so being able to make use of Python features like tuples and list comprehensions was enjoyable.

Here is the code, with my personal identification information removed, and which I hereby release into the public domain:

import cgi

from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

import gdata.service
import gdata.calendar
import gdata.calendar.service
import atom.service
import gdata.alt.appengine

import string
import time

class Cleanendar(webapp.RequestHandler):
  def __init__(self):

    manager = CalendarManager()
    self.commands = { 'n': manager.north, 's': manager.south, '': manager.clear }

  def post(self):

    if self.request.get('event') == 'MO':
      if self.request.get('uid') == MY_ZEEP_UID:

        self.response.headers['Content-Type'] = 'text/plain'


  def process_command(self, body):

    if body in self.commands:
      return ''

      return 'Bad command [' + body + ']'

class CalendarManager:

  def __init__(self):

    self.service = self.get_service()
    (self.calendar, hcalendar) = self.get_calendars(self.service) ='/')[-1]
    self.holidays = self.get_events('/')[-1])

    self.prefix = 'Cleanendar: '
    self.begin = time.strptime(time.strftime('%Y%m%d', time.localtime()) + '1230', '%Y%m%d%H%M')
    self.end = time.strptime(time.strftime('%Y%m%d', time.localtime()) + '1400', '%Y%m%d%H%M')
    self.daynames = ['Mo','Tu','We','Th','Fr','Sa','Su']

  def get_service(self):

    service = gdata.calendar.service.CalendarService(MY_GOOGLE_ID, MY_GOOGLE_PW, MY_APP_ID)
    return service

  def get_calendars(self, service):

    feed = service.GetAllCalendarsFeed()
    calendar = None
    holidays = None
    for i, c in enumerate(feed.entry):
      if c.title.text == "Cleanendar":
        calendar = c
      elif c.title.text == "2009 NYC Alternate Side Parking":
        holidays = c
    return (calendar, holidays)

  def north(self):

  def south(self):

  def clear(self):
    events = self.get_events(
    for i, e in enumerate(events.entry):
      if e.title.text.startswith(self.prefix):

  def set_days(self, days):
    self.add_event(self.make_event(self.begin, "Move car", days))
    self.add_event(self.make_event(self.end, "Move back", days))

  def get_events(self, id):
    return self.service.GetCalendarEventFeed('/calendar/feeds/' + id + '/private/full')

  def make_event(self, t, desc, days):
    event = gdata.calendar.CalendarEventEntry()
    event.title = atom.Title(text=self.prefix + 'Street cleaning')
    event.content = atom.Content(text=desc)
    event.recurrence = self.make_recurrence(t, days)
    event.reminder = self.make_reminder()
    return event

  def add_event(self, event):
    return self.service.InsertEvent(event, '/calendar/feeds/' + + '/private/full')

  def make_recurrence(self, t, days):
    data = ('DTSTART;TZID=UTC;VALUE=DATE-TIME:' + time.strftime('%Y%m%dT%H%M%S', t) + '\r\n' +
      'DURATION:PT5M\r\n' +
      'RRULE:FREQ=WEEKLY;BYDAY=' + string.join([self.daynames[d] for d in days], ',') + ';' +
      'UNTIL=' + time.strftime('%Y', t) + '1231\r\n' +
      'EXDATE:' + string.join([time.strftime('%Y%m%d', d) + time.strftime('T%H%M%S', t) for d in self.make_exclusions(days)], ',') + '\r\n')
    return gdata.calendar.Recurrence(text=data)

  def make_exclusions(self, days):
    e = []
    for i, h in enumerate(self.holidays.entry):
      for t in [time.strptime(w.start_time, '%Y-%m-%d') for w in h.when]:
        if t.tm_wday in [0,3]:
    return e

  def make_reminder(self):
    return gdata.calendar.Reminder(minutes=10, method='sms')

application = webapp.WSGIApplication([('/', Cleanendar)],

def main():

if __name__ == "__main__":

No comments: