Who Am I – A Facebook Game, Part 2

This is the second part of ‘Who Am I – A Facebook Game’. The first part can be found here.

Where Were We?

In the first part we introduced Who Am I – a Facebook game that helps you get a better understanding of your personality through friends feedback.
We talked a little about Python and Flask and then proceeded to create a simple hello world web application in Flask.

In This Post

The final code for this post can be found in the whoami repository on github.
In this post we are going to add the core game functionality into our application – we will create the player’s home page that shows her personality and the test pages that allows the player to define his personality and give feedback to her friends.
Initially I wanted to make the game a facebook game in this post but it got too long. So facebook will have to wait for the next post.

Building the Game

Here is where we left off in the first post:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def main():
    return home()

@app.route('/home')
def home():
    return 'You are an amazing person'

if __name__ == '__main__':
    app.run()

Running this script creates a web server that listens on port 5000 and shows a hello world message when you browse to localhost:5000.
A quick reminder for what this code does:

  1. The first line imports the flask library
  2. The next line creates the flask app
  3. The main() and show_personality() methods are called by Flask when the user browses to the url
    specified by the @app.route decorator. For example, when we browse to 127.0.0.1/, Flask will call the main() method and whatever this method returns, Flask will wrap in an HTTP response and return to the browser
  4. The ‘app.run()’ line starts the development server1 that is included with Flask which starts listening to connections from outside

By default, when you browse to the application, you get the personality page which can’t say much right now.
We will build the self test page along with its model and then build the personality page.

A Little Design

We want to finish the basic functionality of the 3 views of Who Am I – the user home page, the self test page and the friend test page.
Before we do that let’s think a little about how we want to do this and design a simple solution.

Login Page

A *very* simple login page so we know who is the current user. Note that once we connect to facebook we will not need this page because logins will be taken care by facebook.

Home Page

This is the page where we display the Johari window.
To show it, we need to load all test results, perform some analysis on them and render the HTML.
We also want here links to the self test and friend test pages.

Self Test View

This page will show a list of adjectives with check boxes. The player will select which adjectives describe her and submit the form.
Besides displaying the test form, we also need to take care of persisting the submitted form and redirecting to the home page when finished.

Friend Test View

This will be the same as the self-test view with the addition of an input where you can specify the player you are filling the test for.
We will implement this view using Jinja2’s template inheritance.

The Model

The model is that part of the application that represents its data.
To design the model we first need to think what we’re going to do with it.
We need to do several things:

  1. Get self test results from players and save them
  2. Get test results from players about their friends and save them
  3. To build the johari window, we need to get self test results for a player and results from his friends and combine them together to produce the window

We will design 2 model objects for this – a user object containing user details such as name, user name, password; and a test result object with the result submitter, result subject and list of selected adjectives.
It will look like this:

Model V1

Model V1

The results object contains the results and is linked to the user object by 2 properties – submitter and subject of the result. If the results are for a self test then the submitter and subject point to the same user.
Note that when I say object above, I mean object in the general sense of ‘something’ and not an instance of a Python class. We know how we want our model to behave but we still did not discuss the actual implementation.

Persistence

Most model objects are persisted in one way or another. You can have transient model objects for various tasks but usually you need to persist parts of your model like user data.
We are going to use SQLite in order to persist the test results. It’s a simple and easy database and has built in support in Python.
We are not going to persist user details because we want to make this into a facebook game. So the user details will be stored in Facebook and we will fetch them when needed.

Persisting Test Results

Let’s take another look at what we want to do with the test results.
We want to calculate the 3 relevant sections of the johari window – open, blind and hidden. We can think of 2 sets – the set of adjectives selected by the player and the set of adjectives selected by her friends. To produce the different window sections we need to do set operations. For example, the open section will contain adjectives that are in both sets (the intersection).
Our model should make it easy for us to store the submitted test form and to load the 2 sets of adjectives for a player.
Our database schema will contain a single table with the following columns:

  1. submitter_id – the id of the player that submitted the test
  2. subject_id – the id of the player which is the subject of the test
  3. adjective – the adjective selected by the submitter for the subject

