Source

lukeplant_python / lukeplant_me_uk / bibleverses / web / cgi-bin / lib / bibleverses / views.py

import simplejson
from urllib import unquote
from bibleverses.webutils import HttpResponse, dispatch
from bibleverses import db

#### Error numbers ===

MSG_NO_EMAIL_ADDRESS = 1
MSG_NO_PASSWORD = 2
MSG_EMAIL_TAKEN = 3
MSG_EMAIL_NOT_FOUND = 4
MSG_PASSWORD_INCORRECT = 5

#### Helpers #####

class JsonResponse(HttpResponse):
    def __init__(self, obj):
        super(JsonResponse, self).__init__()
        self.content = simplejson.dumps(obj)
        self.content_type = "text/javascript"

def success(**kwargs):
    kwargs['success'] = True
    return JsonResponse(kwargs)

def failure(**kwargs):
    kwargs['success'] = False
    return JsonResponse(kwargs)

def authentication_error():
    return failure(error="Authentication error")

##### Decorators ######

def catch_all(func):
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception, e:
            return JsonResponse({"error": str(e)})
    return wrapper

def never_cache(func):
    def wrapper(*args, **kwargs):
        resp = func(*args, **kwargs)
        resp.headers['Cache-Control'] = "no-cache, no-store, private";
        return resp
    return wrapper

def require_user(func):
    """Adds user as attribute of request object if correct creds,
    otherwise returns authentication_error()"""
    def wrapper(request, *args, **kwargs):
        user = get_user_from_request(request)
        if user is None:
            return authentication_error()
        else:
            request.user = user
            return func(request, *args, **kwargs)
    return wrapper

##### Model ######

class User(object):
    def __init__(self, uid, email, password):
        self.id = uid
        self.email = email
        self.password = password

    def create(email, password):
        db.execute("INSERT INTO users values (NULL, %s, %s);", email, password)
    create = staticmethod(create)

    def retrieve(email):
        q = db.execute("SELECT id, email, password FROM users WHERE email = %s;", email)
        if len(q) > 0:
            return User(q[0][0], q[0][1], q[0][2])
        else:
            return None
    retrieve = staticmethod(retrieve)

    def check_password(self, password):
        return self.password == password

    def get_verses(self):
        return [row[0] for row in
                db.execute("SELECT verseref FROM verses WHERE userid = %s ORDER BY verseref;", self.id)]

    def add_verse(self, verseref):
        if len(db.execute("SELECT id FROM verses WHERE verseref = %s AND userid = %s;", verseref, self.id)) == 0:
            db.execute("INSERT INTO verses (id, userid, verseref) VALUES (NULL, %s, %s);", self.id, verseref)

    def remove_verse(self, verseref):
        db.execute("DELETE FROM verses WHERE verseref = %s AND userid = %s;", verseref, self.id)

##### View functions ####

def register(request):
    email = request.POST.get('email', '').strip()
    password = request.POST.get('password', '').strip()
    if len(email) == 0:
        retval = failure(validation=[MSG_NO_EMAIL_ADDRESS, "Email address was not supplied"])
    elif len(password) == 0:
        retval = failure(validation=[MSG_NO_PASSWORD, "Password not supplied"])
    else:
        if User.retrieve(email) is not None:
            retval = failure(validation=[MSG_EMAIL_TAKEN, "Email address already taken."])
        else:
            try:
                User.create(email, password)
                retval = success()
            except Exception, e:
                retval = failure(error=str(e))
    return retval

def login(request):
    email = request.POST.get('email', '').strip()
    password = request.POST.get('password', '').strip()

    user = User.retrieve(email)
    if user is not None:
        if user.check_password(password):
            return success()
        else:
            return failure(validation=[MSG_PASSWORD_INCORRECT, "Password is incorrect."])
    else:
        return failure(validation=[MSG_EMAIL_NOT_FOUND, "Email address not found."])

def get_user_from_request(request):
    email =  unquote(request.get_cookie('email', ''))
    password = unquote(request.get_cookie('password',''))
    u = User.retrieve(email)
    if u is not None and u.check_password(password):
        return u
    else:
        return None

def verses(request):
    return success(verselist=request.user.get_verses())
verses = require_user(verses)

def add_verse(request):
    verseref = request.POST.get('verse')
    if verseref is None or verseref == '':
        return failure()
    request.user.add_verse(verseref)
    return success(verse=verseref)
add_verse = require_user(add_verse)

def remove_verse(request):
    verseref = request.POST.get('verse')
    if verseref is None or verseref == '':
        return failure()
    request.user.remove_verse(verseref)
    return success(verse=verseref)
remove_verse = require_user(remove_verse)

##### Database creation/upgrade #####

def init_db(request):
    db.execute("""
CREATE TABLE users (
       id integer PRIMARY KEY,
       email text UNIQUE,
       password text
);

CREATE TABLE verses (
        id integer PRIMARY KEY,
        userid integer,
        verseref text,
        foreign key (userid) REFERENCES users(id)
)


""")
    return success()

def drop_db(request):
    db.execute("""
DROP TABLE users;
DROP TABLE verses;
DROP TABLE metainfo;
""")
    return success()

def upgrade1(request):
    try:
        res = db.execute("SELECT value FROM metainfo WHERE name='dbversion';");
    except:
        db.execute("""
CREATE TABLE metainfo (
        name text PRIMARY KEY,
        value text
)
""")
        db.execute("INSERT INTO metainfo (name, value) VALUES ('dbversion', '2');")
        import sha
        for (email, password) in db.execute("SELECT email, password FROM users;"):
            db.execute("UPDATE users SET password = %s WHERE email = %s;", sha.sha(email+password).hexdigest(), email)
    # Otherwise don't need to upgrade
    return success()

def debug(req):
    return HttpResponse(content=\
"""method:
%r

path:
%r

POST:
%r

GET:
%r

COOKIES:
%r

Environ:
%r

""" % (req.method, req.path, req.GET, req.POST, req.COOKIES, req.environ),
                        content_type="text/plain")


#############################################

urls = [
    ('^/register/$',    register),
    ('^/login/$',       login),
    ('^/verses/$',      verses),
    ('^/addverse/$',    add_verse),
    ('^/removeverse/$', remove_verse),
    ('^/debug/$',       debug),
#    ('^/initdb/$',      init_db),
#    ('^/dropdb/$',      drop_db),
#    ('^/upgrade1/$',    upgrade1),
]

urls = [(regex, catch_all(never_cache(f))) for regex, f in urls]

def main():
    dispatch(urls)