Combining Tele-communications and E-commerce (Asterisk, Java, and Python - Oh My!)

This very gentle introduction to development with Asterisk will demonstrate how to use the AGI. Along the way you'll learn about Python and Java as well. I'll assume you're able to set up and configure Asterisk.

The Asterisk Gateway Interface (AGI) allows an application you write (in whatever language you choose) to communicate directly with Asterisk using simple descriptive text commands. Normally you place your application in /var/lib/asterisk/agi-bin and Asterisk will execute it when it is encountered in the dial plan. Most of the commands are explained here. They are fairly straight forward, so I'll only demonstrate a few. There are three other AGIs you should be aware of. There is the dead AGI which allows you to run an application after a call ends. There is the fast AGI which lets your application communicate with Asterisk over TCP. And there is the extended AGI which is supposed to let your application view the audio stream on file descriptor three. But I don't know the state of this last AGI.

Now here is your task. M&M wants to add a new color to their current selection. The choices are pink, emerald green, and mahogany. You will be responsible for developing the phone based voting system that will allow customers to vote for these new delicious sounding candies. In addition, you will need to make the results available to the public on a website in a graphical format.

First take a look at the code below. You can find agi.py in the resources section (along with a link to the explanation). You'll also want the connection code.

#!/usr/bin/env python

#resources
#http://home.cogeco.ca/~camstuff/agi.html
#http://sourceforge.net/projects/mysql-python

import agi
from connect 
import get_mysql_connection

OPTIONS = "123"
interface = agi.AGI()
select_map = {1: "pink",
	      2: "green",
	      3: "mahogany"}

select_stmt = 'SELECT votes FROM colors WHERE color="%s"'
update_stmt = 'UPDATE colors SET votes=%d WHERE color="%s"'

try:
	(res,int_res)=interface.Cmd('STREAM FILE voteprompt "%s"' % OPTIONS)
	db = get_mysql_connection()
	color = select_map[int(chr(int_res))]
	db.query(select_stmt % color)
	rs = db.store_result()
	val = rs.fetch_row()[0][0]#returns a tuple of tuples
	db.query(update_stmt % (val+1,color))
	db.close()
except:
	pass

This is our interface to asterisk. Every time a caller dials in, this script will be run. The first line of the try block executes the STREAM FILE command. The first argument is the name of the file to play, and the second argument are the keys the caller can press. In this example, the caller can press one, two, or three for the three colors. The next line connects to the database. The number is mapped to a color and the corresponding row is selected from the database. The number of votes is incremented and put back. That's all.

Now let's take a look at the code for generating a plot of the results. In order to do this, we'll use Jython for its graphics libraries. I won't go much into the graphics libraries, but you can have a look at the references below.

#!/usr/bin/env jython

#resources
#http://www.aion-needlecrafts.co.uk/info/dmcval2.htm
#http://www.geocities.com/marcoschmidt.geo/java-save-jpeg-thumbnail.html
#http://www-106.ibm.com/developerworks/library/j-begjava/
#http://java.sun.com/docs/books/tutorial/2d/TOC.html#display

import time
import connect
from com.sun.image.codec.jpeg 
import JPEGImageEncoder,JPEGEncodeParam,JPEGCodec
from java.awt import Graphics2D, Color
from java.awt.image import BufferedImage
from java.io import BufferedOutputStream,FileOutputStream
from java.awt.geom import Rectangle2D

MAX_X = 300
MAX_Y = 200

def write_image(votes,name):m = max(votes.values())
	if m==0:
		return
	for k in votes:
		votes[k] = MAX_Y-int(votes[k]*float(MAX_Y)/m)

	#create the image
	image = BufferedImage(MAX_X,200,BufferedImage.TYPE_INT_RGB)
	g2d = image.createGraphics()g2d.setPaint(Color.white)
	g2d.fill(Rectangle2D.Double(0,0,MAX_X,MAX_Y))
	
	g2d.setPaint(Color(243,71,139)) #Cyclamen Pink
	g2d.fill(Rectangle2D.Double(0,votes["pink"],100,MAX_Y))
	g2d.setPaint(Color.black)
	g2d.drawString("Pink", 35,190)
	
	g2d.setPaint(Color(49,128,97)) #Emerald Green - MED
	g2d.fill(Rectangle2D.Double(100,votes["green"],200,MAX_Y))
	g2d.setPaint(Color.black)
	g2d.drawString("Emerald Green", 105,190)
	
	g2d.setPaint(Color(209,102,84)) #Mahogany - MED
	g2d.fill(Rectangle2D.Double(200,votes["mahogany"],300,MAX_Y))
	g2d.setPaint(Color.black)
	g2d.drawString("Mahogany", 220,190)

	#write to file as a JPEG
	bos = BufferedOutputStream(FileOutputStream(name+".jpeg"))
	encoder = JPEGCodec.createJPEGEncoder(bos)
	param = encoder.getDefaultJPEGEncodeParam(image)
	param.setQuality(1.0, False)
	encoder.setJPEGEncodeParam(param)
	encoder.encode(image)
	bos.close()

while True:
	time.sleep(60)
	db = connect.get_jdbc_connection()
	stmt = db.createStatement()
	rs = stmt.executeQuery("SELECT * FROM colors")
	results = {}
	while rs.next():
		md = rs.getMetaData()
		results[rs.getObject(1)] = rs.getObject(2)
	db.close()
	write_image(results,"votes")

First we go into a loop that executes every 60 seconds (making this effectively an image server). Then we select the total votes for each of the colors from the database. Last the image generation routine is called with the votes. The numbers are normalized. A new image is created, and the background is set to white. Each of the three colors is painted on the background as a vertical bar with the height corresponding to the number of votes. Some identifying text is placed beneath each bar. Then the image is written out as a jpeg.

The last bit of code is for handling database connections in Python and Jython and is not discussed here. If you have a decent understanding of Python, you should be able to follow the code.

Exercises (Warning - I haven't tried these.)
1) Add two more colors to the choices.
2a) Try rewriting the plot generation routine to make a pie chart.
2b) Change it to an animated gif and modify it to show changes over time.
2c) Put a web page around it and make it accessible from the internet.
3) Modify all the code to use Jython (for consistency and simplicity). Separate the image server from the rest of the code by using Java's RMI.

home