RkTaggy - example Google Wave robot

Making robots for Google Wave is bit hard at the moment as there isn't much tutorials, examples and most of robots with public code is written in nasty Java... To make a Wave robot in Python you need:
  • Googl App Engine account. A robot must be (for now) hosted on GAE, as an GAE application. You don't have to know datastore or webapp to start with making a robot (but you have to know how to deploy you application to GAE servers)
  • The official robot Python Tutorial is a good start to get to know how to make a robot skeleton.
  • And when creating new code the API Documentation will be our friend
  • Robots can be tested only from GAE, so you will need to deploy them often and debug a lot (objects, data) with logging.debug(). The logged data will be available in the application admin panel:
    wavemybot1

RkTaggy - example robot

The rktaggy@appspot.com robot is an simple example robot that will (currently) insert image with charts based on data you enter in a blip (wave message). Just add him to a wave.
# -*- coding: utf-8 -*-
from waveapi import events
from waveapi import model
from waveapi import robot
from waveapi import document
import logging

from barcharts import make_barcharts

def OnRobotAdded(properties, context):
	"""
	robot added, add the initial message
	"""
	root_wavelet = context.GetRootWavelet()
	root_wavelet.CreateBlip().GetDocument().SetText("""I'm a BBCode like BBCode ;) robot that has some nifty tags that you can use...
[barchart]Chart Title
label1,value1
label2,value2
label3,value3
...,...
[/barchart]""")

def OnBlipSubmitted(properties, context):
	"""
	New blip added. parse it for supported tags
	"""
	#logging.debug('properties')
	#logging.debug(properties)
	blipId = properties['blipId']
	logging.debug('blipId %s' % blipId)
	blip = context.GetBlipById(blipId) #OpBasedBlip
	if blip:
		doc = blip.GetDocument() # OpBasedDocument
		#elems = blip.GetElements()
		#logging.debug('elements')
		#logging.debug(elems)
		#logging.debug('doc')
		#logging.debug(doc)
		text = doc.GetText()
		#logging.debug('text')
		#logging.debug(text)
		
		urls = make_barcharts(text)
		if len(urls) > 0:
			for i in urls:
				img = document.Image(url=i)
				doc.AppendElement(img)
		
		#doc.SetText(text)


if __name__ == '__main__':
	myRobot = robot.Robot('rktaggy',
		image_url='http://rktaggy.appspot.com/site_media/logo.png',
		version='23',
		profile_url='http://rktaggy.appspot.com/')
	myRobot.RegisterHandler(events.BLIP_SUBMITTED, OnBlipSubmitted)
	myRobot.RegisterHandler(events.WAVELET_SELF_ADDED, OnRobotAdded)
	myRobot.Run()
This robot parse new messages (blips) using the BLIP_SUBMITTED event that is handled by OnBlipSubmitted function. This function has two arguments: properties and context. When you debugg them you will notice that properties has "blipId" with the ID of blip that have been added. So if we have the ID we can get the whole object of the blip using context.GetBlipById(ID) (you can look also on waveapi/model.py, Context class). We will get a OpBasedBlip object (check the API docs about it) for which we can get OpBasedDocument object using GetDocument method. The document object contains all data of a blip - text and other add-ons (attachments for example). We use GetText method to get the text of the message and pass it to make_barcharts function (described later), that extracts the data needed for charts and returns URL to charts on Google Charts (the charts are images). To add those charts to a Blip we add them as Elements using the document.Image class, which wants a URL to image as argument (which we already have).
The make_barcharts function looks like this:
# -*- coding: utf-8 -*-
from re import findall
from pygooglechart import GroupedVerticalBarChart
from pygooglechart import Axis
import logging

def make_barcharts(text):
	urls = []
	barcharts = findall( r'(?xs)\[barchart\](.*?)\[/barchart\]''', text)
	
	for i in barcharts:
		try:
			values = []
			labels = []
			maxvalue = 0
			title = ''
			data = i.split('
')
			if len(data) > 0:
				title = data[0]
				del data[0]
				for d in data:
					d = d.strip()
					if len(d) > 0:
						elem = d.split(',')
						labels.append(elem[0])
						values.append(int(elem[1]))
						if int(elem[1]) > maxvalue:
							maxvalue = int(elem[1])
				chart = GroupedVerticalBarChart(300, 300, y_range=(0, maxvalue))
				chart.set_bar_width(10)
				chart.set_colours(['00ff00'])
				chart.set_bar_spacing(15)
				chart.set_group_spacing(50)
				chart.add_data(values)
				labs = chart.set_axis_labels(Axis.BOTTOM, labels)
				chart.set_axis_style(labs, '202020', font_size=12, alignment=0)
				vlabs = chart.set_axis_labels(Axis.LEFT, values)
				chart.set_axis_style(vlabs, '202020', font_size=12, alignment=0)
				index = chart.set_axis_labels(Axis.TOP, [title])
				chart.set_axis_style(index, '202020', font_size=14, alignment=0)
				chart.set_axis_positions(index, [50])
				
				urls.append(chart.get_url())
		except:
			logging.error('EXCEPTION FOR')
			logging.error(i)
			pass
	return urls
The function looks for tags of this structure:
[barchart]Chart Title
label,int_value
label,int_value
label,int_value
[/barchart]
And then it extracts labels, values and title and makes a chart using pygooglechart module. The pygooglechart will return a URL for the chart (which we later insert to the blip).
Currently you can't add HTML to a blip using the API (like with SetText) in an easy way. The problem is known, and the Wave Team is working on it ;) Currently some developers suggest using SetAnnotation to add some HTML markup to the blip text. In our case we use the "easy" way of inserting an image.

Source Code

To reuse the code you will have to change the app name in app.yaml and in the robot code (to a name of your registered app on GAE)
RkBlog

Web development, 18 October 2009

Comment article
Comment article RkBlog main page Search RSS Contact