Skip to main content

Making a quiz web app with python and flask

Edit:When you are creating a web app with html templates, then you will have to save the html file in templates folder in the Current Working Directory(CWD). If you save the file in the CWD directly you will get a TemplateNotFound error. Thank you Udhay for pointing it out.
 
In this post we will create a quiz website using python. I will be using the flask framework. After reading this tutorial you will learn form submission, flask templates, python code in flask templates, shuffling the questions and options with the random module and few others. 

Please note that this tutorial is not big as it seems to be. Some of the code has been rewritten to maintain consistency and also font size is somewhat big so that your eyes won't get stressed reading this tutorial. Also the content has not occupied the full width of the page.

In this tutorial I am assuming that you are having a very basic understanding of the flask framework. Please refer the documentation if you don't. You can also create a similar kind of application using Bottle framework, a little bit of code might change.

Okay folks, the briefing is completed, fasten your seat belts and get ready for takeoff ;)

We will create a quiz related to the seven wonders of the world published by New7wonders. It is a multiple choice question type quiz, but we can modify it for fill-in-the-blanks type(After reading the tutorial you can do it for yourself) also.


First we will create some questions and corresponding options that we will be asking the user. Remember that in our code first option will always be the correct answer. But in our web app when the user is presented with the question, the options are shuffled.

origianl_questions = {
 #Format is 'question':[options]
 'Taj Mahal':['Agra','New Delhi','Mumbai','Chennai'],
 'Great Wall of China':['China','Beijing','Shanghai','Tianjin'],
 'Petra':['Ma\'an Governorate','Amman','Zarqa','Jerash'],
 'Machu Picchu':['Cuzco Region','Lima','Piura','Tacna'],
 'Egypt Pyramids':['Giza','Suez','Luxor','Tanta'],
 'Colosseum':['Rome','Milan','Bari','Bologna'],
 'Christ the Redeemer':['Rio de Janeiro','Natal','Olinda','Betim']
}
Next we will write a function which will shuffle the questions and also the options(Even though dictionary is not an ordered sequence we will be shuffling it to make sure that each user is presented with different options): I am saving this program as quiz.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import random, copy
original_questions = {
 #Format is 'question':[options]
 'Taj Mahal':['Agra','New Delhi','Mumbai','Chennai'],
 'Great Wall of China':['China','Beijing','Shanghai','Tianjin'],
 'Petra':['Ma\'an Governorate','Amman','Zarqa','Jerash'],
 'Machu Picchu':['Cuzco Region','Lima','Piura','Tacna'],
 'Egypt Pyramids':['Giza','Suez','Luxor','Tanta'],
 'Colosseum':['Rome','Milan','Bari','Bologna'],
 'Christ the Redeemer':['Rio de Janeiro','Natal','Olinda','Betim']
}

questions = copy.deepcopy(original_questions)


def shuffle(q):
 """
 This function is for shuffling 
 the dictionary elements.
 """
 selected_keys = []
 i = 0
 while i < len(q):
  current_selection = random.choice(q.keys())
  if current_selection not in selected_keys:
   selected_keys.append(current_selection)
   i = i+1
 return selected_keys


questions_shuffled = shuffle(questions)


for i in questions_shuffled:
 random.shuffle(questions[i])
 print('''
 Where is {} located?
 {}
 Correct Answer is: {}
 ''').format(i,questions[i],original_questions[i][0])
When you run the above code in your command prompt or terminal(CLI) you will get output something similar to this:

 Where is Petra located?
 ["Ma'an Governorate", 'Zarqa', 'Jerash', 'Amman']
 Correct Answer is: Ma'an Governorate
 

 Where is Machu Picchu located?
 ['Cuzco Region', 'Lima', 'Tacna', 'Piura']
 Correct Answer is: Cuzco Region
 

 Where is Taj Mahal located?
 ['Mumbai', 'Agra', 'Chennai', 'New Delhi']
 Correct Answer is: Agra
 

 Where is Colosseum located?
 ['Bari', 'Rome', 'Bologna', 'Milan']
 Correct Answer is: Rome
 

 Where is Egypt Pyramids located?
 ['Luxor', 'Suez', 'Giza', 'Tanta']
 Correct Answer is: Giza
 

 Where is Christ the Redeemer located?
 ['Olinda', 'Betim', 'Rio de Janeiro', 'Natal']
 Correct Answer is: Rio de Janeiro
 

 Where is Great Wall of China located?
 ['Beijing', 'Tianjin', 'China', 'Shanghai']
 Correct Answer is: China
Okay now lets see what actually happened?
Line 1: We have imported random, copy
Line2: The questions dictionary which we have created earlier.
Line 13: We have made a deepcopy of the original_questions so that if we make any changes to the duplicated dictionary it won't affect the original dictionary. See this answer to understand more about cloning a list or dictionary in python. Jack answer for How to clone or copy a list in python. The person has written a very good answer and it is a must read for everyone who is a python freak.
Line 16-28: We are creating a function which will take the keys of the dictionary and shuffle them. We can also use random.shuffle(questions.keys()), but as we are here to learn something new, so I have created a function which will shuffle the keys. If you want to know more then see these: How to iterate through a dict in random order, how to get a random value in python dictionary.
Line 31: We are calling our function which we created in line 16 with the parameter of the our new cloned dictionary questions, which will give the output of the keys which have been randomly shuffled.
Line 34-40: Here we are iterating through our newly created dictionary and also the original dictionary to display the output. Even though we are not going to use this block of code directly in our further app we will be using a similar concept to publish our web app. So understanding this block of code is optional.

