Karrigell/Home page

Home page

edit

For the moment we'll ignore the CDs and begin writing the home page

Create a new folder mycds in your Karrigell distribution, under the www folder. With your favorite text editor, save this text in a file called index.ks

def index():
    print "<h1>My CD collection</h1>"

In your browser ask localhost/mycds/index.ks/index

The extension "ks" (Karrigell Service) means that the script is a Python script where functions match urls : here, index.ks/index means that the function index() will send the data back to the browser

In fact

  • if you don't specify a function name, the index() function in the script will be run, so you could have asked localhost/mycds/index.ks
  • and if you don't even specify a script name in a folder, the script called index will be run. So all you have to type really is localhost/mycds

Notice the print statement in the index() function. In Karrigell scripts, the print statement sends the content to the browser ; by default, this content is supposed to be HTML code

Adding a page counter

edit

The page counter will print the number of times that the page has been seen. We'll use a file, counter.txt, to store the number of visits and increment it for each visit

def index():
    print "<h1>My CD collection</h1>"
    try:
        visits = int(open('counter.txt').read())
    except IOError:
        # first visit : the file does not exist
        visits = 0
    visits += 1
    out = open('counter.txt','w')
    out.write(str(visits))
    out.close()
    print "%s visits" %visits

Each time the page is reloaded, you will see the number of visits incremented by 1

Notice that the file is opened by the usual open() function ; relative paths are converted to absolute paths relatively to the script directory

A better page counter

edit

A page counter should not increment if the same user repeatedly reloads the same page. This requires a way to identify the browser which is sending the request. For this task, web programming provides session management, and Karrigell has a very simple implementation of this concept

Use the built-in Session() function

Karrigell scripts are run in a namespace prepared by the framework : among the names available in scripts, you have this Session() function, which returns an object specific to each individual client (it uses cookies). This object is an ordinary Python object, to which you can set any attribute you like

Here, for a new client, we will create an attribute user to the session object. If a client requests the home page and his session object already has this attribute, the counter will not be incremented

def index():
    print "<h1>My CD collection</h1>"
    try:
        visits = int(open('counter.txt').read())
    except IOError:
        # first visit : the file does not exist
        visits = 0
    if not hasattr(Session(),"user"):
        visits += 1
        out = open('counter.txt','w')
        out.write(str(visits))
        out.close()
        Session().user = None    # create attribute user
    print "%s visits" %visits

Including scripts

edit

This page counter could be used in other parts of the application, so it's better to put it in a separate file : save this part of the script in counter.py

try:
    visits = int(open('counter.txt').read())
except IOError:
    # first visit : the file does not exist
    visits = 0
    if not hasattr(Session(),"user"):
    visits += 1
    out = open('counter.txt','w')
    out.write(str(visits))
    out.close()
    Session().user = None   # create attribute user
print "%s visits" %visits

To include this script in the home page, change the script index.ks to this :

def index():
    print "<h1>My CD collection>/h1>"
    Include('counter.py')

Include() is another Karrigell built-in function : it takes a script url as argument, an inserts the result of this script's execution in the page

To make counter.py even more modular, the file name could be passed as argument to Include() :

def index():
    print "<h1>My CD collection>/h1>"
    Include('counter.py',counter_file='counter.txt')

The keyword argument to Include() will be in the execution namespace when counter.py is run. Rewrite counter.py with this parameter :

try:
    visits = int(open(counter_file).read())
except IOError:
    # first visit : the file does not exist
    visits = 0
if not hasattr(Session(),"user"):
    visits += 1
    out = open(counter_file,'w')
    out.write(str(visits))
    out.close()
    Session().user = None   # create attribute user
print "%s visits" %visits

Logging in/out

edit

Let's add the link for users to log in to the application

def index():
    print "<h1>My CD collection</h1>"
    print '<a href="login">Login</a><br>'
    Include('../counter.py',counter_file='counter.txt')

The link leads to the reference "login". It is a relative url to the base url, host/records/index.ks/index, so it resolves to host/records/index.ks/login, meaning that the link will invoke the execution of the function login() in the script index.ks

This function will generate an HTML form asking the user's login and password, and submitting these values to an authentication test

def login():
    print '<h1>Login</h1>'
    print '<form action="check_login" method="post">'
    print 'Login <input name="login"><br>'
    print 'Password <input type="password" name="passwd"><br>'
    print '<input type="submit" value="Ok">'
    print '</form>'

The attribute "action" for the form is check_login. Again, this means that the function check_login() in index.ks will be run

Unlike index() and login(), this function will take arguments : the submitted login and password. The arguments must be called like the names used in the input fields of the form : login and passwd. For the moment we'll use a very simple test to see if the user is allowed to log in

def check_login(login,passwd):
    if login=="john" and passwd=="doe":
        Session().user = login
        print "logged in"
    else:
        print "try again"

Inside this function, if the test succeeds, the attribute user of the session object is set to the user's login

For the moment, all we have is the result of the test. After the test, we should be automatically sent back to the home page. The HTTP protocol provides a redirection feature, and Karrigell implementation is once again very straightforward : a built-in exception called HTTP_REDIRECTION

Change the function check_login() like this :

def check_login(login,passwd):
    if login=="john" and passwd=="doe":
        Session().user = login
    raise HTTP_REDIRECTION,"index"

This means that after the function is run, the browser redirects to the specified url, here the one that matches the function index() in the script

We can now change the home page to welcome the authenticated user with his name, and allow him to logout :

def index():
    print "<h1>My CD collection</h1>"
    logged = hasattr(Session(),"user") and Session().user is not None
    if logged:
        print 'Logged in as %s<br>' %Session().user
        print '<a href="logout">Logout</a><br>'
    else:
        print '<a href="login">Login</a><br>'
    Include('../counter.py',counter_file='counter.txt')

The logout function will only set Session().user to None and redirect to the home page :

def logout():
    del Session().user
    raise HTTP_REDIRECTION,"index"

Summary

edit

The programming style used by Karrigell is based on namespace : in the scripts, instead of importing modules or using environment variables, a number of built-in names are available for the most common tasks. So far we have covered session management with the built-in name Session(), inclusion of scripts with Include() and HTTP redirection with the built-in exception HTTP_REDIRECTION

This makes programming with Karrigell extremely straightforward, thus easy to code and to maintain

While other formats are available (see the documentation for pure-Python scripts, CGI, Python Inside HTML, HTML Inside Python) I suggest to use "Karrigell Services" to group most of the application logic in one script ; peripheric modules can be imported or included

In the next section we will write the code to manage the CD database