Django session message via middleware

Django session message is a wonderful app in django to provide messages for the users. http://code.google.com/p/django-session-messages/
It is used to provide system wide announcements and user specific messages for both Authenticated and Anonymous users in a django system.

The app works via the context processors. This is means, it is very easy to integrate with your application.

But for us at taggle, we had issues when we deployed our view caches. It created odd behaviors resulting in wrong messages posted to wrong people and some messages not getting posted as the pages got cached on the server. To get around this we decided to pass the session messages via cookies and then read it via javascript at the user end and then display it.

Here is the middleware which puts the session message in the cookies, and the front end javascript takes care of the rest.

from django.conf import settings
from django.utils.encoding import smart_str
import urllib

class SessionMessageMiddleware(object):

    def process_response(self, request, response):
        """
        Sticks the session_messages into the cookies for the current session.   
        Each message is seperated by ###.
        """

        try:
            domain = settings.COOKIE_DOMAIN_NAME
        except AttributeError:
            domain = None

        session_messages = LazyMessages(request)
        s_message = ""

        if 'session_messages' in request.COOKIES:
            if len(request.COOKIES['session_messages']) == 0:
                for m in session_messages:
                    s_message += urllib.quote(smart_str(m)) + "###"
            else:
                s_message = request.COOKIES['session_messages']
        else:
            for m in session_messages:
                s_message += urllib.quote(smart_str(m)) + "###"

        if domain != None:
            response.set_cookie("session_messages",s_message,domain=settings.COOKIE_DOMAIN_NAME)
            response.set_cookie("session_gaevents",s_gmessage,domain=settings.COOKIE_DOMAIN_NAME)
            response.set_cookie("session_mids",s_mids,domain=settings.COOKIE_DOMAIN_NAME)
        else:
            response.set_cookie("session_messages",s_message)
            response.set_cookie("session_gaevents",s_gmessage)
            response.set_cookie("session_mids",s_mids)

        return response

At the front end we use jQuery to dismantle the cookie and then display it. Here is the small snippet we use.

