tag:blogger.com,1999:blog-49553132509973331142024-03-13T10:09:46.457+05:30Bellicose BeliefsIt's all in the codesPranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.comBlogger98125tag:blogger.com,1999:blog-4955313250997333114.post-9312156669299658702011-09-20T12:09:00.002+05:302011-09-20T12:09:53.699+05:30Solr gem and xml ruby gem How it matters<div dir="ltr" style="text-align: left;" trbidi="on">
<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript">
</script><iframe allowtransparency="true" frameborder="0" scrolling="no" src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fcodecontrol.blogspot.com&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" style="border: none; height: 80px; overflow: hidden; width: 450px;"></iframe>
<span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displaytext="ShareThis"></span>
<br />
<p>If you use <b>solr-ruby</b> gem, you should be well aware that Solr attempts to create an XML doc from the provided doc-hash. It first attempts to use 'xml/libxml', which if not available, falls back to REXML. It is recommended to use libxml.</p>
<p>All you need to do is<br />
<pre>
gem install libxml-ruby
</pre>
If you are lucky enough, that's all for you. However, many a times we face issues like
<pre>
Building native extensions. This could take a while...
ERROR: Error installing libxml-ruby:
ERROR: Failed to build gem native extension.
/usr/bin/ruby extconf.rb
checking for socket() in -lsocket... no
checking for gethostbyname() in -lnsl... yes
checking for atan() in -lm... no
checking for atan() in -lm... yes
checking for inflate() in -lz... no
checking for inflate() in -lzlib... no
checking for inflate() in -lzlib1... no
checking for inflate() in -llibz... no
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers. Check the mkmf.log file for more
details. You may need configuration options.
</pre>
If this is the case, you should install zlib-devel and libxml2-devel packages.
<pre>
yum -y install zlib-devel
yum -y install libxml2-devel
gem install libxml-ruby
</pre>
</p>
<p>That sorts it all. You are good to go </p>
<br /></div>
<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0tag:blogger.com,1999:blog-4955313250997333114.post-62803891900585863042011-09-19T12:14:00.002+05:302011-09-19T12:15:11.871+05:30ruby fetch_hash ArgumentError: NULL pointer given<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fcodecontrol.blogspot.com&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe>
<span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displayText="ShareThis"></span>
<br />
Recently, One of our servers started to have the following error
<pre class="toolbar: false; smart-tabs: true;">
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'mysql'
=> true
irb(main):003:0> conn = Mysql.connect('db01', 'xxxxxx', 'xxxxxx', 'employee')
=> #<Mysql:0x2b8538797f78>
irb(main):004:0> a = conn.query("SELECT * FROM employees WHERE id >= 8500 AND id < 9000")
=> #<Mysql::Result:0x2b853878dd20>
irb(main):005:0> a.fetch_hash
ArgumentError: NULL pointer given
from (irb):5:in `fetch_hash'
from (irb):5
irb(main):006:0> exit
</pre>
The method fetch_hash is a standard method and works. Here is the versions of MySQL and ruby that I used
<pre>
-bash-3.2$ mysql --version
mysql Ver 14.12 Distrib 5.0.89, for unknown-linux-gnu (x86_64) using readline 5.1
-bash-3.2$ ruby --version
ruby 1.8.7 (2009-06-12 patchlevel 174) [x86_64-linux]
</pre>
If you face this issue in your servers or anywhere try the following workaround. They have worked for me, and they should work for you as well.
<ol><li> uninstall the MySQL-shared-compat package</li><li>Re Install mysql gem</li></ol><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0tag:blogger.com,1999:blog-4955313250997333114.post-56028661614783622282011-03-16T10:03:00.002+05:302011-03-16T10:09:46.863+05:30Control activity feeds on facebook<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2011%252F03%252Fcontrol-activity-feeds-on-facebook.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe> <span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displayText="ShareThis"></span> <br />
<br />
<h2>Controlling activity feeds on facebook</h2><p>Facebook has a setting which lets you chose amongst your friends and pages, whose updates you want to see and whose you want to skip. Here it is how</p><br />
<p>Next to Most Recent, there is a drop down button. Click on that to gain access to control options. The drop down menu comes only when you are viewing the Most Recent news feeds only. So if you don't see an option, just click on Most Recent. The page will refresh itself and the menu options will come.</p><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-NfnlmKgZJrg/TYA8ujYpv8I/AAAAAAAALiM/Yry9Bv0So_Y/s1600/2.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="334" width="235" src="http://2.bp.blogspot.com/-NfnlmKgZJrg/TYA8ujYpv8I/AAAAAAAALiM/Yry9Bv0So_Y/s400/2.png" /></a></div><br />
<p>Select Edit Options in the drop down menu</p><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-_DDattNpmNY/TYA8uxhLJnI/AAAAAAAALiU/cSiPZcDH8X4/s1600/4.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="289" width="400" src="http://1.bp.blogspot.com/-_DDattNpmNY/TYA8uxhLJnI/AAAAAAAALiU/cSiPZcDH8X4/s400/4.png" /></a></div><br />
<p>Now conveniently you can choose to show news from <ol><li>Those friends and pages you interact with most or </li>
<li>All friends and pages.</li>
</ol>The default setting is "friends and pages" you interact with most. You can also edit friends whose feeds have been blocked in the list.</p><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-VCEvvl5fu20/TYA8vEG2rzI/AAAAAAAALic/Ing5t5BkmeQ/s1600/5.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="295" width="400" src="http://1.bp.blogspot.com/-VCEvvl5fu20/TYA8vEG2rzI/AAAAAAAALic/Ing5t5BkmeQ/s400/5.png" /></a></div><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0Gurgaon, Delhi, India28.46385 77.01783828.444986 76.9886555 28.482714 77.0470205tag:blogger.com,1999:blog-4955313250997333114.post-37027208100581693942011-03-15T19:19:00.002+05:302011-03-16T11:22:03.607+05:30Recursively traversing Python dictionary and removing keys<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2011%252F03%252Frecursively-traversing-python.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe> <span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displayText="ShareThis"></span> <br />
<br />
<h2>Code Snippet to recursively traverse a dictionary and remove certain key/value pair</h2><br />
In case of complex dictionaries like<br />
<pre class="brush: python; toolbar: false; smart-tabs: true;">my_blog = { '_created' : datetime.datetime (2007,01,03),
'_updated' : datetime.datetime (2011,06,11),
'name' : 'MyBLive',
'latest_post' : { '_created' : datetime.datetime (2007,06,9),
'_updated' : datetime.datetime (2007,069),
'name' : 'Hello World !' }
</pre><br />
The problem with this is that datetime.datetime entities are not JSON Serializable. One approach could be to provide a serializer for datetime.datetime entities as suggested by <i>verte</i> over IRC. If you want to have a datetime.datetime aware JSONSerializer, you should have a look at the <b><a target="_blank" href="http://code.djangoproject.com/browser/django/trunk/django/core/serializers/json.py">django.core.serializers.json</a></b> module<br />
<br />
In my case, we were using a Google App Engine application and the JSON response need not contain these key/value pairs so it makes more sense if these are removed from the dictionary.<br />
<br />
<pre class="brush: python; toolbar: false; smart-tabs: true;">def rm (d, l):
"""
Removed from dictionary "d" all those key-value pairs the keys of which
are defined as a list in "l"
"""
if not l: return d
if reduce ( lambda x,y: x or y, [x in d.keys () for x in l]):
[d.pop (x, None) for x in l]
[rm (x, l) for x in d.values () if isinstance (x, dict)]
return d
</pre><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0tag:blogger.com,1999:blog-4955313250997333114.post-36963637153217712432011-02-25T12:55:00.002+05:302011-03-15T19:19:24.580+05:30Versioned Entity Caching for high read/write on Google App Engine Python<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2011%252F02%252Fversioned-entity-caching-for-high.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe> <span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displayText="ShareThis"></span> <br />
The usage of memcache for lowering the load on App Engine Datastore is well known. Here is an approach to versioned caching of datastore entities in App Engine. The Model is deigned primarily for purposes where entity is updated very frequently. The idea for these kind of entity generated while working on the <a href="http://blog.myblive.com/2010/12/multiuser-chatroom-with-app-engine.html">Multiuser Chat room for App Engine using Channel APIs</a>.<br />
<br />
<h2>Expectations</h2><p>The Model is designed with the following factors in mind<br />
<ul><li>Very frequent read/write access to entity</li>
<li>Effective usage of memcache to reduce load on datastore operations</li>
<li>Strong consistency between datastore and memcache</li>
</ul></p><br />
<h2>Design Plan</h2><p>The Entities have an internal version number. This version number is increased everytime there is an update in the entity. </p><br />
<pre class="brush: python; toolbar: false; smart-tabs: true;">from google.appengine.ext import db
# The maximum difference in revisions acceptable at any instant
# between memcached values and the datastore values. The higher it
# is, the greater catastrophe when memcache goes down, but lesser
# datastore usage. The lesser it is, the more consistent your datastore
# and memcache are, and higher datastore operations. 4~6
FAULT_TOLERANCE = 4
class GlobalVersionedCachingModel(db.Model):
"""
The Model uses internal versioning of information with prime focus on very
high read/writes and consistency.
Every entity has a datastore's version number information and the version
number from memcache. When the entity is updated, it happens in memcache
only and the memcached version number increases. If this number is greater
than the datastore version number by a certain amount called
"fault tolerance", then the datastor entity is sync'd with the memcache
entity.
"""
_db_version = db.IntegerProperty (default=0, required=True)
_cache_version = db.IntegerProperty (default=0, required=True)
_fault_tolerance = db.IntegerProperty(default = FAULT_TOLERANCE)
</pre><br />
<h3>Approach #1</h3><p>Initially, both the versions are set to 0 when the entity is created. <i>_fault_tolerance</i> is the maximum allowed difference between the cache version and the datastore version. Since the entity is primarily read from and written to memcache, and later updated to datastore, the datastore version number can be behind the memcache version number. When the difference between the two exceeds fault tolerance, then the datastore is updated with the memcache details.</p><p>The downside of this approach is that when the memcache goes out, then the datastore entity is fetched. This entity, in worst case scenario, could be lagging from the actual entity by a maximum of <i>_fault_tolerance</i> factor. Fault Tolerance can be decreased to improve the consistency between memcached entity and the datastore entity but that will result in higher datastore read/write operations. </p><br />
<h3>Approach #2</h3><p>In this approach, instead of directly writing into the datastore, we initiate a task queue and that gets the task done for us. The good part of this approach is that we can keep smaller values of <i>_fault_tolerance</i> and still expect faster processing. This approach is ideal where strong consistency is required between the entities and latency shall also be minimal<p><br />
<h2>Methods</h2><br />
<pre class="brush: python; toolbar: false; smart-tabs: true;">def get2 (keys, **kwargs):
keys, multiple = datastore.NormalizeAndTypeCheckKeys (keys)
getted_cache = memcache.get_multi (map (str, keys))
ret = map (deserialize_entities, getted_cache.values ())
keys_to_fetch = [key for key in keys if getted_cache.get(key, None) is not None]
getted_db = db.get(keys_to_fetch)
memcache_to_set = dict ((k,v) for k,v in zip (map (str,keys_to_fetch),
map (serialize_entities, getted_db)))
ret.extend(getted_db)
memcache.set_multi (memcache_to_set)
if multiple:
return ret
if len (ret) > 0:
return ret[0]
class GlobalVersionedCachingModel(db.Model):
"""
The Model uses internal versioning of information with prime focus on very
high read/writes and consistency.
Every entity has a datastore's version number information and the version
number from memcache. When the entity is updated, it happens in memcache
only and the memcached version number increases. If this number is greater
than the datastore version number by a certain amount called
"fault tolerance", then the datastor entity is sync'd with the memcache
entity.
"""
_db_version = db.IntegerProperty (default=0, required=True)
_cache_version = db.IntegerProperty (default=0, required=True)
_fault_tolerance = db.IntegerProperty(default = FAULT_TOLERANCE)
created = db.DateTimeProperty (auto_now_add=True)
updated = db.DateTimeProperty (auto_now=True)
@property
def keyname (self):
return str (self.key ())
def remove_from_cache (self, update_db=False):
"""
Removes the cached instance of the entity. If update_db is True,
then updates the datastore before removing from cache so that no data
is lost.
"""
if update_db:
self.update_to_db()
memcache.delete(self.keyname)
def update_to_db (self):
"""
Updates the current state of the entity from memcache to the datastore
"""
self._db_version = self._cache_version
logging.info('About to write into db. Key: %s' %self.keyname)
self.update_cache ()
return super (GlobalVersionedCachingModel, self).put ()
def update_cache (self):
"""
Updates the memacahe for this entity
"""
memcache.set (self.keyname, serialize_entities (self))
def put (self):
self._cache_version += 1
memcache.set (self.keyname, serialize_entities (self))
if self._cache_version - self._db_version >= self._fault_tolerance or \
self._cache_version == 1:
self.update_to_db ()
def delete (self):
self.remove_from_cache()
return super (GlobalVersionedCachingModel, self).delete ()
@classmethod
def get_by_key_name (cls, key_names, parent=None, **kwargs):
try:
parent = db._coerce_to_key (parent)
except db.BadKeyError, e:
raise db.BadArgumentError (str (e))
rpc = datastore.GetRpcFromKwargs (kwargs)
key_names, multiple = datastore.NormalizeAndTypeCheck (key_names, basestring)
logging.info(key_names)
keys = [datastore.Key.from_path (cls.kind (), name, parent=parent) for name in key_names]
if multiple:
return get2 (keys)
else:
return get2 (keys[0], rpc=rpc)
</pre><br />
<h2>Similar Readings</h2><ul><li> <a href="http://appengine-cookbook.appspot.com/recipe/models-caching/" target="_blank">Models Caching</a> at App Engine CookBook</li>
<li> This article at Cook Book - <a href="http://appengine-cookbook.appspot.com/recipe/entity-caching-with-internal-version-for-high-readwrite-ops/">http://appengine-cookbook.appspot.com/recipe/entity-caching-with-internal-version-for-high-readwrite-ops/</a><br />
</ul><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0Bengaluru, Karnataka, India12.9715987 77.594562712.6370402 77.1276437 13.3061572 78.0614817tag:blogger.com,1999:blog-4955313250997333114.post-13106214206566039922011-02-08T14:21:00.001+05:302011-02-08T14:22:32.702+05:30Python Script to check mysql server<div dir="ltr" style="text-align: left;" trbidi="on"><script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript">
</script><iframe allowtransparency="true" frameborder="0" scrolling="no" src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2011%252F02%252Fpython-script-to-check-mysql-server.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" style="border: none; height: 80px; overflow: hidden; width: 450px;"></iframe> <span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displaytext="ShareThis"></span> </div>A friend of mine asked me to create a python script which checks if the mysql server is running or not. So, I could come up with this simple script. Although, there are many script available to do the same, in probably better manner, but still here it is. Hope you find it useful.<br />
<br />
<script class="brush: python" type="syntaxhighlighter">
#!/usr/bin/env python
__author__ = 'Pranav Prakash <pranav@myblive.com>'
# A Script that polls a MySQL Server and returns a status
# which tells if the server is responding (active) or not
# (inactive)
import sys
import os
import socket
HOST = 'localhost'
PORT = 3306
CURR_DIR = os.path.dirname(__file__)
LOGFILE = os.path.join(CURR_DIR, 'poll.log')
class PollMySQL(object):
def __init__(self, host=None, port=None):
self.host = host
self.port = port
self.server_response = False
def poll(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((self.host, self.port))
s.send('ping')
data = s.recv(1024)
if data != '' and data is not None:
self.server_response = True
except:
pass
def is_server_active(self):
self.poll()
return self.server_response
def main(argv):
host = HOST or argv[1]
port = PORT or argv[2]
if host and port:
server_activity = PollMySQL(host, port).is_server_active()
print server_activity
else:
show_help()
sys.exit(0)
def show_help():
"""
Shows a help about how to use this particular
polling script
"""
print """Usage: ./polling.py [HOST] [PORT]
HOST: The HOST of the server
PORT: The PORt used for MySQL Server
"""
if __name__ == '__main__':
main(sys.argv)
</script><br />
<br />
<h2>Resources</h2><ol><li>Repo: <a href="http://code.google.com/p/pranav/source/browse/poll_mysql/polling.py">http://code.google.com/p/pranav/source/browse/poll_mysql/polling.py</a></li>
</ol><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com1tag:blogger.com,1999:blog-4955313250997333114.post-90498063383901494382011-02-02T15:23:00.001+05:302011-02-02T15:24:03.555+05:30Dear Google, Take from Facebook what you have build over the years - the web<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2011%252F02%252Fdear-google-take-from-facebook-what-you.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe> <span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displayText="ShareThis"></span><br />
<br />
<h2>Me, Myself and My Google</h2><p>I remember the first time I was plugged to internet back in 1997. I created my email id. It was on <a href="http://en.wikipedia.org/wiki/Rediff">rediff.com</a>. Then I surfed some porn sites. Then I saw <a href="http://en.wikipedia.org/wiki/Yahoo">yahoo.com</a>. Almost everyone I knew was on rediff. Very soon, almost everyone was on Yahoo. And the most exciting thing to do was to do private and public chats on Yahoo Messenger. Then came the days when Yahoo chatrooms started to flood with nonsense messages, porn websites became popup machines and soon the internet became a wide wide shit. Those were the days when suddenly everything (I used) across the internet became too obstructive, complex and confusing.</p><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/_5_28o544oU8/TUkow4hERhI/AAAAAAAALc0/rEdKvjKpc4o/s1600/google-or-god.JPG" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="300" width="400" src="http://3.bp.blogspot.com/_5_28o544oU8/TUkow4hERhI/AAAAAAAALc0/rEdKvjKpc4o/s400/google-or-god.JPG" /></a></div><br />
<br />
<p>In the midst of all those came <a href="http://www.google.com">google.com</a>. A neat and clean interface for searching the web, which was perhaps the biggest problem at that time. Google took the challenge <i>to organize the world's information and make it universally accessible and useful</i>. And they did a great job with their search engine. People started liking google and used Yahoo, Rediff and MSN for their emails. Then came <a href="http://en.wikipedia.org/wiki/Gmail">gmail.com</a> and suddenly out of nowhere everyone had a gmail.com account. It was again neat and clean. And then again, all of a sudden, the voice chats, buzzes, beautiful big smileys, public chatrooms, multiple screen names, erotic talks on Yahoo chat came to and end simple non rich text formatted, non fancy UIed chat windows. Somehow people liked it. Probably they were bored of all the glittering stuff, or maybe some principle of UX design came into play. Ever since, Google became a part of everyone's life with products like Docs, PicasaWeb, Blogger, News, Scholar, Books, Knol and many more. People had faith in Google, their ideas and their values. Off lately there are various slanders on Google about the way they use data to create relevant text ads, but despite all these, a vast majority of people still love Google.</p><br />
<h2>What The <u>F</u>acebook?</h2><p>Social Networking was something that was not so popular in India. Not in 2004, the year I went for my Bachelors in Engineering, the year Facebook was founded and the year Orkut was founded. Sooner, everyone I knew was on Orkut and it was kinda fun. We used to meet up, post scraps to each others, and had communities where we discussed the C++ puzzles. Life was easy and fun. This probably continued till early 2008s (in India) when people were not accepting facebook because they found it too complex to use. Probably people just could not visualize what is "writing on wall". And then facebook kept evolving, changing its UX to make it more resemble the real world interactions. That is where Orkut was left behind. Orkut was good for novice users who expected barely scraps and messages and communities for interactions. But as it goes in the real world, you appreciate, like, comment on your friends' activities. All of a sudden, all this was possible on Facebook. One other problem with Orkut was of privacy. I remember there were communities where pictures of real girls from their profile was hotlinked and posted. This obviously irritated a lot of people and they started to remove their content from Orkut. Almost at the same time, as I precisely remember, the Indian community was starting to look towards facebook because it was social, it was fun and it was secure. Orkut was still being used by those who were new to social networking and then they gradually graduated to use facebook.</p><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_5_28o544oU8/TUkpdHgZEfI/AAAAAAAALc8/V7kJxJNoM1I/s1600/facebook.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="300" width="400" src="http://1.bp.blogspot.com/_5_28o544oU8/TUkpdHgZEfI/AAAAAAAALc8/V7kJxJNoM1I/s400/facebook.jpg" /></a></div><br />
<br />
<p>Soon, everyone I knew was on facebook, even my mom, my dad and my 8 year old cousin. Life became easy. Google released OpenSocial in late 2007 and people started to develop games and apps for Orkut. It could have been a big hit if people had been using Orkut or they had interest. Orkut failed to attract new users and even continue old users. Orkut started to die, and pictures of Orkut - R.I.P became popular on the internet.</p><br />
<h2>You like it</h2><p>Sure you will ask me, what's the big deal with that. Google is in search and ads, and facebook is in social networking. Facebook already beat Google's Orkut, so do they still compete? The answer is - more now than ever. </p><p>Sometimes in last year or maybe last to last year "Like Button" was introduced, and very soon every webpage on the planet started to have a "Like This" button. (Even this page has one, please like/recommend it. Thank you :-) The concept is pretty neat. Your friends see that you like/recommend <b>Bellicose beliefs</b>, and they will probably come and read what is it. If they "like" it, it goes on virally. Alternatively, if you come to Bellicose Beliefs two years later and you see that one of your friends like it, you will be more interested in reading the content. This is a win win situation for the web admins/ bloggers/ any other website and probably that is the reason for it becoming so popular, so soon. Now looking at the other aspect, facebook has a record of all the pages you like and your friends like and their friends like. So, they are more close to figuring out your taste, your work, your life and provide you better ads/recommendations. Everything tailored, just for you.</p><p>Now let's see how google tracks internet usage - "Ads by Google". Most of the ads you see over the internet are served by Google's Ad server. Every ad stores a cookie onto your PC and this helps track the click, the content. All this is done on a very broad scale and the results are generated so that they are fit for a majority of people who fall under the same category. This is where difference sneaks in. Google does not track individuals, it just track the pattern formed by many users and uses that pattern to server ads, which might not be suited for you, but are suited for a majority of population. It works.</p><br />
<h2>What's the need for a search engine?</h2><p>Probably you've started to notice it. Every single link you share, every single web content you "like/recommend" comes in your facebook search box. So, if you are looking for something that you probably have liked in past, just go to search box and start typing it's name. You will see it coming there as in the diagram below.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_5_28o544oU8/TUkcWYZ8kPI/AAAAAAAALco/0F93TZtqYAA/s1600/bing1.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="400" width="320" src="http://1.bp.blogspot.com/_5_28o544oU8/TUkcWYZ8kPI/AAAAAAAALco/0F93TZtqYAA/s400/bing1.png" /></a></div>For more details, you can go to "Show All results" and alongwith the things that are related to facebook, you see results from Bing (which <a href="http://googleblog.blogspot.com/2011/02/microsofts-bing-uses-google-search.html">as per Google</a>, copies their search result). If this continues to grow, fewer and fewer people will go to Google for searching something, and that will be done only when facebook fails to deliver. So the search for which Google is known, shall be taken by facebook and Google will get only facebook's leftovers.</p><br />
<h2>Does <u>f</u>acebook pose a serious threat to Google's main source of revenue?</h2><p>In order to understand this, we first need to figure out what is Google's main source of revenue. Google's main source of revenue is Adwords which accounted for USD 23b in 2009 (http://investor.google.com/fin_data.html). <br />
<br />
Facebook's revenues for 2009 were as<br />
<ol><li>$125 million from brand ads</li>
<li>$150 million from Facebook's ad deal with Microsoft</li>
<li>$75 million from virtual goods</li>
<li>$200 million from self-service ads. (http://www.businessinsider.com/breaking-down-facebooks-revenues-2009-7)</li>
</ol><br />
This total amounts to USD 550m or USD 0.55b. Facebook's total revenue in 2009 is 46 times smaller than the revenue of Google Adwords.'Looking at this figure it is clear that Facebook is not posing a serious threat to Google's revenue.<br />
Now here is the present scenario. Facebook Like Button on various sites, Facebook places, Facebook questions - Facebook knows which content you like/read, where you visit and what are your doubts. Using this data, Facebook can possibly come up with an Ad Sever that has if not more, equal powers as the Google Ad Servers. If they come up with this, and you start seeing "Ads by Facebook" instead of "Ads by Google", then the war will get more interesting.<br />
</p><br />
<h2>The social web - by Google</h2><p>In this particular text, I will try to figure out, how the web could be more social with the help of Google. Google already has, and in much better form what facebook is offering - social interaction, videos, emails, news, web search but in separated forms. Things are are at different URLs and one must remember/click/bookmark each location. Let's say Google comes up with a social web - a portal that is sufficient for all your needs and is social, I would call it <b>GoSocial</b>. It would be a mashup of Google Search, Gmail, Buzz, Picasaweb, Docs, Music Search, YouTube, Blogger, Knol, Groups, Latitude and Maps. So you can upload pictures which will be stored in your picasaweb albums. You can blog and your blogsppot.com address shall be linked to your GoSocial account. You can upload videos on GoSocial and they will be stored and served by YouTube. You can create documents and share them with Google Docs running in the backend. You can interact with groups and can create questions and answers with the Google Groups engine in background. You can create community pages with the help of Knol. You can use Latitude to implement a feature similar to facebook places, even better in some aspects. Maybe, I need to think more so that I can come up with a possible layout of this social web. But this is my general idea. I am a die hard Google fan. Everytime I visit their numerous services, I can picture different modules of a system which is highly social, real time and most important of all is not evil, lying around. In my personal opinion it's time to come up with a version of web, which is more social and Google, you can do it. </p><p>Over the decade, Google has helped build the web, make it safe, clean and present the data in an organized manner to all the inhabitants on the planet. Why let someone else harness it when it essentially belongs to its creator.</p><br />
<p>There are many more things in my mind to write about, so keep watching for a second part of this post</p><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com3tag:blogger.com,1999:blog-4955313250997333114.post-77512258729244048682011-01-12T12:43:00.002+05:302011-01-12T13:56:44.497+05:30Multiuser Chatroom with App Engine Channel API - Part 2<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2011%252F01%252Fmultiuser-chatroom-with-app-engine.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe> <span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displayText="ShareThis"></span> <br />
The <a href="http://blog.myblive.com/2010/12/multiuser-chatroom-with-app-engine.html">Part-1 of designing multiuser chat room with app engine</a> deals with providing a basic information of how to create a simple chatroom/gameroom with App Engine Channel API. That article was more based on the <a href="http://code.google.com/p/channel-tac-toe">tic-tac-toe example</a> provided by the Google App Engine team.<br />
<br />
In this post, I will talk about the ways in which this can be optimized using memcache.<br />
<br />
But before that, let's look at the complications with the previous one<br />
<ol><li>The Channel ID is a function of <i>only</i> userid. This means that one user can not login from multiple clients and can expect consistency. There will always be an inconsistency and improper outgoing messages on the channels.</li>
<li>Too much datastore operations. For every action, there is too much datastore operations going on. As the number of players keep on increasing, this problem becomes more intense. A <a href="http://blog.myblive.com/2010/12/multiuser-chatroom-with-app-engine.html?showComment=1294793371027#c1418966453817606373">developer reported</a> that he had to wait several seconds for things to happen while doing it with ~30 players. This is an optimized version and will have much better serving time</li>
<li>Coding style. Well frankly, I was not satisfied with the coding style in prev version, so in the hope of creating better and beautiful code, I decided to rewrite it and I came up with this new file <a href="http://code.google.com/p/pranav/source/browse/chat-channel/tournament.py?r=dc23292096c0f5f84fa1461091544ef032e50a9d">tournament.py</a>, which is pretty neat.</li>
</ol><br />
First of all, in order to get an insight of effective memcaching in datastore entities, go through <a href="http://blog.notdot.net/2009/9/Efficient-model-memcaching">this post on Nick's blog</a>. This has been completely used.<br />
<br />
<h2>EfficientModel</h2><p>In order to do good optimizations, I have a base class EfficientModel from which my datastore classes will be derived. Entities of kind EfficientModel are designed to be in memcache most of the time they are required. All the operations on these entities take place in memcache only. After the memcached entity has been updated "certain" number of times, the change is replicated in the datastore. The EfficientModel has an attribute mc_version, which stores the version number of this entity in memcache. The concept is that every entity has a revision number associated. Whenever there is any change in one of the attributes/property of the entity it's version number increases by one. mc_version stored the version number of entity in memcache. db_version stores the version number of entity in the datastore. The difference between memcache version and datastore version is called as FAULT NUMBER. When fault_number goes beyond a certain number known as fault_tolerance, then the memcache entity and the datastore entity are sync'd.</p><br />
<script class="brush: python" type="syntaxhighlighter">
class EfficientModel(db.Model):
mc_version = db.IntegerProperty(default = 0)
db_version = db.IntegerProperty(default = 0)
created = db.DateTimeProperty(auto_now_add = True)
updated = db.DateTimeProperty(auto_now = True)
@property
def keyname(self):
return self.key().name()
@property
def memcache_key(self):
raise NotImplementedError
@classmethod
def from_id(cls, id):
existing_entity_mc = deserialize_entities(memcache.get(id))
if existing_entity_mc is None:
existing_entity_db = cls.get_by_key_name(id, parent = cls.find_parent(id))
if existing_entity_db is None:
return cls(key_name = unicode(id))
return existing_entity_db
return existing_entity_mc
@classmethod
def fetch_from_id(cls, id):
return cls(key_name = unicode(id))._get()
def _from_memcache(self):
return deserialize_entities(memcache.get(self.memcache_key))
def _get(self):
memcached_entity = self._from_memcache()
if memcached_entity is not None:
return memcached_entity
return self.get_or_insert(key_name = self.keyname)
def _store(self, force = False):
self.mc_version += 1
memcache.set(self.keyname, serialize_entities(self))
if self.mc_version - self.db_version >= FAULT_TOLERANCE or force:
logging.info('sync_to_db started for %s. mc_version: %d, db_version: %d'%(
self.keyname, self.mc_version, self.db_version))
self.sync_to_db()
elif self.mc_version < self.db_version:
logging.info('sync_from_db started')
self.sync_from_db()
def sync_to_db(self):
self.db_version = self.mc_version
db.put(self)
self._store()
def sync_from_db(self):
from_ds = db.get(self.keyname)
memcache.set(self.keyname, serialize_entities(from_ds))
self._store()
@classmethod
def find_parent(cls, id):
raise NotImplementedError
</script>
<strong>Properties, Classmethods and Functions</strong>
<ul>
<li><strong>keyname</strong>, <i>property.</i> : Returns the key name of the entity. Equivalent to <pre>
.key().name()
</pre></li>
<li><strong>memcache_key</strong>, <i>property</i> : The derived class is expected to define this property. This is supposed to return the key which will be used in memcache while storing and retrieving this entity. It is assumed at some places the memcache_key shall be same as keyname.</li>
<li><strong>from_id(id)</strong>, <i>classmethod</i> : This classmethod returns the entity based on the id passed. It first tries to fetch the entity from memcache. If not found, it attempts to fetch the entity from datastore. If that is not found, then it creates an entity with the key_name as the id passed and returns that.</li>
<li><strong>_from_memcache</strong>, <i>private function</i> : Returns the memcached snapshot of the entity</li>
<li><strong>_get</strong>, <i>private function</i> : Attempts to get <i>self</i> from memcache. If not found, goes through a <i>get_or_insert</i> call.</li>
<li><strong>fetch_from_id(id)</strong>, <i>classmethod</i> : The difference between fetch_from_id and from_is is that, fetch_from_id always creates an entity in the datastore if it does not already exists. While in case of from_id a dummy object (one which is not in the datastore, yet) is returned.</li>
<li><strong>sync_from_db</strong>, <i>function</i> : Sync the entities from datastore to memcache. Essentially, the entity is copied to memcache from datastore and the versions are updated</li>
<li><strong>sync_to_db</strong>, <i>function</i> : Sync the entities from memcache to data store. Essentially, the entity is copied from memcahe to datastore and the versions are updated</li>
<li><strong>_store</strong>, <i>private function</i> : Stores the entity in memcache and updates the mc_version. If the fault becomes more than fault tolerance, it syncs the entity across datastore and memcache.</li>
<li><strong>find_parent(id)</strong>, <i>classmethod</i> : This is not implemented and the derived class is expected to work on this. This is supposed to return the key name of the parent entity of this entity.</li>
</ul><h2>
Channels</h2><p>
The channel ids created are a function of userid as well as the time.time(). Whenever a channel id is created, it is stored as a property in the Player model as well as the Game model. Duplicacy of data, helps do better reads.
<script class="brush: python" type="syntaxhighlighter">
def gen_channel(userid):
seed = userid + str(int(time.time()))
return md5.md5(seed).hexdigest()
</script><br />
</p><br />
<h2>Player Class</h2><br />
The Player Class has more or less the same functions, but their writing style became little different due to change in coding style.<br />
<br />
<script class="brush: python" type="syntaxhighlighter">
class Player(EfficientModel):
"""
Stores information about which player is added into which game
room. This of course assumes that a player can be in only one
game room at a time.
"""
assets = db.TextProperty(default = '')
name = db.StringProperty()
channels = db.StringListProperty()
@property
def memcache_key(self):
return self.key().name()
@property
def gameroom(self):
if self.parent():
return self.parent().key().name()
def get_channels(self):
return self._get().channels
def create_channel(self):
new_channel = gen_channel(self.keyname)
self.channels.append(new_channel)
self._store()
return new_channel
def die(self):
raise NotImplementedError
def do_action(self, action, **kwargs):
raise NotImplementedError
@classmethod
def find_parent(cls, id):
return Game.all(keys_only = True).filter('players = ', id).get()
def chat(self, message):
if self.gameroom is not None:
self._store()
Game.from_id(self.gameroom).update_chat(self, message)
def leave_tournament(self):
if self.gameroom is not None:
Game.from_id(self.gameroom).expel(self)
</script><br />
<br />
<h3>Game Class</h3><br />
The important parts of the class are shown here. For a more detailed version, have a look at the source code.<br />
<br />
<script class="brush: python" type="syntaxhighlighter">
class Game(EfficientModel):
"""
The Model to store the details of a particular
game room.
"""
players = db.StringListProperty()
chat = db.TextProperty()
active = db.BooleanProperty(default = True)
channels = db.StringListProperty()
deltas = {}
@property
def memcache_key(self):
return 'tournament_' + self.key().name()
@classmethod
def join_latest_or_new(cls, player):
latest_roomkey = memcache.get(LATEST_GAMEROOM)
if latest_roomkey:
cls.from_id(latest_roomkey).add_player(player)
return latest_roomkey
return cls().new(player)
def can_add_player(self):
if MAX_PARTICIPANTS == -1: return True
return len(self.players) < MAX_PARTICIPANTS
def add_player(self, player):
if self.can_add_player():
logging.info('Adding to existing game')
if player.keyname not in self.players:
self.players.append(player.keyname)
self._store()
p = Player.get_or_insert(key_name = player.keyname,
parent = self)
memcache.set(player.keyname, serialize_entities(player))
self.deltas.update({'new_player' : 1,
'name' : player.keyname,})
self.update_channels(player)
return self.send_updates()
else:
return self.new(player)
@classmethod
def new(cls, player):
if not player: return
logging.info('Creating new gameroom...')
new_room = cls(key_name = str(time.time()),
players = [player.keyname])
sim_player = Player(key_name = player.keyname,
parent = new_room,
channels = player.channels)
db.put([new_room, sim_player])
memcache.set_multi({LATEST_GAMEROOM : new_room.key().name(),
new_room.memcache_key: serialize_entities(new_room),
sim_player.keyname : serialize_entities(sim_player),
})
new_room.update_channels(sim_player)
return new_room
@classmethod
def continue_tournament(cls, player):
room = cls.from_id(player.gameroom)
room.update_channels(player)
def update_channels(self, player):
new_channels = player.get_channels()
if isinstance(new_channels, list):
self.channels.extend(new_channels)
if isinstance(new_channels, str):
self.channels.append(new_channels)
self.channels = list(set(self.channels))
self._store(True)
def send_updates(self):
message = simplejson.dumps(self.deltas)
for channel_id in self.channels:
channel.send_message(channel_id, message)
@classmethod
def find_parent(cls, id):
return None
def update_chat(self, player, message):
self.deltas['chat'] = message
if self.chat:
self.chat += '
' + self.deltas.get('chat')
else:
self.chat = self.deltas.get('chat')
self._store()
self.send_updates()
def end(self):
"End the game"
player_keys = [db.Key.from_path('Game', self.keyname, 'Player', x) for x in self.players]
db_keys = [db.key()]
db_keys.extend(player_keys)
memcache_keys = self.players
memcache_keys.append(self.memcache_key)
memcache.delete_multi(memcache_keys)
db.delete(db_keys)
def expel(self, player):
"Expel player from the game"
memcache.delete(player.memcache_key)
def txn(game_obj, player_key, player_id):
db.delete(player_key)
game_obj.players.pop(player_id)
game_obj.put()
db.run_in_transaction(txn, self, db.Key(player.keyname), player.keyname)
self.deltas.update({'expel': 1,
'name' : player.keyname})
self.send_updates()
</script>
This is a better optimized version. I am still working on it on a bigger and wider scale and will keep updating about the scaling issues of this approach and how to fix them.
<h3>
Resources</h3><ol>
<li>Multiuser Chatroom with App Engine Channel API - Part 1: <a href="http://blog.myblive.com/2010/12/multiuser-chatroom-with-app-engine.html">http://blog.myblive.com/2010/12/multiuser-chatroom-with-app-engine.html</a>
<li>Source Code: <a href="http://code.google.com/p/pranav/source/browse/chat-channel" target="_blank">http://code.google.com/p/pranav/source/browse/chat-channel</a></li>
<li>Channel API: <a href="http://code.google.com/appengine/docs/python/channel" target="_blank">http://code.google.com/appengine/docs/python/channel</a> </li>
<li>Discuss: <a href="https://groups.google.com/d/topic/google-appengine-python/4wqVzjOoemI/discussion" target="_blank">Google Groups Discussion</a></li>
<li>Tic Tac Toe App: <a href="http://code.google.com/p/channel-tac-toe" target="_blank">http://code.google.com/p/channel-tac-toe</a>
</ol><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com1Bengaluru, Karnataka, India12.9715987 77.594562712.6370402 77.1276437 13.3061572 78.0614817tag:blogger.com,1999:blog-4955313250997333114.post-74651478956614881482010-12-28T18:38:00.006+05:302011-01-12T12:57:49.365+05:30Multiuser Chatroom with App Engine Channel API<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2010%252F12%252Fmultiuser-chatroom-with-app-engine.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe> <span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displayText="ShareThis"></span><br />
<br />
<strong>UPDATE</strong><br />
Have a look at <a href="http://blog.myblive.com/2011/01/multiuser-chatroom-with-app-engine.html">part 2 of the series</a>, with focus on optimization.<br />
<br />
Perhaps the most primitive use of Google App Engine <a href="http://code.google.com/appengine/docs/python/channel/" target="_blank">Channel API</a> is the use of chat between two people in real time. This concept can be extended to chatrooms - where many people chat simultaneously with each other, or gamerooms - where many people play some game simultaneously in real time amongst each other.<br />
<br />
<br />
The docs don't say much about the quota details of Channel API, so here it is<br />
<br />
<strong>Free Quota</strong><br />
Channel API Calls 46,310,400<br />
Channels Created 8,640<br />
Channel Data Sent 1,046.00 GBytes<br />
<br />
<strong>Paid Quota</strong><br />
Channel API Calls 91,995,495<br />
Channels Created 95,040<br />
Channel Data Sent 2,088.13 GBytes<br />
<br />
The example application <a href="http://code.google.com/p/channel-tac-toe" target="_blank">tic-tac-toe</a> shows to a quite good level how to use Channel API for playing duo-player game. I find some scope for improvements in that app. Let's review them : <br />
<ol><li>Everytime a player starts a new game, a new client id (for channel) is created. The cap provided on Channels Creates is few, so this should be used judiciously</li>
<li>The game room generates depends on the User. So, if the user who initiated the play reloads the page, the game is gone. This works for the case of tic-tac-toe but does not go for chatrooms or gamerooms</li>
</ol><br />
Apart from above issues, there are somethings that a serious gameroom/ chatroom needs and they are as follows:<br />
<ol><li>Automatic room creation. The room is automatically created as soon as number of players exceed the MAX_PLAYERS limit</li>
<li>Every player has some assets (in a gameroom), and this keeps on changing with actions performed. So there needs to be state persistent information stored at the backend</li>
<li>If the player reloads the page or opens a couple of new tabs, consistency of information should be maintained.</li>
<li>Usually in a chat, when someone joins late, they want to be updated of the chats that took place when they were not there</li>
</ol>.<br />
<br />
Using above points as my guiding light, I tried to create an app which does the above in a net and beautiful manner. The app's source code is located at <a href="http://code.google.com/p/pranav/source/browse/chat-channel" target="_blank">http://code.google.com/p/pranav/source/browse/chat-channel</a>.<br />
<br />
<h3>Structure</h3><br />
The concept of the gameroom/chatroom goes like this - When a player wants to play a game, he is taken to a room. The room could be a new room (if no room exists or all others are full), or an existing room. <br />
There are two entity kinds - Game and PlayerGame. The Game Entity Kind contains information about the game room. This is where you can keep a track of all the events happening in a room like maybe chat between users, or collaborative drawing or the moves in a tic-tac-toe. The PlayerGame is a child entity of the Game entity, which contains the information about the game the user is playing. For those games which go in a sequential manner where player1 does something and then player2 does something, PlayerGame could have very well been inside the Game entity, but in a game where many players can do multiple actions and their actions affect their state in some way or the other datastore congestion might occur. To avoid all those, its made in a separate entity.<br />
<br />
<script class="brush: python" type="syntaxhighlighter">
class Game(db.Model):
"""
The Model to store the details of a particular
game room.
"""
players = db.StringListProperty()
chat = db.TextProperty()
active = db.BooleanProperty(default = True)
created = db.DateTimeProperty(auto_now_add = True)
updated = db.DateTimeProperty(auto_now = True)
class PlayerGame(db.Model):
"""
Stores information about which player is added into which game
room. This of course assumes that a player can be in only one
game room at a time.
"""
assets = db.TextProperty(default = '')
created = db.DateTimeProperty(auto_now_add = True)
updated = db.DateTimeProperty(auto_now = True)
</script><br />
<br />
Then we need two wrapper Classes so that we can abstract the functioning. Our classes can very well be <strong>Player</strong> and <strong>Tournament</strong>.<br />
<br />
<script class="brush: python" type="syntaxhighlighter">
class Player(object):
"""
A Player class
"""
userid = None
assets = ''
name = ''
updates = {}
_player = None
def __init__(self, userid=None):
self.userid = userid
def _get_tournament(self):
if self.userid:
return Game.all(keys_only=True).filter('players = ', self.userid).get()
def get_player(self):
if self._player:
return self._player
if self.userid:
self._player = PlayerGame.get_by_key_name(self.userid,
parent = self._get_tournament())
return self._player
def get_gameroom(self):
pg = self.get_player()
if pg:
return pg.parent().key().name()
def die(self):
playergame_key = db.Key.from_path('PlayerGame', self.userid)
game = Game.all().filter('user = ', self.userid).get()
def txn(userid, pg_key, g):
db.delete(pg_key)
g.users.pop(userid)
db.put(g)
db.run_in_transaction(txn, self.userid, playergame_key, game_key)
</script><br />
<br />
The Player class contains all the wrapper functions that a player can possible do inside a chatroom/gameroom. Similarly, there shall be a Tournament class which will do all the activities that can be done in a tournament<br />
<script class="brush: python" type="syntaxhighlighter">
class Tournament(object):
"""
A class for tournament
"""
game = None
room = None
delta_chat = ''
updates = {}
def __init__(self, room=None):
self.updates = {}
self.room = room
def get_tournament(self):
if self.game:
return self.game
if self.room:
self.game = Game.get_by_key_name(self.room)
return self.game
def can_add_player(self):
if MAX_PARTICIPANTS == -1:
return True
return len(self.get_tournament().players) < MAX_PARTICIPANTS
def add_player(self, player):
if self.can_add_player():
game = self.get_tournament()
if player.userid not in game.players:
game.players.append(player.userid)
db.put(game)
PlayerGame.get_or_insert(key_name = player.userid,
parent = game)
self.updates.update({'new_player' : 1,
'name' : player.userid})
self.send_update()
else:
return self.new(player)
def new(self, player):
"A new room shall be created only if there is a player to go into"
if not player: return
self.room = str(time.time())
self.game = Game(key_name = self.room,
players = [player.userid])
playergame = PlayerGame(key_name = player.userid,
parent = self.game)
db.put([self.game, playergame])
memcache.set(LATEST_GAMEROOM, self.room)
return self.room
@classmethod
def join_new_or_latest(cls, player):
latest_roomkey = memcache.get(LATEST_GAMEROOM)
if latest_roomkey:
cls(latest_roomkey).add_player(player)
return latest_roomkey
return cls().new(player)
@classmethod
def continue_tournament(cls, player):
"When the player was already in a tournament continue from there itself"
channel.send_message(player.get_channel(), cls().get_all_updates())
def chat(self, player, message):
delta_chat = message
game = self.get_tournament()
if game.chat:
chat = game.chat + '
' + delta_chat
else:
chat = delta_chat
game.chat = chat
db.put(game)
self.updates.update({'delta_chat' : delta_chat})
self.send_update()
def get_player_channels(self):
game = self.get_tournament()
return [Player(x).get_channel() for x in game.players]
def get_game_message(self):
update = self.updates
return simplejson.dumps(update)
def get_all_updates(self):
update = self.updates
update.update({'all' : 'all'})
return simplejson.dumps(update)
def send_update(self):
message = self.get_game_message()
for channel_id in self.get_player_channels():
channel.send_message(channel_id, message)
</script>
<h3>
Flow</h3>Whenever a user comes to the app, first of all it is checked if he was in an existing room or not. If found, then his activities are continued from that stage. If not found then the user is taken to a room. The room could be a new room, if all others are full, or no room is present. The user can perform activities in that room. Filling of rooms goes in <b>F</b>irst <b>C</b>ome, <b>F</b>irst <b>S</b>erve order. So, when the next user wishes to join a room, the latest room created is checked for vacancy and the user is dropped there. The user is then free to perform actions and have fun.
What is typically important here is that, initially a Channel is created between the client and the server, and then using that same channel all the future communication for rooms goes. One advantage of this is that we don't need to create a lot of channels for every user. For this it is very important that the algo which is used to create channel client id has only one variable as userid. (<i>What will happen if we have two variables like userID and timestamp to create the channel client id?</i>).
<script class="brush: python" type="syntaxhighlighter">
def gen_channel(userid):
return md5.md5(userid).hexdigest()
</script><br />
<br />
After the channel is created, the client send requests to join the room. This is taken care by the server and accordingly the user is dropped into a new room or an existing room<br />
<br />
<script class="brush: python" type="syntaxhighlighter">
class JoinGame(BaseHandler):
def post(self):
"""
Processes the req from a client/player to join a gameroom
"""
user = users.get_current_user()
if not user:
return self.redirect(users.create_login_url('/'))
userid = user.user_id()
logging.info('join chat call by %s'%userid)
player = Player(userid)
if not player.get_gameroom():
gameroom = Tournament.join_new_or_latest(player)
return
Tournament.continue_tournament(player)
</script><br />
<br />
This is in a nutshell, a proof of concept about building huge chat-rooms/ game-rooms using Channel API of Google App Engine. <br />
<br />
<h3>Resources</h3>Read more about optimizing this code in <a href="http://blog.myblive.com/2011/01/multiuser-chatroom-with-app-engine.html">part 2 of the series</a>.<br />
<ol><li>Source Code: <a href="http://code.google.com/p/pranav/source/browse/chat-channel" target="_blank">http://code.google.com/p/pranav/source/browse/chat-channel</a></li>
<li>Channel API: <a href="http://code.google.com/appengine/docs/python/channel" target="_blank">http://code.google.com/appengine/docs/python/channel</a> </li>
<li>Discuss: <a href="https://groups.google.com/d/topic/google-appengine-python/4wqVzjOoemI/discussion" target="_blank">Google Groups Discussion</a></li>
<li>Tic Tac Toe App: <a href="http://code.google.com/p/channel-tac-toe" target="_blank">http://code.google.com/p/channel-tac-toe</a><br />
</ol><br />
There are many more things that needs to be taken care of to increase speed and performance, but that shall come in another blog post<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com4Bengaluru, Karnataka, India12.9715987 77.594562712.6370402 77.1276437 13.3061572 78.0614817tag:blogger.com,1999:blog-4955313250997333114.post-27597061432288334642010-11-15T13:28:00.012+05:302010-11-15T14:08:32.334+05:30PyCon India 2010 Videos - Building Scalable Apps using Google App Engine<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript">
</script><iframe allowtransparency="true" frameborder="0" scrolling="no" src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2010%252F11%252Fpycon-india-2010-videos-building.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" style="border: none; height: 80px; overflow: hidden; width: 450px;"></iframe> <span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displaytext="ShareThis"></span> <br />
<br />
<a href="http://2.bp.blogspot.com/_5_28o544oU8/TODosKLsb6I/AAAAAAAALYI/AfKoEbiY8jg/s1600/2.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="66" src="http://2.bp.blogspot.com/_5_28o544oU8/TODosKLsb6I/AAAAAAAALYI/AfKoEbiY8jg/s200/2.png" width="200" /></a>The Video shooting for PyCon India 2010 held on 25th and 26th Sept at Bangalore, was done by the media partner <a href="http://www.vodex.in/">Vodex</a>. They send the DVDs and CDs to people who have opted for it. Here I am sharing the videos from my talk on "Building Scalable Apps Using Google App Engine". The video processing was done excellently in complete synchronization with the presentation slides. As a side effect of this, the videos are available in parts as per the slides.<br />
<br />
<object height="385" width="480"><param name="movie" value="http://www.youtube.com/v/AQYXgINJoXs?fs=1&hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/AQYXgINJoXs?fs=1&hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object><br />
<br />
<object height="385" width="480"><param name="movie" value="http://www.youtube.com/v/zAq4hTf4VUk?fs=1&hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/zAq4hTf4VUk?fs=1&hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object><br />
<br />
<object height="385" width="480"><param name="movie" value="http://www.youtube.com/v/5U9qgSz8Byo?fs=1&hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/5U9qgSz8Byo?fs=1&hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object><br />
<br />
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/t9yvHCoBLZ8?hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/t9yvHCoBLZ8?hl=en&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object><br />
<br />
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/viP9SiFpGYg?hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/viP9SiFpGYg?hl=en&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object><br />
<br />
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/caEzYl_BB-w?hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/caEzYl_BB-w?hl=en&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object><br />
<br />
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/3QJ191_9lJc?hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/3QJ191_9lJc?hl=en&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object><br />
<br />
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/dGlsY8DEqLw?hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/dGlsY8DEqLw?hl=en&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object><br />
<br />
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/Uf3O7OHQUwI?hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/Uf3O7OHQUwI?hl=en&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object><br />
<br />
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/x6sHON7CViE?hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/x6sHON7CViE?hl=en&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object><br />
<br />
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/EfmXTTGG64w?hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/EfmXTTGG64w?hl=en&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object><br />
<br />
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/Hz6ZQamHQ_k?hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/Hz6ZQamHQ_k?hl=en&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com3MSRIT MS RAMAIAH INSTITUTE OF TECHNOLOGY, MSR College Rd, M S Ramaiah Nagar, Mathikere, Bengaluru, Karnataka, India13.0293176 77.56550613.0084126 77.5363235 13.050222600000001 77.5946885tag:blogger.com,1999:blog-4955313250997333114.post-29634432698176471822010-11-06T01:49:00.003+05:302010-11-06T19:49:48.159+05:30Getting rid of facebook group emails, beautifully<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2010%252F11%252Fgetting-rid-of-facebook-group-emails.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe> <span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displayText="ShareThis"></span> <br />
Like many others, after being irritated by facebook's constant emails for every post on a group I am a member of, I made <b><a href="http://digest.myblive.com">Group Email Digest</a></b> (<a href="http://www.facebook.com/apps/application.php?id=106947259372688">facebook page</a>) application which sends me the daily summary of posts on a group. The applications sends out one email per day per group. The first version of the app launched was pretty simple and had some basic bugs which restricted the use of the application. The latest release of the app (the app is still in beta) has fixed those bugs and paved way for more interesting features in the applications.<br />
<br />
Here is a brief summary of bugs fixed, bugs not fixed and things on my mind (to show up in later releases). For a complete and updated detail of issues, visit the <a href="http://bitbucket.org/pranny/facebook-digest-email/issues" target="blank">bug tracker page</a>.<br />
<br />
<strong>UPDATE</strong><br />
The bug related to closed/secret group's emails not being trigged has been fixed.<br />
<br />
<b>Resolved Bugs/ Issues</b><br />
<ol><li><b>Blank Email/Text</b>: A blonde had three swimming pools. One had cold water, the other had warm water and there was no water in the third pool. When asked about the third pool, she said.. well, sometimes I don't feel like swimming. :-) The Group Email Digest app used to send out blank emails, when there had been no recorded activity. This has been fixed, and now, no emails are send if there is no activity.<br />
Likewise, if there were only links shared, videos posted, images uploaded onto the group, there was a similar blank text coming up. This has been alternated by message, description and caption in that order.</li>
<li><b>Digest Emails not generating</b>: For few <i>old</i> group's, the emails were not being triggered. I found this out to be a version change issue on facebok front, and maintaining the compatibility, this has been fixed. The emails for all groups shall be triggering starting Nov 06</li>
<li><b>URLs for <i>old</i> groups not coming properly</b>: The URLs for old facebook groups were not formatted appropriately. This resulted in the clicks taking user to facebook home page instead of taking to the group page. This issue has been fixed.</li>
</ol><br />
<b>Unresolved Bugs/Issues</b><br />
The following issues remain unresolved as of now.<br />
<ol><li><b>Time Zone</b>: All the emails are triggered just after 00:15 hrs UTC. This should ideally be 00:15 hrs User Time Zone. I am looking for a resource efficient method for this, and hopefully should get this fixed soon.</li>
<li><b>Closed/Private Group Emails not trigerring</b>: I am experimenting with delivering digest emails from closed/private group to its members in a resource efficient manner, and hopefully should arrive at some conclusion soon.</li>
</ol><br />
<b>Things in my mind</b><br />
The following features you should hope to see in near future.<br />
<ol><li><b>Localization</b>: You should see the mail in the language of your choice, right? So, very soon, you will receive emails and all the text will be localized based on the information we obtain about you from your facebook account.</li>
<li><b>Activity Graphs</b>: Wanna know how active is a group? Very soon, you will be able to see a graphical visual of the activity taking place in the groups you are a member of.</li>
<li><b>Group Suggestions</b>: Want to do more exiting talks to people of your interests? Group suggestions will be telling you about new active groups based on your taste.</li>
</ol><br />
Did I miss a feature? Suggest features for Group Digest App <a target="_blank" href="http://bitbucket.org/pranny/facebook-digest-email/issues/new">here</a>.<br />
<br />
Saw a bug/issue/concern? Report it <a href="http://bitbucket.org/pranny/facebook-digest-email/issues/new" target="_blank">here</a>.<br />
<br />
Follow <a target="_blank" href="http://twitter.com/groupdigestapp" title="Group Digest App on Twitter">@groupdigestapp</a> on twitter.<br />
<br />
<iframe src="http://www.facebook.com/plugins/likebox.php?href=http%3A%2F%2Fwww.facebook.com%2Fapps%2Fapplication.php%3Fid%3D106947259372688&width=292&colorscheme=light&connections=10&stream=true&header=true&height=587" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:292px; height:587px;" allowTransparency="true"></iframe><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0tag:blogger.com,1999:blog-4955313250997333114.post-59094332477716420622010-11-01T02:25:00.003+05:302010-11-01T02:40:35.024+05:30Facebook Group's Daily Digest Emails<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript">
</script><iframe allowtransparency="true" frameborder="0" scrolling="no" src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2010%252F11%252Ffacebook-groups-daily-digest-emails.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" style="border: none; height: 80px; overflow: hidden; width: 450px;"></iframe> <span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displaytext="ShareThis"></span><br />
<a href="http://2.bp.blogspot.com/_5_28o544oU8/TM3UwBtQe7I/AAAAAAAALXs/b38COieymMs/s1600/50511_106947259372688_6996332_n.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/_5_28o544oU8/TM3UwBtQe7I/AAAAAAAALXs/b38COieymMs/s1600/50511_106947259372688_6996332_n.jpg" /></a>Hey folks. So, wondering about the hell of emails facebook send you for every activity that is done in a group you are member of? Well, there is one official way to get rid of it - revoke permissions about facebook sending you emails. But then, you also don;t want to be left out, right? What if you could have a daily digest sort of thing for facebook groups? Won't that be great? <br />
<br />
Time to welcome <b>Group Email Digest</b> app (<a href="http://www.facebook.com/apps/application.php?id=106947259372688">facebook link</a>, <a href="http://digest.myblive.com/">website</a>). This is a real simple, sweet app that keeps you updated about the activities performed in your facvorite group on a daily basis. The app's motto is "<i>To keep your inbox updated and uncluttered</i>". <br />
<br />
Let me tell you more about this app. When you visit the app's home page (<a href="http://digest.myblive.com/">http://digest.myblive.com</a>), it will ask you to login with your facebook account, and ask the required permissions. The need for various permissions has been very clearly mentioned there itself. After you login, you will find all the groups you are currently a member of. Just check those groups for which you want a daily digest update email and click on "Subscribe Digest Email" at the bottom. If you want to opt out from a particular group, just uncheck the group and again click on "Subscribe Digest Email". That will opt you out.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/_5_28o544oU8/TM3WfGU_aTI/AAAAAAAALX4/6p-8AJ740OU/s1600/group-email-digest-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="279" src="http://2.bp.blogspot.com/_5_28o544oU8/TM3WfGU_aTI/AAAAAAAALX4/6p-8AJ740OU/s320/group-email-digest-2.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_5_28o544oU8/TM3Uz-Jj7KI/AAAAAAAALXw/wx0Hrr1nTrk/s1600/group-email-digest-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="279" src="http://4.bp.blogspot.com/_5_28o544oU8/TM3Uz-Jj7KI/AAAAAAAALXw/wx0Hrr1nTrk/s320/group-email-digest-1.png" width="320" /></a></div><br />
The app is in beta phase. This means that there will be frequent changes in the application's behavior. The changes will be for the good only. Also, there are server limitations on the number of emails per day, that can be send out (since the application is running in free quota now), so we can support only a limited number of users right now. The limit should be somewhere around 1,000 however exact count shall be available only after observation for couple of days.<br />
<br />
Let me know your views, critism and praises, so I will keep improving upon it.<br />
<br />
<iframe src="http://www.facebook.com/plugins/likebox.php?href=http%3A%2F%2Fwww.facebook.com%2Fapps%2Fapplication.php%3Fid%3D106947259372688&width=292&colorscheme=light&connections=10&stream=true&header=true&height=587" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:292px; height:587px;" allowTransparency="true"></iframe><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0tag:blogger.com,1999:blog-4955313250997333114.post-77050555470969609742010-10-24T23:31:00.003+05:302010-10-24T23:36:52.388+05:30Facebook autologin with google account<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript">
</script><iframe allowtransparency="true" frameborder="0" scrolling="no" src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2010%252F10%252Ffacebook-autologin-with-google-account.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" style="border: medium none; height: 80px; overflow: hidden; width: 450px;"></iframe> <span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displaytext="ShareThis"></span> <br />
So, are you tired of logging in everytime on facebook? Putting your email id/facebook username and password. Wish there was a secure way in which you would have been automatically logged into facebook? There is! And the way is by connecting facebook and google account (gmail).<br />
<br />
It's pretty simple, safe and secure. All you have to do is to connect your gmail/google account with facebook once. Then everytime you are logged into gmail and open facebook, you will automatically be logged in. What's more. Even if you try to logout, facebook will relogin you :-)<br />
<br />
Here is how this goes.<br />
<br />
<ol><li>Go to <a href="http://www.facebook.com/find-friends/?ref=sb">find your friends</a> on facebook. It is on the right hand side bar. You will notice something like this. Now enter your gmail ID when asked as shown below.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_5_28o544oU8/TMRu18OTm9I/AAAAAAAALXE/5AK8mKD4X9o/s1600/facebook-google-step1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="188" src="http://4.bp.blogspot.com/_5_28o544oU8/TMRu18OTm9I/AAAAAAAALXE/5AK8mKD4X9o/s320/facebook-google-step1.png" width="320" /></a></div></li>
<li>This will open a pop up as shown below. If you are not already logged into gmail, it will ask you to log in to your gmail account. <div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/_5_28o544oU8/TMRu3IxUZwI/AAAAAAAALXI/x_cLStmXlZ0/s1600/facebook-google-step2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="238" src="http://2.bp.blogspot.com/_5_28o544oU8/TMRu3IxUZwI/AAAAAAAALXI/x_cLStmXlZ0/s320/facebook-google-step2.png" width="320" /> </a></div></li>
<li>After you have logged in, or if you are already logged in, you will see the request from google on behalf of facebook to access your contact information. Below there is a check box about remembering this authorization. Make sure that option is checked for auto login. Don't worry, I will tell you how to revoke this privilage, if you want to later at the end of this post.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/_5_28o544oU8/TMRu3tFaE0I/AAAAAAAALXM/SzHVa4htcJE/s1600/facebook-google-step3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="230" src="http://4.bp.blogspot.com/_5_28o544oU8/TMRu3tFaE0I/AAAAAAAALXM/SzHVa4htcJE/s320/facebook-google-step3.png" width="320" /> </a></div></li>
<li>After authentication, you will see a screen which will be something like the one below. This is where facebook has authenticated your google account and is now, fetching your friends who are on facebook. Let facebook do their job.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/_5_28o544oU8/TMRu4E26WWI/AAAAAAAALXQ/2QBY_Zuut4Q/s1600/facebook-google-step4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="102" src="http://3.bp.blogspot.com/_5_28o544oU8/TMRu4E26WWI/AAAAAAAALXQ/2QBY_Zuut4Q/s320/facebook-google-step4.png" width="320" /> </a></div></li>
<li> Now you will find a list of your contacts from your gmail, who are on facebook. If you wish, you can send them a friend request, or you may skip this step.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_5_28o544oU8/TMRu5PSYDfI/AAAAAAAALXU/6Wzo99NiDv4/s1600/facebook-google-step5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="233" src="http://1.bp.blogspot.com/_5_28o544oU8/TMRu5PSYDfI/AAAAAAAALXU/6Wzo99NiDv4/s320/facebook-google-step5.png" width="320" /></a></div></li>
<li>Now that is all. Your facebook account is connected to your gmail account and you are ready for passwordless login on facebook. To test this, logout of your facebook account, but remain logged in your gmail account. After you have logged out, you will see facebook logging you again into your account. Like the one shown below.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_5_28o544oU8/TMRu6OVZAzI/AAAAAAAALXY/Dy9pU_DICD0/s1600/facebook-google-step6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="192" src="http://1.bp.blogspot.com/_5_28o544oU8/TMRu6OVZAzI/AAAAAAAALXY/Dy9pU_DICD0/s320/facebook-google-step6.png" width="320" /></a></div></li>
<li>If you ever wish to revoke permissions of facebook from your google account, just go to <a href="http://www.google.com/">google.com</a> and click on <a href="https://www.google.com/accounts/ManageAccount?hl=en">Google Account Settings</a>.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_5_28o544oU8/TMRu7G3JpwI/AAAAAAAALXc/m1w23xQX5Hw/s1600/facebook-google-step7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/_5_28o544oU8/TMRu7G3JpwI/AAAAAAAALXc/m1w23xQX5Hw/s1600/facebook-google-step7.png" /></a></div></li>
<li>Next, click on <a href="https://www.google.com/accounts/IssuedAuthSubTokens?hl=en">Change Authorized Websites</a> to see the list of websites you have authorized. Find for Facebook there, and click on <b>revoke access</b>. That's all you have now successfully disconnected facebook and google account.<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/_5_28o544oU8/TMRu7tm0BCI/AAAAAAAALXg/VWsaAmFBPx0/s1600/facebook-google-step8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="106" src="http://2.bp.blogspot.com/_5_28o544oU8/TMRu7tm0BCI/AAAAAAAALXg/VWsaAmFBPx0/s320/facebook-google-step8.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/_5_28o544oU8/TMRu8IK09CI/AAAAAAAALXk/RdoSYfDOZMA/s1600/facebook-google-step9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="28" src="http://1.bp.blogspot.com/_5_28o544oU8/TMRu8IK09CI/AAAAAAAALXk/RdoSYfDOZMA/s320/facebook-google-step9.png" width="320" /></a></div></li>
</ol><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0Jaipur, Rajasthan 302001, India26.917 75.81726.763936 75.5835405 27.070064000000002 76.050459499999988tag:blogger.com,1999:blog-4955313250997333114.post-73379735624869966472010-10-08T18:53:00.001+05:302010-10-08T18:59:26.726+05:30Easy tagCloud jQuery Plugin<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fcodecontrol.blogspot.com&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe> <span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displayText="ShareThis"></span> <br />
This is the 101th post on Bellicose Beliefs (former CodeControl), and today we have with us, an easy tagcloud plugin for jQuery. It is called easy tagcloud because it is really very easy to use and provides options to make life easier.<br />
<br />
The source code can be obtained from GoogleCode Repository of CodeControl-Samples <a href="http://code.google.com/p/codecontrol-samples/source/browse/#hg/myBtagcloud" title="EasyTagCloud Source Code">http://code.google.com/p/codecontrol-samples/source/browse/#hg/myBtagcloud</a><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0tag:blogger.com,1999:blog-4955313250997333114.post-17998605273962622010-10-02T02:27:00.003+05:302010-10-25T00:07:24.907+05:30generating tag cloud in Python<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2010%252F10%252Fgenerating-tag-cloud-in-python.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe> <span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displayText="ShareThis"></span><br />
<br />
Tag Clouds are an amazing way to emphasize on what is needed. Here, is a simple code snippet in Python that will generate tag cloud, on the basis of tags and their count provided.<br />
<br />
The algorithm for generating tag cloud is used from <a href="http://en.wikipedia.org/wiki/Tag_cloud#Creation_of_a_tag_cloud" target="_blank">http://en.wikipedia.org/wiki/Tag_cloud#Creation_of_a_tag_cloud</a><br />
<br />
<script class="brush: python" type="syntaxhighlighter">
def generate_tag_cloud(tags, baseurl='http://www.example.com/tag/', fmax = 20):
"""Generates HTML of tag cloud.
Input params:
tags : A dictionary containing the key as the tag name, and
value as the count of times, it appears
ex: {'tag1': 2, 'tag2': 1, 'tag3': 45}
fmax : The maximum font size to display. <int> pixel
http://en.wikipedia.org/wiki/Tag_cloud#Creation_of_a_tag_cloud
"""
size = lambda f_max, t_max, t_min, t_i : t_i > t_min and int(math.ceil((f_max*(t_i - t_min))/(t_max - t_min))) or 10
f_max = fmax
t_min = min(tags.values())
t_max = max(tags.values())
cloud = []
for tag in tags:
cloud.append({'tag': tag,
'count': tags.get(tag),
'size': size(f_max,t_max,t_min,tags.get(tag))})
s = []
for onetag in cloud:
s.append('<span style="font-size: %dpx"><a href="%s" title="%d Entries">%s</a></span>'
%(onetag.get('size'), baseurl+onetag.get('tag'), onetag.get('count'), tag ))
return '<p class="tag_cloud">
' + ''.join(s) + '</p>'
</script><br />
<br />
<script class="brush: python" type="syntaxhighlighter">
# Assumes the data structure for tag as
# class Tag(object):
# def __init__(self, name, count, permalink):
# self.name = name
# self.count = count
# self.permalink = permalink
# And why would one have such a structure? Well, watch out for http://beamto.us
# source when it is released
#
def gen_tagcloud(tags):
"""Generates the dict for tag cloud based on the input provided
tags = [tag1, tag2, tag3]
"""
conf = 'log' # for logarithmic cloud, try 'linear' for linear
min_weight = min([x.count for x in tags])
max_weight = max([x.count for x in tags])
tag_cloud = []
if conf.get('ALGO').lower().startswith('linear'):
for tag in tags:
w = (tag.count - min_weight)/(max_weight - min_weight)
f = conf.get('MIN-FONT') + (conf.get('MAX-FONT') - conf.get('MIN-FONT') )*w
tag_cloud.append({'tag':tag, 'count':tag.count+1, 'font': f,
'permalink': tag.permalink})
if conf.get('ALGO').lower().startswith('log'):
for tag in tags:
w = (math.log(tag.count or 1) - math.log(min_weight or 1))/\
(math.log(max_weight or 1) - math.log(min_weight or 1))
f = conf.get('MIN-FONT') + (conf.get('MAX-FONT') - conf.get('MIN-FONT') )*w
tag_cloud.append({'tag':tag, 'count':tag.count + 1, 'font':f,
'permalink': tag.permalink})
return tag_cloud
</script><br />
You can tweak and create better versions. Also this is on the Python side, there could be a similar one for JavaScript as well, which generates tag clouds on the fly.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0tag:blogger.com,1999:blog-4955313250997333114.post-31692121239984404882010-10-01T11:26:00.002+05:302010-10-01T11:31:03.077+05:30BeamtoUs - Discover Vote Share<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript">
</script><iframe allowtransparency="true" frameborder="0" scrolling="no" src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2010%252F10%252Fbeamtous-discover-vote-share.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" style="border: none; height: 80px; overflow: hidden; width: 450px;"></iframe><span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displayText="ShareThis"></span><br /><br />
Welcome <a href="http://www.beamto.us/" title="Beamto.Us">Beamto.us</a> (twitter <a href="http://twitter.com/beamtous">@beamtous</a>), a completely new and beautiful music sharing portal. We love to call each single element that you share as a beam. This is also my current project, where I am involved in close coordination with caffeine powered code monkeys - Ritesh Nadhani (idea bubble generator), Taras, Freechin and others. Beamto.us.<br />
<br />
Beamto.us is a music sharing portal targeting specific to genres like psy trance, chillout trance, dark trance, progressive trance and other sub genres in them. You can easily upload your music, or if you have already done that on youtube, soundcloud or other portals, can link from Beamto.us. I'd like to talk more on what are the interesting things that you can do with Beamto.us. The official About Us page says <br />
<blockquote>"Beamto.us Aggregates. The only website in the World that aggregates all electronic music. We use the best back-end technology to bring an excellent interface to all the users. The main page of the site aggregates and lists only the most popular songs voted by the users. Our algorithm has been simulated and tested to be absolutely fair to rankings."</blockquote><br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/_5_28o544oU8/TKV3uNLj2GI/AAAAAAAALWI/nTmLxt-AntA/s1600/27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="228" src="http://3.bp.blogspot.com/_5_28o544oU8/TKV3uNLj2GI/AAAAAAAALWI/nTmLxt-AntA/s320/27.png" width="320" /></a></div><b></b><br />
<b><b><br />
</b><br />
<b><br />
</b><br />
<span class="Apple-style-span" style="font-weight: normal;"><b>Features</b></span></b><br />
<ol><li>Beamto.us allows you to login through your existing Google Account, so you don't need to go through all the registration hassle and pain.</li>
<li>You can upload your music onto our servers or can hotlink your existing website/link for your music at Beamto.us. </li>
<li>Vote Up/ Down a particular beam. And yeah, the votes and rankings do decay so if your beam is hot and new, you are going to be at the top.</li>
<li>Easily share your beams on Facebook, MySpace, Twitter and many other favorite sites, where your friends can see them</li>
<li>Concerned about copyright issues or something else, just let us know. We deal with abuse reports very seriously.</li>
<li>We have lots of space and our servers are on the cloud, so don't worry about a downtime :-) Whenever someone wants to go through your beam, we make sure that it is always up and serving hot.</li>
<li>Its <b>FREE</b> to use. Yes, you don't need to pay in order to use or share your beams. Just come over and we will be doing the things for you.</li>
</ol><br />
The portal is still in its early phases and we have some very exiting plans to make it fun. Yes, that's our concern. Make beaming fun, simple and share !!<br />
<br />
And yes, we are always listening to our users who tell us about our faults and where we need to improve. And we do work on them. So, whenever you feel something is missing, just let us know.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0tag:blogger.com,1999:blog-4955313250997333114.post-13307937114180674232010-09-29T15:34:00.004+05:302010-10-01T11:14:41.650+05:30What Not to do in a tech talk<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fblog.myblive.com%252F2010%252F09%252Fwhat-not-to-do-in-tech-talk.html&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe><br />
<span class="st_email"></span><span class="st_facebook"></span><span class="st_twitter"></span><span class="st_sharethis" displayText="ShareThis"></span><br />
Based on my experiences in attending tech talks and developer conferences recently, and watching YouTube videos of those, which I miss out. I came up with a list of certain things that most people do, and which brings down the interest level of audiences in the talk. This is my effort to sum it all down, and provide solutions to them. This post is continuously updated, so if you guys have points to add, just comment below.<br />
<br />
<h3>Update</h3>Added points 8 and 9 in During the Talk based on suggestions.<br />
<br />
<h2>Before the talk</h2><ol><li><b>Boasting about your talk</b>. It is perfectly normal to discuss with people about your talk and grab audiences, but you should never keep boasting about your talks. This puts in a negative impression about the speaker, even if your talk is a beauty</li>
<li><b>Do Rehearse</b>. Many times, developers are confident about the talk they are about to give, because they might have been working on those stuff for years, but before you go on the stage, make sure that you have rehearsed. This will increase your timeliness and boost your confidence.</li>
</ol>The recent trends in developer talks is to upload them on youtube, so every single mistake that you do in your talk is stored somewhere digitally, forever. So, make sure you are well prepared<br />
<br />
<h2>During the talk</h2><ol><li><b>Don't be unplanned</b>. Don't start off without an agenda, target audience, what is inside the scope of the talk and what is outside of the scope, and finally what the audience should expect from the talk. It is a good idea to let people know the usefulness of the talk they are attending to.</li>
<li><b>Don't exceed timeline</b>. A talk scheduled for 45 minutes, should complete by the end of 46th minute. And yes, that includes the question answer session, discussions and greetings. For every extra minute you consume, you are doing it at the expense of number of people sitting there.</li>
<li><b>Don't be stupid lecturer</b>. The audience in tech talks come to gain from other's experience. So, when you are given an opportunity to talk that's because the talk-selection committee believes that your experience will help others grow and develop applications in your domain, by taking advantage of your experience. Talk about something challenging, how it should be handled, what are common myths, misconceptions, confusions and how to fix those things. People are there not to listen, what they read in the book titled "XYZ for dummies".</li>
<li><b>Avoid temptations</b>. Usually there is a subtopic in your talk, which is your favorite. Probably because you are a <i>guru</i> in it, because you build it; the reason is irrelevant. What is relevant is that you should always avoid the temptation to go into too depth of the topic, stick to the timeline and provide a neutral view of it. Stressing too much on a topic creates a non uniform talk and the audience might loose grip at times</li>
<li><b>Avoid Coding there itself</b> I have seen a lot of engineers who start coding while speaking. This should always be avoided, without exceptions. <i>C</i>oding <i>O</i>n <i>T</i>he <i>S</i>pot or COTS, makes audience loose interest, takes too much time, and if you believe in Murphy's rule, something will go wrong, while you are working there. In any of the cases, audience won't like it. If showing coding is absolutely necessary, make sure you have a video for that, all planned with sub-titles or narration. You can always give your audience a homework to do, which will make sure that the session is alive, even days after.</li>
<li><b>Avoid too much code in slides</b> Showing parts of code, or snippets is absolutely fine, but make sure it is well balanced with the text/graphics in your slides. Keeping a slide mostly occupied with snippets is a bad idea.</li>
<li><b>Identify hecklers</b> There are always some people in the audience, who have a question for every line you speak. Avoid those people taking your talks. That ways, you will save time, annoyance. Reserve some time, usually 5 minutes at the end of your talk, for clarifications of doubts. Also, at this point, I would like to introduce you to <a href="http://moderator.appspot.com">Google Moderator</a>. You can create a "meeting" on Moderator, and give its shortened URL to the audiences in the beginning of your talk. For every question the audience has, they will create a question in the moderator. Others can <i>like</i>, <i>Vote up</i>, <i>Vote down</i> and possibly <i>answer</i> that question. At the end of the talk, take the most voted questions or the questions that you think needs to be addressed at that time. For rest of the questions, you can answer them later after the talk</li>
<li><b>Internet Connectivity and Demo</b> If you are displaying web pages - make sure they are all loaded in tabs in your browser before you start. Also make sure you have offline copies in case internet stops working.</li>
<li><b>Know thy system</b> Make sure you know where your files are - do not spend half your talk searching your file system for those files you want to show.</li>
</ol><br />
<h2>After the talk</h2><ol><li><b>Don't stay behind the fort</b> People will come to you after the talk to clarify things that got left in the session. Depending on the time you have (and you must be prepared for it) you should talk to them and try to solve their issues. They are the ones who were most attentive in your talk and took the pains to see you after the talk. They are your potential fans, and make sure you do not displease them :-)</li>
<li><b>Don't compare</b> Under any circumstances, you should never compare your talk with any one else. This gives a very bad image of you.</li>
<li><b>Don't give false promises</b> If you have shared your email/ moderator URL (and you should) in the talk, you must keep checking them frequently for the next couple of weeks to see if someone has reported any problem/ doubt in your talk.</li>
</ol><br />
As always, I am open to more inputs. Kindly share your ideas and thoughts by commenting below so I will keep on updating this post.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com6tag:blogger.com,1999:blog-4955313250997333114.post-4638158776009588242010-09-29T11:28:00.006+05:302010-09-29T12:03:26.473+05:30CodeControl is now Bellicose Beliefs<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fcodecontrol.blogspot.com&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe><br />There is a major change in the name of the blog. <b>CodeControl</b><i>.blogspot.com</i> is now available at <b>blog</b><i>.MybLive.com</i>. I had plans to rename the blog from CodeControl to Pranav's blog. But a Google for <a href="http://lmgtfy.com/?q=pranav%27s+blog">pranav's blog</a> came up with lots of similar results, so in order to maintain it's uniqueness, I dropped the idea. And then when I was thinking of new terms, a colleague of mine Amrita, came up with Bellicose Beliefs, and I finalised it.<br /><br />This change in blog URL is a step in making up for the MyBLive portal. I plan to use this portal for my applications, opensourced projects, my blog, and many other things related to me, my work and my thoughts.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/00034488531550616063noreply@blogger.com0tag:blogger.com,1999:blog-4955313250997333114.post-38076049264527211212010-09-27T14:32:00.000+05:302010-09-27T14:32:38.677+05:30PyCon India 2010 Experience<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fcodecontrol.blogspot.com&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe><br />
PyCon India this year was held at M.S. Ramaiah Institute of Technology, Bangalore this year on 25th and 26th of Sept. PyCon India 2010 is the primary Python conference in India. A purely volunteer effort, it is being hosted for the second time in India, and has attracted some of the best Python developers in India and abroad.<br />
<br />
This year, I had a chance to speak on "Building Scalable Apps Using Google App Engine". It was an intermediate level talk for 45 minutes. The presentation slides are available at <a href="http://in.pycon.org/2010/talks/5-building-scalable-apps-using-google-app-engine">http://in.pycon.org/2010/talks/5-building-scalable-apps-using-google-app-engine</a><br />
<br />
All the presentation slides and other details are available on PyCon India site at <a href="http://in.pycon.org/2010/talks">http://in.pycon.org/2010/talks</a>. Videos for all the talks shall be made available on YouTube soon, possibly by this week. But could take more time as well. Below are a couple of talks which were quite impressive and should be useful to the developer community are : -<br />
<ol><li>Introduction to Image processing in Python, Anil C R - <a href="http://in.pycon.org/2010/talks/33">http://in.pycon.org/2010/talks/33</a></li>
<li>Python + Wii = Intutive Control, Asim Mittal - <a href="http://in.pycon.org/2010/talks/22">http://in.pycon.org/2010/talks/22</a></li>
<li>Text Parsing in Python, Gayatri Nittala, Madhubala Vasireddy - <a href="http://in.pycon.org/2010/talks/57">http://in.pycon.org/2010/talks/57</a></li>
<li>Rapid 3D game development in Blender, Arun Ravindran - <a href="http://in.pycon.org/2010/talks/37">http://in.pycon.org/2010/talks/37</a></li>
<li>Into 3D with VPython, Deepak Mishra - <a href="http://in.pycon.org/2010/talks/81">http://in.pycon.org/2010/talks/81</a></li>
<li>Agent Based User Simulation and Load Testing, Om Narayan - <a href="http://in.pycon.org/2010/talks/41">http://in.pycon.org/2010/talks/41</a></li>
<li>Internet Protocols in the Python Standard Library, Senthil Kumaran - <a href="http://in.pycon.org/2010/talks/14">http://in.pycon.org/2010/talks/14</a></li>
<li>Building Scalable Apps using Google App Engine, Pranav Prakash - <a href="http://in.pycon.org/2010/talks/5">http://in.pycon.org/2010/talks/5</a></li>
<li>Python Object Model and Introduction to meta programming, Sambasiva S - <a href="http://in.pycon.org/2010/talks/13">http://in.pycon.org/2010/talks/13</a></li>
<li>Test Driven Development, Gayatri Nittala, Madhubala Vasireddy - <a href="http://in.pycon.org/2010/talks/58">http://in.pycon.org/2010/talks/58</a></li>
<li>Python on Mobile Phones, N.Seshadri - <a href="http://in.pycon.org/2010/talks/34">http://in.pycon.org/2010/talks/34</a></li></ol>I personally could not attend all the talks mentioned above, as some were conflicting with others, but I am looking forward to watching their YouTube developer videos. I will be updating you all when the videos are available.<br />
<br />
Also, I had a chance to meet a lot of exiting and energrtic particles - Noufal Ibrahim, Anand Pillai, Abhishek Mishra (<a href="http://twitter.com/ideamonk">@ideamonk</a>) and many more.<br />
<br />
I am already looking forward for PyCon India 2011.<br />
<br />
And yeah, the food was simply awesome. I really liked it :-)<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0MSRIT MS RAMAIAH INSTITUTE OF TECHNOLOGY, MSR College Rd, M S Ramaiah Nagar, Mathikere, Bengaluru, Karnataka, India13.0292896 77.565540413.0083846 77.5363579 13.050194600000001 77.594722900000008tag:blogger.com,1999:blog-4955313250997333114.post-44998925851913769732010-09-09T11:45:00.002+05:302010-09-09T11:54:33.396+05:30Women- Complicated yet so Simple<a href="http://floating-sting.blogspot.com/2010/09/women-complicated-yet-so-simple.html?spref=fb">Life is a Wonderland: Women- Complicated yet so Simple...</a><br />
<br />
<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fcodecontrol.blogspot.com&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe><br />
<br />
I came across this simply amazing blog post about women. And usually, I am not into creating links for other's blog, but I was so tempted to do this. Probably because, I have seen many geeks, who are insensitive to women's feelings. Well, perhaps because we are more into explicit declaration of variables, rather than implicit ones. But, I seriously found it worth reading.<br />
<br />
The following song <b>Seekho Na</b> By <i>Shubha Mudgal</i> suits well to this point.<br />
<object width="480" height="385"><param name="movie" value="http://www.youtube-nocookie.com/v/l2mT5UJoUQc?fs=1&hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube-nocookie.com/v/l2mT5UJoUQc?fs=1&hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0tag:blogger.com,1999:blog-4955313250997333114.post-35217190994615438102010-08-27T17:22:00.002+05:302010-09-13T12:35:02.583+05:30Preparing for the talk in PyCon India 2010<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fcodecontrol.blogspot.com&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=light&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe><br />
So, my talk on '<a href="http://in.pycon.org/2010/talks/5-building-scalable-apps-using-google-app-engine" target="_blank">Building Scalable Apps using AppEngine</a>' in <a href="http://in.pycon.org">PyCon India 2010</a> has been approved. Although I have mentioned in the talk details as to how will my talk proceed, I would like to elaborate more on this.<br />
<br />
<h2>Building Scalable Apps using Google App Engine (Python) (45 minutes )</h2><br />
Now there are a couple of keywords that must be understood clearly. We will proceed one by one on it.<br />
<br />
<b>Building Scalable Apps</b><br />
<br />
So, we will be talking about building scalable applications. Building, as in starting from scratch. I will be presuming that the audience has some knowledge of web application development using a framework in any programming language. However acquaintance with Django, TurboGears is highly preferred. I am not expecting a lot of people to be used with Google App Engine, so I will be talking about Google App Engine in some details. What is Google App Engine, What is the purpose of it's existence, what are its chief features, how is it different from other web application development framework. Then we will be diving into the greatest advantage of Google App Engine over others - <a target="_blank" href="http://en.wikipedia.org/wiki/Scalability">Scalability</a>. In some of the talks that I have taken on App Engine, I have seen developers with a blurred view on Scalability. Most of the times, people are mistaken for what in true sense is scaling of application/infrastructure. Scaling is so important to understand because we have been living a world where most of the things we see around are non scalable. Look around yourself, we have the same roads that existed years ago but traffic has been growing exponentially. The cities are the same, railways are the same and so is everything else. It is getting day by day difficult to sustain the burden we put on our limited resources. But nature and we do scale to certain limits. You heart beats normally at 72 beats per minute. While you are running or playing, your body needs more oxygen and so your heart stats to pump at a faster rate in order to cope up with the increased demand. This is called as <i>scaling up</i>. On the other hand consider your brain. Whatever we see, sense, smell are stored in neurons as patterns. The things we need to recall more frequently are stored at much larger neural networks as compared to things that we need infrequently. This is called as <i>scaling out</i>. I hope this explains the difference between a non-scalable architecture, a scale-up architecture and a scale-out architecture. Having understood that, we must know that Google App Engine is designed to scale out. This means that Google App Engine is very efficient at performing tasks at parallel. And this is what we must keep in mind while developing applications on App Engine.<br />
<br />
<b>Using Google App Engine</b><br />
<br />
What differentiates App Engine is that it provides us the same infrastructure that is used by Google. Apart from many other things, it implies that we will be using <a target="_blank" href="http://en.wikipedia.org/wiki/BigTable">Google BigTable</a> for storing data. AppEngine provides a <a target="_blank" href="http://code.google.com/appengine/docs/python/datastore/">DataStore</a> environment that is build on top of BigTable. DataStore is not SQL, it is not FlatFile, it is not RDBMS. It is BigTable. And it has its own rules, own ways to read/write from datastore, own ways to maintain concurrency and all other things that should be there in a modern database system. And there are certain restrictions, certain flexibility onto it. In order to be able to develop a scalable application using App Engine, it is very necessary that we know what is datastore capable of and what is outside the limits of datastore. In any application, there are more reads as compared to writes, so we need to make sure that our read latency is very low. And here comes the concept of write once, read many. We take care of all the possible ways in which our data might be needed to read, and we store them at the write time only. We also need to take care in how we write data as there are again a lot of overheads involved in Index building, searching, and quering. Apart from all these things we can do with datastore, we have a high speed, in memory cache called as 'Memcache'. Used with a perfect blend of Datastore and memcache, the application can be quite fast and scalable.<br />
<br />
I am also working on gathering the measurable statistics for a scalable code and a non-scalable code. I hope to complete it by this weekend, and i will upload it here soon.<br />
<br />
Apart from this, I will be talking about a little technical background of <a href="http://www.oxylabs.com" target="_blank">Oxylabs Networks</a>, India's leading social apps and game developing company. How we began with MySQL, where MySQL lagged and why we chose AppEngine. How we cater to the needs of our more than 20 million player base with high scalability using App Engine.<br />
<br />
And if all of this excites you, creates a chill down your spine, we are looking forward to give you an opportunity to be a part of our team. Attend the talk for more details on this. <br />
<br />
<br />
<blockquote>PyCon India 2010,<br />
25th and 26th Sept, 2010<br />
M S Ramaiah Institute of Technology, Bangalore, India (<a href="http://goo.gl/maps/xO2e">map</a>)<br />
</blockquote><br />
<h2>Update</h2>Presentation Slide for PyCon India is shared here<br />
<br />
<iframe src="https://docs.google.com/present/embed?id=dcd3c5qw_181f96h85fm" frameborder="0" width="410" height="342"></iframe><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com4Gurgaon, Haryana 122001, India28.47 77.0328.319098 76.7965405 28.620901999999997 77.2634595tag:blogger.com,1999:blog-4955313250997333114.post-44300152544651819502010-08-14T13:48:00.000+05:302010-08-14T13:48:24.019+05:30Google Talk Invisible in Kopete<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fcodecontrol.blogspot.com&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=dark&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe><br />
About three years back, <a href="http://www.facebook.com/siddharth.upmanyu" title="Siddharth Upmanyu">Siddharth Upmanyu</a> found a way of getting invisible in Pidgin, which is demonstrated in my earlier blog post about <a href="http://codecontrol.blogspot.com/2007/10/invisible-mode-for-google-talk-xmpp.html">Invisible Mode for Google Talk</a>. The method is raw and simple, but looks geeky. Couple of days back, <a href="http://www.itech7.com/user/Nilesh">Nilesh Govindrajan</a> looked upon the idea and came up with a way to get this done, right inside Kopete instant messenger. He has demonstrated the method at this blog post on <a href="http://www.itech7.com/Internet/Kopete-Google-Talk-Invisible">iTech7</a>. This method looks great to me, although i haven't tried it myself.<br />
If you people find any bugs or suggestions, do respond here. Also, i would like to see more people coming up with new ways of tweaking other popular messengers like Google Talk, Pidgin, Telepathy to provide a reliable and better invisible mechanism in Google Talk (XMPP) protocol.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0tag:blogger.com,1999:blog-4955313250997333114.post-64829812769719438332010-08-02T23:56:00.001+05:302010-08-02T23:58:22.743+05:30My journey to Python<iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fcodecontrol.blogspot.com&layout=standard&show_faces=false&width=450&action=recommend&font=arial&colorscheme=light&height=35" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:35px;" allowTransparency="true"></iframe><br />
It starts even before one can think. So, i was in my first semester of my Bachelor's in Engineering and it was December of 2004. I was back at my home place, Jaipur for vacations. There i met my school friend <a href="http://www.facebook.com/profile.php?id=624174261" target="_blank" title="Himanshu Bhojwani">Himanshu Bhojwani</a>, who made some real cool robotics and other electronics gadgets and he showed me them. I was quite curious and asked him how did he do all that stuf. Technology and gadgets had always interested me. So, he told me about Parallel Port programming and about Visual Basic 6.0. He said that he did all this by connecting his electronics stuff to parallel port of his computer and use Visual Basic to get his stuff done. That evening while returning back home, i purchased <a href="http://books.google.com/books?id=hjEAE9BMaYQC" target="_blank" title="Parallel Port Programming">Parallel Port Programming</a>. My initial plan was to use C++ to do the programming part, but looking at the ease VB6.0 could make same programs in much less time, i decided to learn VB6.0. I also got a neighbor of mine, who was a Software Engineer at that time, to guide me out with VB. Well, as time went on, i forgot the Parallel port programming, and was making small applications in VB6.0. This language opened a complete new world for me. From C++ to VB 6.0.<br />
<br />
It dates back to the Jan of 2006. I was a fourth semester student student at Bharati Vidyapeeth University, Pune and those were the days of college tech-fest, Bharatiyam. I was in the college technical team, softwares division, supervised by <a target="_blank" title="Hemant Sahni" href="http://www.facebook.com/profile.php?id=509161165">Hemant Sahni</a>, who was technical head at that time. He is real good when it comes to application software development. I was initially working with the networking team, but when he found out that i had good VB skills, he asked me to help him, as he was using VB and SQL Server 2000 for this application he was designing. We were making a software to manage event scheduling, registrations, accounts and other details for the techfest. At the same time, another technical head of our University, <a href="http://www.facebook.com/siddharth.upmanyu" title="Siddharth Upmanyu" target="_blank">Siddharth Upmanyu</a> was working on a online c/c++ compiler. (Yea, the same which was hosted earlier on this blog). Somehow i was quite interested in this "online compiler" business. So, i went to him, and asked 'Sir, how have you made this cool stuff?'. And he gave me a detailed information about the underlying architecture and flow, most of which bounced off my brains. So i went ahead with another question, 'Which language you used to code all this stuff?'. 'Perl', came the reply. I had no clue there was a programming language "Perl". What kind of a name is this, i was thinking. And then i said, "Perl?". Judging my expressions, he said "its a language similar to python in some aspects". And i was even more confused. How can anyone name a language as "Python". Obviously i had no clue what Python was. But I decided not to make another fool of myself, so i said "Oh Python! Its a cool language" and hurried back home. LoL<br />
<br />
After i came back, i did a quick Google for Perl and Python, and decided to learn one of the language. Perl looked a little complex to me, so i went on for Python, and haven't looked back since then. I used Python for almost everything i had to do programatically!! I had always been in love with Python.<br />
<br />
After graduation, I joined <a href="http://harbinger-systems.com" title="Harbinger Systems" target="_blank">Harbinger-Systems</a>, Pune and was working on ColdFusion MX7 and SQl Server 2000. I used Python for some small programs and bot, which helped me in my day to day job. After that i am currently working with <a href="http://oxylabs.com" title="Oxylabs Networks" target="_blank">Oxylabs Networks</a>, where I am working in a language i love, Python :-)<br />
<br />
This is in short, my love story with Python, a tale of inspiration, aspiration and willingness.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com3tag:blogger.com,1999:blog-4955313250997333114.post-59013266833996118612010-07-17T17:08:00.002+05:302010-07-17T17:12:37.020+05:30Is it possible to get users email by hosting gadget on appspot?<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript"></script><iframe src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fcodecontrol.blogspot.com&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=dark&height=80" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:80px;" allowTransparency="true"></iframe><br />
Google App Engine provides its User API service, using which you can allow people with Google Accounts to easily access your application. It works in a manner much similar to Google's other login services. When you login, you are taken to a central page, common for all google services and then after you have authenticated yourself, you are redirected back to the service.<br />
<br />
Since Google uses a central page for login and logout and it is a cookie based authentication, it might trigger question - if someone is logged into gmail or orkut or other such google service is it possible to get their Google Accounts using App Engine's Users API? In order to do so, i deployed an application on App Engine, which would write "hello World!" on the canvas. This gadget XML is generated pragmatically and at the backend, there is a code which checks for a valid Google Account Holder.<br />
<br />
The corresponding code goes as follows:<br />
(There is no space at line 39, the space are mentioned here to go according to the syntax highlighter)<br />
<br />
<br />
<script class="brush: python" type="syntaxhighlighter">
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
class Users(db.Model):
user = db.UserProperty()
created = db.DateTimeProperty(auto_now = True)
updated = db.DateTimeProperty(auto_now_add = True)
class Log(db.Model):
msg = db.TextProperty()
created = db.DateTimeProperty(auto_now=True)
level = db.StringProperty(default='info')
class MainPage(webapp.RequestHandler):
def get(self):
user = users.get_current_user()
if user:
Users.get_or_insert('u_'+user.nickname(), user=user)
else:
Log(msg='User is None').put()
self.response.headers['Content-Type'] = "application/xml"
self.response.out.write("""<?xml version="1.0" encoding="UTF-8" ?>
<module>
<moduleprefs title="Test for Google Account"
description="if it works, all your base belongs to us :-)"
thumbnail=""
screenshot=""
author_email="pranny@gmail.com">
<require feature="opensocial-0.8"/>
</ModulePrefs>
<content type="html">
<![CDATA[
<!-- ALm6fM3dpUaqryfRfRvMMVVBRE43gLP+z/tDZzJMuHUa96fOs1R9ByHuKNVV5ZXmS/770b1pJor8eTk87F9rc1oIUTSIxX6+/al7bR0SdQ1X57cSztoAGKnMNs1S/hEXMtl0fGUIiOOm -->
<h1>
hello World!</h1>] ] >
</Content>
</Module>""")
application = webapp.WSGIApplication([('/', MainPage)], debug=True)
def main():
run_wsgi_app(application)
if __name__ == "__main__":
main()
]]>
</script><br />
<br />
Upon deploying the gadget on orkut and gmail, i found that everytime the gadget loads, i just see a new entry in the Log, saying "User is None".<br />
<br />
This means that you can authenticate only users for your own domain/application, not for all of the Google products services :-)<br />
<br />
The complete source code of the application can be found at its Google Code repository <a href="http://code.google.com/p/codecontrol-samples/source/browse/user-authentication/src/">http://code.google.com/p/codecontrol-samples/source/browse/user-authentication/src/</a>.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0tag:blogger.com,1999:blog-4955313250997333114.post-34130443009675730982010-07-11T02:31:00.003+05:302010-07-11T06:34:26.604+05:30Fedora 13 local service hangs while startup<script src="http://track.mybloglog.com/js/jsserv.php?mblID=2008072503160965" type="text/javascript">
</script><iframe allowtransparency="true" frameborder="0" scrolling="no" src="http://www.facebook.com/plugins/like.php?href=http%253A%252F%252Fcodecontrol.blogspot.com&layout=standard&show_faces=true&width=450&action=recommend&font=trebuchet%2Bms&colorscheme=dark&height=80" style="border: none; height: 80px; overflow: hidden; width: 450px;"></iframe><br />
Things go real funny at times. I downloaded the Fedora 13 i386 the day it was released. I purchased a DVD so that i can burn it and install and experience the few Fedora. I haven't upgraded it since Fedora 10. So, i was under the illusion that i had burned the DVD (which i ddn<br />
t) and while trying to install, it did nothing. So, i underwent this perception that my DVD-RW is bad and i must get it replaced. Today evening i found out that what i was trying was to install Fedora 13 from a blank DVD. LoL :-) Anyways, so i burned it down and tried installing.<br />
<br />
Unfortunately the DVD had some scratches so i decided to go to online repos for the RPMs and other things. I installed Fedora from the online repo at <a href="http://ftp.jaist.ac.jp/pub/Linux/Fedora/releases/13/Fedora/i386/os/">http://ftp.jaist.ac.jp/pub/Linux/Fedora/releases/13/Fedora/i386/os/</a> and i did a minimal installation with LXDE as desktop environment. After the installation, i found out that the service "local" could not be initiated, and the system just seems to hang at that moment. A quick google did not yield any significant result. So i did a bit of R&D on it.<br />
<br />
As per my online research, the "local" service in Fedora 13 is a service which kicks off your X-server. So, if the "local" service is not initiating, it means there is an issue with your X-server. In my case, the possible reason could be a conflict in LXDE Environment or something. The immediate workaround is to get into terminal by CTRL+ALT+F2. CTRL+ALT+F1 is to return to X-server. This key combination did some help in my case, by allowing me to get into the terminal, but nevertheless that did not solved my actual problem. <br />
<br />
So, i had to reinstall Fedora 13 using the online repo i mentioned earlier, and as i am writing this blog, the installation is going on. <strike>Hopefully, it will get over in next ten minutes and i can see my favorite GNOME environment again. Just in case, something weird happens, i will update this post.</strike> The installation went great and right now i am on Fedora 13, GNOME desktop.<br />
<br />
Also there are some interesting things you can do while Fedora 13 is being installed. Like reading <a href="http://xkcd.com/">XKCD</a> :-)<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/_5_28o544oU8/TDjhhBmjmOI/AAAAAAAALRE/BGceI3J4y5U/s1600/110720104281.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="http://2.bp.blogspot.com/_5_28o544oU8/TDjhhBmjmOI/AAAAAAAALRE/BGceI3J4y5U/s320/110720104281.jpg" width="320" /></a></div><br />
<br />
Also, if you have more information about the "local" service in Fedora 13, kindly inform me using the comment form below. I will update the post accordingly.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-4601002425609095";
/* 728x15, created 4/15/09 */
google_ad_slot = "4590454612";
google_ad_width = 728;
google_ad_height = 15;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Pranav Prakashhttp://www.blogger.com/profile/01441311228844889801noreply@blogger.com0