Skip to content

Commit

Permalink
added realtime graphical statistics to websocket chat example
Browse files Browse the repository at this point in the history
  • Loading branch information
flaviogrossi committed Jun 10, 2012
1 parent 8cb7e5f commit e95128a
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 6 deletions.
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -470,3 +470,7 @@ Thanks to (in no particular order):
- `Jeethu Rao <https://github.com/jeethu>`_

- Minor bugfixes and patches

- `Flavio Grossi <https://github.com/flaviogrossi>`_

- Minor code fixes and websockets chat statistics example
75 changes: 69 additions & 6 deletions demos/websocket/chat/chatdemo.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,30 @@
Authentication, error handling, etc are left as an exercise for the reader :)
"""

import cyclone.escape
import cyclone.web
import cyclone.websocket
import os.path
import uuid
import sys
import time
from collections import defaultdict

from twisted.python import log
from twisted.internet import reactor
from twisted.internet import reactor, task

import cyclone.escape
import cyclone.web
import cyclone.websocket


class Application(cyclone.web.Application):
def __init__(self):

stats = Stats()

handlers = [
(r"/", MainHandler),
(r"/chatsocket", ChatSocketHandler),
(r"/", MainHandler, dict(stats=stats)),
(r"/stats", StatsPageHandler),
(r"/statssocket", StatsSocketHandler, dict(stats=stats)),
(r"/chatsocket", ChatSocketHandler, dict(stats=stats)),
]
settings = dict(
cookie_secret="43oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
Expand All @@ -45,7 +54,11 @@ def __init__(self):


class MainHandler(cyclone.web.RequestHandler):
def initialize(self, stats):
self.stats = stats

def get(self):
self.stats.newVisit()
self.render("index.html", messages=ChatSocketHandler.cache)


Expand All @@ -54,11 +67,16 @@ class ChatSocketHandler(cyclone.websocket.WebSocketHandler):
cache = []
cache_size = 200

def initialize(self, stats):
self.stats = stats

def connectionMade(self):
ChatSocketHandler.waiters.add(self)
self.stats.newChatter()

def connectionLost(self, reason):
ChatSocketHandler.waiters.remove(self)
self.stats.lostChatter()

@classmethod
def update_cache(cls, chat):
Expand Down Expand Up @@ -87,6 +105,50 @@ def messageReceived(self, message):
ChatSocketHandler.update_cache(chat)
ChatSocketHandler.send_updates(chat)

class StatsSocketHandler(cyclone.websocket.WebSocketHandler):
def initialize(self, stats):
self.stats = stats

self._updater = task.LoopingCall(self._sendData)

def connectionMade(self):
self._updater.start(2)

def connectionLost(self, reason):
self._updater.stop()

def _sendData(self):
data = dict(visits=self.stats.todaysVisits(),
chatters=self.stats.chatters)
self.sendMessage(cyclone.escape.json_encode(data))


class Stats(object):
def __init__(self):
self.visits = defaultdict(int)
self.chatters = 0

def todaysVisits(self):
today = time.localtime()
key = time.strftime('%Y%m%d', today)
return self.visits[key]

def newChatter(self):
self.chatters += 1

def lostChatter(self):
self.chatters -= 1

def newVisit(self):
today = time.localtime()
key = time.strftime('%Y%m%d', today)
self.visits[key] += 1


class StatsPageHandler(cyclone.web.RequestHandler):
def get(self):
self.render("stats.html")


def main():
reactor.listenTCP(8888, Application())
Expand All @@ -96,3 +158,4 @@ def main():
if __name__ == "__main__":
log.startLogging(sys.stdout)
main()

9 changes: 9 additions & 0 deletions demos/websocket/chat/static/chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ $(document).ready(function() {
updater.start();
});

$(window).unload(function() {
updater.stop()
});

function newMessage(form) {
var message = form.formToDict();
updater.socket.send(JSON.stringify(message));
Expand Down Expand Up @@ -61,6 +65,11 @@ var updater = {
}
},

stop: function() {
updater.socket.close();
updater.socket = null;
},

showMessage: function(message) {
var existing = $("#m" + message.id);
if (existing.length > 0) return;
Expand Down
3 changes: 3 additions & 0 deletions demos/websocket/chat/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
<input type="hidden" name="next" value="{{ request.path }}"/>
{{ xsrf_form_html() }}
</td>
<tr>
<td><a href="/stats" target="_blank">site statistics</a></td>
</tr>
</tr>
</table>
</form>
Expand Down
87 changes: 87 additions & 0 deletions demos/websocket/chat/templates/stats.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<!DOCTYPE html>
<html>
<head>
<script src="http://yandex.st/jquery/1.7.1/jquery.min.js"></script>
<script src="http://yandex.st/jquery/flot/0.7/jquery.flot.min.js"></script>
<script>
$(document).ready(function() {
updater.start();
});

$(document).unload(function() {
});

updater = {
socket: null,

plot: null,
visits: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
chatters: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
points: 10,

options: {
legend: { show: true }
},

start: function() {
if ("WebSocket" in window) {
socket = new WebSocket("ws://localhost:8888/statssocket");
} else {
socket = new MozWebSocket("ws://localhost:8888/statssocket");
}
socket.onmessage = function(event) {
data = JSON.parse(event.data);
updater.update_plot(data);
}
updater.plot = $.plot($('#placeholder'),
[ updater.visits, updater.chatters ],
updater.options);
},

append_data: function(visits, chatters) {
if (updater.visits.length > updater.points) {
updater.visits = updater.visits.slice(1);
updater.chatters = updater.chatters.slice(1);
}
updater.visits.push(visits);
updater.chatters.push(chatters);
},

prepare_data: function(arr) {
var len = arr.length;
var res = [];
for (var i=0; i < len; i++) {
res.push([i, arr[i]]);
}
return res;
},

update_plot: function(data) {
updater.append_data(data.visits, data.chatters);
var visits = updater.prepare_data(updater.visits);
var chatters = updater.prepare_data(updater.chatters);
updater.plot = $.plot($('#placeholder'),
[ {label: 'Global visits', data: visits},
{label: 'Active users', data: chatters}
], updater.options);
},

plot: function(visits, chatters) {
},

stop: function() {
updater.socket.close();
updater.socket = null;
}
};
</script>
<title> Site statistics </title>
</head>
<body>
<div>
<div id="placeholder" style="width:920px;height:400px;float:left;"></div>
<div id="table"></div>
</div>
</body>
</html>

0 comments on commit e95128a

Please sign in to comment.