The player ids are integers. Each player has a unique id. Later when we connect to facebook, this id will be the player’s facebook id. Now it will just be an arbitrary number.
Let’s say a player with id 1 submits her test. Our application will create a row in the database for each adjective the player selected. If the player selected ‘Modest’, ‘Mature’ and ‘Relaxed’ the table will look like this:

submitter_id subject_id adjective
1 1 Modest
1 1 Mature
1 1 Relaxed

We have some duplicate data here but it’s ok, it is just 2 ints and helps us keep a very simple schema. Simple is good. Disk space is cheap.

Configuration

A Flask application has some properties that can be configured. For example, the listenning port, debug mode and other.
We are interested in setting 2 properties:

  • DEBUG – turn on debug mode so problems are displayed in the browser and console
  • SECRET_KEY – define a secret key for the application. We need this in order to enable session management

Now we’ll see a neat Flask trick. Add the following lines above and below the line that creates the Flask app:

DEBUG = True
SECRET_KEY = 'top secret'

app = Flask(__name__)
app.config.from_object(__name__)

The call to from_object will make Flask read all uppercased attributes of the object passed to it and save them in the Flask config. You can set Flask attributes this way, such as the port to listen on, or the debug mode. When the app.config.from_object(__name__) line is executed, Flask will read the DEBUG and SECRET_KEY attributes from our module object. You can read more about the Flask config in its documentation.

Login Page

We want very simple login because it will be replaced later with facebook stuff.
The login page will ask the player for her id which is an integer. That’s it, no password. Once the player supplies his id, it will be used in all other views until the player logs out.
The login page is a regular HTML page. Create a folder called static under your application folder and inside that folder create a login.html file:

<!DOCTYPE html>
<html>
<head>
    <title>Who Am I - Login</title>
</head>
<body>
    <h2>Please login to Who Am I</h2>
    <form action="/home" method="POST">
        <fieldset>
            <legend>Login Details</legend>
            <label for="user_id">Player ID</label><input type="text" id="user_id" name="user_id">
            <br>
            Use an integer as the player id
            <br>
            <input type="submit">
        </fieldset>
    </form>
</body>
</html>

You can it is a form that posts to the /home url. When the player clicks submit, he will be taken to his home page.
Now for the code. Add the following methods to whoami.py (replace the existing main() and home() functions:

from flask import Flask, request, url_for, session

@app.route('/')
def main():
    return home()


@app.route('/login')
def login():
    return redirect(url_for('static', filename='login.html'))


@app.route('/logout')
def logout():
    session['user_id'] = None
    return login()


@app.route('/home', methods=['POST', 'GET'])
def home():
    if request.method == 'POST':
        session['user_id'] = int(request.form['user_id'])
    elif not session.get('user_id'):
        return redirect(url_for('login'))
    return 'You are an amazing person, number %s. <a href="/logout">logout</a>' % session['user_id']

When the user first browses to the game, Flask will run the main() function which calls home().
home() checks if the user is logged in and if not it redirects her to the login page.
Let’s take a closer look at the first few lines of home():

@app.route('/home', methods=['POST', 'GET'])
def home():
    if request.method == 'POST':
        session['user_id'] = int(request.form['user_id'])

Notice that the decorator @app.route() now contains an extra parameter methods. This is how you tell Flask which HTTP methods this function can handle. If you ommit this parameter, the function can handle only GET requests. Here we are also handling POST requests.
request is a variable in the flask module. It contains the current HTTP request. request.method contains the HTTP method of the request. Note that request local to the current request context. What does that mean? Web servers are multi threaded and a lot of time requests are handled concurrently. If 2 players are sending a request to the game at the same time, you can get 2 methods in whoami.py executed concurrently in different threads, each one handling a request for a different player. The request variable will always contain the right request for the currently running code. This is magic performed for us by Flask. To learn more about it you can read about context local in Flask’s documentation.
If the request was done using POST it means that we got here from the login page. We get the value of the user_id from the form data that was posted with the request by using request.form['user_id']. As you probably guessed, request.form is a dictionary containing the posted form values in case a form was posted with the request.
We store the user_id in the session which also has dictionary functionality so it is very straight forward. The value in session['user_id'] will be available for all subsequent requests by the same user until we delete it manually. Read more about Flask sessions here.

    elif not session.get('user_id'):
        return redirect(url_for('login'))

If somehow we got here but the user_id is not set, we redirect to the login page.
The redirect function returns a redirect (301) response that causes the browser to redirect to a different url. To specify the url we use url_for(), another Flask method which takes a name of a **function** as a parameter and returns its path. This is useful because we can change urls of the application without changing the code.
Now it is also easy to understand what the login() function does. It redirects to the login.html file in the static folder.

The Home Page v1

We’ll build the home page in several steps. Right now it will only contain a link to the self-test page and a logout link.
Add a home.html file to the static folder:

<!DOCTYPE html>
<html>
<head>
    <title>Who Am I - Home</title>
</head>
<body>
    <h1>Home</h1>
    <ul>
        <li><a href="/self-test">Self test</a></li>
        <li><a href="/logout">Log out</a></li>
    </ul>
</body>
</html>

Change the return value of home() to redirect to the new file.

The Self-Test

The self test will include a list of 10 adjectives. The player will check the adjectives that best describe her and the application will save her selection.

For this we need the following:

  1. An HTML page with a form of adjectives
  2. A method that shows the self-test page and a method that accepts the submitted form
  3. A database to store the form data

Self-Test HTML Page

The self-test HTML page is simple. It contains a form with 10 checkboxes and a submit button. It should look like this:
The Self-Test page

Yes, I know, it is UGLY. But we’re not dealing here with styling (we’ll do it later, don’t worry).

How do we create this page?

One option is to write the entire HTML for this page in a file and display it when the user asks for it. But this form contains the list of adjectives. If we ever want to change the list of adjectives we will also have to change the HTML page. And this is bad, very bad – we’re mixing our model and view in ways that make it hard to change things. It will be very useful if we could define the list of adjectives in one place and use it everywhere else.

The other option is to define the list of adjectives in our main python script and iterate over the list to build the HTML. But then we will have to build the entire HTML in code. Which is really not fun and a lot of overhead because the HTML has a lot of static content like the tags, the tags, etc.

So we’re going to combine the 2 options by using a template.

Introducing Jinja2

Jinja2 is a template engine which is bundled inside Flask. It is powerful but very simple. You can probably grasp 90% of it in less than a day.

A Jinja2 template looks like this:

<html>
<body>
    <h1>Hello {{ name }}!</h1>
</body>
</html>

As you can see it is very similar to HTML. The only difference is this {{ name }} part inside the header. This is the dynamic part which gets filled in by template engine during run time.
To use this template you will need the following code:

@app.route('/')
def say_hello():
    return render_template('hello.html', name='daramasala')

This is what happens when you run a Flask app with the above code and someone browses to the root url (/):

  1. Flask calls the say_hello() method (because it is decorated with @app.route('/'))
  2. The method calls Flask’s render_template() method passing 2 parameters:
    • 'hello.html' – the name of the template. By default, Flask looks for the file in a /templates folder.
    • name='daramasala' – a named parameter which is used in dynamic parts of the template.
  3. render_template() reads hello.html. Whenever it encounter something surrounded with {{ }} or {% %} it knows it is dynamic content and gets to work. In our case, it knows to replace name with daramasala. It then returns the results.
  4. The Flask framework wraps the rendered template (which is basically a string) in an HTTP response and returns it.

This is the essence of templates and mixing static and dynamic content.
You can read the Jinja2’s documentation or just follow along this post as I introduce other Jinja2 features2.

The Quiz Template

To create the quiz page we will first define the adjectives we want to use.
Somewhere at the beginning of our script, add the following code:

adjectives = ['Judgemental', 'Accepting',
              'Morose', 'Cheerful',
              'Nervous', 'Relaxed',
              'Childish', 'Mature',
              'Vain', 'Modest']

This defines the list of adjectives. Now for the template. Create a self-test.html file in a templates folder under your project folder and fill it with this stuff:

<html>
<head>
    <title>Who Am I - Self Test</title>
</head>
<body>
    <h1>Self Test</h1>
    <p>
        Please select the adjectives that best describe you:
    </p>
    <form action='/self-test' method='POST'>
        Submitter Id: {{ session.user_id }}
        <br>
        {% for adj in adjectives %}
        <input id="a_{{ adj }}" name="a_{{ adj }}" type="checkbox"/>
        <label for="a_{{ adj }}">{{ adj }}</label>
        <br>
        {% endfor %}
        <p>
            <input type="submit"/>
        </p>
    </form>
</body>
</html>

The static parts of this template are very simple – an HTML page with a form and a submit button elements.
The following line takes care of displaying the submitter id which is the current user:

Submitter Id: {{ session.user_id }}

You already know that Jinja2 outputs the value of the expression between double curely braces – {{ }}. But look at the magic here – we can access Flask’s session object directly in the Jinja2 template. Very nice, Flask!
Now let’s take a closer look at the loop construct in the middle:

{% for adj in adjectives %}
... stuff ...
{% endfor %}

This is a for loop that executes everything between the for and endfor. Notice how the template syntax is marked by {% and %}. If you’ve ever written PHP or JSP this is very similar to ‘<?php..?>’ and <%..%>. It tells Jinja2 that there is a piece of code between those brackets. And the for loop syntax itself is the same as in Python. Here we are iterating over the items in the adjectives list. Jinja2 will render everything inside the loop once for every item in adjectives. In each iteration, adj will hold the value of the current item. To use this value inside the loop body we can use {{ adj }} which outputs the value of adj. In our loop we have the following code:

<input id="a_{{ adj }}" type="checkbox">
<label for="a_{{ adj }}">{{ adj }}</label/>

So if adj == 'Cheerful', Jinja2 will output the above HTML, replacing every occurrence of {{ adj }} to produce:

<input id="a_Cheerful" type="checkbox" />
<label for="a_Cheerful">Cheerful</label>

The end result will be a form with a checkbox for each adjective we defined in adjectives. Note that here we use the same string for the label of the adjective and the checkbox id. It will work and it is fine for now but this is not a recommended approach because it mixes implementation details (the checkbox id) with interface elements (the label of the checkbox). The ‘a_’ prefix is used in the id to identify this as an adjective id. You will understand this when we work on the code to save the form data.

The Difference Between {% and {{

What exactly is the difference between using {% %} and {{ }}?
The simple answer is that Jinja2 executes code inside {% %} but evaluates and prints expressions inside {{ }}.
Now let’s try to imagine how this works. It is not necessarily an exact description of how Jinja2 is implemented but the basics are probably the same.
Think of the template interpreter as a system composed from an empty result string, the template string and the interpreter process.
The interpreter reads the template string. As long as it doesn’t encounter special template characters ({{ or {%), it just appends everything it reads to the results string.
When the interpreter encounters {{ it evaluates everything until the closing }} and appends it to the result string. For example, in our case, the interpreter will evaluate adj to be ‘Cheerful’ and write that to the result string.
When the interpreter encounters {% it executes the code inside of it which causes an internal state change in the interpreter but it does not affect the result string yet.
For example, when it read {% for adj in adjectives %}, the interpreter needs to remember 3 things:

  1. We started a loop over the adjectives list
  2. The loop started in position X in the template source
  3. The loop variable is called adj and its value is the first item in adjectives

The interpreter continues to read the template source and output it to the result string. When it encounters {{ adj }} it replaces it with the current value of adj which is the first item in the list.
When the interpreter encounters the closing {% endfor %} it jumps back to X in the template source, updates adj to be the second item in adjectives and repeats the process until we iterated over all items in adjectives.3

Rendering the template

Those were 60 seconds on Jinja2 and template interpreters.
Now make sure you have the template file under a templates folder in your project folder. Add the following function to whoami.py:

@app.route('/self-test')
def self_test():
    return render_template('self-test.html', adjectives=adjectives)

This should be self-explanatory by now but let’s go over it anyway:
@app.route('/self-test') – with this decorator, Flask will call this method when someone browses to http://localhost:5000/self-test.
render_template('self-test.html', adjectives=adjectives) will render the self-test.html using the adjectives in adjectives. Notice that the first adjectives (the function named parameter) is the adjectives used in the template and the second one is the adjectives variable we defined in the code.
Run your app and browse to http://localhost:5000/self-test. You should see the self-test page in all its glory (yes, yes, we talked about it before, it’s ugly, don’t worry. We’ll get to that when we talk about CSS and then you will be sorry you asked :-))
Of course clicking submit does nothing interesting because we still need to implement the part that saves the self-test results.
Now let’s take care of saving the data.
Create a new file in your project folder called schema.sql:

drop table if exists results;
create table results (
    submitter_id integer not null,
    subject_id integer not null,
    adjective text not null
);

-- this index is used for fetch queries
drop index if exists results_ids_idx;
create index results_ids_idx on results (
    subject_id,
    submitter_id
);


-- this index verifies there are no double rows in the db
drop index if exists results_unique_idx;
create unique index results_unique_idx on results (
    subject_id,
    submitter_id,
    adjective
);

Notice the 2 indexes – the first one will be used for the query in home page. The second will be used when submitting results to make sure we don’t put duplicate entries in the database.
Add the following line to the top of whoami.py:

DATABASE = '/tmp/whoami.db'

This defines the path to the sqlite database.
Now we’ll see a neat Flask trick. Add the following line below the line that creates the Flask app:

app = Flask(__name__)
app.config.from_object(__name__)

The call to from_object will make Flask read all uppercased attributes of the object passed to it and save them in the Flask config. You can set Flask attributes this way, such as the port to listen on, or the debug mode. You can also set your own attributes like we did here. You can read more about the Flask config in its documentation.
To open a connection to the database, we’ll create a new function and another function to create the database. Add these functions in your whoami.py file:

def connect_db():
    return sqlite3.connect(app.config['DATABASE'])


def init_db():
    with closing(connect_db()) as db:
        with app.open_resource('schema.sql', mode="r") as f:
            db.cursor().executescript(f.read())
        db.commit()

You will also need to import the sqlite3 package and closing from contextlib (from contextlib import closing).
connect_db() uses the sqlite3 package to open a connection to an sqlite database which is a single file on the disk.
init_db() uses the Flask app object to find the schema.sql file, and passes it’s contents to the db connection to execute it.
I took this code from Flask’s tutorial, it is simple and straightforward.
To run this, you can open the python interpreter, import whoami and call whoami.init_db():

Python 2.7.1 (r271:86832, Jul 31 2011, 19:30:53)
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import whoami
>>> whoami.init_db()
>>> exit()

A nice exercise would be to write a short script to init the db from the command line. We are going to make changes to the schema during development and it will be easier if we have a script to recreate the database. I leave this for you.
One small hint – to write a python script that is runnable from the command line, the first line of your script should be:

#! /usr/bin/env python<br />

To persist the self test results we create 2 new methods:

def update_results(submitter_id, subject_id):
    with closing(connect_db()) as db:
        db.execute('delete from results where submitter_id=? and subject_id=?', [submitter_id, subject_id])
        adjs = [adj[2:] for adj in request.form if adj.startswith('a_')]
        for adj in adjs:
            db.execute('insert into results (submitter_id, subject_id, adjective) values (?, ?, ?)',
                       [submitter_id, subject_id, adj])
        db.commit()


@app.route('/self-test', methods=['POST'])
def save_self_test():
    submitter_id = session['user_id']
    update_results(submitter_id, submitter_id)
    return redirect(url_for('home'))

There are no new Flask concepts in action here so you should be able to understand how this code work.
When we post the form, Flask will call save_self_test() because it is decorated with the right path and method.
save_self_test() gets the current user id from the session object and calls update_results().
update_results accepts 2 parameters – the submitter id and the subject id. In our case they are the same because we are saving the self test. Later when we save the test for a friend the ids will be different.
We use a prepared statement to delete the results of the last self-test the player took.
To get which adjectives the user selected we use a list comprehension on the form data. We extract all inputs with id starting with ‘a_’. Remember we prefixed the adjective checkboxes in our form with ‘_a’? This is why – now even if we have other inputs in the form, our list will only contain the adjective inputs.
We then insert each adjective to the database (in a real application you would probably use batch insertion to speed things up).
One more touch up before we finish the self-test page. Let’s say the player takes the self-test a second time. It will be nice if the adjectives she selected last time were already checked in the form.
Very easy to do. We just need to load the self-test results before displaying the self-test template and make sure we check the correct checkboxes.
To load the results we add a new load_results() function and change self_test() a little:

def load_results(submitter_id, subject_id):
    with closing(connect_db()) as db:
        cur = db.execute('select adjective from results where submitter_id=? and subject_id=?', [submitter_id, subject_id])
        return [row[0] for row in cur.fetchall()]


@app.route('/self-test')
def self_test():
    selected = load_results(session['user_id'], session['user_id'])
    return render_template('self-test.html', adjectives=adjectives, selected=selected)

Nothing fancy here – we load the currently selected adjectives into a list and pass it to the render_temaplate function.
To use it in the self-test template, update the checkbox element in self-test.html:

        <input id="a_{{ adj }}" name="a_{{ adj }}" type="checkbox" {{ "checked" if adj in selected }}/>

We are telling Jinja2 to output the string checked if adj is in selected.
Try it out – log in, take the self test and then take it again to see if your results were persisted.

Home Page v2

Let’s make home page into a template so we can get rid of the hardcoded urls. Delete the home.html file from the static folder and create a file with the same name in the template folder:

<!DOCTYPE html>
<html>
<head>
    <title>Who Am I - Home</title>
</head>
<body>
    <h1>Hello Number {{ session.user_id }}</h1>
    You are an amazing person
    <ul>
        <li><a href="{{ url_for('self_test') }}">Self test</a></li>
        <li><a href="{{ url_for('logout') }}">Log out</a></li>
    </ul>
</body>
</html>

I hope you can change the code of the home() function on your own to render the template.

The Friend Test Page

The friend test page is almost identical to the self-test page. The only difference is that it includes another input box where you enter the id of the friend you’re filling the test for.
Hey, this sounds a lot like class inheritance in programming languages. Well, guess what – Jinja2 has template inheritance.
To inherit from a template you need to define blocks in the parent template using {% block block-name %}...{% endblock %}.
In the child template, you define which template you are inheriting from using {% extends template-name %} and override the blocks you defined in the parent folder.
In our case, we want to change the first part of the form, where we display the id of the current submitter. We want to display a subject id input box below that.
We define a header block and a users block in self-test.html:

<html>
<head>
    <title>Who Am I</title>
</head>
<body>
    <h1>{% block header %}Self Test{% endblock %}</h1>
    <p>
        Please select the adjectives that best describe you:
    </p>
    <form action='{{ action }}' method='POST'>
        {% block users %}
        Submitter Id: {{ session.user_id }}
        <br>
        {% endblock %}
        {% for adj in adjectives %}
...

Note that we also changed the form’s action to {{ action }}. This is because we want to trigger a different action when the form is rendered as part of the friend-test template. Don’t forget to pass the new action parameter to the call to render_template() in self_test():

@app.route('/self-test')
def self_test():
    selected = load_results(session['user_id'], session['user_id'])
    return render_template('self-test.html', action=url_for('save_self_test'), adjectives=adjectives, selected=selected)

And we create a new template – friend-test.html which inherits from self-test.html and overrides the blocks it defines:

{% extends "self-test.html" %}
{% block header %}Friend Test{% endblock %}
{% block users %}
    {{ super() }}
    <label for="subject_id">Subject</label>
    <select id="subject_id" name="subject_id">
        {% for id in friend_ids if not id == session.user_id %}
            <option value="{{ id }}">{{ id }}</option>
        {% endfor %}
    </select>
    <br>
{% endblock %}

The first line tells Jinja2 that this template extends self-test.html. Jinja2 will render self-test.html until it gets to a block. Then it will check if that block is overriden in the new template and if it is – render it instead.
Note the call to {{ super() }} in the 4th line. It renders the original block, much like a call to super in overriden function in a class definition.
The template then display a select box with ids of all the players in the system except the current player.
The code the renders this template and persists the results:

def get_friend_ids():
    with closing(connect_db()) as db:
        cur = db.execute('select distinct submitter_id from results')
        return [row[0] for row in cur.fetchall()]


@app.route('/friend-test')
def friend_test():
    friend_ids = get_friend_ids()
    return render_template('friend-test.html', action=url_for('save_friend_test'), adjectives=adjectives, friend_ids=friend_ids)


@app.route('/friend-test', methods=['POST'])
def save_friend_test():
    submitter_id = session['user_id']
    subject_id = request.form['subject_id']
    update_results(submitter_id, subject_id)
    return redirect(url_for('home'))

We load the ids from the database and pass them to the call to render_template.
To persist the results of the test we use the update_results() function we defined earlier.

Home Page v3

Now that we can collect results from player and friends, we can add the johari window to the home page.
We will load 2 sets from the db – 1 set of the adjectives the player selected. The other set of the adjectives her friends selected for her.
Then calculating the different window sections will just be a matter of set operations. Luckily python supports easy set operations:

def calc_window():
    user_id = session['user_id']
    with closing(connect_db()) as db:
        cur = db.execute('''
            select adjective, count(adjective)
            from results
            where subject_id=?
            group by adjective
            ''', [user_id])
        adj_counts = {row[0]: row[1] for row in cur.fetchall()}
        cur = db.execute('''
            select distinct adjective
            from results
            where subject_id=? and submitter_id<>subject_id
            group by adjective
            ''', [user_id])
        friend_select = {row[0] for row in cur.fetchall()}
        cur = db.execute('''
            select distinct adjective
            from results
            where subject_id=? and submitter_id=subject_id
            group by adjective
            ''', [user_id])
        player_select = {row[0] for row in cur.fetchall()}
        hidden = player_select - friend_select
        open = player_select & friend_select
        blind = friend_select - player_select
        window = dict()
        count = lambda a: dict(adj=a, count=adj_counts[a])
        window['hidden'] = [count(adj) for adj in hidden]
        window['open'] = [count(adj) for adj in open]
        window['blind'] = [count(adj) for adj in blind]

        return window


@app.route('/home', methods=['POST', 'GET'])
def home():
    if request.method == 'POST':
        session['user_id'] = int(request.form['user_id'])
    elif not session.get('user_id'):
        return redirect(url_for('login'))
    window = calc_window()
    return render_template('home.html', window=window)

Let’s talk about calc_window(). Thank goodness we’re using Python otherwise it would have taken a lot more lines of code to write this. All in all, I think it came out pretty straight forward.
The function begins by loading the count of all adjectives for the player, regardless of who chose them. These are stored in an adj-->count dictionary called adj_counts.
It then proceeds to load the adjectives that the player chose and keeps them in the player_select list.
Next are the ajectives selected by the player’s friend which are kept in friend_select.
Now we juggle these sets a little. The hidden section are adjectives selected by the player but not by friends. So we subtract friend_select from player_select. The blind section are adjectives selected by friends but not the player. We calculate it similar to hidden only we reverse the subtraction. To calculate the open section, we find the intesection of player_select and friend_select which are the adjectives selected by both the player and her friends.
The return value is a dictionary of the sections. Each section is a list of dicts, each one containing the adjective and its count.
This is used in the home.html template as follows:

<!DOCTYPE html>
<html>
<head>
    <title>Who Am I - Home</title>
</head>
<body>
    <h1>Hello Number {{ session.user_id }}</h1>
    <h3>Your Window:</h3>
    <table border="1">
        <tr>
            <td>
                <p>Common Knowledge - What you and your friends know about you</p>
                {% for i in window.open %}
                {{ i.adj }}({{ i.count }})&nbsp;
                {% endfor %}
            </td>
            <td>
                <p>Blind - What your friends know about you that you don't</p>
                {% for i in window.blind %}
                {{ i.adj }}({{ i.count }})&nbsp;
                {% endfor %}
            </td>
        </tr>
        <tr>
            <td>
                <p>Hidden - What your friends don't know about you</p>
                {% for i in window.hidden %}
                {{ i.adj }}({{ i.count }})&nbsp;
                {% endfor %}
            </td>
            <td>
                <p>Unknown - What else you've got there, ha?</p>
                &nbsp;
            </td>
        </tr>
    </table>
    <h3>Other Actions:</h3>
    <ul>
        <li><a href="{{ url_for('self_test') }}">Self test</a></li>
        <li><a href="{{ url_for('friend_test') }}">Friend test</a></li>
        <li><a href="{{ url_for('logout') }}">Log out</a></li>
    </ul>
</body>
</html>

We build an (ugly as usual) table and fill in the sections with the relevant adjectives in their counts.

Summary

This is it! We have the basic game working. You can login, fill your own self-test and a test for a friend or two. Then you can logout and login as one of the friends and check out the johari window.

In The Next Post…

In the next part we are going to make this game into a Facebook game by registering it on Facebook. We will open a developer account and register our application on Facebook. In order for this to work, we need an external IP address for our application that Facebook can access.
If you don’t have access to a hosting service, fear not! We will use ngrok to expose our application to the world even though it is running on our localhost (yes, it will work even if you’re behind a NAT).
Instead of logging in, players will have to authorize the game. Instead of selecting friend ids, players will select from a list of their friends. And of course we will have invites. What is a facebook game without pesky invites, no?
You can download the code for this part from the whoami repository on github.
Continue to part 3.


  1. If you are new to web development, here is a crash course in web applications:
    Web applications run in a web server.
    The web server is in charge of listening for incoming HTTP requests from outside. When such a request arrives, the web server handles it and returns a response.
    So how does the web server handle a request? The basic example is a request for a specific HTML file. In this case, the server reads the HTML file (assuming it can find it), wraps it in an HTTP response and sends it back. But what happens if the request is for a python (or PHP or any other) script? The server needs to execute the script and send the result back. How does the server (which is a general application, not written to service python, php or any other scripting language in particular) executes the script? The answer depends on the server but basically you need to install a special library in the web server and tell the server which urls it needs to defer to this library.
    For example, all urls of the form http://myhost.com/app/.... Now when a request comes in, the web server, according to the url of the request, will send it to the specified library which will handle it.
    If you want to know more, you can read about web server, wsgi which is the wsgi library that Flask uses. Or wait for me to publish another post explaining everything in detail 🙂 
  2. If you find it a bit confusing that Jinja2’s documentation is in a different site than Flask, remember the Flask is a combination of Jinja2, Werkzeug and Flask. Werkzeug is the interface between the web server and the Flask application. The Flask code defines the Flask application class which is in charge of getting HTTP requests from Werkzeug, passing them to the user code and returning the HTTP responses to Werkzeug. Jinja2 is used as the template engine so whenever you call render_template() Flask calls into the Jinja2 engine. 
  3. There are many ways in this can be implemented. This is just a way of thinking about what going on inside. When I work with a library or a tool I like having a notion of how it works. It makes learning it easier. Besides, it’s fun to imagine
Advertisements

4 thoughts on “Who Am I – A Facebook Game, Part 2

  1. Great post! I am a beginner at web development and have been following your whoami posts for a while . They’re really awesome. I only have one suggestion so far: can we use list comprehension when calculating Johari window? For instance:

    friend_select = [row[0] for row in cur.fetchall()]
    player_select = [row[0] for row in cur.fetchall()]
    hidden_adj = [adj for adj in player_select if adj not in friend_select]
    open_adj = [adj for adj in player_select if adj in friend_select]
    blind_adj = [adj for adj in friend_select if adj not in player_select]
    window = dict()
    window['hidden'] = {key: value for (key, value) in adj_counts.items() if key in hidden_adj}
    window['open'] = {key: value for (key, value) in adj_counts.items() if key in open_adj}
    window['blind'] = {key: value for (key, value) in adj_counts.items() if key in blind_adj}

    I also made corresponding changes in templates and it worked. I think codes look more concise this way.

  2. Great post so far. I was following along but I encountered some errors at ‘Rendering the template’. I did some troubleshooting until I got stumped, so I downloaded the source from github. When I run:

    whoami-master/post2/whoami.py

    I get:

    KeyError: ‘user_id’

    referencing line 102 in self_test.

    Do you know what’s going on?
    Thanks.

    • Jared,
      Looks like the user_id parameter was not registered in the session or the session is not available. Maybe your browser is configured to block cookies? Flask sessions are implemented using cookies, see http://flask.pocoo.org/docs/quickstart/#sessions.
      About those ‘Rendering the template’ errors you’ve got, I urge you to try and debug them. Flask outputs really good (actually werkzeug does it) and the jinja2 engine outputs useful troubleshooting message when it encounters an error.
      Thanks for following along, enjoy!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s