function showSessionMessages(){
    try{
    s_msg=$.cookie("session_messages");
    $.cookie("session_messages", null, { path : '/', expires: -5});
    if ( s_msg != null){
	s_msg = s_msg.replace(/^"/,"").replace(/"$/,"");
	s_msg = s_msg.replace(/###$/,"");
	msgs=s_msg.split("###");
	msgs=unique(msgs);
	if (msgs.length > 0 && msgs[0].length > 0){
	    $.each(msgs, function(i,l){
		show_message(l);
		});
	}
    }
    }
    catch(err){}
}

Mobile Template Loader – Django

The web is moving towards the mobile world faster and faster. The number of people using mobile browsers are keeping on increasing. The current emergence of the devices like the iPad and other Tablets are increasing the pace of adoption of mobile browsers.

You mostly might have seen m.abc.com or touch.xyz.com etc under certain domains where the mobile browsers are redirected. But, I wanted the mobile browser not to redirect to the a separate domain. I wanted to UI to change itself on the same domain for a mobile browser.

So here is a small snippet I wrote with the little help from here.

The set contains:

  • A new middleware to identify the browser and mark the mobile flag in thread_locals.
  • A template loader to take the mobile template in the folder mobile_templates if present. ie: if you specify a template directory and then add a new file under the directory mobile_templates in the app template directory, it picks up that template than the normal template.
The Middleware:
import re
try:
    from threading import local
except ImportError:
    from django.utils._threading_local import local

_thread_locals = local() 

 
reg_b = re.compile(r"android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|symbian|treo|up\\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino", re.I|re.M)
reg_v = re.compile(r"1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\\-(n|u)|c55\\/|capi|ccwa|cdm\\-|cell|chtm|cldc|cmd\\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\\-s|devi|dica|dmob|do(c|p)o|ds(12|\\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\\-|_)|g1 u|g560|gene|gf\\-5|g\\-mo|go(\\.w|od)|gr(ad|un)|haie|hcit|hd\\-(m|p|t)|hei\\-|hi(pt|ta)|hp( i|ip)|hs\\-c|ht(c(\\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\\-(20|go|ma)|i230|iac( |\\-|\\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\\/)|klon|kpt |kwc\\-|kyo(c|k)|le(no|xi)|lg( g|\\/(k|l|u)|50|54|e\\-|e\\/|\\-[a-w])|libw|lynx|m1\\-w|m3ga|m50\\/|ma(te|ui|xo)|mc(01|21|ca)|m\\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\\-2|po(ck|rt|se)|prox|psio|pt\\-g|qa\\-a|qc(07|12|21|32|60|\\-[2-7]|i\\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\\-|oo|p\\-)|sdk\\/|se(c(\\-|0|1)|47|mc|nd|ri)|sgh\\-|shar|sie(\\-|m)|sk\\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\\-|v\\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\\-|tdg\\-|tel(i|m)|tim\\-|t\\-mo|to(pl|sh)|ts(70|m\\-|m3|m5)|tx\\-9|up(\\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\\-|2|g)|yas\\-|your|zeto|zte\\-", re.I|re.M)

def get_mobile_request():
    return getattr(_thread_locals, 'mobile', None)

def set_mobile_request(mobile):
    setattr(_thread_locals,'mobile',mobile)
    
def unset_mobile_request():
    setattr(_thread_locals,'mobile',None)

class MobileMiddleware():
    def process_request(self, request):
        request.is_mobile = False
        if request.META.has_key('HTTP_USER_AGENT'):
            user_agent = request.META['HTTP_USER_AGENT']
            b = reg_b.search(user_agent)
            v = reg_v.search(user_agent[0:4])
            if b or v:
                request.is_mobile = True
        if request.is_mobile:
            set_mobile_request(request.is_mobile)
            
    def process_response(self, request, response):
        unset_mobile_request()
        return response
Now add the below template loaders to the beginning of your template loader list:
from django.conf import settings
from django.template import TemplateDoesNotExist
from django.utils._os import safe_join
from django.utils.encoding import smart_str
import os
from custom_middlware import get_mobile_request

def get_template_sources(template_name,template_dirs = None):
    """
        Returns the absolute path of the template names when appended to the template_dirs.
        If there is a browser is a mobile browser, the MobileMiddleware sets the mobile flag.
        This function checks for a directory named mobile_templates and finds the template.
    """
    mobile = get_mobile_request()
    #mobile = True
    if mobile is not None and mobile is True:
        try:
            for template_dir in template_dirs:
                parts = template_name.split("/")
                parts.insert(len(parts)-1,"mobile_templates")
                appDirName = smart_str(parts[0])
                parts.insert(0,"templates")
                if appDirName != "tagglecom":
                    parts.insert(0,appDirName)
                mobileTemplatePath = os.sep.join(smart_str(n) for n in parts)
                yield safe_join(template_dir, mobileTemplatePath)
        except UnicodeDecodeError,e:
            # The template dir name was a bytestring that wasn't valid UTF-8.
            raise
        except ValueError,e:
            # The joined path was located outside of this particular
            # template_dir (it might be inside another one, so this isn't
            # fatal).
            pass
        except Exception,e:
            pass   
            
    
    
def load_template_source(template_name, template_dirs=None):
    tried = []
    template_dirs = settings.TEMPLATE_DIRS
    for filepath in get_template_sources(template_name, template_dirs):
        try:
            return (open(filepath).read().decode(settings.FILE_CHARSET), filepath)
        except IOError,e:
            exp = "Error:%s, FilePath:%s" %(e,filepath)
            tried.append(exp)
    if tried:
        error_msg = "Tried %s" % tried
    else:
        error_msg = "Your TEMPLATE_DIRS setting is empty. Change it to point to at least one template directory."
    raise TemplateDoesNotExist, error_msg
load_template_source.is_usable = True
That’s it. This must help you load mobile templates if specified for the template. Else will load the normal template specified by your project.

One liner in python to get a uniquified list

In almost every list processing from various sources, we need to unique elements in the list. Here is an easy method to do so.

 

orig_list = [1,2,"a",3,3,2,"tux"]
unique_list = list(set(orig_list))

print unique_list
>>> ['a', 1, 2, 3, 'tux']

This doesn’t preserve the order of the list. If you want to preserve the order you may want to do something like this

unique_list = list(set(orig_list))
unique_list.sort(cmp = lambda x,y: cmp(orig_list.index(x), orig_list.index(y)))

Disable submit button on form submit

Lets consider this classic problem on the web.

Most people are not aware about the difference between double click and single click on the internet. This creates a lot of programming issues for the back-end guy.

<form method=POST>
<input type="text" id="text-input">
<input type="submit" id="submit-button">
</form>

Here is a small jquery snippet which disables the submit button on form submission. The id of the submit button would be submit-button.

$('#submit-button').click(
function(){
$('#submit-button').attr('disabled', true);
$('#submit-button').closest("form").submit();
});

This code solves most of the common problems.

Lets consider the case where there is a jquery form validation also in place.

Here you will need to disable the button only on completely validated form. The code for that goes below

$('#submit-button').click(
function(){
if($('#submit-button').closest("form").validate().form()){
$('#submit-button').closest("form").submit();
$('#submit-button').attr('disabled', true);
}});

Let me know if you find more interesting techniques.

Two column html with complete height of the browser.

Here is a small snippet that will help you achieve a two column layout with complete height of the browser.

<html>
<body style=”margin:0px;height:100%”> <!– key point in having browser height –>
<div style=”width:50%;background:#600;height:100%;float:left;”></div>
<div style=”width:50%;background:#444;height:100%;float:right;”></div>
</body>
</html>

Here is the result of the above code.

You might want to add the styles to seperate classes and code more neatly. ;)

