The unified diff between revisions [45413da6..] and [3bf9047c..] is displayed below. It can also be downloaded as a raw diff.
#
#
# patch "icalinate.py"
# from [0561e77c659965413efd264fb8aa368c651da16c]
# to [e7e0706e65538dcd569f913a04a4ac97408a79a2]
#
============================================================
--- icalinate.py 0561e77c659965413efd264fb8aa368c651da16c
+++ icalinate.py e7e0706e65538dcd569f913a04a4ac97408a79a2
@@ -14,17 +14,30 @@ import sys
import json
import sys
+def _debug (s):
+ print >> sys.stderr, "debug:", s
+
class EventWrapper(object):
def __init__ (self, vevent, offset):
self.vevent = vevent
self.offset = offset
- self.rruleset_iter = iter (vevent.getrruleset (addRDate=True))
- self.recur ()
+ vevent.getrruleset (addRDate=True)
+ rruleset = vevent.getrruleset (addRDate=True)
+ self.count = 0
+ if rruleset:
+ self.rruleset_iter = iter (rruleset)
+ else:
+ self.next_occurrence = self.vevent.getChildValue ('dtstart')
+ self.rruleset_iter = None
def recur (self):
- self.next_occurrence = self.rruleset_iter.next()
- if self.next_occurrence and self.offset:
- self.next_occurrence -= self.offset
+ self.count += 1
+ if self.rruleset_iter != None:
+ self.next_occurrence = self.rruleset_iter.next()
+ if self.next_occurrence and self.offset:
+ self.next_occurrence -= self.offset
+ elif self.next_occurrence != None and self.count > 1:
+ self.next_occurrence = None
return self.next_occurrence != None
def __cmp__ (self, other_event):
@@ -60,15 +73,21 @@ class EventOccurrence(object):
def duration (self):
self.__calc ()
return self.__duration
+
+ def running (self, now=None):
+ self.__calc ()
+ if now == None:
+ now = datetime.datetime.now ()
+ if self.__duration and self.occurrence + self.__duration >= now:
+ return True
+ return False
- def on_or_later (self, now=None):
+ def later (self, now=None):
self.__calc ()
if now == None:
now = datetime.datetime.now ()
if self.occurrence >= now:
return True
- if self.__duration and self.occurrence + self.__duration >= now:
- return True
return False
def get (self, *args, **kwargs):
@@ -81,7 +100,9 @@ class Upcoming(object):
self.offset = offset
self.heap = []
for vevent in self.ical.vevent_list:
- heapq.heappush (self.heap, EventWrapper (vevent, offset))
+ wrapped = EventWrapper (vevent, offset)
+ if wrapped.recur ():
+ heapq.heappush (self.heap, wrapped)
def __iter__ (self):
while len (self.heap) > 0:
@@ -91,8 +112,9 @@ class iCalinate(object):
heapq.heappush (self.heap, wrapped)
class iCalinate(object):
- def __init__ (self, *args, **kwargs):
- self.upcoming = Upcoming (*args, **kwargs)
+ def __init__ (self, ical_fd, offset=None, criteria=["later", "running"]):
+ self.upcoming = Upcoming (ical_fd, offset=offset)
+ self.criteria = criteria
self.__iter_cache = []
self.back_to_utc = -1 * int (time.time () - time.mktime (time.gmtime ()))
@@ -124,13 +146,19 @@ class iCalinate(object):
entry[k] = self.__unix_from_datetime (v)
else:
entry[k] = str(v)
- print writer.write (rv)
+ return writer.write (rv)
+ def check_running (self, event, now):
+ for fn_name in self.criteria:
+ if not getattr (event, fn_name) (now):
+ return False
+ return True
+
def __iter__ (self):
now = datetime.datetime.now ()
old_count = 0
for event in self.__iter_cache:
- if event.on_or_later (now=now):
+ if self.check_running (event, now):
break
else:
old_count += 1
@@ -138,23 +166,42 @@ class iCalinate(object):
for event in self.__iter_cache:
yield event
for event in self.upcoming:
- if not event.on_or_later (now):
+ if not self.check_running (event, now):
continue
self.__iter_cache.append (event)
yield event
+import httplib
+import urlparse
+import gzip
+
class UriWatchInstantiate:
- def __init__ (self, uri, instantiate_fn, refresh=None):
- self.uri = uri
- self.instantiate_fn = instantiate_fn
- self.refresh = refresh
- if refresh == None:
- self.refresh = datetime.timedelta (hours=2)
- self.__instance = None
- self.instance_dt = None
+ def __init__ (self, uri, instantiate_fn, refresh=datetime.timedelta (hours=2)):
+ self.uri, self.instantiate_fn = uri, instantiate_fn
+ self.refresh = refresh or datetime.da
+ self.__instance, self.instance_dt = None, None
+ self.site = urlparse.urlparse (self.uri)[1]
+ self.etag, self.last_modified = None, None
def get_data (self):
- return open ('rtr.ical').read ()
+ data = None
+ conn = httplib.HTTPConnection (self.site)
+ extra_headers = { 'Accept-encoding' : 'gzip',
+ 'User-agent' : 'iCalinate' }
+ if self.etag != None:
+ extra_headers['If-None-Match'] = self.etag
+ if self.last_modified != None:
+ extra_headers['If-Modified-Since'] = self.last_modified
+ conn.request ("GET", self.uri, headers=extra_headers)
+ r = conn.getresponse ()
+ if r.status == 200: # OK
+ data = r.read ()
+ if r.getheader ('content-encoding', None) == 'gzip':
+ data = gzip.GzipFile (fileobj=StringIO.StringIO (data)).read ()
+ self.last_modified = r.getheader ('last-modified')
+ self.etag = r.getheader ('etag')
+ conn.close ()
+ return data
def instance (self):
now = datetime.datetime.now ()
@@ -174,11 +221,19 @@ def make_uri_wi (uri, icalinate_kw, **kw
# m is for meta
def make_uri_wi (uri, icalinate_kw, **kwargs):
def make_icalinate_cb (contents):
- return iCalinate (StringIO.StringIO (contents), **icalinate_kw)
+ try:
+ return iCalinate (StringIO.StringIO (contents), **icalinate_kw)
+ except vobject.base.ParseError:
+ # deal with abolutely horribly encoded feeds with a giant stick (this is wrong,
+ # but it works in at least some cases.)
+ contents = ''.join ([t for t in contents if ord (t) < 128])
+ return iCalinate (StringIO.StringIO (contents), **icalinate_kw)
return UriWatchInstantiate (uri, make_icalinate_cb, **kwargs)
sources = {
- 'rtrfm' : make_uri_wi ('http://work.papercutmedia.com/rtr/ical', {'offset' : datetime.timedelta(hours=-8)})
+ 'rtrfm' : make_uri_wi ('http://work.papercutmedia.com/rtr/ical', {'offset' : datetime.timedelta(hours=-8)}),
+ 'uwaevents' : make_uri_wi ('http://events.uwa.edu.au/view/publicaffairs/ics/publicaffairs.ics', {'criteria' : ["later"]}),
+ 'dna' : make_uri_wi ('http://www.dnalounge.com/calendar/dnalounge.ics', {}),
}
class FrontPage:
@@ -195,7 +250,11 @@ class iCalinatePage:
def GET (self, source):
if not sources.has_key (source):
return web.notfound ()
- print sources[source].instance ().json_summary ()
+ instance = sources[source].instance ()
+ if instance:
+ print instance.json_summary ()
+ else:
+ return web.internalerror () # fixme, is there a better code for this?
if __name__ == '__main__':
web.run (urls, globals ())