The unified diff between revisions [45413da6..] and [3bf9047c..] is displayed below. It can also be downloaded as a raw diff.

This diff has been restricted to the following files: 'icalinate.py'

#
#
# 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 ())