Skip to content

Commit

Permalink
Initial commit of files
Browse files Browse the repository at this point in the history
  • Loading branch information
mheadd committed Oct 24, 2011
0 parents commit 641093b
Show file tree
Hide file tree
Showing 65 changed files with 4,117 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .couchappignore
@@ -0,0 +1,3 @@
[
"tropo"
]
1 change: 1 addition & 0 deletions .couchapprc
@@ -0,0 +1 @@
{}
48 changes: 48 additions & 0 deletions README.md
@@ -0,0 +1,48 @@
## CouchConf NYC Demo

This is a simple [Tropo](http://www.tropo.com/) + [CouchBase](http://www.couchbase.com/) app I demoed at [CouchConf NYC](http://www.couchbase.com/couchconf-nyc). It's a realtime SMS voting app that was used to select the best rock song over 6 minutes in length, but it can be used as the basis for any kind of voting app you want to build.

The magic is in Tropo's ability to make HTTP requests from inside a running telephony application to a CouchDB instance running on your local machine, your server, in the cloud - anywhere!

Tropo + CouchBase == Cloud Telephony Awesomeness!

## What is a CouchApp?

CouchApps are web applications which can be served directly from [CouchDB](http://couchdb.apache.org). This gives them the nice property of replicating just like any other data stored in CouchDB. They are also simple to write as they can use the built-in jQuery libraries and plugins that ship with CouchDB.

[More info about CouchApps here.](http://couchapp.org)

## Deploying this app

Assuming you just cloned this app from git, and you have changed into the app directory in your terminal, you want to push it to your CouchDB with the CouchApp command line tool, like this:

couchapp push . http://name:password@hostname:5984/mydatabase

If you don't have a password on your CouchDB (admin party) you can do it like this (but it's a bad, idea, set a password):

couchapp push . http://hostname:5984/mydatabase

If you get sick of typing the URL, you should setup a `.couchapprc` file in the root of your directory. Remember not to check this into version control as it will have passwords in it.

The `.couchapprc` file should have contents like this:

{
"env" : {
"public" : {
"db" : "http://name:pass@mycouch.couchone.com/mydatabase"
},
"default" : {
"db" : "http://name:pass@localhost:5984/mydatabase"
}
}
}

Now that you have the `.couchapprc` file set up, you can push your app to the CouchDB as simply as:

couchapp push

This pushes to the `default` as specified. To push to the `public` you'd run:

couchapp push public

Of course you can continue to add more deployment targets as you see fit, and give them whatever names you like.
65 changes: 65 additions & 0 deletions _attachments/index.html
@@ -0,0 +1,65 @@
<html>
<head>
<title>Tropo-Powered Realtime SMS Voting</title>
<link rel="stylesheet" href="style/main.css" type="text/css"></head>
<body>
<h2></h2>
<center>
<div id="row"></div>
</center>
<p class="footer">Powered by <a href="http://tropo.com">Tropo</a></p>
</body>
<script src="vendor/couchapp/loader.js"></script>
<script type="text/javascript" charset="utf-8">

$.couch.app(function(app) {

// Set the database to use.
var db = $.couch.db("couchconfdemo");

// Get the phone number for voting and render.
db.view("couchconfdemo/number", {
success: function(data) {
for(var i=0; i<data.rows.length; i++) {
$("h2").append(data.rows[i].value);
}
}
});

// Get all selections available for voting.
db.view("couchconfdemo/selections", {
success: function(data) {
for(var i=0; i<data.rows.length; i++) {
var selection = "<div id=\"choice\"><p class=\"score\" id=\"" + data.rows[i].id + "\">0</p><div class=\"selection\">";
selection += data.rows[i].id + "</div><p class=\"name\">" + data.rows[i].value.song;
selection += " (" + data.rows[i].value.group + ")</p></div>";
$("#row").append(selection);
}
}
});

// Get all past votes and incremement total.
db.view("couchconfdemo/votes", {
success: function(data) {
for(var i=0; i<data.rows.length; i++) {
var selector = "#" + data.rows[i].value;
var newValue = parseInt($(selector).html()) + 1;
$(selector).html(newValue);
}
}
});

// Watch the changes API for new votes.
db.changes(null, {include_docs: true}).onChange(function (data) {
for(var i=0; i<data.results.length; i++) {
var selector = "#" + data.results[i].doc.selection;
var newValue = parseInt($(selector).html()) + 1;
$(selector).html(newValue);
}
});

});

</script>
<script type="text/javascript" src="/_utils/script/jquery.couch.js"></script>
</html>
51 changes: 51 additions & 0 deletions _attachments/style/main.css
@@ -0,0 +1,51 @@
body {
text-align: center;
background-image: url('http://blog.tropo.com/files/2011/10/Tropo-Vert.png'), url('http://blog.tropo.com/files/2011/10/couch-300x300.png');
background-position: 2% 2%, 98% 2%;
background-size: 10%, 10%;
background-repeat: no-repeat, no-repeat;;
}

#row {
width: 65%;
margin-left: 5px;
}

#choice {
text-align: center;
padding: 5px;
border-style: dashed;
border-width: 1px;
margin: 2px;
margin-top: 0px;
background-color: silver;
}

.score {
font-weight: bold;
color: maroon;
font-size: 115%;
margin-bottom: 2px;
margin-top: 2px;
}

