Skip to content

Commit

Permalink
Merge remote-tracking branch 'tropo-colors/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
danabauer committed May 14, 2013
2 parents 4939f08 + 95b3d45 commit 935f91b
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 0 deletions.
2 changes: 2 additions & 0 deletions sub/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.py[c|o]
*~
1 change: 1 addition & 0 deletions sub/Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: python heroku/simple_colors.py
68 changes: 68 additions & 0 deletions sub/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
Tropo Colors
============

This is a simple application built with Redis and Tropo to change the color of
the background of a web page on voice command. It is meant to be as simple as
possible, so that it can be used as an example to teach programming.

Explanation
-----------

There are three parts to this application:

* A Tropo script (``tropo/colors.py``) that asks the user for a color,
* A server-side Itty app (``heroku/simple_colors.py``) that handles the
storage and retrieval of the current color from a Redis data store, and
* A client-side script that checks on updates to the stored color.

The Tropo script is made to be uploaded to a Tropo account. The Itty app can
be uploaded to Heroku (or anywhere else that can run and serve Python apps).

The Tropo app that you create to run the script should be assigned a phone
number. When you call that number, Tropo will run the script, asking for a
color. If it recognizes the color, it will send out a signal (``request``) to
the Itty app to save the color. The Itty app will receive the ``request`` and
store the color in a Redis datastore (``bucket``).

In the mean time, the client side script will be watching the value of the
color stored in the ``bucket`` and, when it changes, updating the web page.

Why Itty?
---------

I normally create my apps in Django. I could have created a Django project for
this, and used an SQLite database or something, but I wanted something that I
could do in about one module, and that I didn't have to do too much additional
explanation to get through.

So, I started to try Flask (zachwill has a great flask-heroku starter project
on github), but I found that even this seemed to obfuscate what I was trying
to get across. In addition to my framework being light-weight, I wanted my
code to be as light-weight as possible as well, since this is a pedagogical
tool. I don't mind Flask myself, but I felt that things like::

@request('/colors', methods=['POST'])
...

were too much. Itty is the lightest, lowest-configuration framework for Python
I've seen, and gets the point across nicely. For example, the above route in
Itty is::

@post('/colors')
...

A minor difference to be sure, but one big enough to sway me.

Why the Tropo hosted script instead of HTTP API?
------------------------------------------------

There are two ways to use Tropo, and I picked one. At first, I used the Tropo web
API, but decided to simplify things by using a Tropo script instead for two
reasons:

* I was afraid that the overhead of explaining the HTTP back-and-forth that
is done between Tropo and the server app would get in the way of the
programming concepts.
* Using the script, I get to use more of simpler concepts like iteration and
conditionals, reinforcing these concepts and keeping the async HTTP requests
to a minimum.
28 changes: 28 additions & 0 deletions sub/heroku/colors.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<html>
<head>
<title>Tropo Colors</title>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
<script>
$(document).ready(listenForColorChanges);

function listenForColorChanges() {
var url = '/color';

$.get(url, function(data, status, $xhr) {
$('body').css('background-color', data);
$('body h1').html('The color is ' + data);

setTimeout(listenForColorChanges, 1000);
});
}
</script>
</head>

<body>
<h1>The color is ...</h1>

<p>Call (415)889-8683 to change the color.</p>
<p><a href="https://github.com/mjumbewu/tropo_colors">(see the code...)</a></p>
</body>
</html>
35 changes: 35 additions & 0 deletions sub/heroku/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import os
import urlparse

ROOT = os.path.dirname(__file__)

##
# Redis -- a data store
#
# Redis is the data storage system that we use for this project. It allows you
# to store data in really simple ways. Heroku gives us easy access to a redis
# data store using a service called Redis To Go. We have to parse out the
# pieces to use it.

redis_info = urlparse.urlparse(os.environ.get('REDISTOGO_URL', 'redis://user:pass@host:9999/'))

REDIS = {
'host': redis_info.hostname,
'password': redis_info.password,
'port': redis_info.port,
'db': 0,
}

##
# itty -- a web framework
#
# There are several web frameworks for Python, and each give you different
# built-in capabilities. For this application, we want to use as simple a
# framework as possible, and itty is about as simple as it gets. Here we set
# up the "host" and "port" (two things that are fundamental to being able to
# locate things on the web) that Heroku expects to use.

ITTY = {
'host': '0.0.0.0',
'port': int(os.environ.get('PORT', 8080))
}
67 changes: 67 additions & 0 deletions sub/heroku/simple_colors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from itty import *
import redis
import settings


##
# Path : /
# Method : GET
#
# This is the main path for the application. It just gives you the html page
# that you use to view the app. For example, in your browser, go to the url:
#
# http://tropo-colors.herokuapp.com/
#
# (The last '/' on that url because of the definiton below)
#
@get('/')
def _(request):
return serve_static_file(request, 'colors.html', settings.ROOT)


##
# Path : /color
# Method : GET
#
# This is the path that will allow you to get the current color from the
# database. For example, in your browser, go to:
#
# http://tropo-colors.herokuapp.com/color
#
# We use this to tell the web page what color it should be.
#
@get('/color')
def _(request):
bucket = get_a_bucket()

color = bucket.get('color') or 'white'
return color


##
# Path : /color
# Method : POST
#
# This path can also be used to set the current color for the web page. Notice
# that this definition starts with @post instead of @get like the others. By
# convention, on the web, GET usually means "retrieve something", and POST means
# "store something". So, in this case, we're storing the color.
#
@post('/color')
def _(request):
bucket = get_a_bucket()

color = request.body
bucket.set('color', color)
return color


##
# This function will return a bucket (a Redis data storage structure) that we
# can use to store or retrieve colors.
def get_a_bucket():
bucket = redis.Redis(**settings.REDIS)
return bucket


run_itty(**settings.ITTY)
3 changes: 3 additions & 0 deletions sub/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
itty
redis
tropo-webapi-python
43 changes: 43 additions & 0 deletions sub/tropo/colors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from httplib import HTTPConnection
# This script also uses two function from the Tropo module: 'ask', and 'say'.
# Normally this would look like:
#
# from tropo import ask, say
#
# However, in this case, these functions are imported automatically from the
# 'tropo' module on Tropo's server.


color_names = (
'White, Black, Gray, Red, Maroon, Orange, Brown, Yellow, Olive, '
'Lime, Green, Cyan, Teal, Blue, DarkBlue, Fuchsia, Violet, Purple')


def run():

say("Tell me a color and I'll show it to you.")

while True:

answer = ask("What color do you want?",
{'choices': color_names + ', Stop'})

if answer.value == 'NO_MATCH':
say("I'm sorry, I didn't understand. Please try again.")

elif answer.value == 'Stop':
say("Ok, I'll stop. Goodbye.")
break

else:
color = answer.value
save(color)
say("Ok, I'll change the color to " + color)


def save(color):
conn = HTTPConnection('tropo-colors.herokuapp.com')
conn.request('POST', '/color', body=color)


run()

0 comments on commit 935f91b

Please sign in to comment.