Online Quiz App using Flask and PostgreSQL

In the age of Online Education, online examination is a very essential application of EduTech industry. If you have appeared for some kind of online examination or competitive test, you must be wondering how easy or how difficult it is to make such an app. Well, it is not that difficult. In this post we will make you understand how to create a simple Online Quiz App using Flask and PostgreSQL.

Database Table for Online Quiz App

This is a simple application of the level of a minor project for beginners in Flask. So, only one table is used for storing subject wise questions required for the online quiz. Each question is identified by an ID and subject. The last two field in the table are used to apply color to the button representing the question in dashboard and  ‘disabled’ status of the button.

Table questions

Working of the Application

The index page of the application displays blocks with name of the subjects for which quizzes are available. These blocks get the subject names from questions table. The questions table is used to get the distinct subjects which are then passed to the index page using render_tamplate for displaying the clickable blocks of subjects to be chosen by a user.

After a user selects a subject, she is taken to the dashboard where all the questions of the selected subject are displayed as red buttons with question number in the left panel. The first question is displayed in the right panel with the possible answers as radio buttons and a “Save Answer “ button. A user can select any question represented by red color button.

When a user selects an answer from the options and clicks on the “Save Answer” Button that button on left panel for that question becomes green and is disabled. When the user clicks button ”Display Result” from the left panel, the final screen is displayed with a message displaying correct number of answers. “Go to Home Page” button takes back to index page where a user can attempt another quiz

models.py

import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
   
class questions(db.Model):
    __tablename__="questions"
    qid = db.Column(db.Integer, primary_key=True)
    subject =db.Column(db.String, nullable=False)
    question =db.Column(db.String, nullable=False)
    option1 = db.Column(db.String, nullable=True)
    option2 = db.Column(db.String, nullable=True)
    option3 = db.Column(db.String, nullable=True)
    option4 = db.Column(db.String, nullable=True)
    answer = db.Column(db.Integer, nullable=True)
    bcol = db.Column(db.String, nullable=True)   

layout.html

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
    <script src="/static/jsfile.js"></script>
	<title>{% block title %}{% endblock %}</title>

</head>
<body >
	<div class="container">
	<div class="row" style="border-style:solid;padding:20px; text-align: right;">
		<b>{% block pagetitle %}{% endblock %}</b>
	</div>
	<div class="row" style="border:groove;">
	<div class="col-sm-2" style="border-right:groove;">
	{% block questList %}{% endblock %}
	</div>
	<div class="col-sm" style="margin:10%;">
	{% block content %}{% endblock %}
	</div>
	</div>
	
	<div class="row">
	<div id="footer" >
        Copyright 2020 by CSVeda.com
    </div>
	</div>
	</div>
</body>
</html>

jsfile.js (save under static folder of the application)

$( document ).ready(function() {
  $( ".js-click" ).click(function() {
    $( ".js-click" ).css('background', 'green');
  });
});

application.py

from flask import Flask, render_template, jsonify, request,redirect,flash,session
from models import *
folder_name="static"

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "postgresql://postgres:yourpasswrodhere @localhost:5432/OnlineQuiz"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.secret_key = b'hkahs3720/' # use a random string
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
db.init_app(app)

#to change color of question buttons and disable 
def setStatus(qlist):
    qAttempt=[]
    strval=session['result'].strip()
    ans=strval.split(',')
    for i in range(int(len(ans)/2)):
        qAttempt.append(int(ans[2*i]))  
    
    for rw in qlist:
        if rw.qid in qAttempt:
            rw.bcol='green'   # set color
            rw.status='disabled' # disable

@app.route("/")
def index():
    session['result']=""
    subList=questions.query.with_entities(questions.subject).distinct()
    return render_template("index.html",subList=subList)  

@app.route('/quiz', methods=["POST"])
def quiz(): 
    subject= request.form.get('sub')
    questList=questions.query.filter_by(subject=subject).all()
    quest=questions.query.filter_by(subject=subject).first()
    return render_template("dashboard.html",questList=questList, quest=quest) 
    
    
@app.route("/showQuest/<string:subject>,<int:qid>")
def showQuest(subject,qid):
    questList=questions.query.filter_by(subject=subject).all()
    quest=questions.query.filter_by(qid=qid).first()
    setStatus(questList)
    return render_template("dashboard.html",questList=questList, quest=quest)  
    
@app.route('/saveAns',methods=["POST"]) 
def saveAns():
    qid=request.form.get('qid')
    ans=request.form.get('answer')
    sub=request.form.get('subject')
    #update the question id and its selected answer in session variable result
    res=session['result']
    res= res+qid+','+ans+','
    session['result']=res
    questList=questions.query.filter_by(subject=sub).all()
    setStatus(questList)
    quest=questions.query.filter_by(qid=qid).first()
    return render_template("dashboard.html",questList=questList, quest=quest)  
        