5 steps to setup a domain

I recently had to go through a lotta useless confusion on buying and setting up my own domain. Well now for people who are still wondering why the tuxomaniac at wordpress is still active, I’m in the process of moving the blog to my new space. But the page rank of this blog is so high that I don’t feel like moving out. And the domain i purchased holds more than just one blog. So i’m not able to just redirect it to the blog. :(
Let’s get started.
  • Step 1: Decide on a domain name. as in example.com example.net etc etc..
I say go for .com has it has more penetration as in for the internet culture. If you can’t get hold of that go for .net, .info, .in or so for personal things and .org, .net, .in, .me for others.
  • Step 2: Getting it registered. Go to a domain registrar site and check if the one you wish to register is available and buy it.
Check out the registrars at http://en.wikipedia.org/wiki/List_of_top_ranking_domain_registrars . I highly suggest that you have an international credit card or a paypal account to do this. I did it with godaddy.com for example.
  • Step 3: You have 2 choices at this stage.
    • Either point it to a valid hosting space, where you have access to a nameserver or
    • Park your domain at the registrar. This means buying the domain and keeping it for future use.

Lets consider the first part where you intend to host it. Go to your hosting space cpanel if you have access to it and get the list of nameservers. You need atleast 2 of them you which have access to. A typical nameserver will look something like this

ns1.somename.org
ns2.somename.net etc etc.

Give that to the registrar at the required place in the settings of your domain.

  • Step 4: Add the newly purchased domain to your hosting.

Go to the cpanel of the hosting and add the newly purchased domain to the nameservers.

  • Step 5: Wait.

This means you have to wait for sometime so that all the DNS are updated with the domain information and starts pointing to your hosting space. It takes roughly 48-72 hrs.

Note: Also as I came to know that these days the www. part also comes along with the domain you purchase.

BTW, my newly registered domain is www.cyriacthomas.com

Invisible users – A pattern study

First of all, credits to Srijith for his XMPP hack script for finding invisible users.

I just thought abt this small study one day night and wrote a loop over it and I did expect some interesting results.

The script ran for 24hrs taking the list of invisible users every 5 minutes. I manually counted the no of online users at every hr or so for the entire day. :D (yeah.. i kinda have insomnia)

My buddy list has arnd 800 ppl with arnd 100-120 that go online frequently.

The survey ran from Friday Jan 22 2010 8:15pm to Saturday Jan 23 2010 8:15pm. I took these days for the study to get maxium no. of users for the survey.

It is interesting to see that during the peak times there is almost 1/2 the users invisble of the online users.

The only change to this is during the afternoon time. I guess it is coz ppl are of for an afternoon nap then. At that time u can see arnd equal no of visible and invisible users.

Another interesting fact I found was at ard 9pm we have a sharp dip in both the online and invisible users. Which should be because of the dinner time.

It is also worth to note that we have almost equal no of visible and invisible users during the night.. Yea we got a lotta owls here in India. On my statistics the visible users during the night arnd 7-10 are people from abroad. That means we have an equal no of user visible and invisible during the odd time of the night.

A final note: It is almost a year and a half tat google has launched and ppl are loving that feature more than the video.

As I mentioned earlier my study set included abt 100-120 regular online users and abt which 10-15 are from abroad. Most people on my list are at arnd the age of 18-26 and most of them are college goers or freshers in the industry.

Here is a detailed chart on the statistics.

Thanks to google charts and UNIX pipes.

Follow

Get every new post delivered to your Inbox.