- Streaming APIs
- Signing Up for Twitter
- Using Twitter's API with Node.js
- Extracting Meaning from the Data
- Pushing Data to the Browser
- Creating a Real-Time Lovehateometer
- Summary
- Q&A
- Workshop
- Exercises
Creating a Real-Time Lovehateometer
Although the application can now stream tweets to a browser window, it is still not very useful. It is still impossible to answer the question of whether there is more love or hate in the world. To answer the question, you need a way to visualize the data. Assuming that the tweets received from the API are indicative of human sentiment, you set up several counters on the server that increment when the words “love” and “hate” are mentioned in the streaming data that is received. Furthermore, by maintaining another counter for the total number of tweets with either love or hate in them, you can calculate whether love or hate is mentioned more often. With this approach, it is possible to say—in unscientific terms—that there is x% of love and y% of hate in the world.
To be able to show data in the browser, you need counters on the server to hold:
- The total number of tweets containing “love” or “hate”
- The total number of tweets containing “love”
- The total number of tweets containing “hate”
This can be achieved by initializing variables and setting these counters to zero on the Node.js server:
var app = require('express').createServer(), twitter = require('ntwitter'), io = require('socket.io').listen(app), love = 0, hate = 0, total = 0;
Whenever new data is received from the API, the love counter will be incremented if the word “love” is found and so on. JavaScript’s indexOf() string function can be used to look for words within a tweet and provides a simple way to analyze the content of tweets:
twit.stream('statuses/filter', { track: ['love', 'hate'] }, function(stream) { stream.on('data', function (data) { var text = data.text.toLowerCase(); if (text.indexOf('love') !== -1) { love++ total++ } if (text.indexOf('hate') !== -1) { hate++ total++ } }); });
Because some tweets may contain both “love” and “hate,” the total is incremented each time a word is found. This means that the total counter represents the total number of times “love” or “hate” was mentioned in a tweet rather than the total number of tweets.
Now that the application is maintaining a count of the occurrences of words this data can be added to the tweet emitter and pushed to connected clients in real-time. Some simple calculation is also used to send the values as a percentage of the total number of tweets:
io.sockets.volatile.emit('tweet', { user: data.user.screen_name, text: data.text, love: (love/total)*100, hate: (hate/total)*100 });
On the client side, by using an unordered list and some client-side JavaScript, the browser can receive the data and show it to users. Before any data is received from the server, the values are set to zero:
<ul class="percentage"> <li class="love">0</li> <li class="hate">0</li> </ul>
Finally, a client-side listener can be added to receive the tweet event and replace the percentage values with the ones received from the server. By starting the server and opening the browser, you can now answer the question!
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script src="/socket.io/socket.io.js"></script> <script> var socket = io.connect(); jQuery(function ($) { var tweetList = $('ul.tweets'), loveCounter = $('li.love'), hateCounter = $('li.hate'); socket.on('tweet', function (data) { tweetList .prepend('<li>' + data.user + ': ' + data.text + '</li>'); loveCounter .text(data.love + '%'); hateCounter .text(data.hate + '%'); }); }); </script>
Adding a Real-Time Graph
The application is now able to answer the question. Hurray! In terms of visualization, though, it is still just data. It would be great if the application could generate a small bar graph that moved dynamically based on the data received. The server is already sending this data to the browser so this can be implemented entirely using client-side JavaScript and some CSS. The application has an unordered list containing the percentages, and this is perfect to create a simple bar graph. The unordered list will be amended slightly so that it is easier to style. The only addition here is to wrap the number in a span tag:
<ul class="percentage"> <li class="love"> <span>0</span> </li> <li class="hate"> <span>0</span> </li> </ul>
Some CSS can then be added to the head of the HTML document that makes the unordered list look like a bar graph. The list items represent the bars with colors of pink to represent love and black to represent hate:
<style> ul.percentage { width: 100% } ul.percentage li { display: block; width: 0 } ul.percentage li span { float: right; display: block} ul.percentage li.love { background: #ff0066; color: #fff} ul.percentage li.hate { background: #000; color: #fff} </style>
Finally, some client-side JavaScript allows the bars (the list items) to be resized dynamically based on the percentage values received from the server:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script src="/socket.io/socket.io.js"></script> <script> var socket = io.connect(); jQuery(function ($) { var tweetList = $('ul.tweets'), loveCounter = $('li.love'), hateCounter = $('li.hate'), loveCounterPercentage = $('li.love span'), hateCounterPercentage = $('li.hate span'); socket.on('tweet', function (data) { loveCounter .css("width", data.love + '%'); loveCounterPercentage .text(Math.round(data.love * 10) / 10 + '%'); hateCounter .css("width", data.hate + '%'); hateCounterPercentage .text(Math.round(data.hate * 10) / 10 + '%'); tweetList .prepend('<li>' + data.user + ': ' + data.text + '</li>'); }); }); </script>
Whenever a new tweet event is received from Socket.IO, the bar graph is updated by dynamically setting the CSS width of the list items with the percentage values received from the server. This has the effect of adjusting the graph each time a new tweet event is received. You have created a real-time graph!
The application that you created provides a visual representation of whether there is more love than hate in the world based on real-time data from Twitter. Granted this is totally unscientific, but it does showcase the capabilities of Node.js and Socket.IO to receive large amounts of data and to push it out to the browser. With a little more CSS work, the application can be styled to look better (see Figure 14.9).
Figure 14.9. The finished application with additional styling
If you want to run this example yourself, this version is available in the code for this book as hour14/example06.