As we are done with the basic concept in creating the quiz shuffling we will now start concentrating on our web app. 

Creating web app with flask

Lets first start the server. See how to start server from here: Flask - A minimal Application

from flask import Flask

app = Flask(__name__)

@app.route('/')
def quiz():
 return '<h1>Quiz Here</h1>'

if __name__ == '__main__':
 app.run(debug=True)
Now we will extend the app by adding the questions dictionary, the functions which we created and few other things. See the code and try to understand it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from flask import Flask, render_template
import random, copy

app = Flask(__name__)

original_questions = {
 #Format is 'question':[options]
 'Taj Mahal':['Agra','New Delhi','Mumbai','Chennai'],
 'Great Wall of China':['China','Beijing','Shanghai','Tianjin'],
 'Petra':['Ma\'an Governorate','Amman','Zarqa','Jerash'],
 'Machu Picchu':['Cuzco Region','Lima','Piura','Tacna'],
 'Egypt Pyramids':['Giza','Suez','Luxor','Tanta'],
 'Colosseum':['Rome','Milan','Bari','Bologna'],
 'Christ the Redeemer':['Rio de Janeiro','Natal','Olinda','Betim']
}

questions = copy.deepcopy(original_questions)

def shuffle(q):
 """
 This function is for shuffling 
 the dictionary elements.
 """
 selected_keys = []
 i = 0
 while i < len(q):
  current_selection = random.choice(q.keys())
  if current_selection not in selected_keys:
   selected_keys.append(current_selection)
   i = i+1
 return selected_keys

@app.route('/')
def quiz():
 questions_shuffled = shuffle(questions)
 for i in questions.keys():
  random.shuffle(questions[i])
 return render_template('main.html', q = questions_shuffled, o = questions)


if __name__ == '__main__':
 app.run(debug=True)
Don't start the server directly after you have typed the above code. You will have to create templates/main.html(i.e. you will have to create a folder named templates in which you will store the main.html file) which I will teach you after explaining the above code.
I am assuming that you are familiar with the code in the lines 1-32. 
Line 34: I have used @app.route() to bind the URL to the function quiz.
Line 35: We are shuffling the questions and the result which is a list is stored in the variable questions_shuffled.
Line 36: We are starting a for loop and looping through the keys of the questions dictionary. Alternatively you can also loop through the variable questions_shuffled.
Line 37: If you were given a list lets say l=['a','b','c','d']. Now as we know that unlike dictionary which is an unordered sequence, list is ordered. So if you want to shuffle the list randomly we will use random.shuffle(l) which doesn't return anything but all the value of the list are shuffled. For example on my computer

>>> import random
>>> l = ['a','b','c','d']
>>> random.shuffle(l) #Noting is returned but values are shuffled
>>> l
['a', 'c', 'd', 'b']
>>> 
As you can see that the values are shuffled but when the function is called nothing is returned. We have used the same concept in our web application. The for loop will shuffle each and every list of the questions dictionary.
Line 38: The above values are returned to the template which we will see in the next section.
templates/main.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<h1>7 Wonders of the World</h1>
<form action='/quiz' method='POST'>
    <ol>
        {% for i in q %}
            <li>Where is <u>{{i}}</u> located?</li>
            {% for j in o[i] %}
                <input type='radio' value='{{j}}' name='{{i}}' />{{j}}
            {% endfor %}
        {% endfor %}
    </ol>
    <input type="submit" value="submit" />
</form>
Line1-3: These are routine HTML tags. We are using POST method and the action attribute points to '/quiz' the URL which we will have to create.
Line 4: In our python file we have passed q and o as keyword parameters to the template templates/main.html. As we know that the value of q=questions_shuffled, which is a list, we are looping through the list to get the elements of the list.
Line 5: Here {{i}} is replaced by the list element which we get from the for loop.
Line 6: As we know that o=questions, which is a dictionary, we are looping through o[i] where i is the key of the dictionary and thus the output will be a list.
Line 7: We are creating a radio button.
Line 8-12: Pretty simple and I think doesn't need explanation :)
Now run the app(Start the server) and you will see something like this: 
7 wonders of the world, a web app made with flask and python front end photo
Quiz App made with Flask framework and python- Frontend
But If you will answer the questions and press submit then you will find "400 Not Found" Error, This is because we have not yet created the /quiz page. 

We will create the /quiz page and this is the final step. So I will post the whole code for quiz.py. Please don't get annoyed ;)
quiz.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from flask import Flask, render_template, request
import random, copy

app = Flask(__name__)