@app.route("/logout")
def logout():
    #calculate result
    count=0
    txt=""
    strval=session['result'].strip()
    #split result string by ','
    ans=strval.split(',')
    for i in range(int(len(ans)/2)):
        qd=ans[2*i] # get question id
        qn=ans[2*i+1]  # get the sorresponding answer
        tt=int(qd)
        quest=questions.query.filter_by(qid=tt).first()
        actans=quest.answer
        if actans==int(qn):#compare correct answer in questions table with answer chosen by user
            count=count+1 # increment counter
    txt=txt+'You have '+ str(count)+ ' correct questions out of '+ str(int(len(ans)/2))+ ' questions ' # set the result statement
    return render_template("result.html",txt=txt) 

index.html

{% extends "layout_reg.html" %}

{% block title %}
	The Variety Quiz Portal    
{% endblock %}
{% block pagetitle %}
	Choose a Subject
{% endblock %}
{% block body %}

	<form method="POST" action={{url_for('quiz')}}>
	{% for sb in subList %}
		<input type="submit" name="sub" value='{{sb.subject}}' style="height:150px; width:150px;border: 1px solid; word-wrap:normal; margin:10px; padding: 10px;  box-shadow: 5px 10px 10px #888888;">
	{% endfor %}
	</form>
	
{% endblock %}
Online Quiz App home page

quiz.html

{% block title %}
    Static Files Example
{% endblock %}

{% block body %}
{% for q in questList %}
	<a href="{{url_for('quiz',sub=q.subject,qid=q.qid)}}"><button>{{q.qid}}</button></a>
{% endfor %}	
{% endblock %}

Dashboard.html

{% extends "layout.html" %}

{% block title %}
    User Dashboard!
{% endblock %}

{% block pagetitle %}
 Quiz for :{{quest.subject}}   
{% endblock %}

{% block questList %}
<B> Your Questions</B><br><br>
{% for q in questList %}
	<a href="{{url_for('showQuest',subject=q.subject,qid=q.qid)}}"><button value={{q.qid}} {{q.status}} id={{q.qid}}  style="margin:2px; width:40px; height:40px;background-color:{{q.bcol}}" >{{loop.index}}</button></a>
{% endfor %}	

<br><br><a href="{{url_for('logout')}}"><button>Display Result</button></a>
{% endblock %}

{% block content %}
<form method="POST" action={{url_for('saveAns')}}>
<div class="question"><h5><input type="text" name="qid" value={{quest.qid}} style="border-style:none; width:20px"> <input type="text" name="subject" value='{{quest.subject}}' style="border-style:none"> </h5></div>
<div class="qst">{{quest.question}}</div>
	<div class="opt"><input type="radio" name="answer" value="1"> {{quest.option1}}</div>
	<div class="opt"><input type="radio" name="answer" value="2"> {{quest.option2}}</div>
	<div class="opt"><input type="radio" name="answer" value="3"> {{quest.option3}}</div>
	<div class="opt"><input type="radio" name="answer" value="4"> {{quest.option4}}</div>
	<input type="submit" value="Save Answer"/>
</form>
{% endblock %}

<script>
function colorbtn(bid) {
  document.getElementByid(bid).background-color = "green";
}
</script>
Questions dashboard
Result Page

Possible future enhancements of the App

The Online Quiz App is created in its simplest form. It can be extended by adding following features

  • Adding time constraint  for each question or online Quiz on the whole
  • Making it a User login based application and maintaining the record of users about what quizzes they have attempted along with date of attempt and score.
  • The Online Quiz App in this form does not allow modifying answer for an attempted question. It can be enhanced to allow this.

to learn how to create an application using Flask and PostgreSQl click here

5 Comments

  1. Vinu S said:

    layout.html file is missing

    February 18, 2021
    Reply
    • admin said:

      Thanks for updating. Added in the post.

      February 19, 2021
      Reply
  2. Shobhit said:

    Where is the DB file? I am unable to load the index.html

    March 25, 2021
    Reply
    • admin said:

      This is the table
      CREATE TABLE public.questions
      (
      qid integer NOT NULL DEFAULT nextval(‘questions_qid_seq’::regclass),
      subject character varying(50) COLLATE pg_catalog.”default”,
      question character varying(500) COLLATE pg_catalog.”default”,
      option1 character varying(50) COLLATE pg_catalog.”default”,
      option2 character varying(50) COLLATE pg_catalog.”default”,
      option3 character varying(50) COLLATE pg_catalog.”default”,
      option4 character varying(50) COLLATE pg_catalog.”default”,
      answer smallint,
      bcol character varying(10) COLLATE pg_catalog.”default”,
      status character varying(15) COLLATE pg_catalog.”default”,
      CONSTRAINT questions_pkey PRIMARY KEY (qid)
      )

      You need to create the index.html code given in the post

      March 29, 2021
      Reply
  3. Ahmed said:

    Excellent Tutorial, thanks a lot!

    June 14, 2021
    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *