Commits

Anonymous committed 991e8d7

breaks at times on startup, but if it runs through, it works.

Comments (0)

Files changed (1)

 from time import ctime
 # mktime to parse time strings into seconds since epoch
 from time import mktime
+# and scheduling from pyglet
+from pyglet import clock
+
 
 # for identi.ca swarms: urlopen
-from urllib2 import urlopen
+from urllib2 import urlopen, HTTPError
 
 # for xml parsing
 from xml.dom import minidom
     data = property(get_data)
 
 
+from threading import Thread
+
+class Urlgrabber(Thread):
+    def __init__(self, results, url, scene = None):
+        """@param results: a list to which to append the url content.
+        @param url: the url to grab repeatedly."""
+        super(Urlgrabber, self).__init__()
+        self.results = results
+        self.url = url
+        self.keep_grabbing = True
+        self.scene = scene
+
+    def run(self):
+        """repeatedly grab the url and append the results to self.results."""
+        while self.keep_grabbing:
+            try: 
+                f = urlopen(self.url)
+                d = f.read()
+                f.close()
+                self.results.append(d)
+            except HTTPError: pass
+            if self.scene and self.scene.core.win.has_exit:
+                return
+            
+
 class IdenticaFeeder(object):
-    def __init__(self, time_to_play = 60):
+    def __init__(self, url="http://identi.ca/api/statuses/public_timeline.xml", time_to_play = 60, scene = None):
+        self.url = url
         self.last_access_time = time()
         self.max_timestamp = time() + time_to_play
+        self.scene = scene
         self.latest_known_status_date = 0
         self.username_split_chars = "(){}[]<>\\/,. "
+        #: the delay between items to show a continuous flow in between the slow accesses. TODO: move the url access into a thread.
+        self.delay_between_items = 0
+        self.last_access_time = time()
         self._data = []
+        self.downloads = []
+        self.grabber = Urlgrabber(results = self.downloads, url = self.url, scene = scene)
+        self.grabber.start()
+        
     def get_data(self):
         """@return [(date, user, message_url), ...]"""
+        # cleanly shut down the grabber
+        if time() >= self.max_timestamp or (self.scene and self.scene.core.win.has_exit):
+            try:
+                self.grabber.join()
+            except: sleep(0.1)
+            return []
+
         if self._data:
-            d = self._data[0]
+            # add a delay between items to get a continuous flow
+            if time() - self.last_access_time < self.delay_between_items:
+                return []
+            self.last_access_time = time()
+            d = self._data[0:1]
             self._data = self._data[1:]
-            return d
+            print d
+            return [(time(), d[0][1], d[0][2])]
+        # if we have no data left, build new data.
         #: the return data to build
         data = []
-        # get the identi.ca timeline
-        f = urlopen("http://identi.ca/api/statuses/public_timeline.xml")
-        d = f.read()
-        f.close()
+
+        # adjust the timing for elements.
+        start_time = time()
+        while not self.downloads:
+            sleep(0.1)
+        stop_time = time()
+        wait_time = stop_time - start_time
+        self.delay_between_items += wait_time
+        if self.downloads[1:]:
+            self.delay_between_items /= 2
+
+        # get the identica data
+        d = self.downloads[0]
+        self.downloads.remove(d)
+
         # parse it as xml
         xml = minidom.parseString(d)
         struct = xml.childNodes[0].childNodes
                 # time in seconds since epoch
                 date = mktime(parsedate(status.getElementsByTagName("created_at")[0].firstChild.wholeText))
                 # only include newer status messages
-                if date < self.latest_known_status_date:
+                if date <= self.latest_known_status_date:
                     continue
                 # text for receivers
-                text = status.getElementsByTagName("text")[0].firstChild.wholeText
-                user = status.getElementsByTagName("user")[0].getElementsByTagName("name")[0].firstChild.wholeText
+                text = status.getElementsByTagName("text").pop().firstChild.wholeText
+                status_id = status.getElementsByTagName("id").pop().firstChild.wholeText
+                user = status.getElementsByTagName("user").pop().getElementsByTagName("name").pop().firstChild.wholeText
             except: # no text
                 continue
             # check for receivers
             for re in receivers:
                 for c in self.username_split_chars:
                     re = re.split(c)[0]
-                data.append((date, user, re))
+                data.append((date, user, "@"+re))
+            data.append((date, user, text))
 
-        print data
         if data: 
             self._data = data[1:]
             self.latest_known_status_date = max([date for date, user, re in data])
 	if "--identica" in args[0]: 
 	    self.swarm_type = "identica"
 	    args[0].remove("--identica")
+
+        if "--twitter" in args[0]: 
+	    self.swarm_type = "twitter"
+	    args[0].remove("--twitter")
 	
 	if "--activity" in args[0]: 
 	    self.swarm_type = "activity"
 	    # add the actitivity file to the data
 	    self.feeder.read_codeswarm_activity(self.path, time_to_play = self.time)
         elif self.swarm_type == "identica":
-            self.feeder = IdenticaFeeder(time_to_play = self.time)
+            identica = "http://identi.ca/api/statuses/public_timeline.xml"
+            self.feeder = IdenticaFeeder(url = identica, time_to_play = self.time, scene = self)
+        elif self.swarm_type == "twitter":
+            twitter = "http://api.twitter.com/statuses/public_timeline.xml"
+            self.feeder = IdenticaFeeder(url = twitter, time_to_play = self.time, scene = self)
 	else: 
 	    self.feeder = Feeder()
-	
+
         ## The blobs
         #: All authors: name: blob
 	self.blobs = {}
 	self.reltime = self.core.load_text("rel time", font_size = 10.0, x=10, y=10)
 	self.visible.append(self.reltime)
 
+        # limit the FPS to 20 via pyglet internals
+        clock.set_fps_limit(20)
+
+
     def show_change(self, author_name, filepath): 
 	"""Show a change by an author to a file."""
 	# If we don't yet have the called ones, add them. 
 		x, y = self.get_starting_position()
 		author = Author(scene=self, name=filepath, x=x, y=y)#, batch=self.author_batch)
 		self.blobs[filepath] = author
-        elif self.swarm_type == "identica" and False:
-            if not filepath in self.blobs: 
+        elif self.swarm_type == "identica" or self.swarm_type == "twitter":
+            if not filepath in self.blobs:
 		x, y = self.get_starting_position()
-		author = Author(scene=self, name=filepath, x=x, y=y)#, batch=self.author_batch)
-                author.color = (100, 50, 50) + author.color[4]
-		self.blobs[filepath] = author
+                if filepath.startswith("@") and not " " in filepath: # heuristic to recognize a name
+                    author = Author(scene=self, name=filepath, x=x, y=y)#, batch=self.author_batch)
+                    author.color = (200, 50, 50, author.color[3])
+                    self.blobs[filepath] = author
+                else:
+                    f = File(scene=self, name=filepath, x=x, y=y)#, batch=self.file_batch)
+                    self.blobs[filepath] = f
+
         # otherwise we add the files as blobs
 	else: 
 	    if not filepath in self.blobs: 
 		return x, y
 		
 
+    def get_new_changes(self):
+        """Get and process new change data."""
+        try: 
+            data = self.feeder.data
+            print data
+        except:
+            print "can't get data"
+            return
+	
+	for date, author_name, filename in data: 
+	    if author_name and filename: 
+		self.show_change(author_name, filename)
+	    self.time.text = ctime(date)
+	    # Shut down once we reach the maximum timestamp. 
+	    if date == self.feeder.max_timestamp: 
+		self.countdown = COUNTDOWN
+        
+
     def update(self): 
         """Update the stats of all scene objects. 
 
 	    else: 
 		self.countdown -= 1
 
-	data = self.feeder.data
-	
-	for date, author_name, filename in data: 
-	    if author_name and filename: 
-		self.show_change(author_name, filename)
-	    self.time.text = ctime(date)
-	    # Shut down once we reach the maximum timestamp. 
-	    if date == self.feeder.max_timestamp: 
-		self.countdown = COUNTDOWN
+        self.get_new_changes()
 	
 	self.reltime.text = ctime(time())