original_questions = {
 #Format is 'question':[options]
 'Taj Mahal':['Agra','New Delhi','Mumbai','Chennai'],
 'Great Wall of China':['China','Beijing','Shanghai','Tianjin'],
 'Petra':['Ma\'an Governorate','Amman','Zarqa','Jerash'],
 'Machu Picchu':['Cuzco Region','Lima','Piura','Tacna'],
 'Egypt Pyramids':['Giza','Suez','Luxor','Tanta'],
 'Colosseum':['Rome','Milan','Bari','Bologna'],
 'Christ the Redeemer':['Rio de Janeiro','Natal','Olinda','Betim']
}

questions = copy.deepcopy(original_questions)

def shuffle(q):
 """
 This function is for shuffling 
 the dictionary elements.
 """
 selected_keys = []
 i = 0
 while i < len(q):
  current_selection = random.choice(q.keys())
  if current_selection not in selected_keys:
   selected_keys.append(current_selection)
   i = i+1
 return selected_keys

@app.route('/')
def quiz():
 questions_shuffled = shuffle(questions)
 for i in questions.keys():
  random.shuffle(questions[i])
 return render_template('main.html', q = questions_shuffled, o = questions)


@app.route('/quiz', methods=['POST'])
def quiz_answers():
 correct = 0
 for i in questions.keys():
  answered = request.form[i]
  if original_questions[i][0] == answered:
   correct = correct+1
 return '<h1>Correct Answers: <u>'+str(correct)+'</u></h1>'

if __name__ == '__main__':
 app.run(debug=True)
Line 1-38: This is familiar for you as we have discussed it earlier.
Line 41: After the user has submitted the form to /quiz with the POST method the function quiz_answers is called.
Line 45: We are using the for loop to loop through the keys of the questions dictionary. 
Line 46: We are accessing the value user selected using the request which we have imported from flask.
Line 47: If you remember at the starting of this post we used  questions = copy.deepcopy(original_questions). To be simple we are telling python I am making a deepcopy because if I make any changes to the questions dictionary don't apply the changes to original_questions dictionary. Let it be the same as I have defined until I will ask you to change anything for original_questions. And so even if I have made a lot of assignments and computations on questions dictionary the value of the original_questions doesn't change. As we know that the correct answer is the first element in the list of the dictionary we are checking the same.
Line 48-52: These are very easy to understand, I assume :)

templates/main.html doesn't have any change.

Note: If the user submits the form(options) without answering every question(s) he/she may get a bad request 400 page. Try to analyze the reason why it is happening so? Post what you think in the comment box below.

After submitting the form, depending on the number of correct answers given by the user, the web page will look something similar to this:
Submitting the form with python flask
Submitting the form with python flask output
Note: I have written this tutorial keeping in mind that everyone should understand the code behind this easily. But if you want the web app to work more faster you will have to make more changes, so that it will serve more people more efficiently. If you have any code which is simple and effective then please don't hesitate to comment in the comment box below. 'Lets share our knowledge!'

Finally, As always I have tried to write this post in such a way that it is easy for everyone to understand. But If you didn't understand anything or have any doubt then please comment in the comment box below or contact me. I will respond to your question as soon as possible. 

Also comment on how I can improve this post in such a way that it will be easy to understand even for the beginners. Please don't hesitate to comment if you want me to add a topic, or if I have missed any topic or made any typo.

Thank you, Have a nice day :) 

References:
How to clone or copy a list in python
Understanding dict.copy()? Shallow or deep?
Flask Documentation
Random module documentation

Popular posts from this blog

Problem 60 Project Euler Solution with python

Prime pair sets The primes 3, 7, 109, and 673, are quite remarkable. By taking any two primes and concatenating them in any order the result will always be prime. For example, taking 7 and 109, both 7109 and 1097 are prime. The sum of these four primes, 792, represents the lowest sum for a set of four primes with this property. Find the lowest sum for a set of five primes for which any two primes concatenate to produce another prime. This problem is j u st a brute force problem. If you have come here because you don't know the limit upto which you will h ave to gener ate the prime numbers t hen go ahe ad and t r y with 10,000 . When I first start ed solving the problem I chose 1 million(beca use most of the problem s on project E uler have this limit ), but it took very long for the computer to fin d the solution. After searching on the internet then I found many people choosing 10, 000 so I have changed my in put f rom 1 million to 10000 and the output was f ast. He...

Add/Embed SVG to Blogger website

In this post I will tell you my method(trick) of adding SVG images in a blogger website or blog. Before starting , the first thin g I am assu m ing is that you are aware of SVG if you are here. If not please see S calable V ec tor G raphics Recently when I tried to embed a SVG image for a post on pygal, I tried uploading the SVG file and blogger Image uploader came up with an error, because of which I had to find some other way.  SVG File upload Error in Blogger  I started sea rc hing Google " Embed SVG in Blogger " . I found blogorrhea , w h ich gave some i nformatio n on add ing SVG directly as a markup , which worked , but I faced another problem using this . Also th is guy has used lot of Javascript which was confusin g for me, being new to using SVG.   So I first t houg ht of learning on h ow to embed SVG in HTML and t his on e worked out. Actually we can embed SVG in HTML i n following ways: Using Object tag Using Iframe tag Using embed...