This is the milestone project that I have created for the “Practical Python” module, which is part of “Full Stack Web Development Course” offered by Code Institute.
This project will be built using logic driven application technologies which have been learnt within the Practical Python module.
- A web application game will ask players to answer questions based on a pictorial or text-based-riddle.
- Players will enter their answer into a text area and submit their answers via a form.
- if a player guesses the answer correctly, They are then asked to answer the next question, If guessed incorrectly, Their incorrect answer is stored and then printed below the riddle, The text area is cleared so they can guess again.
- Multiple players can play the game at the same time within their own browser.
- Players will be identified by their own unique usernames when they log into the game.
- A leader board will show the scores of players ranking them from highest to lowest.
A web application game with questions about world geography, questions for the game have been gathered from the following website sporcle.com, As I am creating a question based game I took the option to not give the player(s) the option of guessing the question again as its based on questions rather than riddles.
- The player has a choice of the following six categories
- Geography 1 ? (18 Questions)
- Geography 2 ? (18 Questions)
- Least Populous Of The Three ? (27 Questions)
- Odd One Out Capitals ? (18 Questions)
- Who Owns These Islands ? (20 Questions)
- Which City has the highest elevation above sea level ? (20 Questions)
- A website with questions about geography to test knowledge on world geography.
- To provide a game that player(s) can log into, and then be able to play by answering a series of questions. If a question is answered incorrectly, the game will respond saying the answer was incorrect and continue to the next question.
- The player's score will be incremented each time the question is answered correctly. When the game has been completed, The player(s) can then see their final score (showing correct & incorrectly answered questions.
- A leaderboard option will show the scores of players ranking them from highest to lowest.
The player must be able to
- Use a unique username to play the game.
- Be able to answer a pictorial or text-based-riddle question by typing in there answer.
- Check there answer and provides feedback to them about their answer.
- Be asked the next question in a series of questions.
- Be able to play the game again if desired.
- A way for users to create a unique username via a login facility.
- A source of questions to ask.
- A text area and submit button for the player to enter their answer via a form.
- A logic driven process to check the player's answer for a question, and provide feedback, if the answer is correct or incorrect.
- Increment the score by one for correct answers.
- Show a leader board with the scores / ranking from other players.
- Allow multiple players to be able to play game at same time.
Located at /static/wireframe/My Practical Python Milestone Project Game Planning.pdf
Located at /static/wireframe/My Practical Python Milestone Project Wireframe.pdf
Technologies used in the construction of this project include,
- Bootstrap is a framework for building responsive, mobile-first websites.
- Cloud9 IDE is a cloud-based integrated development environment (IDE) used as development environment workspace.
- CSS3 is a simple mechanism for adding style (e.g., fonts, colors, spacing) to Web documents.
- CsvJson Online tool to convert CSV or TSV formatted data to JSON , For validating and formatting of JSON Files.
- Flask Microframework is a microframework for Python.
- Font Awesome is a font and icon toolkit.
- Git open source distributed version control system.
- GitHub is a Web-based hosting service for version control using Git.
- Heroku lets you deploy, run and manage applications written in Ruby, Node.js, Java, Python, Clojure, Scala, Go and PHP.
- HTML5: is code that describes web pages.
- JavaScript Javascript is a dynamic computer programming language. And most commonly used as a part of web pages
- Natsort Simple yet flexible natural sorting in Python
- Pencil is an open-source GUI prototyping tool used to create Wireframe mockup.
- Python 3.4.3 is a scripting language. updated to
3.6.6
- Slack Slack is a collaboration hub that connects your organization
- StartBootstrap Free Bootstrap themes and templates.
- Install Flask Framework into your workspace in Cloud9 by typing the following command,
sudo pip3 install Flask
Once installed you will see the following message in the Terminal Window.
Successfully installed Flask Jinja2 itsdangerous click Werkzeug MarkupSafe
Then Type in the following command
sudo pip3 freeze --local
This will show the packages and Versions that Flask has installed,
Click==7.0
Jinja2==2.10
updated to2.10.1
MarkupSafe==1.1.0
updated to1.1.1
Werkzeug==0.14.1
updated to0.15.5
itsdangerous==1.1.0
The following basic Flask application file (run.py) was created in the Cloud9 environment.
import os # Using operating system dependent functionality.
from flask import Flask # From module flask import class Flask.
app = Flask(__name__) # Construct an instance of Flask class for our webapp.
# Route Decorator
@app.route('/') # URL '/' to be handled by main() route handler.
# '/' navigates to localhost:5000.
def hello(): # define function that returns "Hello World".
return "Hello Flask"
if __name__ == '__main__': # __name__ will be equal to "__main__"S
# If conditional statement is satisfied
app.run(host=os.environ.get('IP'), # launches the Flask's built-in development web server.
# host=os.environ.get('IP') gets IP Adress from operating system.
port=int(os.environ.get('PORT')), # os.environ.get('PORT') gets PORT we want to open.
debug=True) # # Enable reloader and debugger.
Run run.py to check that setup and minimal flask application runs ok in Cloud9 environment.
Click Run button and your see the following in the terminal window.
Note: That Warning message is due to debug=True
Your code is running at https://practical-python-project-bennettpe.c9users.io.
Important: use os.getenv(PORT, 8080) as the port and os.getenv(IP, 0.0.0.0) as the host in your scripts!
* Serving Flask app "run" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 381-125-642
Click on the https://practical-python-project-bennettpe.c9users.io. and your should see the following in your browser which confirms everything is running OK.
Hello Flask
I decide to download and use the following free one page bootstrap theme (Creative) from StartBootstrap
-
Right click on the Download Button.
-
Click on Copy link address
-
In Cloud9 environment Create a folder called static.
-
In terminal window type cd static/
-
Then type wget and then paste copied Copy link address as below,
$ wget https://github.com/BlackrockDigital/startbootstrap-creative/archive/gh-pages.zip
-
The following zipped file will be saved in the static directory.
-
Type unzip gh-pages.zip which will UnZip the file.
-
The following directory will be created startbootstrap-creative-gh-pages
-
Type
mv startbootstrap-creative-gh-pages/css startbootstrap-creative-gh-pages/img startbootstrap-creative-gh-pages/js startbootstrap-creative-gh-pages/scss startbootstrap-creative-gh-pages/vendor .
which will Move the files needed into the following file directories CSS,IMG,JS,SCSS,VENDOR -
Type
rm -rf startbootstrap-creative-gh-pages
to delete unwanted files etc. -
Type
rm gh-pages.zip
to deleted unwanted zip file.
Once the Bootstrap theme has been down loaded I needed to edit my templates so it picked up the downloaded theme correctly.
- Go to the github page of the theme (Creative) https://github.com/BlackrockDigital/startbootstrap-creative
- Copy the link and script lines from index.html into base.html and amend directory locations.
- Copied rest of index.html code into base.html
- Created index.htm and added the following code
{% extends 'base.html' %}
{% block content %}
{% endblock %}
The following file directory structure was created in the Cloud9 environment.
├── data # Quiz Data files (Each category contains these txt files)
│ ├── capitals
│ │ ├── capitals_correct_answers.txt
│ │ ├── capitals_incorrect_answers.txt
│ │ ├── capitals_leaders.txt
│ │ └── capitals_username.txt
│ │
│ ├── geography1
│ ├── geography2
│ ├── highest
│ ├── islands
│ └── populous
│
├── static
│ ├── css # Bootstrap files
│ ├── images # Quiz category image files
│ │ ├── Geography1
│ │ ├── Geography2
│ │ ├── Least Populous Of The Three
│ │ ├── Odd One Out Capitals
│ │ ├── Website
│ │ ├── Which City Has The Highest Elevation Above Sea Level
│ │ └── Who Owns These Islands
│ │
│ ├── img # Website category images
│ ├── js # Bootstrap files
│ ├── scss # Bootstrap files
│ ├── vendor # Bootstrap files
│ └── wireframe # Mockup and Planning files
│
├── templates # html templates
│ ├── base.html
│ ├── completed_quiz.html
│ ├── footer.html
│ ├── get_username.html
│ ├── head.html
│ ├── leader.html
│ ├── navbar_quiz.html
│ ├── navbar_username.html
│ ├── quiz.html
│ └── script.html
│
├── READMD.md
├── requirements.txt
├── run.py # Python File
├── test_* # Manual unit testing file(s)
└── Procfile
The Questions for the game where loaded into .json file(s) in the data directory folder
practical-python-project
└── data
├── capitals
│ └── odd_one_out_capitals_questions.json
├── geography1
│ └── geography2_questions.json
├── highest
│ └── which_city_has_the_highest_elevation_above_sea_level_questions.json
├── islands
│ └── who_owns_these_islands_questions.json
└── populous
└── least_populous_of_the_three_questions.json
The .json file(s) were created as follows
- CSV files created with the following Column variables
question_number,question,option_a,option_b,option_c,option_d,answer,answer_letter,image_source
Least Populous Of The Three ?
and Which City has the highest elevation above sea level ?
categories only have three multiple choice options.
- These files(s) were then converted to a .json file(s) and validated using the following tool CsvJson
The project guidelines stated that a Test Driven Development (TDD) approach should be taken to developing the game, But all of my testing / bug fixes was done from a manual testing approach using print() method ,Building some test* python code when I wanted to create a new piece of logic / functionality or had a issue.
-
After styling my templates I ran run.py to make sure it worked OK had a couple of issues, highlighted in (Bugs and Issues > Development Testing > 1.) which were fixed.
-
After building leaders html page I ran test_get_leaders_score.py and noticed the sorted order was incorrect and it showed score of 9 before 10,highlighted in (Bugs and Issues > Development Testing > 2.) which were fixed.
import os
from datetime import datetime
from flask import Flask, flash, json, render_template, redirect, request, url_for
from natsort import natsorted
def sortkey(n):
return n[3]
def get_leaders_file(filename):
score_players = []
with open(filename,"r") as file:
for line in file.readlines():
score_players.append(line)
sorted_players = []
for player in score_players:
tup = (player.split(':')[0],
player.split(':')[1],
player.split(':')[2],
player.split(':')[3].strip())
sorted_players.append(tup)
return natsorted(sorted_players,key=sortkey,reverse=True)[:6] # natural sort , highest first, top 6
sorted_top = get_leaders_file('./data/geography1/geography1_leaders.txt')
print('Scores\t',sorted_top)
sorted_top = get_leaders_file('./data/capitals/capitals_leaders.txt')
print('Scores\t',sorted_top)
- Before building sign_in html page I ran test_get_username.py to test username logic.
import os
from datetime import datetime
from flask import Flask, flash, json, render_template, redirect, request, url_for
from natsort import natsorted
# ------------------------------------#
# Get Username for capitals category #
# ------------------------------------#
def capitals_get_username():
player = 'Paul'
player = player.lower()
with open('./data/capitals/capitals_username.txt', 'r') as username_list:
active_username = username_list.read().splitlines()
if player in active_username:
message = "username taken"
else:
message = "username not taken"
file = open('./data/capitals/capitals_username.txt', 'a')
file.write(player + '\n')
print(message)
capitals_get_username()
- I used open source Bootstrap theme (Creative) by Start Bootstrap so responsive screen issues should be ok.
- The quiz is responsive and works best on a large to medium screen, horizontally displayed.
- On smaller screens, it works fine, but there is a small amount of scrolling on the 'Quiz answer options 1,2,3,4' and the 'Questions correct vs incorrect section' which I am going to see if I can improve.
- I have now fixed the scrolling issue on the 'Questions correct vs incorrect section' by adding
padding-left: 5px;
in CSS file and removingcol-lg-4 col-sm-6 mx-auto
from the completed_quiz.html file
-
Had an issues when styling my templates,
- Images not being displayed
Had not amended directory location for .jpg files changedimg/portfolio
tostatic/img/portfolio
- Font awesome icons not being displayed, Had missing ending " on link.
- Images not being displayed
-
Had an issues when sorting leaders txt files,
- When trying to sort by scores it was ordering score 9 before 10 this was due to python sort order and needed to be changed to natural sort order.
- Installed natsort (see below)
- Updated python code in function
def get_leaders_file(filename):
as follows:
# natural sort , highest first, top 6 return natsorted(sorted_players,key=sortkey,reverse=True)[:6]
Installing Natsort
Install Natsort into your workspace in Cloud9 by typing the following command,
sudo pip3 install natsort
Once installed you will see the following message in the Terminal Window.
Successfully installed natsort
Then Type in the following command
sudo pip3 freeze --local > requirements.txt
This will show the packages and Versions that Natsort has installed, and these have been copied
to the requirements.txt file.
Click==7.0
Flask==1.0.2
Flask-DebugToolbar==0.10.1
Flask-Testing==0.7.1
Jinja2==2.10.1
MarkupSafe==1.1.1
Werkzeug==0.15.5
blinker==1.4
itsdangerous==1.1.0
natsort==7.0.1
-
The refactoring of the *_quiz.html, caused a issue as the Highest & Populous categories have 3 questions instead of 4 so the 4th question were showing up as blank, it was fix as follows:
catnum=Three
for 3 questions andcatman=Four
for 4 questions in the quiz sections of the run.pycatnum = 'Three' # if three questions catnum = 'Four' # if four questions
In the quiz.html code the following was added so the fourth question would be shown if
catnum == Four
{% if catnum == 'Four' %}
<span class="text-dark"> 4. <span class="text-light"> {{ questions[index]["option4"] }} </span>
{% endif %}
- The registering and signing in for usernames , I had not added anything to check for duplicate usernames so added the following code for each category.
- Run.py
This code checks the *_username.txt file to see if the username has previously been entered, If so message is shown in get_username.html to highlight this and you are asked to enter a new username, if you have already registered then you are asked to sign-in, if its a new username then you are taken to the sign-in page.
urlname = '/capitals_get_username'
urlforname = '/capitals_sign_in'
if request.method == 'POST':
with open('./data/capitals/capitals_username.txt', 'r') as username_list:
active_username = username_list.read().splitlines()
player = request.form['get_username'].lower()
if player in active_username:
message = "USERNAME taken"
else:
file = open('./data/capitals/capitals_username.txt', 'a')
file.write(player + '\n')
open('./data/capitals/capitals_correct_answer.txt', 'w').close() # Create correct_answers.txt
open('./data/capitals/capitals_incorrect_answer.txt', 'w').close() # Create incorrect_answers.txt
return redirect(url_for('capitals_sign_in'))
return render_template('get_username.html',category=catname, img_id=imgname, url_id=urlname, urlforname=urlforname, username_message=message, username_player=player)
This is the code for the sign-in section which asked you to enter the username and then takes you to the category questions page. if you input a username which is not in the username text file a message is shown asking if you have already registered, with a link back to the registration section.
# --------------------------------#
# Sign in for capitals category #
# --------------------------------#
@app.route('/capitals_sign_in', methods=['GET', 'POST'])
def capitals_sign_in():
catname = 'Odd One Out Capitals'
imgname = './static/img/portfolio/thumbnails/image-from-rawpixel-id-442135.jpg'
urlname = '/capitals_sign_in'
urlforname = '/capitals_get_username'
if request.method == 'POST':
with open('./data/capitals/capitals_username.txt', 'r') as username_list:
active_username = username_list.read().splitlines()
player = request.form['get_username'].lower()
if player in active_username:
open('./data/capitals/capitals_correct_answer.txt', 'w').close() # Create correct_answers.txt
open('./data/capitals/capitals_incorrect_answer.txt', 'w').close() # Create incorrect_answers.txt
return redirect(url_for('capitals_user', capitals_username = request.form['get_username']))
else:
message = "Sorry, this USERNAME is incorrect. New User?"
return render_template('sign_in.html',category=catname, img_id=imgname, url_id=urlname, urlforname=urlforname, signin_message=message, username_player=player)
-
I had not added code as per project brief for the following:
Multiple players can play the game at the same time within their own browser
, so needed to add Flask Sessions to be able to achieve this,
But apparently there where no video's in section 7. Practical Python for this so used the following you tube video's to understand about flask sessions Flask Session
After discussing the issue in slack Practical Python thread , apparently the flask mini project video's have been amended on the 05/12/18 to include information on sessions.Secret Key and
from flask import session
had to be added to allow flask sessions to be used ,the secret key was generated using the following code
# How to generate a secret key with Python
# via http://flask.pocoo.org/docs/quickstart/
import os
print(os.urandom(16))
-
I have made changes to the get_username processing as follows which reduces the number of HTML templates from one per question category to one for all categories (get_username.html) so I could then delete the unwanted HTML templates.
deleted: templates/capitals_get_username.html deleted: templates/geography1_get_username.html deleted: templates/geography2_get_username.html deleted: templates/highest_get_username.html deleted: templates/islands_get_username.html deleted: templates/populous_get_username.html
Changed from this
username_list.write(request.form['capitals_username']+ '\n')
return redirect(url_for('capitals_user', capitals_username = request.form['capitals_username']))
return render_template('capitals_get_username.html',category=catname, img_id=imgname)
Changed to this
urlname = '/capitals_get_username'
username_list.write(request.form['get_username']+ '\n')
return redirect(url_for('capitals_user', capitals_username = request.form['get_username']))
return render_template('get_username.html',category=catname, img_id=imgname, url_id=urlname)
-
I have made changes to the quiz processing as follows which reduces the number of HTML templates from one per question category to one for all categories (get_usernamequiz.html) so I could then delete the unwanted HTML templates.
deleted: templates/capitals_quiz.html deleted: templates/geography1_quiz.html deleted: templates/geography2_quiz.html deleted: templates/highest_quiz.html deleted: templates/islands_quiz.html deleted: templates/populous_quiz.html
Changed from this
return render_template('capitals_quiz.html',
capitals_questions = questions,
Changed to this
cattext = 'Can you choose the capital that is NOT on the given continent'
return render_template('quiz.html',
category_text = cattext,
capitals_questions = questions,
- I have made changes to the get_username.html so that the
Register Username
&Sign-in
links are on the same html page rather than clicking buttonRegister Username
which if already register would then give you a link to thesign_in.html
page.
Instructions for deploying Python app onto a hosing site: Heroku hosing site
If you have not Signed up to Heroku then you need to start from Signing Up To Heroku , otherwise start from In Heroku (Part One)
-
Signing Up To Heroku
- Sign up to Heroku
- Click on New to Heroku?
Sign Up
at the bottom of the Log in to your account panel. - Complete the Form by entering your details as required and in 'Primary Development Language Box' Enter
Python
. - After completing the Form your will receive a 'Verification Email', which can take up to 15 minutes to receive.
- Open the 'Verification Email' and click on the link and you will be prompted to Enter a password and click
Here To Proceed button
.
-
Heroku Checklist
The following needs to be created
- Create a requirements.txt file.
- Create a Procfile file.
- Create a new Heroku app.
- Create any Config variables.
- Push the code to Heroku.
-
In Heroku (Create app)
- Log in to Heroku
- Select New and Create new app.
- Create App name > Select Choose a region > Then Create app.
-
From Cloud9 (Readying for deployment)
-
Making the run.py file ready for deployment
We need to make the secret key an environment variable and its going to look for a variable called
SECRET
, the 2nd argument is the default value if Flask cannot find the variable called SECRET, so we apply the following changes in the app.secret_key method.
-
app.secret_key = os.getenv("SECRET", "b'\xa0\xba+\xe5\xaa\x8b\xac\x01\x96\x1f<)86\x84\x04")
We need to add these default fallback values to our IP and port in the app.run() method, so we add 0.0.0.0
for the IP and 5000
for the port and then we won't have to set these in Heroku, also we set debug=True
to debug=False
in production.
app.run(host=os.getenv("IP", "0.0.0.0"), port=int(os.getenv("PORT","5000")), debug=false )
-
Type in the following command in the terminal window, which creates the Procfile file (Remember to use a capital P in Procfile).
echo web: python run.py > Procfile
The Procfile file containsweb: python run.py
which tells Heroku to start a process called web and to runpython run.py
when it starts. 3. Type in the following command in the terminal window, which creates the pip requirements.txt file.sudo pip3 freeze --local > requirements.txt
The requirements.txt file contains a list of items to be installed, defining the modules imported to Heroku:
4. Type in the following command in the terminal window which adds all project files:$ git add .
5. Type in the following command in the terminal window to create a default message for the first commit to Heroku:$ git commit -m "Added Procfile for deployment"
6. Type in the following command into the terminal window to run the heroku login command$ heroku login
7. Type into the terminal window your email address and password. 8. Type into the terminal window$ git remote -v
Heroku references have already been added. 9. Now you are logged into Heroku you need to create a new heroku app by typing the following command$ heroku apps:create my-practical-python-project
,once created it will also give us a git address as well. 10. Before we push our app to Heroku we need to set our environment variableshttps://dashboard.heroku.comm/apps
-
In Heroku (Config vars)
- Refresh the Heroku dashboard and you should see your new heroku app
my-practical-python-project
has been created, Click on that and then go toSettings
>Reveal Config Vars
, at the moment we don't have any config vars. - So in the Key field enter
SECRET
and in the Value field enterb'\xa0\xba+\xe5\xaa\x8b\xac\x01\x96\x1f<)86\x84\x04
and then click on Add Button.
- Refresh the Heroku dashboard and you should see your new heroku app
-
In Cloud9 (Build the source)
- Once that's gone we can Push the project to Heroku so we go back into cloud9 terminal window and type the following command
$ git push -u heroku master
this will build the source and then install everything from the requirements.txt file, watch the installation log for error. This has now deployed our app to Heroku.
- Once that's gone we can Push the project to Heroku so we go back into cloud9 terminal window and type the following command
-
In Heroku (Open app)
- Click Open app
Select new tab, [my practical python project](https:// my-practical-python-project.herokuapp.com/)
- Click Open app
- Background image for Top of website taken from pixabay
- The Six background Images used for ‘Quiz Selection Section’ of website where taken from Rawpixel.
- All Images in categories taken from Wikimedia
- I would like to thank my fellow students for their help with my (Advice, Bug fixing, Issues, Queries) via Slack
- I would also like to thank my Code Institute Mentor Chris Zielinski (Display name ckz8780_mentor)