.name {
font-weight: bold;
}

.selection {
float: left;
font-size: 125%;
font-weight: bold;
color: blue;
margin-top: 3px;
text-decoration: overline;
}

h4 {
margin-top: 5px;
}

.footer {
font-size: 80%;
}
1 change: 1 addition & 0 deletions _id
@@ -0,0 +1 @@
_design/couchconfdemo
4 changes: 4 additions & 0 deletions couchapp.json
@@ -0,0 +1,4 @@
{
"name": "Tropo-Powered Realtime SMS Voting",
"description": "CouchApp"
}
11 changes: 11 additions & 0 deletions evently/items/_changes/data.js
@@ -0,0 +1,11 @@
function(data) {
// $.log(data)
var p;
return {
items : data.rows.map(function(r) {
p = (r.value && r.value.profile) || {};
p.message = r.value && r.value.message;
return p;
})
}
};
18 changes: 18 additions & 0 deletions evently/items/_changes/mustache.html
@@ -0,0 +1,18 @@
<p>Customize this format here: <tt>ddoc.evently.items._changes.mustache</tt></p>
<h3>Recent Messages</h3>
<ul>
{{#items}}
<li>
<div class="avatar">
{{#gravatar_url}}<img src="{{gravatar_url}}" alt="{{name}}"/>{{/gravatar_url}}
<div class="name">
{{nickname}}
</div>
</div>
<p>{{message}}</p>
<div style="clear:left;"></div>
</li>
{{/items}}
</ul>
<p><em>Protip:</em> If you setup continuous replication between this database and a remote one, this list will reflect remote changes in near real-time.</p>
<p>This would be a good place to add pagination.</p>
5 changes: 5 additions & 0 deletions evently/items/_changes/query.json
@@ -0,0 +1,5 @@
{
"view" : "recent-items",
"descending" : "true",
"limit" : 50
}
14 changes: 14 additions & 0 deletions evently/profile/profileReady/mustache.html
@@ -0,0 +1,14 @@
<p>Most applications will customize this template (<tt>ddoc.evently.profile.profileReady.mustache</tt>) for user input.</p>

<div class="avatar">
{{#gravatar_url}}<img src="{{gravatar_url}}"/>{{/gravatar_url}}
<div class="name">
{{name}}
</div>
</div>

<form>
<label>New message from {{nickname}}: <input type="text" name="message" size=60 value=""></label>
</form>

<div style="clear:left;"></div>
12 changes: 12 additions & 0 deletions evently/profile/profileReady/selectors/form/submit.js
@@ -0,0 +1,12 @@
function() {
var form = $(this);
var fdoc = form.serializeObject();
fdoc.created_at = new Date();
fdoc.profile = $$("#profile").profile;
$$(this).app.db.saveDoc(fdoc, {
success : function() {
form[0].reset();
}
});
return false;
};
1 change: 1 addition & 0 deletions language
@@ -0,0 +1 @@
javascript
34 changes: 34 additions & 0 deletions rewrites.json
@@ -0,0 +1,34 @@
[
{
"from": "",
"to": "index.html"
},
{
"from": "vendor/*",
"to": "vendor/*"
},
{
"from": "app/*",
"to": "app/*"
},
{
"from": "style/*",
"to": "style/*"
},
{
"from": "_view/*",
"to": "_view/*"
},
{
"from": "_changes/*",
"to": "_changes/*"
},
{
"from": "couchconfdemo/*",
"to": "../../*"
},
{
"from": "couchconfdemo/",
"to": "../../"
}
]
60 changes: 60 additions & 0 deletions tropo/sms-vote.php
@@ -0,0 +1,60 @@
<?php

// CouchDB settings
define("COUCH_HOST", "");
define("COUCH_PORT", "");
define("COUCH_DB_NAME", "");
define("COUCH_USER", "");
define("COUCH_PASS", "");

// Function to save vote.
function saveVote($id, $vote) {

$doc = json_encode(array("selection" => $vote));
$url = COUCH_HOST.":".COUCH_DB_PORT."/".COUCH_DB_NAME."/$id";

$putData = tmpfile();
fwrite($putData, $doc);
fseek($putData, 0);

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_INFILE, $putData);
curl_setopt($ch, CURLOPT_INFILESIZE, strlen($doc));
curl_setopt($ch, CURLOPT_USERPWD, COUCH_USER . ":" . COUCH_PASS);
curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Conflict update means someone tried to vote twice. :-(
if ($code != '201') {
_log("*** HTTP response code: $code ***");
return false;
}
return true;

}

// If the caller uses the voice channel, there is no initialText.
if(!$currentCall->initialText) {
$vote = ask("Please enter the number of the selection you wish to vote for.", array("choices" => "1,2,3,4,5,6", "attempts" => 3));
}

// If the text channel is used, initialText is used to complete ask();
else {
$vote = ask("", array("choices" => "[ANY]", "attempts" => 1));
}

// Save the vote.
if(saveVote($currentCall->callerID, $vote->value)) {
if($currentCall->channel == "VOICE") {
say("Thank you, your vote has been recorded.");
}
else {
_log("*** Vote recorded for " . $currentCall->callerID . " ***");
}
} else {
say("Sorry, there was a problem saving your vote.");
}

?>

0 comments on commit 641093b

Please sign in to comment.